운영체제 수업을 수강하며 정리한 내용을 작성하려고 합니다.
OS design
운영체제는 규모가 매우 크고 복잡한 소프트웨어이기 때문에 구조를 신중히 고려해야 한다.
디자인 목표
- Fairness
- Real time
- High performance
- Scalability
- Stability/reliability/robustness
- Security/integrity
- Usability
- Compatibility
- Energy consumption
여러 디자인 목표를 동시에 달성할 수는 없다. 목표 간 Trade-off가 존재하기 때문이다.
GPOS vs SPOS
- General Purpose OS(일반 용도 OS)
- 일반적인 용도의 OS, 여러 특성들을 골고루 만족
- Special Purpoose OS(특수 용도 OS)
- 특수한 용도의 OS (e.g. 군용, 발전소, IoT 등)
- 용도별로 특별한 특성이 요구됨
OS 디자인 원칙: Policy & Mechanism
Policy
- 운영체제로 하여금 무엇을 하게 할 것인가?
- 운영체제가 이루기 위한 목표 (e.g. 동영상 재생의 무손실 보장)를 달성하기 위한 기법
Mechanism
- 무엇을 어떻게 할 것인가? 알고리즘과 자료구조
→ 설계 원칙 = Policy, 구현 = Mechanism을 서로 분리함으로써 설계 복잡도가 감소한다
OS 구조 설계 방식: Layering

- OS의 복잡도를 줄이기 위한 방안으로, Layer 함수들로 이루어짐.
- Layer: 인터페이스와 기능의 정의가 명확 (e.g. OSI 7 layers)
- 하나의 layer는 인접한 layer와만 통신
장점
- 인접한 Layer과의 API만 고려해서 개발하면 된다(설계의 복잡도를 낮춤)
- 관리가 편리함
단점
Layering vs Modularity

Modularity: 프로그램을 분할하는 단위, 레이어와 달리 다수의 분할단위와 인접할 수 있음
Layering: Layer의 수정이 다른 layer와 독립적임
OS - between kernel and user

OS의 구조를 나타낸 이미지.
CPU의 실행 모드

- 모든 명령은 CPU에 instruction으로 들어가게 되는데, kernel과 user의 명령을 구분하기 위해 CPU는 2가지 이상의 모드가 있다.
- 실행 모드는 시스템 보호를 목적으로 존재하며, 실행 모드의 권한에 따라 실행 가능한 명령어를 제한한다.
- 실행 모드는 하드웨어에서 지원하며, Intel의 경우 ring 0~3, 4개의 모드를 제공한다.
- kernel은 kernel mode에서 작업을 수행하며, 모드간의 전환을 mode switching이라고 한다.
- 높은 권한이 필요한 모드에서 수행가능한 명령어를 privileged instruction이라고 한다. privileged instruction의 예시는 다음과 같다.

User mode vs Kernel Mode
User mode
- Kernel 모드에 비해 낮은 권한의 실행 모드
- 어플리케이션이 실행되는 모드
- Privilege 명령어 실행은 불가능
Kernel mode
- 모든 권한을 가진 실행 모드
- 운영체제가 실행되는 모드
- Privilege 명령어 실행 및 레지스터 접근 가능
실행 모드 전환(mode switch)
- CPU의 실행 모드는 설정은 시스템 보호가 목적
- User mode에서 실행중인 어플리케이션이 kernel mode의 권한이
필요한 서비스를 받기 위한 방법이 필요
System call

- User mode에서 Kernel mode로 진입하기 위한 인터페이스(함수를 호출하는 것과 유사)
- 커널에서 제공하는 protected 서비스를 이용하기 위하여 필요
- open(), write(), shm(), fork()
- 유저의 응용 프로그램은 시스템 콜을 직접 호출하기보다, High-Level API (application programming interface)를 사용하여 간접적으로 호출
- standard C library - read(), write(), open(), close() 등

(User mode) user application이 system call(API 사용)을 한다. → (Kernel mode) system call table에서 해당 함수를 찾아서 실행하게 된다.
System call: 파라미터 전달
💡 시스템 콜에 파라미터를 어떻게 전달할 것인가?

- 레지스터에 파라미터 저장
- 레지스터 = CPU가 연산을 처리하는 데 필요한 데이터를 일시적으로 저장하기 위한 메모리
- 레지스터: 속도는 빠르고 단순하지만, 저장할 수 있는 개수에 제한이 있음. 보통 5개 이하의 파라미터를 사용
- ram과 같은 메모리를 이용
- 메모리의 어딘가에 파라미터를 저장하고, 메모리 시작 주소를 레지스터에 넣어 전달
- 파라미터들은 메모리에 연속된 공간에 저장되어야 함. 리눅스에서 사용
System call handler
💡 system call 요청을 받았을 때, 이에 대응하는 루틴
- 파라미터 위치 확인
- 파라미터 복사
- 파라미터를 커널이 사용 가능한 메모리 영역으로 복사
- 복사를 통해 커널 내에서의 연산을 독립적으로 분리하여 수행함
- 목적: 유저 영역과 커널 영역을 분리하여 커널을 악의적으로 이용하는 상황으로부터 보호(메모리 액세스 위반 또는 데이터 손상의 위험)
- 연산 후 결과 반환
Kernel architecture designs(커널 구조 디자인)
- Monolithic kernel
- Microkernel
- Virtualization, 가상화 (hypervisor, container, etc)
Monolithic kernel

- 커널이 분리되어 있지 않고, 한 덩어리로 구성됨(하나의 주소 공간)
- 커널을 시스템 콜을 통해 접근
- 커널이 사용자와 같은 주소 공간에 위치
- 주소 공간을 커널 코드와 사용자가 나누어서 사용
- App의 관점에서는 자신의 주소공간 중 일부에 커널 코드 영역이 맵핑되는 것처럼 간주
주소 공간 (address space)
- 논리적(물리 메모리) 실체에 대응되는 주소 범위를 정의한 공간
- 물리적으로 메모리는 존재하는데, 이것을 어떻게 지칭할 것인가?
- 같은 주소 공간에 있는 경우, 주소를 이용하여 접근 가능
- 주소 공간을 알지 못하는 경우, 그 주소공간에 포함된 실체에 접근할 수 없음
- 접근할 수 있는 다른 방법을 제공하여야만 접근 가능
- A 프로세스의 1번 주소와 B 프로세스의 1번 주소를 물리적으로 다른 주소 공간으로 나눔(프로세스별 각각의 주소 공간 존재)