System Call - File Descriptor

Hyungseop Lee·2023년 11월 13일
0

The Linux/Unix Kernel

  1. kernel은 computer가 처음 시작될 때, disk에서 RAM으로 program을 load해준다.
  2. 커널은 여러 프로세스, application 사이에서 CPU, RAM, Disk를 공유할 수 있도록 한다.
  3. application들이 보내는 system call을 처리한다
  4. peripherals(마우스, 키보드, 모니터 등 주변 기기)들을 관리한다.

Linux Operating Systems

  • User spaceKernel space로 나눠져 있다.

System Call

  1. System call은 application이 Linux kernel의 기능을 사용하기 위해 대화하는 방법이다.
  2. System call은 Linux kernel에 대한 programmer’s functional interface 이다.
  3. System call are just like binary routines, call directly into the heart of Linux Kernel.

System Calls VS Function Calls

  • Function Call :

    • 어떤 함수에서 다른 함수를 호출할 때를 Function Call.
    • Kernel Space까지 내려가지 않고, User space에 있는 함수들끼리 호출관계가 있다.
  • System Call :

    • User space가 아니라 Kernel에 있는 루틴으로 처리된 결과가 프로세스에 적용된다.
    • Kernel Space까지 내려가 커널 내부에 존재하는 함수(sub routine)들을 호출하여 커널의 기능을 사용한다.

Linux에는 system call이 몇 개 있을까?

  • C, C++ programming lanuguage에서 unistd.h는 POSIX OS API에 대한 access를 제공하는 header file.
    단일 UNIX 사양의 기반인 POSIX.1 표준에 의해 정의되므로 모든 POSIX 호환 OS 및 compiler에서 사용.
    • in window, vi /usr/include/x86_64-linux-gnu/asm/unistd.h
      in ubuntu,  vi /usr/include/asm-generic/unistd.h
      ➡️ 436개가 있다.

Don’t mix system calls with standard library calls

  • Examples of System Calls :
    getuid(), fork(), exec(), ...

  • C Standard library Calls :
    Is printf() a system call?
    ➡️ No.
    write()라는 system call이 있고, 그것의 사용을 편리하게 만든 C library function이 printf()이다.
    write() 함수는 버퍼에 있는 내용을 그대로 출력하지만, 
    printf() 함수는 표준 출력 모드로 동작하여 종료 문자(\n\0)을 만났을 때 내용을 출력하기 때문에
    write() 과 printf() 을 혼합하여 사용하는 것은 좋지 않다.

POSIX

  • POSIX(Portable Operating System Interface) : UNIX 계열의 운영체제에 사용되는 명령어들(system call)의 표준을 정의.
    (https://en.wikipedia.org/wiki/File:Timeline_of_Unix_families.svg)

    • UNIX 계열 운영체제에 여러 변종들이 많이 나오면서,
      system call들이 서로 조금씩 달라지면서 소스코드 간의 호환성에 문제가 발생하였다..
      이를 해결하기 위해 표준이 등장. POSIX
    • UNIX 운영체제에 관련된 system call, C library 등이 정의되어 있다.
  • POSIX는 여러 개의 chapter로 이루어져 있다.

Handling system call erros

  • System call returns status value indicating whether the call succeeded or failed.
  • all system calls return a value of -1 if an error occurs.
  • Every process contains a global variable called "errno",
    • errno : The header file "error.h" contains a list of the predefined error
    • perror() : a library function that describes system-call errors.
      errno은 번호로 return되기 때문에 어떤 error인지 확인하기 쉽지 않다.
      따라서 perror()로 사람이 읽기 쉽게 설명된 error를 출력해준다.

정리)
System call은 그것이 성공하거나 실패했을 때 status value를 return.
모든 system call은 error가 발생하면, -1 return 한다.
모든 process는 “errno” 이라는 global variable을 갖고 있다.
errno 변수는 우리가 define하는 것이 아니라 <errno.h> 에 define되어 있기 때문에 그냥 사용하면 된다.
perror() 는 errno에 해당하는 사람이 읽을 수 있는 문자열로 return 해주는 function이다.
따라서 errno 변수와 perror() 함수를 사용하기 위해 <errno.h> 헤더파일을 include 해야 한다.

"error.h"

  • vi /usr/include/asm-generic/errno-base.h : 기본적인 errno에 대해 확인

    • vi /usr/include/asm-generic/errno.h : errno에 대한 자세한 정보 확인

man 2 getpid()

  • system call 관련 manual page를 보기 위해서는 man 2

  • ERRORS : 항상 systemcall의 manual page에서 ERRORS를 살펴봐야 한다.

  • CONFORMING TO : 여러 unix 계열이 있는데, 어떤 시스템에서 사용이 가능한지? (소스레벨에서의 호환성. binary 레벨과 다름)

  • SYNOPSIS :
    platform 간 차이(0x32, 0x86)를 없애기 위해 pid_t type을 사용.
    size_t, pid_t같은 자료형들은 sys/types.h 헤더파일에 정의되어 있는 primitive data type이라고 한다.
    운영체제에 따라 data type의 크기와 표현방식이 다르기 때문에 보다 portable한 프로그램을 만들기 위해 사용한다.

Example

pid = 5787
ppid = 2892 = bash id
➡️ "1_pid" 실행파일을 실행시킨 parent process가 bash라는 것을 알 수 있다.

strace

  • strace : 프로그램의 시스템콜이 호출되는 것을 추적하여 보여준다.
    • strace ./실행파일명 :

      ➡️ 1_pid process에서 호출한 system call 정보를 보여줌
      ➡️ printf() 실제로 write()라는 system call로 동작하는 것을 알 수 있다.

In Unix, Everything is a file

  • Unix에서 실제로 모든 것이 파일은 아니고
    CPU, Memory, File, 통신 채널 등이 파일을 통해서 접근이 가능하다.

File Descriptors

  • 파일을 사용하기 위해, 커널에 해당 파일을 사용하겠다는 의사를 표현하고,
    해당 파일에 대한 file descriptor를 얻어야 한다. (Non - negative integer)

  • 보통 descriptor는 open() 이라는 system call을 통해서 얻어와야 하는데,
    그렇지 않고도 이미 open되어 있는 descriptor가 3개가 있다.

    • Standard file descritpors

Open, Close

  • int open(const char* pathname, int flags, mode_t mode)
    • int flags :
    • mode_t mode :

Example

  • open 성공, 실패 :
    • errno = 2, perror() msg 이유 :

Open, Read, Close

  • ssize_t read(int fd, void* buf, size_t nbytes);
    • the number of bytes(nbytes)보다 작은 bytes가 return되는 경우
      1. EOF reached before requested number of bytes have been read
      2. Reading from a terminal device, one "line" read at a time
      3. socket으로 data를 받는데, network가 좋지 않아서 몇 개의 data만 받은 경우
      4. Interruption by a signal

Example

  • open, read, close :

lseek

  • lseek(int fd, off_t offset, int whence)
    fd에 해당하는 filed이 offset을 원하는 위치로 이동시키는 system call.
    whence는 기준점. 기준점을 기준으로 offset만큼 떨어진 곳으로 포인터 이동.
    whence에는 3가지 옵션이 있다.
    1. SEEK_SET : 현재 file의 시작 지점부터 offset만큼 떨어진 곳으로 이동
    2. SEEK_CUR : 현재 file offset으로부터 offset만큼 떨어진 곳으로 이동
    3. SEEK_END : 현재 file의 마지막 지점의 그 다음부터 offset만큼 떨어진 곳으로 이동

Open, Read, Write, Close

Example (cp 명령어 구현)

Example (tee 명령어 구현)

  • tee :
profile
Efficient Deep Learning Model

0개의 댓글