운영체제 10판(Abraham Silberschatz)을 읽고 정리한 글입니다. 일부 내용은 생략 되었습니다.

운영체제 서비스
운영체제는 프로그램 실행 환경을 제공한다. 운영체제는 프로그램과 그 프로그램의 사용자에게 특정 서비스를 제공한다. 물론 운영체제마다 제공하는 서비스는 다르지만 공통된 부류가 있다는 것을 발견할 수 있다. 운영체제가 제공하는 서비스는 다음과 같다.
- 사용자 인터페이스(user interface): 거이 모든 운영체제는 사용자 인터페이스를 제공한다. (GUI, 터치 스크린, CLI)
- 프로그램 수행(program executive): 시스템은 프로그램을 메모리에 적재해 실행 할 수 있어야 한다. 프로그램은 정상적이든, 혹은 비정상적인든 실행을 끝낼 수 있어야 한다.
- 입출력 연산(I/O operation): 수행 중인 프로그램은 입출력을 요구할 수 있다. 이러한 입출력에는 파일 혹은, 입출력 장치가 연관될 수 있다. 특정 장치에 대해서는 특수한 기능(네트워크 인터페이스에서 일거나 파일 시스템에 쓰기 등)이 요구될 수 있다. 효율과 보호를 위해, 사용자들은 통상 입출력 장치를 직접 제어할 수 없다. 따라서 운영체제가 입출력 수행의 수단을 제공해야 한다.
- 파일 시스템 조작(file system manipulation): 파일 시스템은 특히 중요한 분야이다. 명백히, 프로그램은 파일을 읽고 쓸 필요가 있다. 프로그램은 또한 이름에 의해 파일을 생성하고 삭제할 수 있고 지정된 파일을 찾을 수 있어야하고 파일의 정보를 열거할 수 있어야 한다. 마지막으로 몇몇 프로그램은 파일 소유권에 기반을 둔 권한 관리를 이용하여 파일이나 디렉터리의 접근을 허가하거나 거부할 수 있게 한다. 많은 운영체제들은 때로는 개인의 선택에 따라 그리고 때로는 특정 특성과 성능 특성을 제공하기 위하여 다양한 파일 시스템을 제공한다.
- 통신(communication): 한 프로세스가 다른 프로세스의 정보를 교환해야 할 필요가 있는 여러 상황이 있다. 이러한 통신을 수행하는 두가지 중요한 방법이 있다. 첫번째는 동일한 컴퓨터에서 수행되고 있는 프로세스들 사이에서 일어나고, 두번째는 네트워크에 의해 함께 묶여 있는 서로 다른 컴퓨터 시스템상에서 수행되는 프로세스들 사이에서 일어난다.통신은 공유 메모리를 통해서 구현될 수도 있고, 메세지 전달 기법을 사용하여 구현될 수 있는데, 후자의 경우 정보의 패싴들이 운영체제에 의해 프로세스들 사이를 이동한다.
- 오류 탐지(error detection): 운영체제는 모든 가능한 오류를 항상 의식하고 있어야한다. 오류는 CPU, 메모리 하드웨어, 입출력 장치, 또는 사용자 프로그램에서 일어날 수 있다. 운영체제는 올바르고 일관성 있는 계싼을 보장하기 위해 각유형의 오류에 대해 적당한 조치를 해야 한다. 물론 운영체제가 오류에 어덯게 반응하며 수정하는가에 다양한 변종이 존재한다. 디버깅 설비는 시스템을 효율적으로 사용할 수 있는 사용자와 프로그래머의 능력을 향상시킨다.
다음은 시스템 자체의 효율적인 동작을 보장하기 위한 운영체제의 기능이다.
- 자원 할당(resource allocation): 다수의 프로세스나 다수의 작업이 동시에 실행될때, 그들 각각에 자원을 할당해 주어야 한다. 운영체제는 여러 가지 다른 종류의 자원을 관리한다. 어떤 것은 특수한 할당 코드를 가질 수 있지만, 다른 것은 훨씬 일반적인 요청과 방출 코드를 가질 수 있다. 예를 들면, CPU를 최대한 효율적으로 이용하기 위해 운영체제는 CPU 스케줄링 루틴이 CPU의 속도, 반드시 실행해야할 프로세스들, CPU 스케줄링 루틴이 CPU의 속도, 반드시 실행해야 할 프로세스들, CPU의 처리 코어의 개수와 다른 요이들을 고려하도록 해야 한다. 또한 프린터, USB 저장장치 드라이브 및 다른 주변 장치를 할당하는 루틴이 있을 수 있다.
- 기록 작성(logging): 우리는 어떤 프로그램이 어떤 종류의 컴퓨터 자원을 얼마나 많이 사용하는지를 추적할 수 있길 원한다. 이와 같은 기록 관리는 회계, 또는 단순히 사용 통계를 내기 위해 사용된다. 사용 통계는 컴퓨팅 서비스를 개선하기 위해 시스템을 재구성하고자 하는 시스템 관리자에게 귀중한 자료가 될 수 있더.
- 보호(protection)와 보안(security): 다중 사용자 컴퓨터 시스템 또는 네트워크로 연결된 컴퓨터 시스템에 저장된 정보의 소유자는 그 정보의 사용을 통제하길 원한다. 서로 다른 여러 프로세스가 병행하게 수행될 때, 한 프로세스가 다른 프로세스나 운영체제 자체를 방해해서는 안 된다. 보호는 시스템 자원에 대한 모든 접근이 통제되도록 보장하는 것을 필요로한다. 외부로부터의 시스템 보안 또한 중요하다. 이러한 보안은 각 사용자가 자원에 대한 접근을 원할 때 통상 패스워드를 사용해서 시스템에게 자기 자신을 인증하는 것으로부터 시작된다. 보안은 네트워크 어댑터등과 같은 외부 입출력 장치들을 부적합한 접근 시도로 부터 지키고, 침입의 탑지를 위해 모든 접속을 기록하는 것으로 범위를 넓힌다. 만약 시스템이 보호되고 보안이 유지되려면, 시스템 전체에 걸쳐 예벙책이 재정되어야 한다. 하나의 사슬은 가장 약한 연결 고리만큼만 강한 법이다.
사용자와 운영체제 인터페이스
명령 인터프리터
Linux, UNIX 및 Windows를 포함한 운영체제 대부분은 명령 인터프리터를 프로세스가 시작되거나 사용자가 처음 로그인 할 때 수행되는 특수한 프로그램으로 취급한다. 선택할 수 있는 여러 명령 인터프리터를 제공하는 시스템에서 이 해석기는 셸이라고 불린다.

명령 인터프리터의 중요한 기능은 사용자가 지정한 명령을 가져와서 그것을 수행하는 것이다. 이 수준에서 제공된 많은 명령은 파일을 조작한다. 즉, 생성, 사젝, 리스트, 프린트, 복사, 수행 등을 한다. UNIX 시스템에서 사용 가능한 다양한 셸은 이런 방식으로 실행된다. 이 명령어들은 두가지 일반적인 방식으로 구현될 수 있다.
한 가지 방법은 명령 인터프리터 자체가 명령을 실핼할 코드를 가지고 있는 경이다. 예를 들면, 한 파일을 삭제하기 위한 명려은 명령 인터프리터가 자신의 코드의 한 부분으로 분기하고, 그 코드 부분이 매개변수를 설정하고 적절한 시스템 콜을 한다. 이 경우 제공될 수 있는 명령의 수가 명령 인터프리터의 크기를 결정하는데, 그 이유는 각 명령이 자신의 구현 코드를 요구하기 때문이다.
여러 운영체제 중에 UNIX에 의해 사용되는 다른 대안의 접근 방법은, 시스템 프로그램에 의해 대부분의 명령을 구현하는 것이다. 이러한 경우 명령 인터프리터는 전혀 그명령을 알지 못한다. 단지 메모리에 적재되어 실행될 파일을 식별하기 위해 명령을 사용한다. 따라서 파일을 삭제하는 UNIX명령은 rm이라 불리는 파일을 찾아서, 그 파일을 메모리에 적재하고, 그것을 매개변수 file.txt로 수행한다. rm 명령고 관련된 로직은 rm이라는 파일 내의 코드로 완전하게 정의한다. 이러한 방법으로 프로그래머는 적합한 프로그램 로직을 가진 새로우 파일을 생성함으로써 시스템에 새로운 명령을 쉽게 추가할 수 있다. 명령 인터프리터 프러그램은 이제 아주 작아질 수 있으며, 새로운 명령을 추가하기 위해 변경될 필요가 없다.
그래픽 기반 사용자 인터페이스
운영체제와 접촉하는 두 번째 방식은 사용자 친화적인 그래픽 기반 사용자 인터페이스 또는 GUI를 통하는 방식이다. 이 방식에서는 명령어 라인 인터페이스를 통하여 사용자가 직접 명령어를 입력하는 것이 아린라 데스크톱이라고 특징지어지는 마우스를 기반으로 하는 윈도 메뉴 시스템을 사용한다. 사용자는 마우스를 움직여 마우스 포인터를 프로그램, 파일, 시스템 기능들을 나타내는 화면상의 이미지에 위치시킨다. 마우스 포인터의 위치에 따라 아무스 버튼을 누름으로써 프로그램을 호출하거나 파일 혹은 디렉토리를 선택할 수도 있고, 또는 명령을 포함한 메뉴를 잡아당길 수 도 있다.
전통적으로 UNIX 시스템은 명령어 라인 인터페이스가 주류를 이루었다 그러나 K Desktop Environment 및 GNU 프로젝트에 의한 GNOME 데스크톱과 같은 다양한 공개 소스 프로젝트 등을 통해 디자인이 크게 향상된 다양한 GUI인터페이스를 사용할 수 있게 되었다. KDE 및 GNOME 데스크톱은 Linux 및 다양한 UNIX 시스템에서 실해되며 공개 소스 라이선스로 사용할 수 있다. 즉 특정 라이선스 조건하에서 소스 코드를 읽고 수정하는 것이 쉽다는 것을 의미한다.
터치스크린 인터페이스
대부분의 모바일 시스템에서 명령 라인 인터페이스나 마우스 및 키보드 시스템이 실용적이지 않기 때문에 스마트폰 및 휴대용 태블릿 컴퓨터는 일반적으로 터치스크린 인터페이스를 사용한다. 사용자는 터치스크린에서 손가락을 누르거나 스와이프 하는 등의 제스처를 취하여 상호 작용한다. 초기 스마트폰에는 물리적 키보드가 포함되어 있었지만 이제 스마트폰과 태블릿의 대부분은 터치스크린에서 키본드를 시뮬레이션 한다. 예를 들어 iPad iPhone은 모두 Springboard 터치스크린 인터페이스를 사용한다.

시스템 콜
시스템 콜은 운영체제에 의해 사용 가능하게 된 서비스에 대한 인터페이스를 제공한다. 특정 저수준 작업은 이셈블리어 명령을 사용하여 작성되어야 하더라도 이러한 호출은 일반적으로 C, C++언어로 작성된 함수 형태로 제공한다.
예제
운영체제가 어떻게 시스템 콜을 사용할 수 있게 만드는지에 대해 논의하기 전에 시스템콜이 어떻게 사용되는지를 설명하는 예를 보도록하 하자. 한 파일로부터 데이터를 읽어서 다른 파일로 복사하는 간단한 프로그램을 작성한다고 가정해 보자. 프로그램이 필요로 하는 첫 번째 입력은 두 개의 파일, 즉 입력 파일과 출력 파일의 이름일 것이다. 이 이름들은 운영체제 설계에 따라 여러 가지 방법으로 지정할 수 있다. 한 가지 방법은 명령의 일부로 두 파일의 이름을 전달하는 것이다.
cp test cp_test
이 명령은 test 파일을 cp_test에 복사(생성)한다. 두 번째 방법은 프로그램이 사용자에게 이름을 요청하는 것이다. 대화형 시스템에서 이 방법은 일련의 시스템 콜이 필요하다. 먼저 화며에 프롬프트 메세지를 작성한 다음 키보드에서 두 파일의 이름을 지정하는 문자를 읽는다. 마우스 기반 및 아이콘 기반 시스템에서 파일 이름 메뉴는 일반적으로 창에 표시된다. 그란 다름 사용자는 마우스를 사용하여 소스 이름을 선책할 수 있으며 대상 이름을 지정할 수 있는 창을 열 수 있다. 이 일련의 작업을 위해서는 많은 I/O 시스템 콜이 필요하다.
일단 두 개의 파일 이름이 얻어지면, 프로그램은 반드시 입력 파일을 오픈 하고, 출력 파일을 생성한 후 오픈한다. 각각의 이러한 연산은 또 다른 시스템 콜을 필요로 하며, 각 시스템 콜에서 오류가 발생하면 처리되어야 한다, 예를 들어, 프로그램이 읿력 파일을 오른하려고 할 때, 그 이름을 갖는 파일이 존재하지 않거나, 그파일에 대한 접근이 금지되어 있는 것을 발견 할 수 있다. 이러한 경우 프로그램은 에러 메세지를 출력하고(또 다른 시스템 콜이다.), 그리고 비정상적으로 종료(또 다른 시스템 콜이다)한다. 망약 입력 파일이 존재하면 새로운 출력 파일을 생성해야 한다. 이때 동일한 이름을 가진 출력 파일이 이미 존재하는 경우가 있다. 이러한 상황은 프로그램을 중단(또 다른 시스템 콜)하거나, 기존 파일을 삭제한 후, 해로운 파일을 생성할 수도 있다. 대화형 시스템에서 또 다른 방법은 기존의 파일을 대체할 것인지, 혹은 프로그램을 중단할 것인지를 사용자에게 물어보는 (시스템 콜)것이다.
이제 두 개의 파일이 준비되면, 입력 파일로 부터 입력 파일로부터 읽어서(시스템 콜), 출력 파일에 기록(시스템 콜)하는 루프에 들어가게 된다. 각 읽기와 쓰기는 가능한 여러가지 오류 상황의 정보를 반환해야 한다. 입력에서, 프로그램이 파일의 끝에 도달하거나 읽기 중에 하드웨어 오류가 발생할 수 도 있다. 쓰기 연산 시 출력 장치에 따라 여러 가지 오류들이 발생할 수도 있다.

마지막으로 전체 파일이 복사된 후, 프로그램은 두 개의 파일을 닫고(시스템 골), 결국 정상적으로 종료하게 된다(시스템 콜).
응용 프로그래밍 인터페이스
방금 파일을 복사하는 상황과 같이, 간단한 프로그램이라도 운영체제의 기능을 아주 많이 사용하게 된다. 종종 초당 수천개의 시스템 콜을 수행하기도 한다. 대부분의 응용 개발자들은 응용 프로그래밍 인터페이스(API)에 따라 프로그램을 설계한다. API는 각 함수에 전달되어야 할 매개변수들과 프로그래머가 기대할 수 있는 반환 값을 포함하여 응용 프로그램어가 사용 가능한 함수의 집합을 명시한다. 응용 프로그매어가 사용 가능한 가장 흔한 세 가지 API는 Windows 시스템을 위한 Window API, POSIX 기반 시스템을 위한 POSIX API, Java 가상 기계에서 실행될 수 있는 프로그램을 위한 Java API이다. 프로그래머는 운영체제가 제공하는 코드의 라이브러리를 통하여 API를 활용한다. UNIX와 Linux시스템에서 C언어로 작성된 프로그램을 위해서 제공되는 라이브러리는 libc로 불린다. 특별히 언급하지 않는 한 이 책에서 사용되는 시스템 콜의 이름은 일반적인 예임을 명심하라. 모든 운영체제는 고유의 시스템 콜 이름을 가진다.
뒤에는 API를 구성하는 함수들은 통상 응용 프로그래머를 대신하여 실제 시스템콜을 호출한다. 예를 들면 Window함수 CreateProcess()는 실제로 Window 커널의 NTCreateProcess() 시스템 콜을 부른다.
왜 응용 프로그래머는 실제 시스템 콜을 부르는 것보다 API에 따라 프로그래밍 하나는 것을 선호하는가? 그렇게 하는 데에는 몇 가지 이유가 있다. 한 가지 이점은 프로그램의 호환성과 관련 있다. API에 따라 프로그램을 설계하는 응용 프로그래머는 자신의 프로그램이 같은 API를 지원하는 어느 시스템에서건 컴파일되고 실행된다는 것을 기대할 수 있다. 게다가 실제 시스템 콜은 종종 좀 더 자세한 명세가 필요하고 프로그램상에서 작업하기가 응용 프로그래머에게 가용한 API보다 더 어렵다. 그럼에도 불구하고 API 함수를 호출하는 것과 커널의 관련된 시스템 콜을 호출하는 것에는 강한 상관관계가 존재한다. 사실 대부분의 POSIX와 Windows API는 Linux 및 Windows 운영체제가 제공하는 고유의 시스템과 유사하다.
시스템 콜을 처리하는 데 있어 중요한 또 다른 요소는 실행시간 환경(RTE)이다.(런타임이라고 불린다) 컴파일러 또는 인터프리터를 포함하여 특정 프로그래밍 언어로 작성된 응용 프로그램을 실행하는 데 필요한 전체 소프트웨어 제품군과 라이브러리 또는 로더와 같은 다른 소프트웨어를 모두 포함한다. RTE는 운영체제가 제공하는 시스템 콜에 대한 연결고리 역활을 하는 시스템 콜 인터페이스를 제공한다. 이 시스템 콜 인터페이스는 API 함수의 호출을 가로채어 필요한 운영체제 시스템 콜을 부른다. 통상 각시스템 콜에는 번호가 할당되고 시스템 콜 인터페이스는 이 번호에 따라 색인되는 테이블을 유지한다. 시스템 콜 인터페이스는 의도하는 시스템 콜을 부르고 시스템 콜의 상태와 반환값을 돌려준다.
호출자는 시스템 콜이 어덯게 구현되고 실행 중 무슨 작업을 하는지 아무것도 알 필요가 없다. 호출자는 단지 API를 준수하고 시스템 콜의 결과로서 운영체제가 무엇을 할 것인지만 이해하면 된다. 따라서 운영체제 인페이스에 대한 대부분의 자세한 내용은 API에 의해 프로그래머로 부터 숨겨지고 RTE에 의해 관리된다.

시스템 콜은 사용되는 컴퓨터에 따라 다른 방법으로 발생한다. 종종, 단순히 원하는 시스템 콜이 무엇인지 보다 더 많은 정보가 요구될 수도 있다. 필요한 정보의 유형과 양은 특정 운영체제와 호출에 따라 다양하다. 예를 들면, 입력을 받아들이기 위해, 입력으로 사용될 파일이나 장치와 함께 읽어 들인 데이터를 저장할 메모리 버퍼의 주소와 길이를 명시할 필요가 있다. 물론 장치나 파일 그리고 길이는 시스템 콜에 암묵적일 수 있다.
운영체제에 매개변수를 전달하기 위해서 세 가지 일반적인 방법을 사용한다. 가장 간단한 방법은 매개변수를 레지스터 내에 전달하는 것이다. 그러나 어떤 경우는 레지스터 보다 더 많은 매개변수가 있을 수 있다. 이러한 경우에 매개변수는 메모리 내의 블록이나 테이블에 저장되고 , 블록의 주소가 레지스터 내에 매개변수로 전달된다.

Linux는 이러한 접근법을 조합하여 사용한다. 5개 이하의 매개변수가 있으면 레지스터가 사용된다. 매개변수의 개수가 5개를 넘으면 블록 방법이 사용된다. 매개 변수는 프로그램에 의해 스택에 넣어질 수도 있고, 운영체제에 의해 꺼내진다. 일부 운영체제는 블록이나 스택 방법을 선호하는데, 이들 접근법은 전달되는 매개변수들의 개수나 길이를 제하지 않기 때문이다.
표준 API 예시
표준 API의 예로서 UNIX와 Linux 시스템에서 제공되는 read()함수를 고려하자. 이 함수에 대한 API는 명령어 라인에 다음 명령어를 입력하여 출력되는 man 페이지에서 얻을 수 있다.
man read

read() 함수를 사용하는 프로그램은 unistd.h 헤더 파일을 포함시켜야 하는데, 이 파일에는 무엇보다도 ssize_t와 size_t 데이터 유형이 정의되어 있다. read()함수에게 전달되는 인자는 다음과 같다.
- int fd - 읽으려는 파일 디스크립터
- void *buf - 데이터를 읽어 들일 버퍼
- size_t count - 버퍼로 읽어 들일 수 있는 최대 바이트 수
읽기가 성공한 경우 읽어 들인 바이트 수가 반환된다. 반환 값이 0인 경우는 파일의 끝에 도달했다는 것을 의미한다. 오류가 발생한 경우 read()는 -1을 반환한다.
시스템 콜의 유형
싯템 콜은 다섯 가지의 중요한 범주, 즉 프로세스 제어, 파일 조작, 장치 조작, 정보 윶 보수와 통신, 보호등으로 묶을 수 있다. 이후 운영체제에 의해 제공되는 여러가지 유형의 시스템 콜을 간단히 논의한다. 이들 시스켐 콜의 대부분은 뒷장에서 논의 되는 개념과 기능들을 지원하거나 그들에 의해 지원된다.

프로세스 제어
실행 중인 프로그램은 수행을 정상적으로 또는 비정상적을 멈출 수 있어야 한다. 만약 현재 실행중인 프로그램을 비정상으로 중지하기 위해 시스템 콜이 호출되거나 프로그램에 문제가 발생해 오류 트랩(trap)을 유발할 경우, 때때로 메모리 덤프가 행해지고 오류 메세지가 생성된다. 이 덤프는 특별한 로그 파일이나 디스크에 기록되고 문제의 원인을 결정하기 위해 디버거에 의해 검사될 수 있다. 정상이거나 비정상인 상황에서, 운영체제는 명령 인터프리터로 제어를 전달해야 한다. 명령 인터프리터는 이어 다음 명령을 읽는다.

대화식 시스템에서 명령 인터프리터는 단순히 다음 명령을 계속 수행하명, 사용자가 오류에 응답하는 적절한 명령을 내릴 것을 가정한다. GUI시스템에서는 팝업 윈도가 사용자에게 오류를 알리고 지시를 기다린다. 일부 시스템에서는 오류가 발생할 경우 특별한 복구 행위를 지시하는 제어 카드를 혀용한다 만약 프로그램이 입력에서 오류를 발견하고 비정상으로 중료하기를 원한 다면, 프로그램은 오류 수준을 정의하기를 원할 수도 있다. 더 높은 등급의 오류 매개변수를 전달함으로써 더 치명적인 오류를 나타낼 수 있다. 이렇게 함으로써 정상 종료를 등급 0의 오류로 정의하여 정상 종료와 비정상 종료를 결합할 수도 있다. 명령 인터프리터, 혹은 다음 프로그램은 이오류 등급을 사용하여 다음 행동을 자동으로 결정할 수 있다.
한 프로그램을 실행하고 있는 프로세스가 다른 프로그램가 다른 프로그램을 적재하고 실행하기를 원할 수 있다. 이기능은 명령 인터프리터가 사용자 명령 또는 마우스의 클릭을 통하여 지시된 프로그램을 실행하는 것을 허용한다. 여기서 흥미 있는 질문은 적재된 프로그램이 종료되었을 때 어디로 되돌려 주느냐 하는 것이다. 이 질문은 기존 프로그램이 유실될지, 보관될지, 새로운 프로그램과 병행하게 실행을 계속하도록 허용할 것인지 하는 문제와 관련 있다.
만약 새로운 프로그램이 종료되었을 때 제어가 기존 프로그램으로 되돌아간다면, 우리는 반드시 기존 프로그램의 메모리 이미지를 보관해야 한다. 따라서 우리는 실질적으로 한 프로그램이 다른 프로그램을 호출하는 기법을 만든 셈이 된다. 만약 두 프로그램이 병행하게 수행된다면, 우리는 다중 프로그램 될 새로운 프로세스를 생성한 것이다. 종종, 이런 특정 목적을 위한 시스템 콜이 있다.(create_process())
만약 우리가 새로운 잡이나 프로세스, 혹은 잡들이나 프로세스들의 집합을 생성한다면, 우리는 그들의 실행을 제정할 수 있어야 한다. 이러한 제어는 잡의 우선순위, 최대 허용 실행 시간 등을 포함하여 잡 혹은 프로세스의 속성들을 결정하고 재설정할 수 있는 능력이 필요하다.
빈번하게 둘 이상의 프로세들은 데이터를 공유한다. 공유되는 데이터의 일관성을 보장하기 위해서 운영체제는 종종 프로세스가 공유 데이터를 잠글 수 있는 시스템콜을 제공한다. 그러면 잠금이 해제될 때까지 어느 프로세스도 데이터에 접근할 수 없게된다. 통산 그런 시스템은 acquire_lock()과 release_lock() 시스템 콜을 제공한다. 병행 프로세들의 조정을 처리하는 이런 유형의 시스템 콜은 6장과 7장에서 상세히 논의된다.
프로세스 제어는 너무 많은 측면과 다양성이 있으므로 우리는 이러한 개념들을 명확히 하기 위해 단일 태스킹 시스템과 다중 태스킹 시스템의 두 예를 사용할 것이다.


통신
통신 모델에는 메세지 전달과 공유 메모리의 두 가지 일반적인 모델이 있다. 메세지 전달 모델에서는 통신하는 두 프로세스가 정보를 교환하기 위하여 서로 메세지를 주고받는다. 메세지는 두 프로세스 사이에 직접 교환되거나 우편함을 통하여 간접적으로 교환 될 수 있다. 통신이 이루어지기 전에 연결이 반드시 열려야 한다. 상대 통신자가 동일한 CPU에 있는 프로세스이든지 또는 통신 네트워크에 의해 연결된 다른 컴퓨터에 있는 프로세스이든지 간에 그 이름을 반드시 알고 있어야 한다. 네트워크의 각 컴퓨터는 호스트 이름을 가지며, 각 컴퓨터는 이들 이름으로 일반적으로 알려져 있다. 맟찬가지로, 각프로세스는 프로세스 이름을 가지고 있으며, 이 이름은 운영체제가 그 프로세스를 가리키는 데 사용될 수 있다. get_hostod()와 get_precessid() 시스템 콜은 이러한 변환을 수행한다. 이들 식별자는 그 후 시스템의 통신 모델에 따라 파일 시스템에 의해 제공되는 범용의 open과 close호출에 전달되거나, 특정 open_connections()과 close_connection()호출에 자신의 허가를 제공한다. 연결을 받아들일 프로세스들의 대부분은 특수목적의 데몬으로서 이들은 그러한 목적을 위해 제공된 시스템 프로그램들이다. 그들은 연결을 위해 대기호출을 수행하고 연결이 루어질 때 깨어난다. 클라이언트로 안ㄹ려진 통신의 출발지와 서버로 알려진 수신 데몬은 이어 read_message()와 wrtie_message()시스템 콜에 의해 메세지들을 교환한다. close_connections() 호출은 통신을 종료한다.
공유 메모리 모델에서, 프로세스는 다른 프로세스가 소유한 메모리 영역에 대한 접근을 위해 shared_momery_create()와 shared_memory_attach() 시스템 콜을 사용한다. 정상적으로 운영체제는 한 프로세스가 다른 프로세스의 메모리에 접근하는 것을 막으려고 한다는 것을 기억하라. 공유 메모리는 두 개 이상으 프로세스가 이러한 제한을 제거하는 데 동의할 것을 필요로 한다. 그런 후, 이들 프로세스는 이러한 공유 영역에서 데이터를 일고 씀으로써 정보를 교환할 수 있다. 데이터의 형식은 운영체제의 제어하에 있는 것이 아니라 이들 프로세스에 의해 결정된다. 프로세스는 또한 동일한 위치에 동시에 쓰지 않도록 보장할 책임을 진다. 이러한 기법은 6장에서 농의한다. 우리는 또한 프로세 모델의 변형으로 디폴트로 메모리를 공유하는, 스레드를 살펴본다. 스레드는 4장에서 다루어질 것이다.
이러한 두가지 방법은 운영체제에서 보편적이며, 시스템 대부분은 둘 다 구현한다. 메세지 전달은 소량의 데이터를 교환할 때 유용한데, 이는 피해야 할 출돌이 없기 때문이다. 메세지 전달은 또한 컴퓨터 간의 통신을 위해 메모리 공유보다 구현하기가 쉽다. 공유 메모리는 한 컴퓨터 안에서 메모리 전송 속도로 수행할 수 있으므로 최대 속도와 편리한 통신을 허용한다. 그렇지만, 보호와 동기화 부분에서 여러 문제점을 가지고 있다.
//보호 내용 생략
시스템 서비스
현대 시스템의 또 다른 면은 시스템 서비스의 집합체이다. 논리적인 커퓨터 계층 구조를 나타내고 있는 그림 1.1을 기억해보자. 하취위 수준은 하드웨어이고, 다름은 운영체제 그다음은 시스템 서비스, 그리고 마지막으로 응용 프로그램이다. 시스템 서비스는, 시스템 유틸리티로도 알려진, 프로그램 개발과 실행을 위해 더 편리한 환경을 제공한다. 글들 중 몇몇은 단순히 시스템 콜에 대한 사용자 인터페이스이며, 반면에 나머지는 휠씬 더복잡하다. 이들은 다음 몇 가지 범주로 분류할 수 있다.
- 파일 관리: 이들 프로그램은 파일과 디렉터리 생성, 삭제, 복사, 개명, 인쇄, 열거하고, 그리고 일반적으로 조작한다.
- 상태 정보: 어떤 프로그램들은 단순히 시스템에게 날짜, 시간, 사용 가능한 메모리와 디스크 공간의 양, 사용자 수, 혹은 이와 비슷한 상태 정보를 묻는다. 다른 프로그램들은 더 복잡하여 상세한 성능, 로깅 및 디버깅 정보를 제공한다 .통상 이 프로그램들은 정보를 단말기나 다른 출력 장치 혹은 파일로 포맷하여 인쇄하거나 GUI에 표시한다. 몇몇 시스템은 환경 설정 정보를 저장하고 검색할 수 있는 등록기능을 지원하기도 한다.
- 파일 변경: 디스크나 다른 저장 장치에 저장된 파일의 내용을 생성하고 변경하기 위해 다수의 문자 편집기를 사용할 수 있ㅌ다. 파일의 내용을 검색하거나 변환하기 위한 특수 명령어가 제공되기도 한다.
- 프로그래밍 언어 지원: 일반적인 프로그래밍 언어들에 대한 컴파일러, 어셈블러, 디버거 및 해석기가 종종 운영체제와 함께 사용자에게 제공되거나 별도로 다운로드 받을 수 있다.
- 프로그램 적재와 수행: 일단 프로그램이 어셈블되거나 컴파일된 후, 그것이 수행되려면 반드시 메모리에 적재되어야 한다. 시스템은 절대 로더, 재배치 기능 로더, 링키지 에디터와 중첩 로더등을 제공할 수 있다. 또한 고급어나 기계어를 위한 디버깅 시스ㅔㅁ도 필요하다.
- 통신: 이들 프로그램은 프로세스, 사용자, 그리고 다른 컴퓨터 시스템들 사이에 가상 접속을 이루기 위한 기법을 제공한다. 이들 프로그램은 사용자가 다른 사용자 화면으로 메세지를 전송하거나, 웹페이지 이곳저곳을 일거나, 전자 우편 메세지를 보내거나, 원거리에 로그인하거나, 한기계에서 다른 기계로 파일을 전송할 수 있게 한다.
- 백그라운드 서비스: 모든 범용 시스템은 부트할 때 특정 시스템 프로그램을 시작시킬 수 있는 방법을 가지고 있다. 이러한 프로세스 중 일부는 자신들의 할 일을 완수하면 종료하는 반면에, 일부는 시스템이 정지될 때까지 계속해서 실행되는 프로세스도 존재한다. 항상 실행되는 시스템 프로그램 프로세스는 서비스, 서브시스템, 또는 데몬으로 알려져 있다. 예를들어 네트워크 데몬과 같이 연결 요청을 올바른 프로세스에게 연결해 주기 위하여 네트워크 연결을 청취하는 서비스가 필요하다,
운영체제 대부분은 시스템 프로그램과 함께 일반적인 문제점을 해결하거나 일반적인 연산을 수행하는 데 유용한 프로그램들도 제공한다. 이러한 응용 프로그램에는 웹브라우저, 워드프로세서와 텍스트 포맷터 등등 포함된다.
사용자 대부분이 보는 운영체제의 관점은 실제의 시스템 골에 의해서보다는 시스템 프로그램과 응용에 의해 정의된다. 사용자 PC를 생각해보자. 자신의 컴퓨터가 macOS운영체제를 수행할 때는, 사용자는 마우스와 윈도 인터페이스로 조작하는 GUI를 볼 것이다. 한편 하나의 원도우에 UNIX쉘을 수행시킬 수 도 있다. 이둘은 모두 동일한 시스템 콜을 사용하지만, 이호출은 다르게 보이고 다른 방법으로 동작한다. 사용자관점을 혼란스럽게 마들기 위하여 macOS에서 Windows를 이중 부팅시키는 사용자를 생각해보자. 같은 하드웨어를 사용하는 동일한 사용자는 이제 완전히 다른 두개의 인터페이스와 동일한 물리지거 자원을 이용하는 두개의 응용 집한을 가지게 된다. 같은 하드웨어상에서 사용자는 여러 사용자 인터페이스에 순차적 또는 병행하게 노출된다.
운영체제 10판(Abraham Silberschatz)을 읽고 정리한 글입니다. 일부 내용은 생략 되었습니다.

운영체제 서비스
운영체제는 프로그램 실행 환경을 제공한다. 운영체제는 프로그램과 그 프로그램의 사용자에게 특정 서비스를 제공한다. 물론 운영체제마다 제공하는 서비스는 다르지만 공통된 부류가 있다는 것을 발견할 수 있다. 운영체제가 제공하는 서비스는 다음과 같다.
- 사용자 인터페이스(user interface): 거이 모든 운영체제는 사용자 인터페이스를 제공한다. (GUI, 터치 스크린, CLI)
- 프로그램 수행(program executive): 시스템은 프로그램을 메모리에 적재해 실행 할 수 있어야 한다. 프로그램은 정상적이든, 혹은 비정상적인든 실행을 끝낼 수 있어야 한다.
- 입출력 연산(I/O operation): 수행 중인 프로그램은 입출력을 요구할 수 있다. 이러한 입출력에는 파일 혹은, 입출력 장치가 연관될 수 있다. 특정 장치에 대해서는 특수한 기능(네트워크 인터페이스에서 일거나 파일 시스템에 쓰기 등)이 요구될 수 있다. 효율과 보호를 위해, 사용자들은 통상 입출력 장치를 직접 제어할 수 없다. 따라서 운영체제가 입출력 수행의 수단을 제공해야 한다.
- 파일 시스템 조작(file system manipulation): 파일 시스템은 특히 중요한 분야이다. 명백히, 프로그램은 파일을 읽고 쓸 필요가 있다. 프로그램은 또한 이름에 의해 파일을 생성하고 삭제할 수 있고 지정된 파일을 찾을 수 있어야하고 파일의 정보를 열거할 수 있어야 한다. 마지막으로 몇몇 프로그램은 파일 소유권에 기반을 둔 권한 관리를 이용하여 파일이나 디렉터리의 접근을 허가하거나 거부할 수 있게 한다. 많은 운영체제들은 때로는 개인의 선택에 따라 그리고 때로는 특정 특성과 성능 특성을 제공하기 위하여 다양한 파일 시스템을 제공한다.
- 통신(communication): 한 프로세스가 다른 프로세스의 정보를 교환해야 할 필요가 있는 여러 상황이 있다. 이러한 통신을 수행하는 두가지 중요한 방법이 있다. 첫번째는 동일한 컴퓨터에서 수행되고 있는 프로세스들 사이에서 일어나고, 두번째는 네트워크에 의해 함께 묶여 있는 서로 다른 컴퓨터 시스템상에서 수행되는 프로세스들 사이에서 일어난다.통신은 공유 메모리를 통해서 구현될 수도 있고, 메세지 전달 기법을 사용하여 구현될 수 있는데, 후자의 경우 정보의 패싴들이 운영체제에 의해 프로세스들 사이를 이동한다.
- 오류 탐지(error detection): 운영체제는 모든 가능한 오류를 항상 의식하고 있어야한다. 오류는 CPU, 메모리 하드웨어, 입출력 장치, 또는 사용자 프로그램에서 일어날 수 있다. 운영체제는 올바르고 일관성 있는 계싼을 보장하기 위해 각유형의 오류에 대해 적당한 조치를 해야 한다. 물론 운영체제가 오류에 어덯게 반응하며 수정하는가에 다양한 변종이 존재한다. 디버깅 설비는 시스템을 효율적으로 사용할 수 있는 사용자와 프로그래머의 능력을 향상시킨다.
다음은 시스템 자체의 효율적인 동작을 보장하기 위한 운영체제의 기능이다.
- 자원 할당(resource allocation): 다수의 프로세스나 다수의 작업이 동시에 실행될때, 그들 각각에 자원을 할당해 주어야 한다. 운영체제는 여러 가지 다른 종류의 자원을 관리한다. 어떤 것은 특수한 할당 코드를 가질 수 있지만, 다른 것은 훨씬 일반적인 요청과 방출 코드를 가질 수 있다. 예를 들면, CPU를 최대한 효율적으로 이용하기 위해 운영체제는 CPU 스케줄링 루틴이 CPU의 속도, 반드시 실행해야할 프로세스들, CPU 스케줄링 루틴이 CPU의 속도, 반드시 실행해야 할 프로세스들, CPU의 처리 코어의 개수와 다른 요이들을 고려하도록 해야 한다. 또한 프린터, USB 저장장치 드라이브 및 다른 주변 장치를 할당하는 루틴이 있을 수 있다.
- 기록 작성(logging): 우리는 어떤 프로그램이 어떤 종류의 컴퓨터 자원을 얼마나 많이 사용하는지를 추적할 수 있길 원한다. 이와 같은 기록 관리는 회계, 또는 단순히 사용 통계를 내기 위해 사용된다. 사용 통계는 컴퓨팅 서비스를 개선하기 위해 시스템을 재구성하고자 하는 시스템 관리자에게 귀중한 자료가 될 수 있더.
- 보호(protection)와 보안(security): 다중 사용자 컴퓨터 시스템 또는 네트워크로 연결된 컴퓨터 시스템에 저장된 정보의 소유자는 그 정보의 사용을 통제하길 원한다. 서로 다른 여러 프로세스가 병행하게 수행될 때, 한 프로세스가 다른 프로세스나 운영체제 자체를 방해해서는 안 된다. 보호는 시스템 자원에 대한 모든 접근이 통제되도록 보장하는 것을 필요로한다. 외부로부터의 시스템 보안 또한 중요하다. 이러한 보안은 각 사용자가 자원에 대한 접근을 원할 때 통상 패스워드를 사용해서 시스템에게 자기 자신을 인증하는 것으로부터 시작된다. 보안은 네트워크 어댑터등과 같은 외부 입출력 장치들을 부적합한 접근 시도로 부터 지키고, 침입의 탑지를 위해 모든 접속을 기록하는 것으로 범위를 넓힌다. 만약 시스템이 보호되고 보안이 유지되려면, 시스템 전체에 걸쳐 예벙책이 재정되어야 한다. 하나의 사슬은 가장 약한 연결 고리만큼만 강한 법이다.
사용자와 운영체제 인터페이스
명령 인터프리터
Linux, UNIX 및 Windows를 포함한 운영체제 대부분은 명령 인터프리터를 프로세스가 시작되거나 사용자가 처음 로그인 할 때 수행되는 특수한 프로그램으로 취급한다. 선택할 수 있는 여러 명령 인터프리터를 제공하는 시스템에서 이 해석기는 셸이라고 불린다.

명령 인터프리터의 중요한 기능은 사용자가 지정한 명령을 가져와서 그것을 수행하는 것이다. 이 수준에서 제공된 많은 명령은 파일을 조작한다. 즉, 생성, 사젝, 리스트, 프린트, 복사, 수행 등을 한다. UNIX 시스템에서 사용 가능한 다양한 셸은 이런 방식으로 실행된다. 이 명령어들은 두가지 일반적인 방식으로 구현될 수 있다.
한 가지 방법은 명령 인터프리터 자체가 명령을 실핼할 코드를 가지고 있는 경이다. 예를 들면, 한 파일을 삭제하기 위한 명려은 명령 인터프리터가 자신의 코드의 한 부분으로 분기하고, 그 코드 부분이 매개변수를 설정하고 적절한 시스템 콜을 한다. 이 경우 제공될 수 있는 명령의 수가 명령 인터프리터의 크기를 결정하는데, 그 이유는 각 명령이 자신의 구현 코드를 요구하기 때문이다.
여러 운영체제 중에 UNIX에 의해 사용되는 다른 대안의 접근 방법은, 시스템 프로그램에 의해 대부분의 명령을 구현하는 것이다. 이러한 경우 명령 인터프리터는 전혀 그명령을 알지 못한다. 단지 메모리에 적재되어 실행될 파일을 식별하기 위해 명령을 사용한다. 따라서 파일을 삭제하는 UNIX명령은 rm이라 불리는 파일을 찾아서, 그 파일을 메모리에 적재하고, 그것을 매개변수 file.txt로 수행한다. rm 명령고 관련된 로직은 rm이라는 파일 내의 코드로 완전하게 정의한다. 이러한 방법으로 프로그래머는 적합한 프로그램 로직을 가진 새로우 파일을 생성함으로써 시스템에 새로운 명령을 쉽게 추가할 수 있다. 명령 인터프리터 프러그램은 이제 아주 작아질 수 있으며, 새로운 명령을 추가하기 위해 변경될 필요가 없다.
그래픽 기반 사용자 인터페이스
운영체제와 접촉하는 두 번째 방식은 사용자 친화적인 그래픽 기반 사용자 인터페이스 또는 GUI를 통하는 방식이다. 이 방식에서는 명령어 라인 인터페이스를 통하여 사용자가 직접 명령어를 입력하는 것이 아린라 데스크톱이라고 특징지어지는 마우스를 기반으로 하는 윈도 메뉴 시스템을 사용한다. 사용자는 마우스를 움직여 마우스 포인터를 프로그램, 파일, 시스템 기능들을 나타내는 화면상의 이미지에 위치시킨다. 마우스 포인터의 위치에 따라 아무스 버튼을 누름으로써 프로그램을 호출하거나 파일 혹은 디렉토리를 선택할 수도 있고, 또는 명령을 포함한 메뉴를 잡아당길 수 도 있다.
전통적으로 UNIX 시스템은 명령어 라인 인터페이스가 주류를 이루었다 그러나 K Desktop Environment 및 GNU 프로젝트에 의한 GNOME 데스크톱과 같은 다양한 공개 소스 프로젝트 등을 통해 디자인이 크게 향상된 다양한 GUI인터페이스를 사용할 수 있게 되었다. KDE 및 GNOME 데스크톱은 Linux 및 다양한 UNIX 시스템에서 실해되며 공개 소스 라이선스로 사용할 수 있다. 즉 특정 라이선스 조건하에서 소스 코드를 읽고 수정하는 것이 쉽다는 것을 의미한다.
터치스크린 인터페이스
대부분의 모바일 시스템에서 명령 라인 인터페이스나 마우스 및 키보드 시스템이 실용적이지 않기 때문에 스마트폰 및 휴대용 태블릿 컴퓨터는 일반적으로 터치스크린 인터페이스를 사용한다. 사용자는 터치스크린에서 손가락을 누르거나 스와이프 하는 등의 제스처를 취하여 상호 작용한다. 초기 스마트폰에는 물리적 키보드가 포함되어 있었지만 이제 스마트폰과 태블릿의 대부분은 터치스크린에서 키본드를 시뮬레이션 한다. 예를 들어 iPad iPhone은 모두 Springboard 터치스크린 인터페이스를 사용한다.

시스템 콜
시스템 콜은 운영체제에 의해 사용 가능하게 된 서비스에 대한 인터페이스를 제공한다. 특정 저수준 작업은 이셈블리어 명령을 사용하여 작성되어야 하더라도 이러한 호출은 일반적으로 C, C++언어로 작성된 함수 형태로 제공한다.
예제
운영체제가 어떻게 시스템 콜을 사용할 수 있게 만드는지에 대해 논의하기 전에 시스템콜이 어떻게 사용되는지를 설명하는 예를 보도록하 하자. 한 파일로부터 데이터를 읽어서 다른 파일로 복사하는 간단한 프로그램을 작성한다고 가정해 보자. 프로그램이 필요로 하는 첫 번째 입력은 두 개의 파일, 즉 입력 파일과 출력 파일의 이름일 것이다. 이 이름들은 운영체제 설계에 따라 여러 가지 방법으로 지정할 수 있다. 한 가지 방법은 명령의 일부로 두 파일의 이름을 전달하는 것이다.
cp test cp_test
이 명령은 test 파일을 cp_test에 복사(생성)한다. 두 번째 방법은 프로그램이 사용자에게 이름을 요청하는 것이다. 대화형 시스템에서 이 방법은 일련의 시스템 콜이 필요하다. 먼저 화며에 프롬프트 메세지를 작성한 다음 키보드에서 두 파일의 이름을 지정하는 문자를 읽는다. 마우스 기반 및 아이콘 기반 시스템에서 파일 이름 메뉴는 일반적으로 창에 표시된다. 그란 다름 사용자는 마우스를 사용하여 소스 이름을 선책할 수 있으며 대상 이름을 지정할 수 있는 창을 열 수 있다. 이 일련의 작업을 위해서는 많은 I/O 시스템 콜이 필요하다.
일단 두 개의 파일 이름이 얻어지면, 프로그램은 반드시 입력 파일을 오픈 하고, 출력 파일을 생성한 후 오픈한다. 각각의 이러한 연산은 또 다른 시스템 콜을 필요로 하며, 각 시스템 콜에서 오류가 발생하면 처리되어야 한다, 예를 들어, 프로그램이 읿력 파일을 오른하려고 할 때, 그 이름을 갖는 파일이 존재하지 않거나, 그파일에 대한 접근이 금지되어 있는 것을 발견 할 수 있다. 이러한 경우 프로그램은 에러 메세지를 출력하고(또 다른 시스템 콜이다.), 그리고 비정상적으로 종료(또 다른 시스템 콜이다)한다. 망약 입력 파일이 존재하면 새로운 출력 파일을 생성해야 한다. 이때 동일한 이름을 가진 출력 파일이 이미 존재하는 경우가 있다. 이러한 상황은 프로그램을 중단(또 다른 시스템 콜)하거나, 기존 파일을 삭제한 후, 해로운 파일을 생성할 수도 있다. 대화형 시스템에서 또 다른 방법은 기존의 파일을 대체할 것인지, 혹은 프로그램을 중단할 것인지를 사용자에게 물어보는 (시스템 콜)것이다.
이제 두 개의 파일이 준비되면, 입력 파일로 부터 입력 파일로부터 읽어서(시스템 콜), 출력 파일에 기록(시스템 콜)하는 루프에 들어가게 된다. 각 읽기와 쓰기는 가능한 여러가지 오류 상황의 정보를 반환해야 한다. 입력에서, 프로그램이 파일의 끝에 도달하거나 읽기 중에 하드웨어 오류가 발생할 수 도 있다. 쓰기 연산 시 출력 장치에 따라 여러 가지 오류들이 발생할 수도 있다.

마지막으로 전체 파일이 복사된 후, 프로그램은 두 개의 파일을 닫고(시스템 골), 결국 정상적으로 종료하게 된다(시스템 콜).
응용 프로그래밍 인터페이스
방금 파일을 복사하는 상황과 같이, 간단한 프로그램이라도 운영체제의 기능을 아주 많이 사용하게 된다. 종종 초당 수천개의 시스템 콜을 수행하기도 한다. 대부분의 응용 개발자들은 응용 프로그래밍 인터페이스(API)에 따라 프로그램을 설계한다. API는 각 함수에 전달되어야 할 매개변수들과 프로그래머가 기대할 수 있는 반환 값을 포함하여 응용 프로그램어가 사용 가능한 함수의 집합을 명시한다. 응용 프로그매어가 사용 가능한 가장 흔한 세 가지 API는 Windows 시스템을 위한 Window API, POSIX 기반 시스템을 위한 POSIX API, Java 가상 기계에서 실행될 수 있는 프로그램을 위한 Java API이다. 프로그래머는 운영체제가 제공하는 코드의 라이브러리를 통하여 API를 활용한다. UNIX와 Linux시스템에서 C언어로 작성된 프로그램을 위해서 제공되는 라이브러리는 libc로 불린다. 특별히 언급하지 않는 한 이 책에서 사용되는 시스템 콜의 이름은 일반적인 예임을 명심하라. 모든 운영체제는 고유의 시스템 콜 이름을 가진다.
뒤에는 API를 구성하는 함수들은 통상 응용 프로그래머를 대신하여 실제 시스템콜을 호출한다. 예를 들면 Window함수 CreateProcess()는 실제로 Window 커널의 NTCreateProcess() 시스템 콜을 부른다.
왜 응용 프로그래머는 실제 시스템 콜을 부르는 것보다 API에 따라 프로그래밍 하나는 것을 선호하는가? 그렇게 하는 데에는 몇 가지 이유가 있다. 한 가지 이점은 프로그램의 호환성과 관련 있다. API에 따라 프로그램을 설계하는 응용 프로그래머는 자신의 프로그램이 같은 API를 지원하는 어느 시스템에서건 컴파일되고 실행된다는 것을 기대할 수 있다. 게다가 실제 시스템 콜은 종종 좀 더 자세한 명세가 필요하고 프로그램상에서 작업하기가 응용 프로그래머에게 가용한 API보다 더 어렵다. 그럼에도 불구하고 API 함수를 호출하는 것과 커널의 관련된 시스템 콜을 호출하는 것에는 강한 상관관계가 존재한다. 사실 대부분의 POSIX와 Windows API는 Linux 및 Windows 운영체제가 제공하는 고유의 시스템과 유사하다.
시스템 콜을 처리하는 데 있어 중요한 또 다른 요소는 실행시간 환경(RTE)이다.(런타임이라고 불린다) 컴파일러 또는 인터프리터를 포함하여 특정 프로그래밍 언어로 작성된 응용 프로그램을 실행하는 데 필요한 전체 소프트웨어 제품군과 라이브러리 또는 로더와 같은 다른 소프트웨어를 모두 포함한다. RTE는 운영체제가 제공하는 시스템 콜에 대한 연결고리 역활을 하는 시스템 콜 인터페이스를 제공한다. 이 시스템 콜 인터페이스는 API 함수의 호출을 가로채어 필요한 운영체제 시스템 콜을 부른다. 통상 각시스템 콜에는 번호가 할당되고 시스템 콜 인터페이스는 이 번호에 따라 색인되는 테이블을 유지한다. 시스템 콜 인터페이스는 의도하는 시스템 콜을 부르고 시스템 콜의 상태와 반환값을 돌려준다.
호출자는 시스템 콜이 어덯게 구현되고 실행 중 무슨 작업을 하는지 아무것도 알 필요가 없다. 호출자는 단지 API를 준수하고 시스템 콜의 결과로서 운영체제가 무엇을 할 것인지만 이해하면 된다. 따라서 운영체제 인페이스에 대한 대부분의 자세한 내용은 API에 의해 프로그래머로 부터 숨겨지고 RTE에 의해 관리된다.

시스템 콜은 사용되는 컴퓨터에 따라 다른 방법으로 발생한다. 종종, 단순히 원하는 시스템 콜이 무엇인지 보다 더 많은 정보가 요구될 수도 있다. 필요한 정보의 유형과 양은 특정 운영체제와 호출에 따라 다양하다. 예를 들면, 입력을 받아들이기 위해, 입력으로 사용될 파일이나 장치와 함께 읽어 들인 데이터를 저장할 메모리 버퍼의 주소와 길이를 명시할 필요가 있다. 물론 장치나 파일 그리고 길이는 시스템 콜에 암묵적일 수 있다.
운영체제에 매개변수를 전달하기 위해서 세 가지 일반적인 방법을 사용한다. 가장 간단한 방법은 매개변수를 레지스터 내에 전달하는 것이다. 그러나 어떤 경우는 레지스터 보다 더 많은 매개변수가 있을 수 있다. 이러한 경우에 매개변수는 메모리 내의 블록이나 테이블에 저장되고 , 블록의 주소가 레지스터 내에 매개변수로 전달된다.

Linux는 이러한 접근법을 조합하여 사용한다. 5개 이하의 매개변수가 있으면 레지스터가 사용된다. 매개변수의 개수가 5개를 넘으면 블록 방법이 사용된다. 매개 변수는 프로그램에 의해 스택에 넣어질 수도 있고, 운영체제에 의해 꺼내진다. 일부 운영체제는 블록이나 스택 방법을 선호하는데, 이들 접근법은 전달되는 매개변수들의 개수나 길이를 제하지 않기 때문이다.
표준 API 예시
표준 API의 예로서 UNIX와 Linux 시스템에서 제공되는 read()함수를 고려하자. 이 함수에 대한 API는 명령어 라인에 다음 명령어를 입력하여 출력되는 man 페이지에서 얻을 수 있다.
man read

read() 함수를 사용하는 프로그램은 unistd.h 헤더 파일을 포함시켜야 하는데, 이 파일에는 무엇보다도 ssize_t와 size_t 데이터 유형이 정의되어 있다. read()함수에게 전달되는 인자는 다음과 같다.
- int fd - 읽으려는 파일 디스크립터
- void *buf - 데이터를 읽어 들일 버퍼
- size_t count - 버퍼로 읽어 들일 수 있는 최대 바이트 수
읽기가 성공한 경우 읽어 들인 바이트 수가 반환된다. 반환 값이 0인 경우는 파일의 끝에 도달했다는 것을 의미한다. 오류가 발생한 경우 read()는 -1을 반환한다.
시스템 콜의 유형
싯템 콜은 다섯 가지의 중요한 범주, 즉 프로세스 제어, 파일 조작, 장치 조작, 정보 윶 보수와 통신, 보호등으로 묶을 수 있다. 이후 운영체제에 의해 제공되는 여러가지 유형의 시스템 콜을 간단히 논의한다. 이들 시스켐 콜의 대부분은 뒷장에서 논의 되는 개념과 기능들을 지원하거나 그들에 의해 지원된다.

프로세스 제어
실행 중인 프로그램은 수행을 정상적으로 또는 비정상적을 멈출 수 있어야 한다. 만약 현재 실행중인 프로그램을 비정상으로 중지하기 위해 시스템 콜이 호출되거나 프로그램에 문제가 발생해 오류 트랩(trap)을 유발할 경우, 때때로 메모리 덤프가 행해지고 오류 메세지가 생성된다. 이 덤프는 특별한 로그 파일이나 디스크에 기록되고 문제의 원인을 결정하기 위해 디버거에 의해 검사될 수 있다. 정상이거나 비정상인 상황에서, 운영체제는 명령 인터프리터로 제어를 전달해야 한다. 명령 인터프리터는 이어 다음 명령을 읽는다.

대화식 시스템에서 명령 인터프리터는 단순히 다음 명령을 계속 수행하명, 사용자가 오류에 응답하는 적절한 명령을 내릴 것을 가정한다. GUI시스템에서는 팝업 윈도가 사용자에게 오류를 알리고 지시를 기다린다. 일부 시스템에서는 오류가 발생할 경우 특별한 복구 행위를 지시하는 제어 카드를 혀용한다 만약 프로그램이 입력에서 오류를 발견하고 비정상으로 중료하기를 원한 다면, 프로그램은 오류 수준을 정의하기를 원할 수도 있다. 더 높은 등급의 오류 매개변수를 전달함으로써 더 치명적인 오류를 나타낼 수 있다. 이렇게 함으로써 정상 종료를 등급 0의 오류로 정의하여 정상 종료와 비정상 종료를 결합할 수도 있다. 명령 인터프리터, 혹은 다음 프로그램은 이오류 등급을 사용하여 다음 행동을 자동으로 결정할 수 있다.
한 프로그램을 실행하고 있는 프로세스가 다른 프로그램가 다른 프로그램을 적재하고 실행하기를 원할 수 있다. 이기능은 명령 인터프리터가 사용자 명령 또는 마우스의 클릭을 통하여 지시된 프로그램을 실행하는 것을 허용한다. 여기서 흥미 있는 질문은 적재된 프로그램이 종료되었을 때 어디로 되돌려 주느냐 하는 것이다. 이 질문은 기존 프로그램이 유실될지, 보관될지, 새로운 프로그램과 병행하게 실행을 계속하도록 허용할 것인지 하는 문제와 관련 있다.
만약 새로운 프로그램이 종료되었을 때 제어가 기존 프로그램으로 되돌아간다면, 우리는 반드시 기존 프로그램의 메모리 이미지를 보관해야 한다. 따라서 우리는 실질적으로 한 프로그램이 다른 프로그램을 호출하는 기법을 만든 셈이 된다. 만약 두 프로그램이 병행하게 수행된다면, 우리는 다중 프로그램 될 새로운 프로세스를 생성한 것이다. 종종, 이런 특정 목적을 위한 시스템 콜이 있다.(create_process())
만약 우리가 새로운 잡이나 프로세스, 혹은 잡들이나 프로세스들의 집합을 생성한다면, 우리는 그들의 실행을 제정할 수 있어야 한다. 이러한 제어는 잡의 우선순위, 최대 허용 실행 시간 등을 포함하여 잡 혹은 프로세스의 속성들을 결정하고 재설정할 수 있는 능력이 필요하다.
빈번하게 둘 이상의 프로세들은 데이터를 공유한다. 공유되는 데이터의 일관성을 보장하기 위해서 운영체제는 종종 프로세스가 공유 데이터를 잠글 수 있는 시스템콜을 제공한다. 그러면 잠금이 해제될 때까지 어느 프로세스도 데이터에 접근할 수 없게된다. 통산 그런 시스템은 acquire_lock()과 release_lock() 시스템 콜을 제공한다. 병행 프로세들의 조정을 처리하는 이런 유형의 시스템 콜은 6장과 7장에서 상세히 논의된다.
프로세스 제어는 너무 많은 측면과 다양성이 있으므로 우리는 이러한 개념들을 명확히 하기 위해 단일 태스킹 시스템과 다중 태스킹 시스템의 두 예를 사용할 것이다.


통신
통신 모델에는 메세지 전달과 공유 메모리의 두 가지 일반적인 모델이 있다. 메세지 전달 모델에서는 통신하는 두 프로세스가 정보를 교환하기 위하여 서로 메세지를 주고받는다. 메세지는 두 프로세스 사이에 직접 교환되거나 우편함을 통하여 간접적으로 교환 될 수 있다. 통신이 이루어지기 전에 연결이 반드시 열려야 한다. 상대 통신자가 동일한 CPU에 있는 프로세스이든지 또는 통신 네트워크에 의해 연결된 다른 컴퓨터에 있는 프로세스이든지 간에 그 이름을 반드시 알고 있어야 한다. 네트워크의 각 컴퓨터는 호스트 이름을 가지며, 각 컴퓨터는 이들 이름으로 일반적으로 알려져 있다. 맟찬가지로, 각프로세스는 프로세스 이름을 가지고 있으며, 이 이름은 운영체제가 그 프로세스를 가리키는 데 사용될 수 있다. get_hostod()와 get_precessid() 시스템 콜은 이러한 변환을 수행한다. 이들 식별자는 그 후 시스템의 통신 모델에 따라 파일 시스템에 의해 제공되는 범용의 open과 close호출에 전달되거나, 특정 open_connections()과 close_connection()호출에 자신의 허가를 제공한다. 연결을 받아들일 프로세스들의 대부분은 특수목적의 데몬으로서 이들은 그러한 목적을 위해 제공된 시스템 프로그램들이다. 그들은 연결을 위해 대기호출을 수행하고 연결이 루어질 때 깨어난다. 클라이언트로 안ㄹ려진 통신의 출발지와 서버로 알려진 수신 데몬은 이어 read_message()와 wrtie_message()시스템 콜에 의해 메세지들을 교환한다. close_connections() 호출은 통신을 종료한다.
공유 메모리 모델에서, 프로세스는 다른 프로세스가 소유한 메모리 영역에 대한 접근을 위해 shared_momery_create()와 shared_memory_attach() 시스템 콜을 사용한다. 정상적으로 운영체제는 한 프로세스가 다른 프로세스의 메모리에 접근하는 것을 막으려고 한다는 것을 기억하라. 공유 메모리는 두 개 이상으 프로세스가 이러한 제한을 제거하는 데 동의할 것을 필요로 한다. 그런 후, 이들 프로세스는 이러한 공유 영역에서 데이터를 일고 씀으로써 정보를 교환할 수 있다. 데이터의 형식은 운영체제의 제어하에 있는 것이 아니라 이들 프로세스에 의해 결정된다. 프로세스는 또한 동일한 위치에 동시에 쓰지 않도록 보장할 책임을 진다. 이러한 기법은 6장에서 농의한다. 우리는 또한 프로세 모델의 변형으로 디폴트로 메모리를 공유하는, 스레드를 살펴본다. 스레드는 4장에서 다루어질 것이다.
이러한 두가지 방법은 운영체제에서 보편적이며, 시스템 대부분은 둘 다 구현한다. 메세지 전달은 소량의 데이터를 교환할 때 유용한데, 이는 피해야 할 출돌이 없기 때문이다. 메세지 전달은 또한 컴퓨터 간의 통신을 위해 메모리 공유보다 구현하기가 쉽다. 공유 메모리는 한 컴퓨터 안에서 메모리 전송 속도로 수행할 수 있으므로 최대 속도와 편리한 통신을 허용한다. 그렇지만, 보호와 동기화 부분에서 여러 문제점을 가지고 있다.
//보호 내용 생략
시스템 서비스
현대 시스템의 또 다른 면은 시스템 서비스의 집합체이다. 논리적인 커퓨터 계층 구조를 나타내고 있는 그림 1.1을 기억해보자. 하취위 수준은 하드웨어이고, 다름은 운영체제 그다음은 시스템 서비스, 그리고 마지막으로 응용 프로그램이다. 시스템 서비스는, 시스템 유틸리티로도 알려진, 프로그램 개발과 실행을 위해 더 편리한 환경을 제공한다. 글들 중 몇몇은 단순히 시스템 콜에 대한 사용자 인터페이스이며, 반면에 나머지는 휠씬 더복잡하다. 이들은 다음 몇 가지 범주로 분류할 수 있다.
- 파일 관리: 이들 프로그램은 파일과 디렉터리 생성, 삭제, 복사, 개명, 인쇄, 열거하고, 그리고 일반적으로 조작한다.
- 상태 정보: 어떤 프로그램들은 단순히 시스템에게 날짜, 시간, 사용 가능한 메모리와 디스크 공간의 양, 사용자 수, 혹은 이와 비슷한 상태 정보를 묻는다. 다른 프로그램들은 더 복잡하여 상세한 성능, 로깅 및 디버깅 정보를 제공한다 .통상 이 프로그램들은 정보를 단말기나 다른 출력 장치 혹은 파일로 포맷하여 인쇄하거나 GUI에 표시한다. 몇몇 시스템은 환경 설정 정보를 저장하고 검색할 수 있는 등록기능을 지원하기도 한다.
- 파일 변경: 디스크나 다른 저장 장치에 저장된 파일의 내용을 생성하고 변경하기 위해 다수의 문자 편집기를 사용할 수 있ㅌ다. 파일의 내용을 검색하거나 변환하기 위한 특수 명령어가 제공되기도 한다.
- 프로그래밍 언어 지원: 일반적인 프로그래밍 언어들에 대한 컴파일러, 어셈블러, 디버거 및 해석기가 종종 운영체제와 함께 사용자에게 제공되거나 별도로 다운로드 받을 수 있다.
- 프로그램 적재와 수행: 일단 프로그램이 어셈블되거나 컴파일된 후, 그것이 수행되려면 반드시 메모리에 적재되어야 한다. 시스템은 절대 로더, 재배치 기능 로더, 링키지 에디터와 중첩 로더등을 제공할 수 있다. 또한 고급어나 기계어를 위한 디버깅 시스ㅔㅁ도 필요하다.
- 통신: 이들 프로그램은 프로세스, 사용자, 그리고 다른 컴퓨터 시스템들 사이에 가상 접속을 이루기 위한 기법을 제공한다. 이들 프로그램은 사용자가 다른 사용자 화면으로 메세지를 전송하거나, 웹페이지 이곳저곳을 일거나, 전자 우편 메세지를 보내거나, 원거리에 로그인하거나, 한기계에서 다른 기계로 파일을 전송할 수 있게 한다.
- 백그라운드 서비스: 모든 범용 시스템은 부트할 때 특정 시스템 프로그램을 시작시킬 수 있는 방법을 가지고 있다. 이러한 프로세스 중 일부는 자신들의 할 일을 완수하면 종료하는 반면에, 일부는 시스템이 정지될 때까지 계속해서 실행되는 프로세스도 존재한다. 항상 실행되는 시스템 프로그램 프로세스는 서비스, 서브시스템, 또는 데몬으로 알려져 있다. 예를들어 네트워크 데몬과 같이 연결 요청을 올바른 프로세스에게 연결해 주기 위하여 네트워크 연결을 청취하는 서비스가 필요하다,
운영체제 대부분은 시스템 프로그램과 함께 일반적인 문제점을 해결하거나 일반적인 연산을 수행하는 데 유용한 프로그램들도 제공한다. 이러한 응용 프로그램에는 웹브라우저, 워드프로세서와 텍스트 포맷터 등등 포함된다.
사용자 대부분이 보는 운영체제의 관점은 실제의 시스템 골에 의해서보다는 시스템 프로그램과 응용에 의해 정의된다. 사용자 PC를 생각해보자. 자신의 컴퓨터가 macOS운영체제를 수행할 때는, 사용자는 마우스와 윈도 인터페이스로 조작하는 GUI를 볼 것이다. 한편 하나의 원도우에 UNIX쉘을 수행시킬 수 도 있다. 이둘은 모두 동일한 시스템 콜을 사용하지만, 이호출은 다르게 보이고 다른 방법으로 동작한다. 사용자관점을 혼란스럽게 마들기 위하여 macOS에서 Windows를 이중 부팅시키는 사용자를 생각해보자. 같은 하드웨어를 사용하는 동일한 사용자는 이제 완전히 다른 두개의 인터페이스와 동일한 물리지거 자원을 이용하는 두개의 응용 집한을 가지게 된다. 같은 하드웨어상에서 사용자는 여러 사용자 인터페이스에 순차적 또는 병행하게 노출된다.