핀토스를 구현하며 시스템 콜에 대해서 공부하고 로직을 작성해보았다.
시스템 콜 호출시에는 유저모드에서 커널모드로 전환되는 과정이 있는데
유저모드가 무엇인지, 커널모드는 무엇인지, 한번 알아보자
일반 사용자 애플리케이션이 실행되는 모드
CPU 권한이 제한된 상태
제한된 명령만 실행 가능: 하드웨어 접근, I/O 직접 제어, 커널 메모리 접근 금지
시스템 콜을 통해 커널 기능을 요청해야 함
보호된 메모리 공간만 접근 가능 (자신의 프로세스 주소 공간)
웹 브라우저, 게임, 사용자 애플리케이션 등
printf(), open(), read() 등은 내부적으로 시스템 콜을 사용하여 커널에 요청함
운영체제 커널이 동작하는 모드
CPU의 모든 명령과 자원에 접근 가능
전체 메모리 접근 가능, I/O 장치 제어, 스케줄링, 파일 시스템 관리 가능
특권 명령 실행 가능 (예: 인터럽트 플래그 조작, 페이지 테이블 변경 등)
실수나 버그 발생 시 시스템 전체가 영향을 받을 수 있음
시스템 콜 처리
인터럽트 핸들러, 디바이스 드라이버
스케줄러, 메모리 관리자 등 OS의 핵심 기능
전환은 "트랩(trap)"이나 "인터럽트"에 의해 발생한다 !
소프트웨어가 명시적으로 발생시킨 예외
의도적인 커널 진입
주로 시스템 콜처럼, 사용자 프로그램이 커널 기능을 요청할 때 사용됨
⛏️ 즉, 유저모드 → 커널모드 진입을 의도적으로 수행할 때 사용
하드웨어나 타이머 등 외부 이벤트로 발생
프로그램 흐름과는 무관하게 비동기적으로 발생
예시: 키보드 입력, 디스크 완료, 타이머 알람 등
커널은 인터럽트를 받아서 처리 루틴으로 이동한 뒤, 원래 실행 중이던 유저 프로그램으로 돌아옴
시스템 콜: 유저 프로그램이 커널 서비스를 요청 (예: int 0x80, syscall)
인터럽트: 하드웨어 이벤트 (키보드 입력, 타이머 등) → 커널 진입
예외(Exception): 잘못된 연산 (0으로 나누기, 페이지 폴트 등)
전환 시 CPU는 자동으로 커널 스택으로 스위칭하고, 현재 컨텍스트를 저장한 뒤 커널 코드로 진입함
유저 프로그램에서 시스템 콜 실행
예: write(fd, buffer, size) 호출
내부적으로는 syscall 또는 int 0x80 등의 트랩 명령어 실행
CPU가 트랩 발생을 감지
현재 실행 상태(레지스터, 스택 등)를 커널 스택에 저장
CPU의 모드 플래그를 커널모드로 전환
인터럽트 벡터/시스템 콜 벡터에 등록된 커널 진입 지점으로 이동
커널에서 시스템 콜 핸들러 실행
syscall_handler() 또는 유사한 함수가 실행되어 시스템 콜 수행작업 완료 후 유저모드 복귀
커널은 저장된 레지스터 상태를 복원
유저 스택/레벨로 복귀하며 iretq 또는 sysret 등을 통해 다시 유저모드로 돌아감
// 유저모드 코드
write(fd, buf, size); // -> trap (syscall)
// 커널 진입
syscall_entry() // -> syscall_handler()
-> switch (syscall number)
-> sys_write(fd, buf, size);
// 커널 작업 끝나면
-> intr_exit() or do_iret() // -> 유저모드 복귀
✅ 트랩은 의도된 커널 진입 (예: 시스템 콜)
✅ 인터럽트는 외부 이벤트에 의한 커널 진입 (예: 타이머, 키보드)
✅ 시스템 콜 호출 시 유저모드 → 트랩 → 커널모드로 전환되어 커널 함수 실행 후 복귀
운영체제의 핵심은 “어떻게 안전하게 사용자 요청을 받아 커널이 처리하고 다시 돌아오는가”인데,
유저/커널 모드 분리 + 트랩/인터럽트 메커니즘 + 시스템 콜 처리 흐름이 바로 그걸 가능하게 해주는 구조이다!