Chapter 2. 운영체제 구조
운영체제는 사용자 및 프로그램에 서비스를 제공하여 프로그램 실행 환경을 제공한다.
이번 장에서 사용자, 프로그래머, 운영체제 설계자의 관점에서 바라본 운영체제의 세 가지 측면을 모두 살펴볼 것이다.
2.1 운영체제 서비스
운영체제는 프로그램 실행 환경을 제공하며, 프로그램과 그 프로그램의 사용자에게 특정 서비스를 제공한다.
- user interface : 거의 모든 운영체제는 사용자 인터페이스를 제공한다.
- GUI : 그래픽 사용자 인터페이스 / 터치 스크린 인터페이스 / CLI : 명령어 라인 인터페이스 ⇒ 일부 시스템은 둘 또는 세가지 모두의 형태를 제공한다.
- Program execution : 시스템은 프로그램을 메모리에 적재해 실행시킬수 있어야한다. 그리고 정상적이든, 비정상적이든 실행을 끝낼 수 있어야한다.
- I/O operation : 수행중인 프로그램은 입출려글 요구할 수 있다. 효율과 보호를 위해, 사용자들은 통상 입출력 장치를 직접 제어할 수 없다. 따라서 운영체제가 입출력 수행의 수단을 제공해야한다.
- 파일 시스템 조작: 프로그램은 파일을 읽고 쓸 필요가 있다. 운영체제는 다양한 파일 시스템을 제공한다.
- 통신(communication) : 통신을 수행하는 두가지 중요한 방법이 있다. 첫번째는 동일한 컴퓨터에서 수행되고 있는 프로세스들 사이에서 일어나고, 두번째는 네트워크에 의해 함께 묶여있는 서로 다른 컴퓨터 시스템 상에서 수행되는 프로세스 사이에서 일어난다. 통신은 공유 메모리를 통해 구현될수도, 메세지 전달 기법을 사용하여 구현될 수도 있는데, 후자의 경우 정보의 패킷들이 운영체제에 의해 프로세스들 사이를 이동한다.
- error detection : 운영체제는 모든 가능한 오류를 항상 의식하고 있어야한다.
사용자에게 도움을 주는 것이 목적이 아니라 시스템 자체의 효율적인 동작을 보장하기 위한 운영체제 기능들도 존재한다.
- 자원할당: 다수의 프로세스나 다수의 작업이 동시에 실행될 때, 그들 각각에 자원을 할당해주어야한다. 운영체제는 여러 다른 종류의 자원을 관리한다.
- logging : 어떤 프로그램이 어떤 종류의 컴퓨터 자원을 얼마나 많이 사용하는 지 추적할 수 있기를 원한다. 사용 통계는 컴퓨팅 서비스를 개선하기 위해 시스템을 재구성하고자하는 시스템 관리자에게 귀중한 자료가 된다.
- 보호와 보안: 다중 사용자 컴퓨터 시스템 혹은 네트워크로 연결된 컴퓨터 시스템에 저장된 정보의 소유자는 그 정보의 사용을 통제하길 원한다. 서로 다른 여러 프로세스가 병행하게 수행될 때, 한 프로세스가 다른 프로세스나 운영체제를 방해해서는 안된다.
2.2 사용자와 운영체제 인터페이스
사용자가 운영체제와 접촉하는 방식에는 여러 방법이 있다. 여기서는 3가지 기본적인 방법에 대해 논의한다. (1) 명령 인터프리터, (2) 그래픽 사용자 인터페이스, (3) 터치스크린 인터페이스
2.2.1 명령 인터프리터 Command Interpreter
명령 인터프리터의 중요한 기능은 사용자가 지정한 명령을 가져와서 그것을 수행하는 것이다. 많은 명령은 파일을 조작한다. 즉, 생성, 삭제, 리스트, 프린트, 복사, 수행 등을 한다. 이 명령어들은 두가지 일반적인 방식으로 구현될 수 있다.
- 명령 인터프리터 자체가 명령을 실행할 코드를 가지는 경우
- 시스템 프로그램에 의해 대부분의 명령을 구현하는 경우
2.2.2 그래픽 기반 사용자 인터페이스 Graphical User Interface
데스크톱이라고 특정지어지는 마우스를 기반으로하는 윈도 메뉴 시스템을 사용한다. 사용자는 마우스를 움직여 마우스 포인터를 프로그램, 파일, 시스템 기능 등을 나타내는 화면 상의 아이콘에 위치시킨다. 마우스 포인터의 위치에 따라 마우스를 누름으로써 프로그램을 호출하거나 파일 혹은 디렉터리를 선택할 수도 있고, 또는 명령을 포함한 메뉴를 잡아 당길 수도 있다.
2.2.3 터치스크린 인터페이스
모바일 시스템에서는 명령라인 인터페이스나 마우스 및 키보드 시스템이 실용적이지 않기 때무에 일반적으로 터치 스크린 인터페이스를 사용한다. 사용자는 터치 스크린에서 손가락을 누르거나 스와이프 하는 등의 제스처를 취하여 상호작용한다. iPad와 iPhone 모두 Springboard 터치스크린 인터페이스를 사용한다.
2.2.4 인터페이스의 선택
명령어라인 또는 그래픽 기반 인터페이스를 사용할 것인지는 개인의 선호에 달려있다. 명령어 라인 인터페이스는 보통 반복적으로 해야 하는 작업을 쉽게 할 수 있게 하는데, 프로그래밍이 가능하다는 사실이 한 몫을 차지한다.
2.3 시스템 콜!
시스템 콜은 운영체제에서 제공하는 서비스에 대한 인터페이스를 제공한다. 프로그래머는 시스템 콜 서비스에 액세스 하기 위해 시스템 콜의 API를 사용한다.
2.3.1 예제
한 파일로부터 데이터를 읽어서 다른 파일로 복사하는 간단한 프로그램을 작성한다고 가정해보자. cp in.txt out.txt (UNIX cp 명령)

이 일련의 작업을 위해서 많은 I/O 시스템 콜이 필요하다.
2.3.2 응용 프로그래밍 인터페이스
이렇게 간단한 프로그램이라도 운영체제의 기능을 아주 많이 사용하게 된다. 종종 초당 수천개의 시스템 콜을 수행하기도 한다.
사용자의 대부분은, 이러한 정도의 상세를 결코 알지 못한다. 대부분의 응용 개발자들은 응용 프로그래밍 인터페이스 (application programming interface, API)에 따라 프로그램을 설계한다.
💡 API는 ⇒ 각 함수에게 전달 되어야할 매개변수들과, 프로그래머가 기대할 수 있는 반환 값을 포함하여 응용프로그래머가 사용 가능한 함수의 집합을 명시한다.
응용 프로그래머가 사용가능한 가장 흔한 세가지 API는
- Windows 시스템을 위한 Windows API
- POSIX 기반 시스템을 위한 POSIX API (거의 모든 버전의 UNIX, Linux 및 Mac OS X를 포함한다.)
- Java 가상 기계에서 실행될수 있는 프로그램을 위한 Java API이다.
프로그래머는 운영체제가 제공하는 코드의 라이브러리를 통해 API를 활용한다.
UNIX와 Linux 시스템에서 C언어로 작성된 프로그램을 위해서 제공되는 라이브러리는 libc
API를 구성하는 함수들은 통상 응용프로그래머를 대신하여 실제 시스템 콜을 호출한다.
왜 응용 프로그래머는 실제 시스템 콜을 부르는 것보다 API에 따라 프로그래밍하는 것을 선호할까? 그렇게 하는데에는 몇가지 이유가 있다.
- 프로그램의 호환성
- API에 따라 프로그램을 설계하는 응용 프로그래머는 자신의 프로그램이 같은 API를 지원하는 어느 시스템이건 컴파일 되고 실행되는 것을 기대할 수 있다.
- 게다가 시스템 콜은 종종 더 자세한 명세가 필요하고 프로그램 상에서 작업하기가 응용 프로그래머에게 가용한 API보다 더 어렵다.
- 시스템 콜을 처리하는 데 있어 중요한 또 다른 요소는 실행시간 환경(RTE)이다.
- RTE는 운영체제가 제공하는 시스템 콜에 대한 연결고리 역할을 하는 시스템 콜 인터페이스를 제공한다.
- 이 시스템 콜 인터페이스는 API 함수의 호출을 가로채어 필요한 운영체제 시스템 콜을 부른다.
- 시스템 콜 인터페이스는 의도하는 시스템 콜을 부르고 시스템 콜의 상태와 반환값을 돌려준다.
- 호출자는 시스템 콜이 어떻게 구현되고 실행중에 무슨 작업을 하는지 아무것도 알 필요가 없다
- 호출자는 단지 API를 준수하고 시스템 콜의 결과로서 운영체제가 무엇을 할 것인지만 이해하면 된다.
- 따라서 운영체제 인터페이스에 대한 대부분의 자세한 내용은 API에 의해 프로그래머로부터 숨겨지고 RTE에 의해 관리된다.
API, 시스템 콜, 운영체제의 관계가 그림 2.6에 도시되어있다.

- 운영체제에 매개변수를 전달하기 위해 세가지 일반적인 방법을 사용한다.
-
가장 간단한 방법은 매개변수를 레지스터 내에 전달하는 것
- 어떤 경우는 레스터보다 더 많은 매개변수가 있을 수 있다.
-
이런 경우 매개변수는 메모리 내의 블록이나 테이블에 저장됨.
- 블록의 주소가 레지스터 내에 매개변수로 전달된다.
Linux는 이런 접근 법을 조합하여 사용하는데, 5개 이하의 매개변수가 있으면 레지스터가 사용된다. 매개변수의 개수가 5개를 넘으면 블록 방법이 사용된다.
-
매개변수는 프로그램에 의해 스택에 넣어질(push) 수 있고, 운영체제에 의해 꺼내진다(pop off).
일부 운영체제는 블록이나 스택 방법을 선호하는데, 이들 접근법은 전달되는 매개변수들의 개수나 길이를 제한하지 않기 때문이다.
2.3.3 시스템 콜의 유형
시스템 콜은 다섯 가지의 중요한 범주 등으로 묶을 수 있다.
- 프로세스 제어
- 끝내기, 중지
- 적재, 수행
- 프로세스 생성, 프로세스 종료
- 프로세스 속성 획득, 프로세스 속성 설정
- 시간을 기다림
- 이벤트를 기다림, 이벤트를 알림
- 메모리 할당 및 자유화
- 파일 조작
- 파일 생성, 파일 삭제
- 열기, 닫기
- 읽기, 쓰기, 위치 변경
- 파일 속성 획득 및 설정
- 장치 조작
- 장치를 요구, 장치를 방출
- 읽기, 쓰기, 위치 변경
- 장치 속성 획득, 장치 속성 설정
- 장치의 논리적 부착 또는 분리
- 정보 유지 보수
- 시간과 날짜의 설정과 획득
- 시스템 데이터의 설정과 획득
- 프로세스, 파일, 장치 속성의 획득
- 프로세스, 파일, 장치 속성의 설정
- 통신과 보호
- 통신 연결의 생성, 제거
- 메세지의 송신, 수신
- 상태 정보 전달
- 원격 장치의 부착 및 분리
- get file permissions
- set file permissions
2.3.3.1 프로세스 제어
- 실행중인 프로그램은 수행을 정상적(end())으로 또는 비정상적으로(abort()) 멈출 수 있어야한다.
- 만약 현재 실행 중인 프로그램을 비정상적으로 중지하기 위해 시스템 콜이 호출되거나 프로그램에 문제가 발생해 오류 트랩(trap)을 유발할 경우, 때때로 메모리 덤프가 행해지고 오류 메세지가 생성된다.
- 이 덤프는 특별한 로그파일이나 디스크에 기록되고 문제의 원인을 결정하기 위해 디버거에 의해 검사될 수 있다.
- 정상적이거나 비정상적인 상황에서, 운영체제는 명령 인터프리터로 제어를 전달해야한다. → 명령 인터프리터는 이어 다음 명령을 읽는다.
2.3.3.2 파일 관리
- 우선 파일을 생성(create())하고 삭제(delete())할 수 있어야 한다. 파일을 생성되면 그것을 열고(open()) 사용해야한다. 또한 읽고(read()), 쓰고(write()), 그리고 위치 변경(re-position()), 예를 들면, 되감기(rewind())나 파일의 끝으로 건너뛰기) 할 수 있다. 마지막으로, 파일을 더 이상 사용하지 않음을 나타내는 파일 닫기(close())가 필요하다.
- 이 시스템은 파일 이름이나 파일 속성의 일부를 요구한다.
- 파일 시스템이 파일을 조직하기 위해 디렉터리 구조를 가진다면, 우리는 디렉터리에 대해서도 이와 같은 연산 집합이 필요할 것이다.
2.3.3.3 장치 관리
- 운영체제에 의해 제어되는 다양한 자원들은 장치로 간주될 수 있다.
- 이 장치들의 일부는 물리 장치(ex) 디스크 드라이브)이고 다른 장치들은 추상적 혹은, 가상적 장치로 생각할 수 있다.(ex) 파일)
- 다수의 사용자가 동시에 사용하는 시스템은 독점적인 장치 사용을 보장받기 위해 우선 그 장치를 (request())하는 것을 요구한다.
- 그 장치의 사용이 끝나면, 우리는 그것을 반드시 방출(release()) 해야한다.
- 파일과 마찬가지로 그 장치를 익고 쓰고, 위치 변경할 수 있다.
- 사실, 입출력 장치와 파일 간에는 유사성이 매우 많기 때문에, 많은 운영체제가 이들 둘을 통합된 파일-장치 구조로 결합하였다.
2.3.3.4 정보 유지 관리
- 많은 시스템 콜은 단순히 사용자 프로그램과 운영체제 간의 정보 전달을 위해 존재한다.
- 현재시간(time())과 날짜를 되돌려 주는 (date()) 시스템 콜
- 다른 시스템 콜 집합은 프로그램 디버깅에 도움이 된다.
- dump() - 메모리를 덤프하기 위한 시스템 콜
- Linux 시스템에서 사용가능한 프로그램 strace는 실행될 때마다 각 시스템콜을 나열
- 많은 운영체제는 프로그램의 시간 프로파일(time profile)을 제공한다.
- 시간 프로파일은 그 프로그램이 특정위치, 혹은 위치의 집합에서 수행한 시간을 나타낸다.
- 시간 프로파일은 추적 설비(tracing facility)나 정규 타이머 인터럽트가 필요하다.
- 더욱이 운영체제는 현재 운영되고 있는 모든 프로세스에 관한 정보를 가지고 있으며, 이러한 정보에 접근하기 위한 시스템 콜이 있다.
- 프로세스 정보를 획득하고 설정하기 위한 시스템 콜 (get_process_attributes() 및 set_process_attributes())
2.3.3.5 통신
- 통신 모델에는 메세지 전달과 공유 메모리의 두가지 일반적인 모델이 있다.
- 메세지 전달 모델에서는 통신하는 두 프로세스가 정보를 교환하기 위하여 서로 메세지를 주고 받는다.
- 메세지는 두 프로세스 사이에 직접 교환되거나 우편함을 통하여 간접적으로 교환될 수 있다.
- 상대 통신자가 동일한 CPU에 있는 프로세스이든지 통신 네트워크에 의해 연결된 다른 컴퓨터에 있는 프로세스이든지 간에 그 이름을 반드시 알고 있어야한다.
- 통신이 이루어지기 전에 연결이 반드시 열려야한다.
- 공유 메모리 모델에서, 프로세스는 다른 프로세스가 소유한 메모리 영역에 대한 접근을 위해 shared_memory_create()와 shared_memory_attach() 시스템 콜을 사용한다.
- 일반적으로 운영체제는 한 프로세스가 다른 프로세스의 메모리에 접근하는 것을 막으려고한다.
- 공유 메모리는 두개 이상의 프로세스가 이러한 제한을 제거하는 데 동의할 것을 필요로한다.
- 이들 프로세스는 이러한 공유 영역에서 데이터를 읽고 씀으로써 정보를 교환할 수 있다.
- 프로세스는 또한 동일한 위치에 동시에 쓰지 않도록 보장할 책임을 진다.
- 이러한 두가지 방법은 운영체제에서 보편적이며, 시스템 대부분은 둘다 구현한다.
- 메세지 전달의 장점
- 소량의 데이터를 교환할 떄 유용한데, 이는 피해야할 충돌이 없기 떄문이다.
- 메모리 공유보다 구현하기가 쉽다.
- 공유 메모리의 장점
- 한 컴퓨터 안에서는 메모리 전송 속도로 수행할 수 있으므로 최대 속도와 편리한 통신을 허용한다.
- 그렇지만 보호와 동기화 부분에서 여러 문제점을 가지고 있다.
2.3.3.6 보호
보호는 컴퓨터 시스템이 제공하는 자원에 대한 접근을 제어하기 위한 기법을 지원한다.
역사적으로 보호는 다수의 사용자를 가지는 다중 프로그램 시스템에서만 고려되는 문제였으나 → 네트워킹과 인터넷의 출현으로 서버에서 휴대폰 컴퓨터까지 모든 컴퓨터 시스템에서 보호를 고려하여야한다.
- 통상 보호를 지원하는 시스템 콜은 set_permission()과 get_permission()을 포함한다.
- 파일이나 디스크 같은 자원의 허가 권한을 얻는데 사용된다.
- allow_user()와 deny_user() 시스템 콜은 특정 사용자가 지정된 자원에 대해 접근이 허가 또는 불허되었는지를 명시한다.
2.4 시스템 서비스 <-?
- 논리적인 컴퓨터 계층 구조 : 최하위 수준 = 하드웨어, 다음은 운영체제, 그다음은 시스템 서비스, 그리고 마지막으로 응용 프로그램이다.
- 시스템 서비스는, 시스템 유틸리티(system utility)로도 알려진, 프로그램 개발과 실행을 위해 더 편리한 환경을 제공한다.
- 그들 중 몇몇은 단순히 시스템 콜에 대한 사용자 인터페이스이며, 반면에 나머지는 훨씬 더 복잡하다.
- 이들은 몇가지 범주로 분류할 수 있다.
- 파일 관리
- 상태 정보
- 파일 변경
- 프로그래밍언어지원
- 프로그램 적재와 수행
- 통신
- 백그라운드 서비스
2.5 링커와 로더
-
일반적으로 프로그램은 디스크에 이진 실행 파일로 존재한다
-
CPU에서 실행하려면 프로그램을 메모리로 가져와 프로세스 형태로 배치해야한다.
-
이 절에서는 프로그램을 컴파일하고 메모리에 배치하여 사용가능한 CPU 코어에서 실행될 수 있게 되기까지의 절차를 단계별로 설명한다.

-
소스파일은, 임의의 물리 메모리 위치에 적재되도록 설계된 오브젝트 파일로 컴파일된다.
- 이러한 형식을 재배치 가능 오브젝트 파일이라고한다.
-
다음으로 링커는 이러한 재배치 가능 오브젝트 파일을 하나의 이진 실행 파일로 결합한다.
- 링킹 단계에서 표준 C 또는 수학 라이브러리 같은 다른 오브젝트 파일 또는 라이브러리도 포함될 수 있다.
-
로더는 이진 실행 파일을 메모리에 적재하는데 사용되며, CPU 코어에서 실행할 수 있는 상태가 된다.
링크 및 로드와 관련된 활동은 주로 재배치로, 프로그램 부분에 최종 주소를 할당하고 프로그램 코드와 데이터를 해당 주소와 일치하도록 조정하여 프로그램이 실행될 때 코드가 라이브러리 함수를 호출하고 변수에 접근할 수 있게 한다
2.6 응용 프로그램이 운영체제마다 다른 이유
기본적으로 한 운영체제에서 컴파일된 응용 프로그램은 다른 운영체제에서 실행할 수 없다.
- 응용프로그램이 운영체제에 따라 달라지는 몇가지 이유가 있다. 여기에는 프로그램 실행 파일에 대한 서로 다른 이진 형식, CPU에 따라 다른 명령 집합 및 운영체제마다 다른 시스템 콜이 포함된다.
각 운영체제는 고유한 시스템 콜 집합을 제공한다. 시스템 콜은 응용 프로그램이 사용할 수 있도록 운영체제가 제공하는 서비스 집합의 일부이다. ⇒ 시스템콜은 어느정도 같더라도 다른 장벽으로 인해 응용 프로그램을 다른 운영체제에서 실행하기 어렵다.
그러나 여러분이 여러 운영체제를 사용해 온 경우, 동일한 응용 프로그램을 다른 운영체제에서 사용했을 수도 있다. 이건 어떻게 가능할까?
⇒ 다음 세가지 방법 중 한 가지를 사용하여 응용 프로그램이 여러 운영체제에서 실행될 수 있게 만들 수 있다.
💡
- 응용 프로그램은 운영체제마다 인터프리터가 제공되는 인터프리터 언어로 작성될 수 있다.
- 인터프리터는 소스 프로그램의 각 라인을 읽고, 상응하는 기계어 명령을 실행하고, 해당 운영체제의 시스템 콜 호출한다.
- 기계어 코드로 구성된 응용프로그램에 비해 성능이 떨어지고, 인터프리터는 각 운영체제 기능의 일부만 제공하므로 관련 응용프로그램의 기능도 제한될 수 있다.
- 응용 프로그램은 실행 중인 응용 프로그램을 포함하고 있는 가상 머신을 가진 언어로 작성될 수 있다.
- 가상머신은 언어의 RTE 중 일부이다.
- 이 RTE는 메인 프레임에서 스마트폰에 이르기까지 많은 운영체제에 이식 또는 개발되었으며 이론적으로 모든 Java 앱은 RTE가 제공되는 어디서나 실행될 수 있다.
- 이러한 종류의 시스템은 위에서 설명한 인터프리터 시스템과 유사한 단점을 가진다.
- 응용 프로그램 개발자는 컴파일러가 기기 및 운영체제 고유의 이진 파일을 생성하는 표준 언어 또는 API를 사용할 수 있다.
- 응용 프로그램은 실행될 각 운영체제로 이식되어야한다.
- 가장 많이 잘 알려진 예는 다양한 UNIX 운영체제 변종 간의 소스 코드 호환성을 유지하기 위한 POSIX API와 표준 집합이다.
- 아키텍처 수준에서 이진 코드의 여러 구성요소가 주어진 아키텍처에서 특정 운영체제와 상호작용할 수 있는 방법을 정의하는데는 ABI가 사용된다. (application binary interface)
- 따라서 ABI는 아키텍처 수준의 API다.
- 이진 실행 파일이 특정 ABI에 따라 컴파일되고 링크된 경우 해당 ABI를 지원하는 다른 시스템에서 실행될 수 있어야한다.
2.7 운영체제 설계 및 구현
이 절에서는 운영체제를 설계하고 구현할 때 우리가 당면하는 문제점을 논의한다. 물론 이들 설계 문제점에 대한 완전한 해결책은 없지만 성공적인 접근 방법들이 있다.
2.7.1 설계목표
시스템을 설계하는 데 첫째 문제점은 시스템의 목표와 명세를 정의하는 일이다.
시스템을 설계하는데 있어 요구조건은 근본적으로 사용자 목적과 시스템 목적의 두가지 기본 그룹으로 나눌 수 있다.
- 사용자 목적: 시스템은 사용하기 쉽고 편리하며, 배우기 쉽고, 믿을 수 있고, 안전하고, 신속해야한다.
- 시스템 목적: 시스템을 설계, 생성, 유지, 조작해야하는 사람들의 입장에서 운영체제는 설계, 구현, 유지 보수가 쉬워야 하며, 또한 적응성, 신뢰성, 무오류, 효율성을 가져야한다.
2.7.2 기법과 정책
한가지 중요한 원칙은 기법으로부터 정책을 분리하는 것이다.
기법은 어떤 일을 어떻게 할 것인가를 결정하는 것이고, 정책은 무엇을 할 것인가를 결정하는 것이다.
EX) 타이머 구조는 CPU보호를 보장하기 위한 기법이지만, 특정 사용자를 위해 타이머를 얼마나 오랫동안 설정할지는 정책적 결정이다.
2.7.3 구현
-
운영체제는 대부분 C또는 C++과 같은 고급언어로 작성된다.
-
커널의 최하위 레벨은 어셈블리어 및 C로 작성될 수 있다
-
상위 레벨 루틴은 C 및 C++로 작성될 수 있으며,
-
시스템 라이브러리는 C++또는 상위레벨 언어로 작성될 수 있다.
- Android는 좋은 예를 제공한다.
- 커널은 대부분 약간의 어셈블리어를 함께 사용하고 C로 작성되었다.
- 대부분의 안드로이드 시스템 라이브러리는 C또는 C++로 작성되며
- 시스템에 개발자 인터페이스를 제공하는 응용 프레임 워크는 대부분 java로 작서오딘다.
-
운영체제를 고급 수준 언어로 구현하는 것의 장점
- 코드를 빨리 작성 가능.
- 더욱 간결, 이해하기 쉽고, 디버그 하기 쉽다.
- 다른 하드웨어로 이식하기 쉽다.
-
단점
- 속도가 느리고 저장장치가 많이 소요된다
- 그러나 이것은 현재의 시스템에서는 주된 문제가 아니다.
-
운영체제의 주요 성능 향상은 우수한 어셈블리어 코드 보다는 좋은 자료구조와 알고리즘의 결과일 가능성이 크다.
- 아마도 인터럽트 핸들러, 입출력 관리자, 메모리 관리자와 CPU 스케줄러가 가장 긴급한 루틴일 것이다.
- 시스템이 개발되고 올바르게 작동된 후 병목 지점을 식별하고 보다 효율적으로 작동하도록 재구성할 수 있다.
2.8 운영체제 구조 _ Operating-System Structures
현대의 운영체제와 같이 크고 복잡한 시스템 → 적절하게 동작하고 쉽게 변경되려면 신중히 제작되어야한다.
일반적인 접근 방법은 한개의 일관된 시스템보다는 태스크를 작은 구성요소로 분할 하는 것이다.
이절에서는 이러한 구성요소들이 어떤 방법으로 상호 연결되고 하나의 커널로 결합되는 지를 알아본다.
2.8.1 모놀리식 구조_Monolithic Structure
운영체제를 구성하는 가장 간단한 구조는 구조가 아예 없는 것이다.
즉, 커널의 모든 기능을 단일 주소 공간에서 실행되는 단일 정적 이진 파일에 넣는 것이다.
이 방법은 운영체제를 설계하는 일반적인 기술이다.
- 모놀리식의 커널의 명백한 단순성에도 불구하고 이 구조는 구현 및 확장하기 어렵다.
- 그러나 성능면에서 뚜렷한 이점이 있는데, 시스템 콜 인터페이스는 오버헤드가 거의 없고 커널 안에서의 통신 속도가 빠르다.
- 모놀로식 커널의 단점에도 불구하고, 속도와 효율성은 이 구조의 증거를 여전히 UNIX, Linux 및 Windows 운영체제에서 발견할 수 있는 이유를 설명한다.
2.8.2 계층적 접근_Layered Approach
시스템의 한 부분을 변경하면 다른 부분에 광범위한 영향을 줄 수 있으므로 모놀리식 접근법은 종종 밀접하게 결합된 시스템으로 불린다.
↔ 대안으로 느슨하게 결합된 시스템을 설계할 수 있다.
- 이러한 시스템은 기능이 특정 기능 및 한정된 기능을 가진 개별적이며 작은 구성요소로 나뉜다.
- 이 모든 구성요소가 합쳐져 커널을 구성한다.
- 장점: 한 구성요소의 변경이 해당 구성요소에만 영향을 미치고 다른 구성요소에는 영향을 미치지 않으므로 시스템 구현자가 시스템의 내부 동작을 더 자유롭게 생성하고 변경가능하다.
시스템은 다양한 방식으로 모듈화 될 수 있는데, 한 가지 방식이 계층적 접근 방식이다.
-
운영체제가 여러 층으로 나누어지고, 최하위 층은 하드웨어이고, 최상위 층은 사용자 인터페이스이다.

그림 2.14 계층 구조의 운영체제
-
계층적 접근 방식의 주된 장점은 구현과 디버깅의 간단함에 있다.
-
층들은 단지 자신의 하위층들의 서비스와 기능들만을 사용하도록 선택된다.
-
이러한 접근 방법은 시스템의 검증과 디버깅 작업을 단순화한다.
-
만일 어느층의 디버깅 중 오류가 발견되면 그 하위의 층은 이미 디버깅되었기 때문에 오류는 반드시 그 층에 있다. → 따라서 시스템을 계층으로 나누면 시스템의 설계나 구현이 간단해진다.
-
각층은 자신보다 하위 수준의 층에 의해 제공된 연산들만 사용해 구현한다.
-
이러한 연산들이 어떻게 구현되는지는 알 필요 없고, 무엇을 하는지만 알면 된다.
-
그러므로 각층은 특정 데이터구조, 연산, 그리고 하드웨어의 존재를 상위층에 대해 숨기게된다.
-
계층화된 시스템은 컴퓨터 네트워크(TCP/IP) 및 웹 응용 프로그램에서 성공적으로 사용됐다.
-
그럼에도 불구하고 순수한 계층 접근 방식을 사용하는 운영체제는 비교적 적다.
- 각 계층의 기능을 적절히 정의해야하며
- 이런 시스템의 전반적인 성능은 운영체제 서비스를 얻기 위해 사용자 프로그램이 여러 계층을 통과해야하는 오버헤드로 인해 열악하다.
- 그러나 어느정도의 계층화는 현대 운영체제에서 공통적이다.
- 더 많은 기능을 가진 더 적은 개수의 층
- 층 기능의 정의 및 상호작용의 문제를 피하면서 모듈화된 코드의 장점을 최대한 활용할 수 있다.
2.8.3 마이크로 커널
초기 UNIX가 모놀로식 구조를 가졌다. UNIX가 확장함에 따라, 커널이 커지고 관리하기 힘들어졌다. 1980년대 중반에, 카네기멜론 대학교의 연구자들이 마이크로커널 접근 방식을 사용하여 커널을 모듈화한 Mach라 불리는 운영체제를 개발했다.
- 이 방법은 모든 중요치 않은 구성요소를 커널로부터 제거하고, 그들을 별도의 주소공간에 존재하는 사용자 수준 프로그램에 구현하여 운영체제를 구성하는 방법이다.
전형적인 마이크로커널의 구조
- 마이크로 커널 접근법의 장점:
- 운영체제의 확장이 쉽다.
- 모든 새로운 서비스는 사용자 공간에 추가되며, 따라서 커널을 변경할 필요없다.
- 커널이 변경되어야만할때에는, 마이크로커널이 작은 커널이기 때문에 변경할 대상이 비교적 적은 경향이 있다.
- 따라서 이러한 운영체제는 한 하드웨어로부터 다른 하드웨어로 이식이 쉽다.
- 서비스 대부분이 커널이 아니라 사용자 프로세스로 수행되기 때문에 더욱 높은 보안성과 신뢰성을 제공한다.
- 마이크로 커널 접근법의 단점:
- 가중된 시스템 기능 오버헤드 때문에 성능이 나빠진다.
- 메세지 복사 및 프로세스 전환과 관련된 오버헤드는 마이크로커널 기반 운영체제의 성장에 큰 장애였다.
- 마이크로커널 운영체제의 가장 잘 알려진 실례는 macOS 및 iOS 운영체제의 커널 구성요소인 Darwin이다. 실제로 Darwin은 두개의 커널로 구성되며 그 중 하나는 Mach 마이크로 커널이다.
2.8.4 모듈
운영체제를 설계하는 데 이용되는 최근 기술 중 최선책은 아마 적재가능 커널 모듈(lodable kernel modules, LKM) 기법의 사용일 것이다.
-
이 접근법에서는 커널은 핵심적인 구성요소 집합을 가지고 있고, 부팅 때 또는 실행 중에 부가적인 서비스들을 모듈을 통하여 링크할 수 있다.
-
설계의 주안점은 커널은 핵심 서비스를 제공하고 다른 서비스들은 커널이 실행되는 동안 동적으로 구현하는 것이다.
-
서비스를 동적으로 링크하는 것은 새로운 기능을 직접 커널에 추가하는 것보다 바람직하다.
- 후자의 경우 수정사항이 생길 때마다 커널을 다시 컴파일 해야하기 때문이다.
-
예를들어 CPU 스케줄링과 메모리 관리 알고리즘은 커널에 직접 구현하고 다양한 파일 시스템을 지원하는 것은 적재가능모듈을 통하여 구현가능하다.
-
전체적인 결과는 커널의 각 부분이 정의되고 보호된 인터페이스를 가진다는 점에서 계층구조를 닮았다. 그러나 모듈에서 임의의 다른 모듈을 호출할 수 있다는 점에서 계층 구조보다 유연하다.
-
중심 모듈은 단지 핵심 기능만을 가지고 있고 다른 모듈의 적재 방법과 모듈들과 어떻게 통시하는지 안다는 점에서는 마이크로 커널과 유사하다. 그러나 통신하기 위하여 메세지 전달을 호출할 필요가 없기 떄문에 더 효율적이다.
-
이러한 유형의 설계는 Linux, Mac OS X, Solaris 및 Windows등의 현대 UNIX를 구현하는 일반적인 추세이다.
- Linux는 주로 장치 드라이버와 파일 시스템을 지원하기 위해 적재가능 커널 모듈을 사용한다.
- LKM은 시스템이 시작되거나 USB장치가 실행 중인 시스템에 접속되는 경우와 같이 런타임 중에 커널에 “삽입”될 수 있따. 또한 런타임중에 커널에서 제거될 수도 있다.
- Linux의 경우 LKM은 동적 및 모듈식 커널을 허용하면서 모놀리스식 시스템의 성능이점을 유지한다.
2.8.5 하이브리드 시스템
사실 엄격하게 정의된 하나의 구조를 채택한 운영체제는 거의 존재하지 않는다.
대신 다양한 구조를 결합하여 성능, 보안 및 편리성 문제를 해결하려는 혼용 구조로 구성된다.
- 예를들어, Linux는 운영체제 전부가 하나의 주소공간에 존재하여 효율적인 성능을 제공하기 때문에 모놀리식 구조이다.
- 그러나 이 운영체제들은 모듈을 사용하기 때문에 새로운 기능을 동적으로 커널에 추가할 수 있다.

2.9 운영체제 빌딩과 부팅
운영체제가 생성된 후에는 하드웨어에서 새 운영체제를 사용할 수 있어야한다. 그러나 하드웨어는 커널의 위치 또는 커널을 적재하는 방법을 어떻게 알 수 있을까?
커널을 적재하여 컴퓨터를 시작하는 과정을 시스템 부팅이라고한다.
부팅과정
- 부트스트랩 프로그램 또는 부트로더라고 불리는 작은 코드가 커널 위치를 찾는다.
- 커널이 메모리에 적재되고 시작된다.
- 커널은 하드웨어를 초기화한다.
- 루트 파일 시스템이 마운트 된다.
(부트 로더는 운영체제를 메모리에 적재하고 초기화를 수행하며 시스템 실행을 시작한다.)
컴퓨터 전원을 켜면 BIOS라고 하는 비휘발성 펌웨어에 있는 소형 부트 로더가 실행된다.
이 초기 부트로더는 일반적으로 부트 블록이라고 하는 디스크의 정해진 위치에 있는 두번째 부트 로더를 적재하는 작업만 한다. 부트 블록에 저장된 프로그램은 전체 운영체제를 메모리에 적재하고 실행을 시작하기에 충분히 정교할 수도 있다.
2.10 운영체제 디버깅
넓게는 디버깅은 하드웨어와 소프트웨어에서의 시스템의 오류를 발견하고 수정하는 행위이다. 성능 문제는 버그로 간주되므로 시스템에서 처리 중에 발생하는 병목현상을 제거하여 성능을 향상시키려는 성능 조정(performance tuning)도 디버깅에 포함된다.
이 절에서는 커널과 프로세스 오류 및 성능 문제의 디버깅에 관해 탐구한다.
2.10.1 장애 분석_Failure Analysis
프로세스가 실패한다면, 운영체제 대부분은 오류 정보를 로그파일에 기록한다.
운영체제는 또한 프로세스가 사용하던 메모리를 캡쳐한 코어 덤프(core dump)를 취하고 차후 분석을 위해 파일로 저장한다. (초창기에 메모리를 “코어”라고 칭했다.)
커널 장애는 크래시(crash)라고 불린다. 프로세스 장애와 마찬가지로 오류 정보가 로그 파일에 저장되고 메모리의 상태가 크래스 덤프(crash dump)에 저장된다.
⇒ 운영체제 디버깅과 프로세스 디버깅은 종종 두 태스크의 근본적인 차이에 의해 서로 다른 도구와 기법을 사용한다.
2.10.2 성능 관찰 및 조정
성능 조정은 처리 병목 지점을 제거함으로써 성능을 향상시키려 한다고 언급하였다. 병목 지점을 발견하기 위하여 시스템 성능을 감시할 수 있다. 따라서 시스템 동작을 측정하고 표시할 수 있는 방법을 가지고 있어야한다.
- 도구는 프로세스별 또는 시스템 전체의 관찰을 제공하느냐로 특징이 묘사될 수 있다.
- 이러한 관찰을 위해 도구는 카운터 또는 추적의 두가지 접근 방식 중 하나를 사용할 수 있다.
- 카운터 또는 추적을 사용하여 운영체제의 성능을 관찰할 수 있다. 카운터는 시스템 전체 또는 프로세스별 통계의 모음인 반면에 추적은 운영체제를 통한 프로그램의 실행을 추적한다.
2.10.2.1 카운터 Counters
운영체제는 일련의 카운터를 통해 호출된 콜 횟수 또는 네트워크 장치 또는 디스크에 수행된 작업 수와 같은 시스템 활동을 추적한다.
2.10.3 추적 Tracing
카운터 기반 도구는 커널에서 유지 관리하는 특정 통계의 값에 대해 간단히 문의하는 반면, 추적도구는 시스템 콜과 관련된 단계와 같은 특정 이벤트에 대한 데이터를 수집한다.