본문 바로가기
Research/Study

[2025.06.01] System call 호출 흐름

by how-are-you 2025. 6. 1.

유저 스페이스에서 시스템 콜(System Call)을 발생시키면, 커널의 기능을 호출하기 위한 명확하고 고정된 흐름이 진행됩니다. 이 과정은 보안과 안정성을 보장하면서 커널의 리소스를 사용할 수 있도록 하는 핵심 메커니즘입니다. 아래에 그 전체 과정을 구조적으로 정리합니다.


🧭 시스템 콜 호출 흐름 요약 (x86-64 기준)

🔹 1. 유저 스페이스 애플리케이션의 시스템 콜 요청

write(fd, buf, len); // 예: 파일에 데이터 쓰기
  • C 라이브러리(glibc)는 내부적으로 시스템 콜 번호와 인자들을 CPU 레지스터에 설정한 후 syscall 어셈블리 명령어를 사용하여 커널로 진입합니다.

🔹 2. 레지스터에 인자와 시스템 콜 번호 설정

  • Linux x86-64 기준 레지스터 할당:
  • 역할레지스터
    시스템 콜 번호 RAX
    첫 번째 인자 RDI
    두 번째 인자 RSI
    세 번째 인자 RDX
    네 번째 인자 R10
    다섯 번째 인자 R8
    여섯 번째 인자 R9
     

🔹 3. CPU 명령어 실행 (syscall)

  • 이 명령은 사용자 모드 → 커널 모드로 전환
  • CPU는 **MSR(Machine Specific Register)**에 설정된 syscall 진입점을 통해 커널의 시스템 콜 처리 루틴에 진입

🔹 4. 커널의 시스템 콜 진입 루틴 실행

  • 커널은 시스템 콜 번호(RAX)를 읽고, 내부 시스템 콜 테이블(sys_call_table)에서 해당 함수 포인터를 찾음
  • 예: sys_write 함수 호출

🔹 5. 커널 함수 수행

  • 커널 공간에서 시스템 콜에 해당하는 함수가 실행되며, 하드웨어나 커널 자원을 접근
  • 예: 파일 디스크립터를 통해 커널 내부 버퍼에 데이터를 기록하거나 하드디스크 드라이버와 상호작용

🔹 6. 결과 값 레지스터에 저장 후 반환

  • 커널 함수는 결과 값을 RAX에 저장하고 sysret 명령을 통해 사용자 모드로 복귀

🔹 7. 유저 스페이스로 복귀 및 리턴 값 수신

  • 애플리케이션은 시스템 콜의 반환 값을 받고 다음 코드를 이어 실행

🗂 예시: write() 시스템 콜

ssize_t write(int fd, const void *buf, size_t count);

실제 호출 흐름:

 
1. write() → glibc 래퍼
2. 내부적으로 syscall 번호 1 (write) 사용
3. RAX=1, RDI=fd, RSI=buf, RDX=count 설정
4. syscall 명령 실행
5. 커널 진입 → sys_write()
6. 결과 반환 (쓰기 바이트 수)
7. 사용자 공간 복귀

⚠️ 시스템 콜이 중요한 이유

기능역할
사용자 공간 보호 유저가 커널 메모리나 장치에 직접 접근 불가
커널 리소스 접근 창구 파일 시스템, 메모리, 네트워크 등
일관된 인터페이스 제공 다양한 하드웨어에도 통일된 API 제공
보안 및 권한 관리 커널이 접근을 필터링, 제어함
 

📌 요약 정리

단계설명
1 사용자 함수 → C 라이브러리 → syscall 준비
2 레지스터에 syscall 번호/인자 설정
3 syscall 명령으로 커널 진입
4 커널은 시스템 콜 테이블에서 해당 함수 실행
5 커널 함수가 동작하고 결과를 RAX에 저장
6 sysret으로 유저 스페이스 복귀
7 애플리케이션은 결과 받아 계속 실행
 

 


이 구조를 이해하면, 시스템 콜 트레이싱(strace), 퍼포먼스 분석, 보안 정책 구성(예: seccomp, SELinux), 드라이버 개발까지도 원리적으로 연결해서 접근할 수 있게 됩니다.