IT_Study/Operating System

Operating System (11) : System Programming, SystemCall, File Descriptor

__Vivacé__ 2023. 4. 3. 16:26

시스템 프로그래밍

 

시스템 (System)

구성 요소들이 상호작용하는 집합체를 의미

 

컴퓨팅 시스템 (Computing System)

CPU, 기억장치 , 입출력장치 등이 상호작용하는 집합체

 

구성

1. Hardware

    - CPU (중앙 처리 장치) : 명령어를 해석하고 실행하여 데이터를 처리

    - Memory (기억 장치) : 실행 중인 프로그램이나 데이터를 일시적으로 저장

    - Peripheral (주변 장치) : CPU와 Memory 외 모든 입/출력 장치

        - 저장장치 : 데이터를 영구적으로 저장할 수 있는 공간

        - Graphic

        - 입/출력 장치

        - LAN Card : 컴퓨터를 네트워크에 연결하여 데이터를 주고받게 해줌

        - USB Interface 등

 

2. Software

    - Application Level : 최종 사용자를 위해 특정 작업을 수행하도록 설계된 소프트웨어

        - ex) 워드프로세서, 그래픽 편집기, 게임 등

    - Middleware Level : 운영체제와 응용 소프트웨어 사이에서 중개 역할을 수행하는 소프트웨어

        - ex) 데이터베이스, 웹 서버, API 등

            *API(Application Programming Interface) : App level S/W가 사용하게끔 만든 함수

    - Low Level : 하드웨어 또는 운영체제와 직접적으로 상호작용하는 소프트웨어

        - ex) 리눅스 커널, 장치 드라이버, 펌웨어 등

 

임베디드 시스템 (Embedded System)

일반적인 컴퓨터 시스템과는 다르게, 특정 목적을 수행하는데 특화된 컴퓨터 시스템

 

 

 

https://scslab-intern.gitbooks.io/linux-kernel-hacking/content/chapter05.html

 

 


용어 정리 해보기

1. UFS
2. 인포테인먼트
3. ROS
4. 코봇
5. ECU

1. Kernel 이란
2. Interface 는 무엇인가
3. API 는 무엇인가
4. System Call 이란
5. OS 는 무엇이고 , Firmware 는 무엇인가
6. Memory 란
7. NVM
8. POSIX
9. RTOS

 

SystemCall로 Application 개발하기

 

POSIX (Portable Operating System Interface)

운영체제의 표준을 정의하는 것을 목적으로 하는 국제 표준

    - IEEE (Institute of Electrical and Electronics Engineers)에서 제정

        *IEEE : 전기 및 전자 기술의 국제 표준을 제정하는 단체

    - 다양한 운영체제에서 같은 기능을 제공하도록 하는 API를 정의

 

System Call 과 POSIX 차이

1. POSIX : OS 가 App 에게 제공하는 API 들의 표준
2. System Call : 리눅스 가 App 개발자들을 위해 제공하는 API

따라서, System Call 에는 POSIX API 호환도 있고 아닌것도 있다.

 

임베디드 시스템 과거 현재
APP 전용 OS에 맞는 맞춤 제작 필요 System Call API (in Linux)
POSIX API 사용 (리눅스 / RTOS)
OS 맞춤 제작 필요 자체 제작 / Firmware 개발
기존 만들어진 OS (RTOS, Linux, Android) 이용
H/W 맞춤 제작 필요 맞춤 제작 필요

→ OS를 쓰는 임베디드 시스템의 Application 개발자들은 POSIX API를 쓰면서 App 을 만들어낸다

 


System Call

App은 OS에 접근할 수 있는 권한이 없기 때문에, Kernel에게 request를 해야 한다. 이러한 기능을 해주는 API를 System Call이라고 함

 

파일 입출력 / 모니터 출력 / 키보드 입력 등이 필요할 때, App은 권한이 없기 때문에, System Call을 사용해야 한다.

 

 

파일 처리 API

1. open() : fopen이 사용하는 syscall

    - 파일 열기

    - fopen : Syscall 을 포함한 Wrapper Function  |  High Level API라고 부름  |  속도도 open보다 빠르다 함

2. read() : fscanf가 사용하는 syscall

    - 파일 읽기 : null 문자를 고려해서 1000이 아닌 999칸을 읽는다.

3. write() : fprintf가 사용하는 syscall

 

4. close() : fclose가 사용하는 syscall

    - 파일 닫기

    - 파일 디스크립터만 적어주면 된다!

 

 

Man page 보여 Header file 추가하기

1. vim에서 API 이름을 적고, command mode로 변경

2. [page 숫자] + SHIFT + K

    - 1 : Linux Shell Command

    - 2 : System Call

    - 3 : Linux Library

3. 필요한 Header File을 복사해서 파일에 추가

 

 

 

 

File Descriptor (Number)

프로그램이 파일에 접근할 때, OS가 부여해주는 번호를 의미, 이 번호로 파일을 관리함

open()의 parameter 살펴보기

1st argument : path
2nd argument : flag
    - 필수 옵션
        - O_RDONLY : 읽기 전용으로 파일 열기
        - O_WRONLY : 쓰기 전용으로 파일 열기
        - O_RDWR : 읽기 및 쓰기 모드로 파일 열기
    - 추가 옵션 (or 연산 사용)
        - O_CREAT : 없으면 새로 생성
        - O_APPEND : 덧붙이기
        - O_TRUNC : 파일 내용 제거 후 사용

3rd argument : mode
    - Create 할 때 파일 권한, 0xxx값을 넣어주면 된다 (8진수)  |  Create 외에는 그냥 default 값(0)이 들어감
        - 0777 : rwxrwxrwx
        - 0644 : rw-r--r--
    - Other의 권한은 명령어가 안 먹힐 때가 많음 (보안상 권한 문제가 발생한다고 함)
    - Open할 때 파일권한은 없어도 된다.

---

open은 error가 잦다.
    - fd는 항상 정수로 취급, 음수일 때는 error를 뜻함
    - 따라서, error 처리에 대한 로그메세지를 남길 수 있음

 


Error 메세지 매크로

해당 메세지로 에러 난 위치 확인 가능

 1. __FILE__ : 에러 file 이름

 2. __LINE__ : 에러 line 번호

 3. exit(1) : return 값 1, 오류로 인한 종료

int main(){
    int fd = open("./test.txt", 0_RDONLY, 0644);
    if (fd < 0){
        printf("[%s :: %d] FILE OPEN ERROR\n", __FILE__, __LINE__);
        exit(1);
    }
}

 


Read 다루기

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/stat.h>
  5 #include <fcntl.h>
  6 #include <stdlib.h>
  7
  8 int main(){
  9     int fd = open("./text.txt", O_RDONLY, 0);
 10
 11     if (fd<0){
 12         printf("OPEN ERROR\n");
 13         exit(1);
 14     }
 15
 16     while(1){
 17         char buf[11] = {0};
 18         ssize_t i = read(fd, buf, 10);
 19
 20         printf("==========READ==========\n");
 21         printf("%s\n", buf);
 22         printf("==========READ==========\n");
 23         printf("%ld\n", i);
 24
 25         if (i < 10) break;
 26
 27     }
 28
 29     close(fd);
 30
 31 }

ssize_t : signed size_t를 의미  |  read의 return 값은 항상 ssize_t이다.

 

파일에서 읽어온 size를 기억하고 있기 때문에, 원하는 byte만큼 출력을 조절할 수 있다.

 


구조체 다루기

 

1. 저장하기

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/stat.h>
  5 #include <fcntl.h>
  6 #include <stdlib.h>
  7
  8 struct Node{
  9     int x, y;
 10     int lev;
 11 };
 12
 13 int main(){
 14
 15     int fd = open("./text.txt", O_WRONLY | O_TRUNC, 0);
 16
 17     if (fd<0){
 18         printf("FILE OPEN ERROR\n");
 19         exit(1);
 20     }
 21
 22     struct Node temp = {3, 5, 99};
 23
 24     write(fd, (void*)&temp, sizeof(temp));
 25
 26
 27     close(fd);
 28
 29     return 0;
 30 }

 

깨져 보이지만, 정확한 데이터가 저장되어있다.

 

2. 가져오기

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include <stdlib.h>
  6 #include <unistd.h>
  7
  8 struct Node{
  9     int x, y;
 10     int lev;
 11 };
 12
 13 int main(){
 14
 15     int fd = open("./text.txt", O_RDONLY, 0);
 16
 17     if (fd < 0){
 18         printf("ERROR OCCURED\n");
 19         exit(1);
 20     }
 21
 22
 23     struct Node temp;
 24
 25     read(fd, &temp, sizeof(temp));
 26
 27     printf("%d %d %d\n", temp.x, temp.y, temp.lev);
 28
 29
 30
 31     return 0;
 32 }

잘 가져와지는 것을 볼 수 있다

 


lseek

기준점에서 offset만큼 떨어져 있는 곳으로 파일 offset 으로 옮기는 시스템콜

off_t lseek (int fd, off_t offset, int whence);
    - off_t : 정수 (long), 음수 가능
    - offset : 떨어져있는 곳
    - whence : 기준    
        - SEEK_CUR : 현재 위치에서부터
        - SEEK_SET : 시작 지점에서부터
        - SEEK_END : 끝 지점에서부터

활용 예제

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/stat.h>
  5 #include <fcntl.h>
  6 #include <stdlib.h>
  7 #include <string.h>
  8
  9 int main(){
 10     int fd = open("./text.txt", O_RDONLY, 0);
 11
 12     if (fd<0){
 13         printf("OPEN ERROR\n");
 14         exit(1);
 15     }
 16
 17     char buf[11] = {0};
 18     int n;
 19
 20     n = lseek(fd, 0, SEEK_CUR);  // 현재 위치에서 0만큼 이동한 값을 n에 저장
 21     printf("%d\n", n);
 22     read(fd, buf, 5);  // 현재 위치부터 5 size만큼의 문자열을 buf에 저장
 23     printf("%s\n", buf);
 24
 25     memset(buf, 0, 10);  // buf를 NULL 문자열로 초기화
 26
 27     n = lseek(fd, -6, SEEK_END);  // -6인 것 주의!
 28     printf("%d\n", n);
 29     read(fd, buf, 5);
 30     printf("%s\n", buf);
 31
 32     lseek(fd, 0, SEEK_SET);
 33
 34     while(1){
 35         memset(buf, 0, 10);
 36
 37         read(fd, buf, 3);
 38
 39         printf("%s\n", buf);
 40
 41         if (strcmp(buf, "GHI") == 0){
 42             n = lseek(fd, 5, SEEK_CUR);
 43             read(fd, buf, 3);
 44             printf("%s\n", buf);
 45             break;
 46         }
 47
 48     }
 49
 50     close(fd);
 51
 52     return 0;
 53 }

결과가 잘 나오는 것을 확인

 


Write

RDWR : 덮어쓰기

 

 

Del 키 덮어쓰기

0x7F는 ASCII CODE 상 지우기 문자이므로, 출력 시 앞글자가 지워진다/

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/types.h>
  4 #include <sys/stat.h>
  5 #include <fcntl.h>
  6 #include <string.h>
  7 #include <stdlib.h>
  8
  9 int main(){
 10     int fd = open("./text.txt", O_RDWR, 0666);
 11
 12     if (fd<0){
 13         printf("FILE OPEN ERROR\n");
 14         exit(1);
 15     }
 16
 17     lseek(fd, 1, SEEK_SET);
 18
 19     char buf[10] = {0x7F};
 20     int n = strlen(buf);
 21     write(fd, buf, n);  // buf 내용에서 n size 만큼 fd에 기록
 22
 23     lseek(fd, 3, SEEK_SET);
 24     write(fd, buf, n);
 25
 26     close(fd);
 27
 28     return 0;
 29 }