
저번 글에서 운영체제의 개요를 쭉 훑어보았다.
이번에는 운영체제의 구조를 한번 살펴보자.
운영체제는 프로그램과 그 프로그램의 사용자에게 특정 서비스를 제공한다.
운영체제의 서비스 종류는 아래와 같다.

자원 할당, 파일 접근과 같이 운영체제를 통해 사용할 수 있는 서비스에 대한 인터페이스를 제공한다.
프로그래밍을 할 때, 입출력 작업, 메모리 할당과 같은 요청은 시스템 콜 함수를 통해 운영체제에게 요청하여 이루어진다.
이러한 시스템 콜은 API(Application Programming interface)로 제공된다.
API는 각 함수에 전달되어야 할 매개 변수와 반환값을 포함하여 프로그래머가 사용 가능한 함수들의 집합을 명시한다.
프로그래머가 사용 가능한 가장 흔한 API 세 가지는 다음과 같다.

프로그래머가 시스템 콜 API를 호출하면, 시스템 콜 API가 내부적으로 시스템 콜을 호출하는 식으로 동작한다.
프로그래머는 API 명세에 맞게 호출만 하면, 내부 시스템 콜은 어떤식으로 동작하는지 몰라도 된다.


일반적으로 프로그램은 보조기억장치에 이진 실행 파일(a.out 또는 program.exe)로 존재한다.
프로그램을 실행하려면 프로그램을 메모리에 적재해서 프로세스로 배치되어야 한다.
프로그램 컴파일부터 실행까지의 과정을 한번 살펴보자.

그림의 동적 링크 라이브러리는 Windows에서 사용하는 DLL과 같은 "동적 라이브러리"를 말한다.
DLL을 사용하면, 프로그램 실행 중 필요한 부분만 적재할 수 있다는 것이다.
또한 여러 프로세스가 하나의 라이브러리를 공유하기 때문에, 메모리도 절약할 수 있다.
오브젝트 파일 및 실행 파일은 일반적으로 표준화된 형식을 가진다.
이 표준 형식은 프로그램에 대한 메타데이터를 포함하고 있다.
UNIX, Linux 시스템의 경우 이 형식을 "ELF(Executable and Linkable Format)"이라고 한다.
오브젝트 파일과 실행 파일 각각을 위한 별도의 ELF 형식이 사용된다.
Windows 시스템은 "PE(Portable Executable)" 형식을 사용하고 macOS는 "Mach-O" 형식을 사용한다.
기본적으로 한 운영체제에서 컴파일된 프로그램은 다른 운영체제에서 실행할 수 없다.
왜 그럴까?
각 운영체제는 고유한 시스템 콜 집합을 제공하기 때문이다.
운영체제간 시스템 콜이 어느정도 같더라도, 다른 장벽으로 인해 다른 운영체제에서 프로그램을 실행하기는 어렵다.
시스템의 low level 관점에서 아래와 같은 문제가 존재한다.
그러나 다음 세 가지 방법 중 한 가지를 사용하여 프로그램이 여러 운영체제에서 실행될 수 있게 만들 수 있다.
운영체제가 설치된 컴퓨터에서는 컴퓨터 전원이 켜지면 해당 운영체제를 사용할 수 있어야 한다.
즉, 메모리에 운영체제를 적재해야 하는데 커널의 위치를 어떻게 알 수 있을까?
저번 글에서 잠깐 말했듯이, "부트스트랩" 프로그램이 커널을 찾고 메모리에 적재한다.
그럼 부트스트랩은 어떻게 실행되는걸까?
컴퓨터 전원을 처음 켜면 BIOS라고 하는 비휘발성 펌웨어(보통 EEPROM)에 있는 부트스트랩이 실행되어 부팅 과정을 거친다.
커널을 메모리에 적재하는 작업을 "부팅"이라고 하며, 부팅 과정은 아래와 같다.
이 시점에서 시스템이 실행 중이라고 말할 수 있다.