시스템 콜


시스템 콜이란?

시스템 콜(system call)이란 운영 체제가 제공하는 일종의 인터페이스로서, 유저 프로그램이 운영 체제의 기능을 사용할 수 있도록 합니다.

예시로, 어떤 파일을 읽고 그 내용을 다른 파일에 복사하는 프로그램을 생각해 봅시다. 이 경우 프로그램에게 입력 파일과 출력 파일의 이름을 전달해줘야 합니다. 이때, UNIX cp 명령을 이용하여 in.txt 파일을 out.txt파일로 복사한다고 해봅시다:

cp in.txt out.txt

혹은 프로그램이 유저에게 창을 띄워 파일 이름을 입력하도록 할 수도 있을 것입니다. 이 방법은 화면에 프롬프트 메세지를 띄우는 시스템 콜과, 키보드로부터 파일 이름을 받는 시스템 콜을 사용할 것입니다. 아니면 마우스와 아이콘을 이용하는 방법도 있을 것입니다. 이 경우에도 여러 I/O 시스템 콜을 사용하게 됩니다.

어떠한 방법으로든 입력 파일과 출력 파일의 이름을 입력받았다면, 프로그램은 먼저 입력 파일을 열고, 출력 파일을 생성하여 열게 됩니다. 이러한 동작들 또한 시스템 콜을 사용합니다. 만약 입력 파일을 여는데 에러가 발생한다면 에러 메세지를 띄우고(시스템 콜 호출), 프로그램을 비정상적으로 종료하게 될 것입니다(시스템 콜 호출). 마찬가지로, 출력 파일의 이름이 중복될 경우 프로그램을 중단시킬 수도 있고(시스템 콜 호출), 기존의 파일을 지우고(시스템 콜 호출) 새로운 파일을 만들 수도 있습니다(시스템 콜 호출). 혹은 사용자에게 프롬프트 메세지를 띄워(시스템 콜 호출) 어떤 동작을 수행할 것인지 물어볼 수도 있습니다.

이러한 일련의 과정들을 마치고 나면 입력 파일을 읽어 나가면서(시스템 콜 호출) 출력 파일에 내용을 기록하게 될 것입니다(시스템 콜 호출). 마지막으로, 입력 파일의 모든 내용이 복사되고 나면 프로그램은 두 파일을 닫고(두 번의 시스템 콜 호출), 콘솔 창이나 화면에 결과를 띄우고(시스템 콜 호출), 정상적으로 프로그램을 종료할 것입니다(마지막 시스템 콜).

이러한 과정을 그림으로 나타내면 다음과 같습니다:

시스템 콜 호출 예시
시스템 콜 호출 예시. 출처: Operating System Concepts (10th Edition)

API

위에서 본 것과 같이, 한 파일의 내용을 다른 파일로 복사하는 단순한 프로그램조차 많은 시스템 콜을 사용합니다. 일반적으로 프로그래머들은 시스템 콜의 세세한 부분까지 신경쓰면서 개발하기보다, 어떤 두 프로그램이 서로 소통하는 일종의 소통 창구인 API(Application Programming Interface)를 이용하여 개발합니다.

프로그래머들은 이러한 API를 통해 시스템 콜을 더 간편하게 사용할 수 있습니다. 예를 들어, Windows에서 새로운 프로세스를 생성하고자 한다면 CreateProcess()라는 API 함수를 이용하여 프로세스를 생성할 수 있습니다. 이 함수를 호출하면 함수 내부에서 실제로 NTCreateProcess()라는 시스템 콜을 호출하여 프로세스를 생성합니다.

이렇게 실제 시스템 콜을 호출하는 대신 굳이 (추상화된) API를 사용하는 이유는,

  • 이식성(portability)이 좋아지기 때문입니다. 프로그래머가 API를 사용하여 프로그램을 작성하면 같은 API를 지원하는 시스템에서는 정상적으로 동작하리라 예측할 수 있습니다.
  • 또한, 실제 시스템 콜은 대게 API보다 더 복잡하고 (프로그래머 입장에서) 다루기 어렵기 때문입니다.

시스템 콜을 처리하는 데 있어 또 다른 중요한 요인은 바로 런타임(RTE, RunTime Environment) 입니다. RTE는 프로그램의 실행환경으로서, 프로그램을 실행하는데 필요한 모든 것(라이브러리 파일, 메모리 및 기타 시스템 리소스 등)을 포함합니다. RTE에는 시스템 콜 인터페이스가 존재하는데, API에서 시스템 콜을 호출하면 이 인터페이스가 API로 부터 해당 호출을 가로채어 실제 시스템 콜을 호출합니다.

일반적으로 각각의 시스템 콜은 일종의 ID값인 특정한 숫자와 매핑되어 있고, 시스템 콜 인터페이스는 이러한 숫자를 통해 인덱싱할 수 있는 테이블을 가지고 있습니다(이전에 살펴본 인터럽트 벡터와 유사합니다). 시스템 콜 인터페이스는 이 테이블을 통해 알맞는 시스템 콜을 호출하고, 시스템 콜의 상태를 반환합니다.

시스템 콜 호출자는 어떻게 시스템 콜이 구현되어 있고 시스템 콜 실행 중에 무엇을 하는지 굳이 알 필요는 없습니다만, API 사용 규칙을 준수하고 시스템 콜을 호출하면 해당 시스템 콜의 결과로 어떤 일이 발생하는지 정도는 알아야 합니다. 아래 그림을 통해 API와 시스템 콜 인터페이스, 그리고 운영 체제 간의 관계를 살펴볼 수 있습니다. 이 그림에서는 사용자 프로그램이 open() 이라는 시스템 콜을 호출하게 되면 어떤 일이 발생하는지 나타내고 있습니다.

open() 시스템 콜을 호출하면 일어나는 일
open() 시스템 콜을 호출하면 일어나는 일. 출처: Operating System Concepts (10th Edition)

시스템 콜을 사용할 때 인자를 넘겨야 할 때가 있는데, 보통 운영 체제에게 인자를 넘기는 방법으로는 다음의 세 가지가 주로 사용됩니다:

  • 가장 단순한 방법으로, 인자를 레지스터에 저장하여 넘길 수 있습니다.
  • 하지만 레지스터 개수 보다 인자의 수가 더 많은 경우, 혹은 인자의 크기가 레지스터보다 큰 경우, 인자들을 블록(혹은 테이블) 단위로 묶어서 메모리에 저장하고 이 메모리의 주소를 레지스터에 저장함으로써 인자를 전달합니다:

    인자를 블록 단위로 넘기는 방법
    인자를 블록 단위로 넘기는 방법. 출처: Operating System Concepts (10th Edition)
  • 세 번째 방법으로는 레지스터에 인자가 저장된 메모리의 주소를 저장하지 않고, 스택을 사용해서 스택에 인자들을 push한 뒤 이후 운영 체제가 스택에 저장된 인자들을 꺼내어(pop) 사용하는 방법이 있을 수 있습니다.

시스템 콜의 종류

시스템 콜은 크게 여섯가지 종류로 분류할 수 있습니다:

  • 프로세스 컨트롤(process control): 프로세스 생성/파괴/실행, event 기다리기, 에러 발생시 메모리 덤프 등.
  • 파일 관리(file management): 파일 생성/삭제, 파일 열기/닫기, 파일 읽기/쓰기, 파일 특성(file attributes) get/set 등.
  • 장치 관리(device management): 장치 요청/릴리즈, 장치 읽기/쓰기, 장치 특성 get/set 등.
  • 정보 유지(information maintenance): 시간(혹은 날짜) get/set, 시스템 데이터 get/set 등.
  • 통신(communications): 통신연결 (communication connection) 생성/삭제, 데이터 전송/수신 등.
  • 보호(protection): 자원에 대한 접근 컨트롤, permission get/set, 사용자의 엑세스 allow/deny 등.