System call

mmmn0616·3일 전

Operating System

목록 보기
2/2

System call

응용 프로그램이 운영체제 커널의 기능을 안전하게 사용하기 위해 요청하는 인터페이스

CPU는 권한 상태에 따라 user mode와 kernel mode 중 하나로 동작한다. 어떤 프로세스를 처리할 때 일반 프로그램이 실행되는 권한이 낮은 모드에서 명령을 처리하다가, 파일 읽기, 장치 제어 등 운영체제가 필요한 작업이 나오면 운영체제가 실행되는 높은 권한의 모드에서 과제를 수행하고 다시 돌아오는 과정이 있다는 의미다.

user mode에서는 하드웨어 직접 접근이나 특권 명령어 실행이 막혀 있다. 프로그램이 마음대로 다른 프로그램의 메모리나 장치를 건드리지 못하게 보호하고, 꼭 필요할 때만 system call로 운영체제에 요청하게 한다.

https://www.geeksforgeeks.org/operating-systems/difference-between-user-mode-and-kernel-mode/



특정 파일에서 어떤 값을 읽어오는 프로그램을 실행한다고 하자.

user mode에서 해당 프로그램을 실행하면, 그 프로그램에 정의된 명령어를 차례대로 실행한다. 파일을 읽어와야 되는 타이밍이 되면 system call이 호출된다. 시스템 콜이 Trap(소프트웨어 인터럽트)이나 syscall 명령어를 일으키면 CPU는 kernel mode로 진입하여 요청을 처리하고 필요한 값을 가져오며 system call을 반환하고 user mode로 돌아온다. 운영체제와 하드웨어가 소통하는 구체적인 과정은 다음과 같다.

  1. 프로그램: 시스템 콜로 운영체제에 “이 파일을 읽어 줘”라고 요청
  2. 운영체제: 그 프로그램에 파일을 읽을 권한이 있는지 확인하고, 파일 내용이 저장된 위치를 찾음.
  3. 운영체제의 드라이버: SSD 컨트롤러에 읽기 요청을 보냄
  4. SSD: 데이터를 전달하고, 운영체제는 프로그램이 그 데이터를 사용할 수 있게 해 줌.






System call 작동 방식

system call이 작동하는 방식은 크게 두 가지가 있다.

옛날 방식: Trap (int 0x80)

시스템 콜을 인터럽트로 처리하는 방식이다. 프로그램이 int 0x80 명령어를 실행하면 0x80번 인터럽트가 실행된다.

CPU가 Trap을 수신하면 1. 현재 상태(PC, 레지스터 등)을 저장하고 2. IDT(인터럽트 테이블)에서 해당 주소(0x80)을 찾는다. 3. 권한을 검사하고 4. 해당 주소에 적힌 시스템 콜 핸들러라는 또 다른 주소로 점프하면서 kernel mode로 전환한다.

매번 테이블을 조회하고 검사, 저장 단계가 있어 느리다는 단점이 있다. 이를 해결한 것이 syscall 명령어 방식이다.

현대 x86-64 방식: syscall 명령어

부팅할 때 OS가 CPU 내부의 특수 레지스터(MSR)에 커널 진입 주소를 저장해둔다.

프로그램이 syscall 명령어를 실행하면 IDT와 같은 테이블 조회 없이 해당 주소로 바로 점프하면서 kernel mode로 전환되는 방식이다. 인터럽트 구조를 거치지 않으니 훨씬 빠르다.

일반적으로 프로그램 코드의 read(), printf()와 같은 라이브러리 함수는 그 내부에서 시스템콜을 호출한다. 즉, 프로그래머는 시스템콜의 구체적인 호출 방식을 몰라도 된다. 대부분의 시스템콜은 라이브러리 코드가 제공하는 API 함수를 통해 호출되기 때문이다.

예를 들어, C++의 printf()를 호출하면 printf()는 내부적으로 write() 시스템콜을 호출한다.

https://forns.lmu.build/classes/spring-2018/cmsi-387/lecture-4R.html

사진에서 확인할 수 있듯이 시스템콜 API는 운영체제에 따라 다르다.



System call 요청 과정

그렇다면 API 함수는 구체적으로 어떻게 시스템 콜을 요청할까?

시스템콜은 운영체제에 따라 다음과 같은 방식으로 번호가 부여되어있다.

프로그램이 이렇게 호출하면

read(fd, buffer, 100);

read() 내부에서는 다음과 같은 절차를 수행한다.

  1. 시스템 콜 번호 0을 레지스터에 넣는다.
  2. 함수의 인자들을 레지스터에 넣는다.
  3. syscall 명령어를 실행한다. → kernel mode
  4. 커널이 번호를 확인하고 해당 처리 코드를 실행한다.


System call parameter passing

시스템 콜에 필요한 인자들을 넘겨주는 방식은 크게 세 가지가 있다.

방식인자를 어떻게 전달하나?
Register인자 값을 정해진 레지스터에 직접 넣음
Block메모리에 인자들을 묶어 저장하고, 그 묶음의 시작 주소를 레지스터에 넣음
Stack메모리의 스택 영역에 정해진 순서·위치로 인자들을 놓음

일반적으로 레지스터를 통해서 넘겨주는 방식은 인자의 개수도 제한되어있고, 그 크기도 제한되어있다. 메모리에 인자를 저장하고 커널이 메모리를 확인하게 하는 방식(Block, Stack)은 더 많은 인자와 더 큰 데이터를 전달할 수 있다.

0개의 댓글