시스템 콜이 무엇인지 설명해 주세요.

사용자 프로세스가 커널의 기능(파일 I/O, 프로세스 생성, 메모리 할당 등)을 안전하게 사용하기 위해 운영체제에 요청하는 프로그래밍 인터페이스입니다.

왜 필요한가?

운영체제는 사용자 모드(User Mode)와 커널 모드(Kernel Mode)를 분리하여 시스템을 보호합니다. 일반 프로세스는 하드웨어나 커널 자원에 직접 접근할 수 없으므로, 시스템 콜을 통해 커널에 작업을 위임합니다. 만약 시스템 콜 없이 직접 접근을 허용하면 악의적인 프로그램이 디스크를 포맷하거나 다른 프로세스의 메모리를 훼손하는 등의 심각한 문제가 발생할 수 있습니다.

동작 흐름 (상세)

① 사용자 프로세스가 라이브러리 함수 호출 (예: read())
② 라이브러리가 시스템 콜 번호를 레지스터에 저장
③ 소프트웨어 인터럽트(Trap) 발생 (x86: int 0x80 또는 syscall 명령어)
④ CPU가 커널 모드로 전환 (모드 비트 1 → 0)
⑤ IDT/시스템 콜 테이블에서 해당 번호의 핸들러 탐색
⑥ 커널이 요청된 작업 수행
⑦ 결과를 레지스터에 저장하고 사용자 모드로 복귀 (모드 비트 0 → 1)
⑧ 라이브러리가 결과를 사용자 프로세스에 반환
┌─────────────────────────────────────────────┐
│              사용자 모드 (User Mode)           │
│                                             │
│   응용 프로그램  →  C 라이브러리 (glibc)       │
│                      │ read() 호출           │
│                      ▼                       │
│              syscall 명령어 실행              │
├─────────────── Trap (모드 전환) ──────────────┤
│              커널 모드 (Kernel Mode)           │
│                      │                       │
│         시스템 콜 테이블에서 핸들러 조회         │
│                      │                       │
│              sys_read() 실행                 │
│                      │                       │
│              결과 반환 + 모드 복귀              │
└─────────────────────────────────────────────┘

시스템 콜 테이블

커널은 각 시스템 콜에 고유 번호를 부여하고 테이블로 관리합니다.

번호 (x86-64 Linux)이름설명
0sys_read파일 읽기
1sys_write파일 쓰기
2sys_open파일 열기
39sys_getpid현재 PID 조회
57sys_fork프로세스 복제
59sys_execve프로그램 실행
60sys_exit프로세스 종료

주요 분류

분류예시설명
프로세스 제어fork(), exec(), exit(), wait()프로세스 생성·실행·종료·대기
파일 관리open(), read(), write(), close()파일 읽기·쓰기·열기·닫기
장치 관리ioctl(), read(), write()디바이스 제어
정보 유지getpid(), time(), gettimeofday()시스템·프로세스 정보 조회
통신socket(), send(), recv(), pipe()프로세스 간 통신(IPC)
메모리 관리mmap(), brk(), munmap()메모리 매핑·할당·해제

사용자 모드 vs 커널 모드

항목사용자 모드커널 모드
권한제한된 명령어만 실행모든 명령어 실행 가능
메모리 접근자신의 주소 공간만전체 메모리 접근 가능
하드웨어 접근불가 (시스템 콜 필요)직접 접근 가능
모드 비트10
전환 계기시스템 콜, 인터럽트, 예외-
실행 주체응용 프로그램OS 커널

Java에서의 시스템 콜

Java는 JVM 위에서 동작하므로 시스템 콜을 직접 호출하지 않지만, JVM의 네이티브 메서드를 통해 내부적으로 시스템 콜이 발생합니다.

// ── 파일 I/O ──
// 내부적으로 open() → read() → close() 시스템 콜 호출
FileInputStream fis = new FileInputStream("/tmp/test.txt");
int data = fis.read();   // sys_read
fis.close();              // sys_close

// ── 프로세스 생성 ──
// 내부적으로 fork() + execve() 시스템 콜 호출
Process p = Runtime.getRuntime().exec("ls -al");

// ── 네트워크 통신 ──
// 내부적으로 socket() → connect() → write() → read() 시스템 콜 호출
Socket socket = new Socket("example.com", 80);
OutputStream out = socket.getOutputStream();
out.write("GET / HTTP/1.1\r\n".getBytes());

// ── 메모리 ──
// JVM이 내부적으로 mmap() 또는 brk()를 통해 힙 메모리 확보
byte[] largeArray = new byte[1024 * 1024]; // 1MB 할당
Java 코드 → JVM (Java Native Interface) → OS 라이브러리 (glibc) → 시스템 콜 → 커널

시스템 콜의 오버헤드

시스템 콜은 모드 전환이 수반되므로 일반 함수 호출보다 비용이 큽니다.

항목일반 함수 호출시스템 콜
모드 전환없음사용자 → 커널 → 사용자
컨텍스트 저장일부 레지스터전체 레지스터 + 스택 전환
소요 시간~수 ns~수백 ns ~ 수 μs
캐시 영향거의 없음TLB/캐시 플러시 가능

이 때문에 성능이 중요한 애플리케이션에서는 시스템 콜 횟수를 줄이는 것이 중요합니다. 예를 들어 BufferedReader나 BufferedOutputStream 같은 버퍼링 기법은 여러 번의 read()/write() 시스템 콜을 하나로 모아 오버헤드를 줄입니다.

// Bad: 1바이트마다 시스템 콜 발생
FileInputStream fis = new FileInputStream("file.txt");
while ((b = fis.read()) != -1) { /* ... */ }

// Good: 버퍼 단위로 시스템 콜 발생 (기본 8KB 버퍼)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.txt"));
while ((b = bis.read()) != -1) { /* ... */ }

CSLOG에서 보기