운영체제

시스템 콜(system call)의 동작 과정

joonior 2025. 3. 10. 00:25

1. 시스템 콜이란 무엇인가?

기본적으로 사용자 어플리케이션은 컴퓨터의 자원에 직접 접근할 수 없다. 컴퓨터의 자원을 어떻게 쓸지에 대해서 관린해주는 것이 운영체제이고, 운영체제는 흔히 우리가 많이 사용하는 Windows, Mac OS, Linux 등이 있다.

 

시스템 콜이란,

"사용자가 특정 자원 (CPU, 메모리, 디스크 등)에 직접 접근할 수 없기 때문에, 운영체제를 통해 대신 요청하는 것이다."

 

2. 시스템 콜의 종류

시스템 콜은 크게 프로세스 관리, 파일 관리, 메모리 관리, 네트워크 통신 등 다양한 기능으로 분류할 수 있다.

 

종류 시스템 콜 설명
1. 프로세스 관리 (Process Management) fork() 새로운 프로세스를 생성 (부모-자식 프로세스)
execve() 실행 중인 프로세스를 새로운 프로그램으로 교체
exit() 현재 프로세스 종료
wait() 자식 프로세스가 종료될 때까지 부모 프로세스 대기
getpid() 현재 프로세스의 ID(PID) 가져오기
getppid() 부모 프로세스의 ID 가져오기
2. 파일 시스템 관리 (File System Management) open() 파일 열기
close() 파일 닫기
read() 파일에서 데이터 읽기
write() 파일에 데이터 쓰기
lseek() 파일의 읽기/쓰기 위치 이동
unlink() 파일 삭제
stat() 파일 정보 가져오기 (크기, 권한 등)
3. 메모리 관리 (Memory Management) brk() 힙 메모리 크기 변경
mmap() 파일 또는 메모리를 매핑하여 공유 메모리 사용
munmap() mmap()으로 매핑된 메모리 해제
sbrk() 힙 영역 크기 변경 (과거에 많이 사용됨)
4. 네트워크 및 IPC (Inter-Process Communication) socket() 소켓 생성 (네트워크 통신 시작)
bind() 소켓과 특정 IP/포트를 연결
listen() 클라이언트 연결을 대기
accept() 클라이언트의 연결 요청 수락
connect() 서버에 연결 요청
send(), recv() 네트워크 데이터를 송수신
pipe() 부모-자식 프로세스 간 파이프(IPC) 생성
shmget() 공유 메모리 생성
shmat() 공유 메모리를 현재 프로세스에 연결
5. 시스템 정보 및 관리 (System Control) gettimeofday() 현재 시간 가져오기
settimeofday() 시스템 시간 설정
uname() OS 및 시스템 정보 가져오기
sysinfo() 시스템 메모리, 부팅 시간 등 정보 가져오기
6. 사용자 및 권한 관리 (User & Permission Management) getuid() 현재 사용자 ID 가져오기
setuid() 프로세스의 사용자 ID 변경
chmod() 파일 권한 변경
chown() 파일 소유자 변경

 

 

3. 시스템 콜의 동작 과정

  1. 사용자 애플리케이션이 시스템 콜을 요청한다.
    • ex. 프로그램이 파일을 연다. 네트워크 패킷을 수신하다.
  2. C 라이브러리에서 제공하는 래퍼(wrapper) 함수 호출한다.
     
    #include <fcntl.h>
    #include <unistd.h>
    
    int main() {
        int fd = open("test.txt", O_RDONLY);
        if (fd == -1) {
            perror("open failed");
            return 1;
        }
        close(fd);
        return 0;
    }
     가령, 위 코드에서 open은 아래와 glib 내에 다음과 같이 구현되어 있음
    int open(const char *pathname, int flags, ...) {
        va_list args;
        mode_t mode = 0;
    
        if (flags & O_CREAT) {
            va_start(args, flags);
            mode = va_arg(args, mode_t);
            va_end(args);
        }
    
        return syscall(SYS_open, pathname, flags, mode);
    }

    우리는 syscall을 직접 사용해서 파일을 open하거나, 어셈블리어를 통해 호출할 수도 있다.
    #include <sys/syscall.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    int main() {
        int fd = syscall(2, "test.txt", O_RDONLY);
        if (fd == -1) {
            perror("open failed");
            return 1;
        }
        syscall(3, fd);
        return 0;
    }
    mov rax, 2         ; SYS_open (2번 시스템 콜)
    mov rdi, filename  ; 파일 경로
    mov rsi, O_RDONLY  ; 읽기 전용 플래그
    syscall            ; 시스템 콜 실행

    다만, 이렇게 직접 호출하는건 이식성이 떨어지며, 직관적이지 못하다.
    • 이식성이 떨어지는 이유: 운영체제마다 CPU 아키텍쳐마다 system call의 번호가 다르다. 가령 Linux (x86_64)에서 open은 2번인 반면, Linux (ARM), macOS (x86_64)에서는 5번이다.
    • 사실, 이 부분은 SYS_open, SYS_close 등을 사용하면 해결할 수 있다.
    • 다만, 리눅스 최신 버전에서는 open의 구현이 보안 이슈로 인해 내부 구현이 openat으로 변경되었고, 이런 점들을 반영하지 못한다.
  3.  커널 모드로 전환한다.
    • 시스템 콜은 커널에서 처리된다.
    • syscall(SYS_openat, dirfd, pathname, flags, mode); 등 시스템 콜 함수를 호출하면, 이 함수가 처리되는데 아키텍쳐에 따라 구현이 상이하다.
      • 현대 시스템에서는 syscall이라는 instruction이 존재한다.(x86_64기준)
        mov rax, 257      ; SYS_openat (시스템 콜 번호)
        mov rdi, -100     ; AT_FDCWD (디렉터리 FD)
        mov rsi, pathname ; 파일 경로
        mov rdx, flags    ; 파일 열기 옵션
        mov r10, mode     ; 파일 모드
        syscall           ; 시스템 콜 실행
         x86에는 sysenter가 ARM에서는 svc(supervisor call)가 있다.
      • 과거 시스템에서는 소프트웨어 인터럽트를 통해서 커널 모드로 전환했다.
        mov eax, 5        ; SYS_open (open 시스템 콜 번호)
        mov ebx, pathname ; 첫 번째 인자 (파일 경로)
        mov ecx, flags    ; 두 번째 인자 (파일 열기 옵션)
        mov edx, mode     ; 세 번째 인자 (파일 모드)
        int 0x80          ; 시스템 콜 호출
        이게 흔히 우리가 운영체제 수업시간에 배우는 방식일거다 (혹시 바뀌었나)
  4. 커널은 시스템 콜 번호를 확인하고, 매핑되어 있는 시스템 콜 핸들러를 호출한다.
  5. 커널은 작업 수행을 한 후에 결과를 반환한다.
    • 일반적으로 작업이 성공한 경우에는 0을 리턴하고 오류가 발생하면 오류에 맞는 정수(음수)를 리턴한다.
  6. 사용자 모드로 다시 복귀한다.
    • sysret (x86_64), sysexit (x86), iret (x86 구형) eret (ARM) 등 명령어를 통해 복귀한다.
    • CPU가 저장된 사용자 모드의 EIP, ESP로 복귀한다. (기타 레지터도 복원)