시스템 콜이, 운영체제에서 어떤 과정으로 실행되는지 설명해 주세요.

사용자 프로세스가 시스템 콜을 호출하면 라이브러리 → 트랩 → 커널 모드 전환 → 핸들러 실행 → 복귀의 과정을 거쳐 커널 기능을 수행합니다.

전체 흐름 요약

사용자 프로세스 → C 라이브러리(glibc) → 트랩(syscall 명령어)
→ 커널 모드 전환 → 시스템 콜 디스패처 → sys_call_table 조회
→ 커널 함수 실행 → 결과 반환 → 사용자 모드 복귀

단계별 상세 설명

1단계: 사용자 프로세스가 API 호출

// Java 코드
FileInputStream fis = new FileInputStream("/tmp/test.txt");
int data = fis.read();
Java API (fis.read())
    │
    ▼
JVM native 메서드 (read0())
    │
    ▼
C 라이브러리 (glibc의 read() 함수)

사용자는 시스템 콜을 직접 호출하지 않고, 라이브러리 함수(Wrapper Function)를 통해 간접 호출합니다.

2단계: 레지스터에 인자 세팅

C 라이브러리가 시스템 콜 번호와 인자를 CPU 레지스터에 저장합니다.

read(3, buf, 100) 호출 시:

┌──────────┬─────────────────────────┐
│ 레지스터  │ 값                       │
├──────────┼─────────────────────────┤
│ RAX      │ 0 (sys_read 번호)        │
│ RDI      │ 3 (파일 디스크립터)        │
│ RSI      │ 0x7fff1000 (버퍼 주소)    │
│ RDX      │ 100 (읽을 바이트 수)      │
└──────────┴─────────────────────────┘

3단계: 트랩 발생 (syscall 명령어 실행)

glibc가 syscall 명령어 실행
    │
    ▼
CPU가 소프트웨어 인터럽트(트랩) 발생
    │
    ├── 현재 상태 저장
    │    ├── RCX ← 복귀할 사용자 코드 주소 (RIP)
    │    └── R11 ← 현재 플래그 레지스터 (RFLAGS)
    │
    ├── 모드 비트 1 → 0 (사용자 모드 → 커널 모드)
    │
    └── MSR 레지스터에 저장된 커널 진입점으로 점프
         (LSTAR MSR → system_call 핸들러 주소)

4단계: 커널 진입 — 시스템 콜 디스패처

system_call 핸들러 진입 (arch/x86/entry/entry_64.S)
    │
    ├── ① 사용자 레지스터를 커널 스택에 저장 (pt_regs 구조체)
    │
    ├── ② RAX 값 유효성 검사
    │    ├── RAX < NR_syscalls → 정상 진행
    │    └── RAX >= NR_syscalls → -ENOSYS 에러 반환
    │
    ├── ③ sys_call_table[RAX]에서 커널 함수 주소 조회
    │
    └── ④ 해당 커널 함수 호출
sys_call_table (시스템 콜 테이블)
┌────────┬──────────────────┐
│ RAX=0  │ → sys_read()     │ ← 이것이 호출됨
│ RAX=1  │ → sys_write()    │
│ RAX=2  │ → sys_open()     │
│ RAX=3  │ → sys_close()    │
│  ...   │    ...           │
└────────┴──────────────────┘

5단계: 커널 함수 실행

sys_read(fd=3, buf=0x7fff1000, size=100)
    │
    ├── ① 파일 디스크립터 테이블에서 fd=3에 해당하는 파일 객체 조회
    │
    ├── ② 권한 검사 (읽기 권한이 있는지)
    │
    ├── ③ 파일 시스템 계층 호출 (VFS → ext4 등)
    │
    ├── ④ 페이지 캐시 확인
    │    ├── 캐시 Hit → 캐시에서 바로 복사
    │    └── 캐시 Miss → 디스크 I/O 요청
    │         ├── 디바이스 드라이버에 요청
    │         ├── DMA로 디스크 → 메모리 전송
    │         └── 디스크 인터럽트로 완료 통보
    │
    └── ⑤ 사용자 버퍼(0x7fff1000)에 데이터 복사
         (copy_to_user 함수 사용)

6단계: 결과 반환 및 사용자 모드 복귀

커널 함수 완료
    │
    ├── ① 반환값을 RAX에 저장
    │    ├── 성공: 읽은 바이트 수 (예: 100)
    │    └── 실패: 음수 에러 코드 (예: -ENOENT)
    │
    ├── ② 커널 스택에서 사용자 레지스터 복원
    │
    ├── ③ sysret 명령어 실행
    │    ├── RCX → RIP (사용자 코드 복귀 주소)
    │    └── R11 → RFLAGS (플래그 복원)
    │
    └── ④ 모드 비트 0 → 1 (커널 모드 → 사용자 모드)
사용자 프로세스가 RAX에서 결과 수신
    │
    ├── RAX >= 0 → 성공, 값을 그대로 반환
    └── RAX < 0  → glibc가 errno 설정 후 -1 반환

전체 과정 한눈에 보기

┌─────────────────── 사용자 모드 ────────────────────┐
│                                                   │
│  ① Java: fis.read()                               │
│  ② JVM native: read0()                            │
│  ③ glibc: read(3, buf, 100)                       │
│  ④ 레지스터 세팅: RAX=0, RDI=3, RSI=buf, RDX=100   │
│  ⑤ syscall 명령어 실행                              │
│                     │                              │
├──────────────── 트랩 (모드 전환) ───────────────────┤
│                     ▼                              │
│  ⑥ CPU: 상태 저장 (RCX=복귀주소, R11=RFLAGS)        │
│  ⑦ CPU: 커널 진입점으로 점프 (LSTAR MSR)             │
│  ⑧ 디스패처: 사용자 레지스터 커널 스택에 저장          │
│  ⑨ 디스패처: sys_call_table[0] → sys_read() 호출    │
│  ⑩ sys_read(): 파일 시스템 → 디스크 I/O → 데이터 복사 │
│  ⑪ RAX = 100 (읽은 바이트 수)                       │
│  ⑫ 사용자 레지스터 복원                              │
│  ⑬ sysret 명령어 실행                               │
│                     │                              │
├──────────────── 모드 복귀 ─────────────────────────┤
│                     ▼                              │
│  ⑭ glibc: RAX 값 확인 → 결과 반환                   │
│  ⑮ JVM: Java 변수에 결과 저장                        │
│  ⑯ Java: data = 읽은 값                            │
│                                                   │
└───────────────────────────────────────────────────┘

핵심 정리

단계위치핵심 동작
① API 호출사용자 모드Java API → JVM → glibc
② 레지스터 세팅사용자 모드RAX에 시스템 콜 번호, RDI~R9에 인자
③ 트랩 발생모드 전환syscall 명령어 → 상태 저장 → 커널 진입
④ 디스패처커널 모드sys_call_table[RAX]에서 핸들러 조회
⑤ 커널 함수 실행커널 모드실제 작업 수행 (I/O, 프로세스 관리 등)
⑥ 결과 반환모드 복귀RAX에 결과 저장 → sysret → 사용자 모드

CSLOG에서 보기