포스트

운영체제 4. 시스템 콜이 무엇인가

운영체제 4. 시스템 콜이 무엇인가

시스템 콜이 무엇인가?

간단하게 말해 응용 프로그램이 커널을 사용하기 위한 인터페이스다.

왜 인터페이스로 만들어두었을까? 응용 프로그램이 함부로 사용하면 안되는 명령어들이 존재한다. 입출력, 메모리 접근, CPU 제어, 타이머 설정, 레지스터 접근 등이 그런 명령어에 해당한다. 이런 중요한 명령어를 특권 명령어라고 부른다. 특권 명령어를 응용 프로그램이 마음대로 사용할 수 있으면 해킹으로 운영체제가 망가질 수 있다. 따라서 운영체제는 유저 모드커널 모드 두가지로 분리하여 특권 명령어는 커널 모드일 때만 실행 가능하도록 제한해두었다.

어떻게 유저 모드에서 특권 명령어를 실행하지 못하도록 구현할까? 단순히 if (mode is not kernal) return; 이런식으로 구현해두진 않았을 것 같다. 방법은 CPU 하드웨어 수준에서 판단하는 것이다. CPU 내의 제어 장치(CU)가 특권 명령어를 실행하기 전에, 모드 레지스터를 체크한다. 현재 특권 모드가 아니면 그냥 명령어 실행 자체를 안해버리고 OS에게 예외를 날린다. 이렇게 구현하면 소프트웨어 수준에선 모드 레지스터를 조작하지 않는 이상 뚫을 수 없고, 사람이 수작업으로 CPU를 뜯어 고친다? 이것도 매우 어렵다.

시스템 콜을 호출했을 때 어떤 과정이 일어나는가?

Pasted image 20250317160627.png

응용 프로그램에서 read() 시스템 콜을 호출했다고 가정하자. 여기서 호출되는 함수는 커널 내의 있는 시스템 콜을 바로 호출하는 것이 아니라, libc.a 라이브러리 내의 함수를 호출한다. 라이브러리 내 함수는 trap 명령어를 실행하도록 구현되어 있다. 시스템 콜을 호출하는 trap 명령어를 실행하기 전에, 시스템 콜의 인자값을 레지스터(%eax)에 미리 저장한다.

Trap 명령어는 무엇인가? 트랩 명령어는 유저 모드에서 실행 가능한, CPU에게 ‘나 이러이러한 작업을 하고싶다’고 요청하는 명령어다. x86 계열에선 'INT (인터럽트_코드)', ARM 계열에선 'SVC (인터럽트_코드)'가 트랩 명령어다.

시스템 콜 트랩 명령어가 실행되면 인터럽트 테이블, 시스템 콜 테이블을 거쳐서 시스템 콜이 호출된다. read()를 실행했을 때 벌어지는 일은 다음과 같다.

  1. CPU가 응용 프로세스를 돌리다가 트랩 명령어를 만난다.
  2. CPU의 제어장치가 인터럽트 루틴을 실행함.
    1. 프로그램의 Context를 Kernel Stack에 저장.
      • 기존의 Context를 저장하면 프로그램에서 인자값으로 넣어뒀던 레지스터가 날아가지 않을까? : 그렇지 않다. Context는 백업만 될 뿐이고 레지스터의 값이 날아가지는 않는다.
    2. 커널 모드로 변경하여 커널 내의 Interrupt Handler 함수를 호출함.
      • 이 작업은 CPU 하드웨어 수준에서 벌어지기 때문에 해킹하기 어렵다.
    3. 트랩 명령어와 같이 주어진 인덱스를 통해 Interrupt Descriptor Table에셔 system_call() 함수 포인터를 얻어옴.
      1. IDT는 인터럽트 코드를 Index로 갖고, 인터럽트를 처리해주는 동작이 정의된 함수 포인터를 Value로 갖는 Table이다.
      2. 함수도 메모리 위에 적재되어 있고, 함수 포인터는 그 함수 어셈블리 주소의 첫번째 주소를 가리킨다.
    4. 프로그램 카운터를 함수 포인터 주소로 변경.
  3. in system_call()
    1. 약속된 레지스터 %eax를 확인하여 Table의 Index를 얻음.
    2. sys_call_table에서 실제 시스템 콜의 함수 포인터를 얻어 시스템 콜 실행.
    3. in read()
      1. 입출력 장치에서 데이터를 읽는다. 추후 자세한 설명...
      2. 결과값을 %eax 레지스터에 저장함.
      3. 리턴
    4. 유저 모드로 복귀하는 특권 명령어 실행 iret
    5. 백업해둔 응용 프로세스의 Context 복구
      • Context를 복구함으로써 자동으로 프로그램 카운터의 주소값이 응용 프로세스로 넘어가 응용 프로세스를 실행하게 됨.

Kernel Stack이 무엇인가?

Pasted image 20250406220932.png

Kernel 또한 프로그램이므로 일반 응용 프로그램이 갖는 Virtual Address Space의 메모리 구조는 기본적으로 갖고있다. 따라서 스택 영역의 메모리도 갖고 있다.

커널은 응용 프로그램마다 각각의 스택 영역을 만들어 데이터가 섞이지 않도록 관리한다. 프로세스의 Context는 Kernel Stack에 저장된다.