Chapter 1. Introduction
- 운영체제는 컴퓨터 시스템의 여러 H/W를 관리함으로써,
응용 프로그래머가 H/W의 세부적인 구현사항들을 일일히 신경쓰지 않고 프로그래밍을 할 수 있게 한다.
- 운영체제는 Application에 서비스와 인터페이스(System Call)를 제공한다.
(System Call은 Trap 명령어를 사용하는 라이브러리 함수이다.)
- 모든 Application은 H/W에 직접 액세스할 수 없으며, 반드시 OS를 거쳐야 H/W에 접근 및 제어할 수 있다.
(대표적인 Interface로, System Call이 있다.)
- 사용자는 User Interface Program으로써 Text-based 환경의 shell이나, GUI 환경을 이용하여 OS를 접하게 된다.
1. Kernel Mode (Supervisor Mode)
- S/W의 대부분의 Fundamental한 부분은 커널 모드에서 구동된다.
- 커널모드에서는 모든 H/W Resource에 완전하게 접근이 가능하며, 모든 H/W Feature를 사용할 수 있다.
- 커널모드에서는 해당 Machine에서 지원되는 모든 명령어를 실행할 수 있다.
- 커널모드에서 구동되는 프로세스는 모두 OS의 일부이다.
(단, OS의 모든 부분이 커널 모드에서만 구동되는 것은 아니다.)
2. User Mode
- 유저모드에서는 해당 Machine에서 지원되는 명령어 중 일부만 실행할 수 있다.
- 또한, 유저모드에서는 H/W에 직접 접근할 수 없다.
- 일반적으로, 유저모드에서는 I/O와 Memory Protection에 관련된 명령어는 사용할 수 없다.
- User Mode에서 OS에게 서비스를 요청하기 위해서는 System Call을 이용해야 한다.
- shell이나 GUI는 유저모드에서 구동되는 S/W들 중, 가장 Low-Level에 속하는 S/W이다.
※ \(\texttt{Trap}\) 명령어는 Mode를 Switching하는 명령어이다.
- Kernel 모드와 User 모드 사이의 전환은 OS를 통해 이루어진다.
- Trap 명령은 보통 Exceptional Situation(0으로 나누는 경우, 오버플로우 등)이 발생되었을 때 자주 실행된다.
1.1 What is an operating system?
1. OS as an Extended Machine (Top-down view)
- OS는 프로그래머에게 Device Driver*와 같은 S/W를 통해 Abstraction Layer을 제공하여
프로그래머가 H/W의 Details를 몰라도 프로그래밍을 가능하게 한다.
(여기서, 프로그래머는 Application Programmer를 의미한다.)
- 또한, OS는 Application의 입장에서 H/W 자원이 무한하게 느껴지게끔 한다. (특히, 메인 메모리)
* Device Driver
- H/W를 제어하기위한 Interface를 제공하는 S/W이다.
- OS는 프로그래머들이 I/O Device들을 쉽게 제어하도록, 다양한 종류의 H/W Driver를 포함하고 있다.
2. OS as a Resource Manager (Bottom-up view)
- OS는 Modern Computer를 구성하는 H/W를 필요로하는 S/W에 적재적소에 할당하는 역할을 한다.
- 현대의 OS에서는 다수의 프로그램이 동시에 구동되는 것을 지원하는데,
이를 가능케하기 위해서는 Multiplexing*을 통한 적절한 H/W 제어가 무엇보다도 중요하다.
* Resource Multiplexing (Sharing)
- 컴퓨터 자원을 관리하는 기법으로, Time Multiplexing과 Space Multiplexing으로 구분된다.
1) Time Mulxtiplexing (Time Sharing)
- H/W 자원을 사용하는 주체(프로그램 혹은 사용자)들이 일정시간씩 돌아가며 H/W자원을 이용하게 하는 형태이다.
ex) 한 프로그램이 Printer를 다 이용하고 나서야, Printer 제어 권한이 다른 프로그램으로 넘겨지는 것은
Printer H/W를 여러 프로그램이 Time Sharing하고 있는 형태에 해당된다.
2) Space Multiplexing (Space Sharing)
- 하나의 H/W 자원을 여러 프로그램 혹은 사용자들이 일정량씩 나눠서 이용하는 형태이다.
- OS는 분할 가능한 H/W 자원을 필요한 사용자에게 적절히 분배하여, 그들이 H/W 자원을 동시에 이용할 수 있도록 한다.
ex) 메인 메모리는 대체로 여러 프로그램들에게 분할되어 할당된다.
(즉, 여러 프로그램들이 동시에 메인메모리에 적재되는 것이 가능하다.)
1.2 History of operating systems
* Analytical Engine
- 영국의 수학자 Charles Babbage에 의해 설계된 최초의 디지털 컴퓨터이다.
- 전부 기계식으로 구현하고자 했고, 그 당시 기술적 한계점으로 인해 제대로 작동되지 않았다.
- 당연하게도, 이 머신에 OS는 포함되지 않았다.
- Babbage가 이 머신에 S/W가 필요함을 느껴 고용한 프로그래머가 Ada Lovelace였다.
(Ada는 최초의 프로그래머이자, 영국 시인 Lord Byron의 딸이다. 또한 그녀의 이름을 딴 프로그래밍 언어 Ada도 존재한다.)
1) The First Generation(1945-1955): Vacuum Tubes(진공관), Plug Boards
- 이 시기에는, 주로 공학자들로 구성된 그룹에서 각자의 머신을 개발, 구동, 유지보수, 프로그래밍했다.
- 모든 프로그래밍은 완전한 기계어로 이루어졌으며, 프로그래밍 자체를 진행하지 않는 경우도 있었다.
(프로그래밍 언어 개념이 없던 시기였으며, 어셈블리어 또한 존재하지 않던 시절이었다.)
- 프로그래밍은 Plugboard에 수천개의 케이블들을 연결하여 머신의 Basic Function을 제어하는 방법으로 이루어졌다.
- OS 또한 존재하지 않아, 프로그래머들은 머신이 위치한 공간에 비치된 Time Sheet에
자신이 사용할 시간을 표시하는 방식으로 나름의 Scheduling을 진행했다.
- Temporary Storage로써 Rotating Capacitor를 사용했다. (저렴한 가격이 장점이었다.)
- 이 시기에 컴퓨터는 간단한 수학적, 수치적 계산(삼각함숫값, 로그값 등)과 대포 탄도 계산등을 수행했다.
- 1950년대 초, 이진 정보 저장 매체 중 하나인 Punched Card가 발명되었고, 이는 Plugboard를 대체하게 되었다.
(Punched Card는 주로, I/O 수단으로 이용되었다.)
Ex. ENIAC, EDVAC
2) The Second Generation(1955-1965): Transistors & Batch Systems
- 1950년대 중반, 트렌지스터가 발명되면서 컴퓨터는 이전보다 생산이 쉬워지고, 비교적 널리 보급될 수 있었다.
- 처음으로, Designer, Builder, Operator, Programmer, Maintenance로 직책이 분리되어 분업을 수행하게 되었다.
- 이 시기의 컴퓨터를 Mainframe이라 불렀으며, 전문적인 Operator에 의해 구동되었다.
- 메인 프레임은 비싼 가격으로 인해, 대기업, 주요 정부 부처, 대학과 같은 대규모의 기관에서만 사용할 수 있었다.
- Job을 메인프레임에서 처리하기 위해, 프로그래머는 Puched Card에 어셈블리어나 Fortran으로 작성한 코드를
Input Room(메인프레임이 위치한 곳)에 있는 Operator에게 건네준 후, Output이 나오기를 기다려야 했다.
- Operator가 Machine Room내에서 이리저리 돌아다니는 시간 만큼, Cost가 낭비되었고,
이를 개선하기 위해 Batch System*이 고안되었다.
- 이 시기에 컴퓨터는 물리학, 공학에서의 편미분방정식과 같은 과학적, 공학적 계산에 이용되었다.
- 이 시기에는 주로 FORTRAN 혹은 어셈블리어를 통해 프로그램이 작성되었고,
주로 이용되는 OS는 FMS(the Fortran Monitor System) 혹은 IBSYS(IBM 7094 머신을 위한 OS)이었다.
- 이 시기에(1956년), IBM이 최초로 Disk라는 Storage를 개발했다.
* Batch System
- 입력되는 Job들을 일정량이 모일 때까지 기다렸다가 한꺼번에 처리한 후, 다음 입력 Job을 처리하는 방식이다.
- 하나의 Job이 진행되는 동안, 다른 Job들은 무조건 기다려야 하는 구조이다.
(위 그림의 경우, 약간의 Pipelining을 진행할 수 있긴 하다.)
- IBM 1401 컴퓨터와 같이, 카드를 읽고, 테이프를 복사하고 출력하는데 특화된 저렴한 컴퓨터가 I/O를 담당하고,
IBM 7094 컴퓨터와 같이, 수치 계산에 특화된 고가의 컴퓨터가 실질적인 계산을 수행하는 구조이다.
3) The Third Generation(1965-1980): ICs & Multiprogramming
- IBM 1401와 7094의 장점들을 결합한, IBM 360 컴퓨터는 최초로 IC(Intergrated Circuit)로 설계된 컴퓨터였다.
- H/W의 가격이 전반적으로 저렴해지고 성능이 개선됨에 따라, System Utilization을 제고시키고자 했다.
- IBM 360에는 OS/360이라는 OS가 탑재되었었는데, 이는 IBM이 고안한 "Single Family" 개념에 기반하여,
IBM에서 생산하는 라인업에 모두 탑재될 수 있는 OS였다.
(비교적 작은 규모의 1401 컴퓨터에서부터, 큰 규모의 7094 컴퓨터에 까지 탑재될 수 있었다.)
- OS/360과 같은 3세대 OS에서부터 Multiprogramming* 개념이 도입되었다.
- Multiprogramming 개념이 도입되기 이전, 7094 컴퓨터에서는 현재 처리중인 Job에 대한 I/O Operation이
실행되는 동안에는 CPU는 Idle 상태에서 I/O 연산이 끝나기를 기다렸다.
(즉, 그만큼의 시간이 낭비되는 구조였다. I/O 연산의 수가 늘어남에 따라 CPU Utility가 급격히 감소했다.)
* Multiprogramming
- OS는 Disk로부터 새로운 Job을 빈 Memory Partition에 할당하는 Spooling* 작업을 수행할 수 있다.
- 2세대 OS와 달리, Job들에 대한 I/O를 다른 컴퓨터로 처리하는 것이 아닌,
메인 컴퓨터의 메모리에 Job들을 Load시켜놓고, Job 1이 I/O 연산중이면, CPU는 그 동안 Job 2를 처리함으로써,
CPU가 Idle 상태로 오래 머무는 것을 방지할 수 있게 되었다.
- 이를 가능하게 하기 위해서는, CPU의 개입없이 단독으로 Job을 처리할 수 있도록 I/O Device가 개선되어야만 했다.
(이런 메커니즘을 지원하기 위해 Interrupt, Polling 기능이 개발되었다.)
- 또한, OS는 다수의 Job들이 서로의 메모리 영역을 침범하지 못하도록 Protection해야 한다.
- 그러나, 3세대 OS 또한 근본적으로 Batch System 방식을 채택했기 때문에 하나의 Job이 CPU를 오랫동안 차지하게 되면,
다른 Job들은 하염없이 기다려야만 하는 상황이 벌어질 수 밖에 없었다.
- 이를 해결하기 위해 고안된 개념이 Timesharing* 기법이다.
* Spool: Simultaneous Peripheral Operation On Line
- 여러 Job들이 한 I/O Device에 Operation을 요청하는 경우에, 해당 I/O Device가 Job들을 순서대로 처리하는 기능이다.
(I/O를 위한 Batch System이라 볼 수 있다.)
- Spool 기능 덕에, IBM 1401 컴퓨터와 같은 I/O를 전담하는 머신의 필요가 사라지게 되었다.
※ Multiprogramming 기법과 Batch System은 반의어가 아니다. 각각은 다른 범주의 기술들이다.
- 하나의 프로그램 입장에서는 Batch System에서의 처리속도가 더 빠르다.
- Multiprogramming 기법에서는 Job들에 대한 CPU Switching에서 발생되는 Overhead가 존재하기 때문이다.
- 즉, Multiprogramming은 Throughput 개선(System Utilization 제고)에 초점을 맞춘 방법이고,
Batch System은 Turnaround Time 개선에 초점을 맞춘 방법이라 볼 수 있다.
- Timesharing System은 기본적으로 Multiprogramming 기법 전제하에 구현되는 시스템이다.
(반대로, Multiprogramming 기법에서는 Timesharing이 이루어진다고 보장할 수 없다.)
* Timesharing System (시분할 시스템)
- 일정 시간동안, 여러 Job이 CPU를 번갈아가며 사용하는 형태이다.
(H/W를 여러 APP이 나눠서 사용하는 형태이다. APP은 H/W 자원을 독점하고 있는듯한 느낌을 받게 된다.)
- 이를 통해, 다수의 APP들의 실행 결과를 즉각적으로, Interactive하게 확인할 수 있게 되었다.
(실제로, CRTs를 통해, 여러 사용자가 동시에 컴퓨터를 Interactive하게 동작시킬 수 있었다.)
- Timesharing System을 구현하는 기법 중 하나로 Timeslicing이 있다.
(Job들이 H/W 자원을 일정 시간 동안만 사용하고 다음 Job에 넘겨주는 형태이다.)
- Timesharing 기법을 최초로 도입한, 1세대 시분할 OS로는 CTSS(Compatible Time Sharing System)가 있다.
(시분할 시스템이 본격적으로 인기를 끌기 시작한건, Protection H/W가 보급되기 시작한 3세대 시분할 OS가 등장한 이후부터 였다.)
- "Computer Utility" 개념이 대두되면서, MIT, Bell Labs, GE의 주도하에 MULTICS(MULTiplexed Information and Computing Service) 라는 새로운 시분할 OS가 개발되어 GE-645 메인프레임 컴퓨터에 탑재되었다.
- 20세기말, Computer Utility 개념이 흐지부지될 뻔한 찰나에, Timesharing 시스템이 Cloud Computing* 시스템으로 개편되어 등장했다.
- 상업적인 성공을 거두진 못했지만, MULTICS는 UNIX*, Linux, iOS, Android와 같은 후대 OS에 큰 영향을 미쳤다.
* Cloud Computing
- Local Computer가 서버에 연결되어, 실질적인 연산은 서버가 처리하고,
그 결과를 Local Computer가 User Interface를 통해 제어하는 형태이다.
* UNIX System
- Bell Labs에서 MULTICS 개발에 참여했던 Ken Thompson에 의해 개발된 OS이다.
- 기존의 OS는 어셈블리어로 작성된데에 반해, UNIX는 C언어로 개발되었다.
(UNIX라 하더라도, CPU에 직접적으로 연관된 부분은 어쩔 수 없이 어셈블리어로 작성되었다.)
- UNIX는 High-Level 언어로 개발되었고, Source가 공개된 덕분에, 개편에 용이했다.
(실제로, UNIX는 각 조직들에 의해 여러가지 버전으로 나뉜다.)
- UNIX는 매우 많은 버전이 존재하며, 가장 유명한 버전으로는
AT&T의 System V와 UC Berkeley의 BSD(Berkeley Software Distribution)가 있다.
- IEEE가 어떤 UNIX 시스템에서 구동될 수 있는 프로그램을 작성가능하게끔 POSIX라는 UNIX 표준을 제정했다.
(대부분의 UNIX 시스템은 이 표준을 따랐다.)
- 버전이 매우 많은 탓에 같은 UNIX 시스템이라 하더라도, 버전에 따라 프로그램의 구동여부가 보장되지 않는 경우가 많았다.
(하지만, 그 이후 Linux가 시장 점유율을 크게 차지하는 바람에 오히려 이 호환성 문제는 얼추 해결되었다.)
4) The Fourth Generation(1980-Present): Personal Computers
- LSI(Large Scale Integration)의 개발과 함께, Personal Computer의 시대가 시작되었다.
(Personal Computer는 초기에는 Microcomputer라고 불렸다.)
- 이 시기에, 각각의 컴퓨터들이 Network로 연결되기 시작했다.
(여러 컴퓨터들이 연결된 "Distributed System" 개념이 이 때 고안되었다.)
- Job들이 여러 컴퓨터들에서 처리됨에 따라, OS가 Interprocess Communication과 Network Stack을 지원하기 시작했다.
- 또한, OS는 분산된 H/W 자원 혹은 S/W 자원을 공유할 수 있도록 여러가지 기법들을 통해 이를 지원했다.
(Load Balancing, Authentication & Access Control 등이 이 기법에 해당된다.)
* Parallel System (1980's)
- 하나의 프로그램을 구동하기 위해, 다수의 CPU가 Interconnect되어 연산하는 형태의 시스템이 고안되었다.
- SMPs (Symmetric Multi-Processors): 다수의 CPU(Core)로 구성된 시스템
- MPPs (Massively Parallel Processors): 굉장히 많은 CPU(Core)로 구성된 시스템
- NOWs (Networks of Workstations): 빠른 네트워크로 연결된 다수의 컴퓨터로 구성된 시스템
- Computational Grid (SETI @home): 인터넷으로 연결된 다수의 컴퓨터로 구성된 시스템
- Clusters: 네트워크로 연결된 다수의 컴퓨터로 구성된 시스템
* Internet (1990's)
- 인터넷이 등장하기 이전에는 ASCII Terminal을 통해 네트워크에 접속하여 아주 간단한 기능들만 이용할 수 있는 형태였다.
- TCP, HTTP, Web Browser는 세계의 컴퓨팅 판도를 바꾸어놓았다.
- Internet의 Hyper-Text 기술은 그 당시 매우 획기적인 것이었으며, 이 시기에 Netscape회사가 설립되어 상업적으로 성공을 거뒀다.
5) The Fifth Generation(1990-Present): Mobile Computers (Ubiquitous Computing)
- Computing이 가능한 Sensor들의 가격이 저렴해지면서, 모든 곳에 Sensor(Processor)들이 Embedded될 수 있었고, Ubiquitous Computing이 가능해졌다.
1.3 Computer hardware review
1.3.1 Processors
* Special Registers
1. PC (Program Counter)
- 다음에 Fetch할 명령어의 주소를 보관하고 있는 레지스터이다.
- 다음 명령어를 Fetch하고 나면, PC는 그 다음 명령어를 포인팅하도록 Update한다.
2. Stack Pointer
- 현재, 메인 메모리에 있는 Stack의 맨 윗 부분의 주소를 보관하고 있는 레지스터이다.
- Procedure들은 저마다의 Stack을 갖고 있으며, 이 Stack에는 레지스터에 저장되지 못한
Input Parameters, Local Variables, Temporary Variables 등이 저장된다.
3. PSW (Program Status Word)
- System Call과 I/O에서 중요한 역할을 하는 레지스터이다.
(General-Purpost Register가 아닌, Special-Purpose Register이다.)
- User Program은 PSW의 모든 정보를, 읽을 수는 있지만 쓰기 연산은 제한적이다.
(PSW에 저장된 Bit들은 보통 Kernel 모드에서 다뤄지거나, 자동으로 설정된다.)
- PSW에는 아래와 같은 정보들이 저장되어 있다.
- Condition Code Bits
- Mode 정보 (User Mode/ Kernel Mode)
- 다양한 Control Bits
* Superscalar CPU
- 기본적인 파이프라인 구조에서 더욱 진보한 형태이다.
- 하나의 Holding Buffer 에서 다수의 Execute Unit로 연결된 구조이기 때문에, 명령어가 종종 순차적으로 실행되지 않을 때가 있다.
- 순차적으로 실행되지 않아도, 프로그램 실행에 문제가 없게끔 H/W가 보장을 하지만, 이러한 복잡한 구조는 OS에 부담을 가중시킨다.
* Multithreading
- CPU가 서로 다른 n개의 스레드 상태를 유지한 상태에서, ns 단위로 포커스 전환을 하게하는 기술이다.
- 완벽한 Parallelism 기법은 아니다.
- 특히, Intel CPU에서 Multithreading 기술은 Hyperthreading 기술이라 부르기도 한다.
- OS는 하나의 CPU내에 존재하는 각각의 스레드를 서로 분리된 CPU로 간주한다.
* Multicore
- 코어란, 스레드보다 더 완전한 프로세서를 의미하며, CPU 내부에 내장된다.
- 코어는 그 자체로 하나의 CPU로 간주된다. (스레드보다 더 완전한 CPU이기 때문에 당연하다.)
* GPU (Graphics Processing Unit)
- 수천개의 작은 코어들로 구성된 프로세서이다.
- Polygon Rendering과 같은, 비교적 작은 계산을 병렬로 처리하는데에 특화되어 있다.
(반대로, 일련의 작업을 처리하는데에는 불리한 조건을 갖고 있다.)
- 또한, GPU는 Network Traffic Encryption 혹은 Network Traffic Processing과 같은 OS 시스템 처리에도 활용될 수 있다.
1.3.2 Memory
* Memory Hierarchy
* Shared L2 Cache - Separated L2 Cache
(a) 각 코어는 각자의 L1 Cache를 보유하고, L2 Cache는 서로 공유하는 형태
- Intel Multicore Chip이 이러한 방식으로 설계되었다.
- 보다 복잡한 Cache Controller가 필요하다.
(b) 각 코어가 각자의 L1 Cache와 L2 Cache를 보유하고 있는 형태
- AMD Chip이 이러한 방식으로 설계되었다.
- Cache Consistent(캐시 일관성) 유지를 더욱 어렵게 한다.
* ROM (Read Only Memory)
- 메인 메모리와 달리, Non-Volatile(비휘발성)이라 전원이 Off되어도 데이터가 보존되는 메모리이다.
- 공장에서 Programmed되어 생산되고, 추후에 수정될 수 없다.
- Bootstrap Loader가 컴퓨터를 부팅할 때 ROM에 있는 데이터를 사용한다.
- 또한, I/O Card가 Low-Level Device를 제어하기 위해 ROM을 이용한다.
* EEPROM (Electrically Erasable PROM)
- ROM과 같은 비휘발성 메모리이지만, ROM과 달리 데이터 수정이 가능하다.
- ROM과 거의 같은 역할을 수행한다.
* CMOS
- Volatile(휘발성) 메모리이다.
- 대부분의 컴퓨터에서 CMOS는 시간, 날짜 정보를 저장해놓거나,
Configuration Parameter(부팅 순서와 같은 정보들)를 저장해놓기 위해 사용한다.
1.3.3 Disks
* Virtual Memory
- 실행중인 Program이 물리적 메모리 용량에 제한받지 않고, 더 많은 메모리가 있는 것처럼 느껴지게 하는 Scheme이다.
- MMU(Memory Management Unit)에 의해, 가상 메모리 주소와 물리 메모리 주소가 서로 변환된다.
1.3.4 I/O Devices
- I/O Device들은 자기 자신을 물리적으로 제어하는 Controller를 갖고 있다.
- Controller들은 OS에게 자신이 제어하는 I/O Device의 Interface를 제공하는 역할을 한다.
- I/O Port Space는 Device Controller들이 가진 Register 형태를 통칭하는 표현이며,
이 Register들을 통해 OS로 부터의 명령을 전달받고 결과를 넘겨준다.
- 이러한 Device Controller들과 OS를 연결해주는 S/W를 Device Driver라 한다.
- I/O가 처리되는 방식은 Busy Waiting 방식, Interrupt 방식, DMA를 활용한 방식이 있다.
* SATA (Serial AT Attachment)
- 대부분의 컴퓨터에서의 Disk의 표준 타입이다.
* Busy Waiting
- 스레드 A가 다른 스레드의 작업이 완료될 때 까지 CPU 접근여부를 무한 Loop를 돌며 묻는 방식이다.
- I/O 드라이버가 I/O 작업을 끝낼 때 까지, 프로그램은 무한 루프를 돌며 I/O 작업이 완료됐는지를 검사하고,
I/O 작업을 마친 드라이버는 적절한 값을 리턴하고, OS는 Caller(프로그램)에게 제어를 다시 넘긴다.
* Interrupt
- I/O Device가 작업을 마치면, Interrupt를 발생시키고, 결과를 리턴하는 방식이다.
- I/O Device가 작업을 수행하는 동안, OS는 Caller를 Block시키고 그 동안 수행할 다른 작업을 찾는다.
- Device Controller가 작업이 완료되었음을 감지하면, Interrupt Signal을 발생시킨다.
- Interrupt가 발생되면, PC와 PSW는 Current Stack에 Push되고, Kerner Mode로 Switch된다.
- Interrupt Handler가 구동되고 있는 중에, 다른 Interrupt가 발생되면 CPU는 해당 Interrupt를 Disable하거나 Re-Enable한다.
* Interrupt Process
Step 1
- CPU로부터 명령을 받은 OS의 Device Driver가 Device Controller에게 수행해야 해야할 작업을
Device Register에 입력함으로써 알린다.
- 작업을 전달받은 Device Controller는 Divice를 구동시킨다.
Step 2
- Device가 작업을 완료하면, Device Controller가 Interrupt Controller에게 Device가 읽거나 쓴 데이터를 넘긴다.
- 이 과정은 OS에 의해 제어되지 않고, Disk Controller H/W가 자체적으로 처리하는 과정이다.
※ Disk Controller가 Interrupt Controller를 상대로 수행하는 I/O Operation(2번 Line)은 비교적 많은 시간이 소요되므로,
멀티프로그래밍 시스템상에서 CPU는 그 시간 동안 다른 Job을 처리한다.
Step 3
- 우선순위가 더 높은 작업을 처리하고 있지 않는 한, Interrupt Controller는 CPU에게 Device가 작업을 완료했음을 알린다.
- 즉, Interrupt Controller가 Interrupt Signal을 CPU에게 보낸다.
Step 4
- Interrupt Controller는 Step 3의 3번 Line으로 Interrupt Signal을 전송함과 동시에,
4번 Line으로 Interrupt를 발생시킨 Device의 번호를 함께 전송한다.
- Interrupt Controller는 다수의 Device를 Bus에 위치시켜서 CPU가 이들이 작업을 다 수행했는지의 여부와,
수행한 결과(데이터)를 읽을 수 있게 한다.
- 일반적으로, 다수의 Device는 동시에 구동되는 것이 흔하다.
- 위 그림은 실행중인 명령어들이 적재된 메인 메모리(D-RAM)를 도식화한 것이다.
(현재 명령어를 처리하다가 인터럽트가 발생한 상황이다.)
- 명령어 실행 중 Interrupt가 발생되면, 자동으로 Kernel 모드로 전환되고,
Interrupt Vector*를 통해, OS의 일부인 Dispatch Handler가 위치한 곳으로 제어가 넘어간다.
- 인터럽트가 발생되면 실행되는 Dispatch Handler Routine이 Figure 1-11. (a)의 4번 Line으로 전달받은
Device Number를 통해 해당 Device의 인터럽트를 처리할 수 있는 Interrupt Handler를 호출한다.
- Interrupt Handler는 Interrupting Device의 Driver의 일부이다.
- Interrupt Handler가 실행되면, Stack의 PC와 PSW가 Pop되어 저장된다.
- Handler의 작업이 완료되면, User Program이 실행하지 못한 명령어를 다시 시작하게끔 재개한다.
(혹은, 스케쥴링 방식에 따라 완전히 새로운 프로세스를 시작할 수도 있다.)
* Interrupt Vector
- Interrupt Handler의 주소를 찾기위한 Index 역할을 하는 Device Number가 저장된 메모리 부분을 의미한다.
- 즉, Interrupt Handler의 주소이다.
* DMA (Direct Memory Access)
- CPU의 중재 없이, 메모리와 Controller 사이를 제어하는 Unit이다.
1.3.5 Buses
PCIe Bus
- Peripheral Component Interconnect Express Bus의 약어이다.
- 아키텍처의 메인 버스 역할을 한다.
- Intel사의 PCI Bus의 후속작으로, PCIe는 출시 이후 Original ISA(Industry Standard Architecture)를 대체했다.
* Shared Bus Architecture
- 다수의 Device들이 하나의 Wire를 공유하며 데이터를 송수신하는 형태를 의미한다.
- 다수의 Device가 공유하는 형태이기 때문에, Arbiter가 필요한 구조이다.
* Parallel Bus Architecture
- 하나의 데이터를 다수의 Wire를 통해 송수신할 수 있는 형태를 의미한다.
- PCI Bus에서는 32-bit 수치가 32개의 Parallel Wire를 통해 전송됐었다.
* Serial Bus Architecture
- 하나의 데이터를 하나의 Wire를 통해 송수신하는 형태를 의미한다.
- PCIe Bus가 이에 해당된다.
DMI Bus
- Direct Media Interface Bus의 약어이다.
- CPU는 메모리와 그래픽 장치들과 PCIe 버스를 통해 연결되고, 그 이외의 장치들과는 DMI 버스를 통해 연결된다.
USB
- Universal Serial Bus의 약어이다.
- 키보드, 마우스와 같은 느린 I/O Device들을 컴퓨터에 연결시키는 버스이다.
* SCSI
- Small Computer System Interface의 약어이다.
- 고성능의 Bus로, 많은 Bandwidth을 요구하는 Device에 사용된다.
- 주로, 서버나 워크스테이션 아키텍처를 구성하는데 사용된다.
1.3.6 Booting the Computer
* BIOS (Basic Input Output System)
- 메인보드에 구성된 시스템으로, Low-Level I/O S/W를 포함하고 있어, 여러가지 I/O 연산을 수행한다.
- BIOS는 CMOS Memory내에 저장되어 있는 Device List중에서 부팅 디바이스를 선택한다.
- Nonvolatile인 Flash RAM에 저장되며, 버그가 발견되면 OS에 의해 업데이트될 수 있다.
- 컴퓨터가 부팅되면, BIOS 또한 시작되며,
가장 먼저 PCIe와 PCI 버스를 통해 RAM과 다른 기본적인 Device들이 잘 응답하는지를 체크한다.
- OS는 BIOS로부터 디바이스 드라이버 유무와 같은, 환경설정 정보를 얻을 수 있다.
- OS가 BIOS로부터 모든 디바이스 드라이버가 정상 작동되고 있음을 확인하면,
이 모든 드라이버를 Kernel에 Load하고, 이를 이용하여 테이블을 초기화하고,
필요한 Background Process를 만들어 OS의 로그인 프로그램을 구동시킨다.
1.4 The operating system zoo
Mainframe OS
- 많은 양의 Job과 I/O 작업을 한꺼번에 처리하는데 특화된 OS이다.
- 하이엔드 웹서버, 대규모의 전자상거래 사이트를 운용하는 서버, 사업체 간 트랜잭션을 다루는 서버와 같은 메인프레임 컴퓨터에 탑재되는 OS이다.
- 대표적으로, IBM OS/390 시스템이 이에 해당된다.
- 메인프레임 OS는 주로, Batch 형태, Transaction processing 형태, Timesharing 형태로 구현된다.
1) Batch Type
- Non-Interactive하며, Job들이 일정량 모이면 처리한다.
- 보험사에서 Claims를 한꺼번에 처리하거나, Store Chain에서 Sales Report를 한 번에 처리하는 데 적합하다.
2) Transaction Processing Type
- 트랜잭션을 처리하는 형태이다.
- 항공사의 비행기 티켓 예매 시스템에 적합하다.
3) Timesharing Type
- 많은 유저들이 사용하는 시스템에 적합한 형태이며, 유저들은 Interactive하게 컴퓨터를 사용할 수 있다.
Server OS
- 고성능 PC, 워크스테이션에 적합한 OS이다.
- 네트워크에 연결되어 있다면, Timesharing을 통해 다수의 사용자를 수용할 수 있다.
- 일반적으로, Print 서비스, File 서비스, 웹 서비스를 제공하는 머신에 사용된다.
- 대표적으로 UNIX 시스템, Windows 시스템이 이에 해당된다.
Multiprocessor OS
- 다수의 프로세서로 구성된 시스템에 적용되는 OS이다.
- 근래의 대부분의 시스템은 멀티 프로세서로 구성되어 있어, 대부분의 OS는 멀티프로세서 OS 개념을 포함하고 있다 봐도 무방하다.
Personal Computer OS
- User와의 Interaction을 중시하는 OS이다.
- 즉, Throughput보다는 Response Time에 초점을 맞춘 OS이다.
- 대표적으로 Windows, Mac, Linux등이 이에 해당된다.
※ 사실 Linux만 해도, 메인프레임, 서버와 같은 머신에서 사용할 수 있다.
즉, 현대의 OS는 대부분 하나의 목적에 특화되어 있지 않고 범용적인 형태로 제공된다.
Handheld Computer OS
Embedded OS
- 크기, 메모리, 전원이 제한된 환경의, 임베디드 시스템에 적용되는 OS이다.
- TV, 전자레인지, 휴대전화 등에 적용된다.
- 약간의 Real-Time의 성격을 가진다.
- 대표적으로 PalmOS, Windows CE(Consumer Electronics) 등이 이에 해당된다.
Sensor-Node OS
Real-Time OS
- 어떤 Job이 완료되기까지의 Deadline을 넘기지 않도록 설계된 OS이다.
- Interrupt 처리방식이 일반적인 OS와 다르다.
- 대표적으로 VxWorks, QNX등이 이에 해당된다.
1) Hard Real-Time OS
- Job에 대한 Deadline을 무조건적으로 보장하는 형태이다.
- 주로 공장 시스템, 비행조정장치, 군용 장치와 같은 Mission-Critical한 시스템에 적용된다.
2) Soft Real-Time OS
- 비교적 Job에 대한 Deadline을 위반하는 것이 어느정도 허용되는 형태이다.
- 디지털 오디오, 멀티미디어 스트리밍 시스템에 적용된다.
Smart Card OS
- CPU Chip을 내장한, 간단한 알고리즘을 처리할 수 있는 카드 크기의 시스템에 적용되는 OS이다.
- 시스템 자원이 매우 제한된 형태이기 때문에, Single Function 만을 지원하는 형태가 일반적이다.
1.5 Operating system concepts
1.5.1 Processes
- Process = Program in Execution
- 프로세스는 OS가 구동중인 프로그램을 관리하기 위해 고안된 개념이다.
- 각각의 프로세스는 Code, Stack, PC, Register, Page, Table, Resource 등으로 구성된다.
(여기서, Table은 프로세스가 사용하는 메모리의 Mapping 정보에 관한 표이다.)
- OS는 Process 단위로 자원을 할당/수거하고, 스케쥴링을 수행한다.
* Execution Context
- 수행중인 프로세스가 사용하는 H/W Resource와 S/W Resource를 합친 개념이다.
- 프로세스가 CPU를 점유하고 있다가, 어떤 이유에서 중단되고, 다시 재개될 때,
CPU는 다른 프로세스를 처리하느라, CPU의 상태가 바뀌었을 것이고,
해당 프로세스를 다시 정상적으로 처리하기 위한 정보들을 CPU는 Execution Context를 통해 알 수 있게 된다.
- 프로그램 자체 코드, 데이터, PC, Register, Virtual Memory Map, OS Resource 등이 Execution Context로 구성된다.
* Process Operation
- OS가 제공하는 Process 연산의 종류는 아래와 같다.
create a process | delete a process | suspend a process |
resume a process | clone a process | inter-process communication |
inter-process synchronization | create a child process = create a subprocess |
delete a child process = delete a subprocess |
1.5.2 Address Spaces (Memory)
1.5.3 Files
- File과 Directory의 논리적 구조는 File System에 구현되어 있다.
- 시스템마다, File에 대한 정의는 차이가 있다.
- UNIX 시스템에서 File은 Byte-Sequence이다.
- Directory는 하위에 File을 갖고 있는, 특별한 형태의 File이다.
- File은 Root Directory로 부터 Tree 구조로 형성되어 있다.
(Windows의 경우, Stroage의 Partition 마다 개별적인 Tree로 구성되어, 다수의 Tree로 구현될 수 있는 형태를 가졌다.)
1.5.4 I/O
- OS Kernel Code의 대부분은 I/O에 관련된 내용이다.
- OS는 Application에 System Call의 형태로, I/O Device와의 Interface를 제공한다.
- Device Driver Routine에는 Device 초기화, I/O 요청, 인터럽트와 에러 처리와 같은 기능들이 구현되어 있고,
OS는 Device Driver Routine을 통해, Device를 관리한다.
(Device Driver Routine는 당연히 Kernel Mode에서 동작한다.)
1.5.5 Protection
* Protected Instruction (Privileged Instruction)
- User Program은 사용할 수 없는, OS Kernel Mode에서만 사용가능한 CPU Instruction을 의미한다.
(User Program은 System Call을 통해 실행하는 방법 밖에 없다.)
- Protected Instruction의 종류는 아래와 같다.
- I/O Device에 직접적으로 접근하는 명령어
- Page Table, TLB등을 관리하는 메모리 관리 명령어
- Protected Processor Register(PSW와 같은)에 설정되는 Mode Bit을 설정하는 명령어
- halt 명령어
* OS Protection
- 아키텍처는 Kernel 모드와 User 모드를 지원하여 프로세서가 Protected Instruction의 실행 가능여부를 판단할 수 있게 한다.
- VAX, x86 프로세서의 경우, 4단계의 Protection Mode를 지원한다.
* Crossing Protection Boundary
- User 모드와 Kernel 모드 사이를 전환하는 메커니즘으로는 아래와 같이, Interruption과 Trap 명령어, Fault가 발생한 경우가 있다.
(결국, Interrupt를 발생시켜야만 Kernel 모드로 전환되어 OS를 불러내는 것이다.)
1) Interruption (H/W Interruption)
- Figure 1-11.(a)에서와 같이, Device Controller가 Interrupt Signal을 발생시키면, OS는 Kernel Mode로 전환시킨다.
- 여기서, Device Controller는 H/W이기 때문에, H/W적인 Interruption으로 구분지었다.
2) Trap (S/W Interruption)
- User Program이 Kernel 모드에서 Protected Instruction을 실행하기 위해선, System Call을 이용하는 수밖에 없다.
- System Call은 Trap 명령어를 실행시켜 Kernel Handler Vector에 저장된 주소로 이동시킨다.(Kernel 모드로 전환한다.)
(이 때, Trap 명령어는 각 프로세서 제조사별로 그 형태가 다를 수 있다.)
- 또한, System Call은 필요한 Parameter를 User Program에게 요구하고, OS가 이를 체크하여 Kernel에 넘겨줄 수도 있다.
- System Call이 실행되는 순간, Caller의 State(Register, Mode Bit 등)는 백업된다.
- OS가 Service를 모두 완료하면, 다시 User 모드로 전환하고, 연산 결과를 Caller에게 리턴한다.
- 이 경우, System Call이라는 S/W에 의해 모드가 전환되는 것이므로, S/W Interruption이라 구분지었다.
3) Fault가 발생한 경우
- 어떤 수치를 0으로 나누는 등의 비정상적인 연산이 수행되면 Fault가 발생되며, 마찬가지로 Kernel의 특정 주소지로 Jump하게 된다.
Example.
- Netscape(WEB Browser Application)가 \(\texttt{read()}\) System Call을 실행하면,
\(\texttt{read()}\) 내에 있는 Trap 명령어에 의해 Kernel 모드로 전환된다.
- 이 때, Kernel 모드로 전환되기 전에 Application의 State가 백업된다.
- 이 경우에서, Kernel 모드로 전환된다 함은, Trap Handler가 위치한 주소로 이동함을 의미한다.
- Trap Handler는 \(\texttt{read()}\)를 수행할 Kernel Routine을 Vector Table 상에서 찾은 다음 PC에 적재시켜, 수행시킨다.
(Linux에는, \(\texttt{read()}\)를 수행할 Kernel Routine이 \(\texttt{sys_read()}\)로 정의되어 있다.)
- \(\texttt{read()}\)가 작업을 완료하면, 읽어들인 bytes 수를 Caller에게 리턴하고,
백업해두었던 Application State를 복원하여 프로그램을 재개한다.
Example. \(\texttt{read()}\) System Call의 개괄적 설계
// read()와 같은 C언어에서 지원하는 System Call은 C언어의 STL로 정의되어 있다.
int read(int fd, char *buf, int size)
// fd : File Descriptor
// buf : 주솟값
// size : read할 데이터의 크기
{
move fd, buf, size to R_1, R_2, R_3
// Argument들을 CPU Architecture Register에 옮긴다.
move READ to R_0
// READ는 특정 상수이다.
int $0x80
// Trap 명령어를 수행한다.
// OS Kernel이 작업을 수행된다.
// Trap Handler가 read()에 해당되는 Kernel Routine을 실행시킨다.
// 커널모드에서 레지스터에 결과값을 저장하고, iret 과 같은 명령어를 통해 다시 User 모드로 전환한다.
// iret 명령어는 Register에 값들을 Restore시키고(Execution Context를 복구하고),
// PC에 Interrupt가 발생된 시점 바로 다음의 명령어 주소를 저장하고 User 모드로 전환하는 명령어이다.
// iret : Interrupt Return
move result to R_result
// 커널모드에서의 작업 결과(result)를 R_result 레지스터에 저장한 후, read() 함수를 종료한다.
// 여기서, 작업 결과란 읽어들인 데이터의 Byte 수를 의미한다.
}
1.5.6 The Shell (Command Interpreter)
- OS에서 Prompt를 띄워주고, 입력된 명령어를 실행시켜주며, 입력된 명령어에 대한 Interpretation이 가능한 프로그램이다.
- 시스템 관리, 대량의 처리 등에 유용한 Shell Scripting 기능을 제공한다.
- Shell은 System S/W로 분류하기도 하고, Application으로 분류하기도 한다.
- Linux에서는 단순한 형태의 Shell인, sh가 bash, csh, tcsh, zsh 등으로 파생되었다.
(이 중, bash는 Linux 시스템의 Default이며, 가장 인기가 있다.)
- MS-DOS, Apple II와 같은 시스템에서는 Command Interpreter가 OS의 일부분이 되기도 했었다.
(OS의 크기가 커지므로, 절대 좋은 선택이 아니다. Shell은 Application처럼, 독립적으로 존재하는 것이 바람직하다. OS의 크기가 커지면 Reliability가 저해된다.)
- 일반적으로, Shell은 Non-Privileged Process이다. 즉, User Mode에서 수행되는 S/W이다.
1.5.7 Ontogeny Recapitulates Phylogeny
1.6 System calls
- 1.5.5 Protection 절에서 예시로 들었던, Netscape APP의 \(\texttt{read()}\) System Call 사용 예시를 더 자세히 풀어낸 그림이다.
Step 1 | - \(\texttt{read()}\) 가 요구하는 Argument 중 \(\texttt{nbytes}\)(Size)를 Stack에 Push한다. ※ Argument Passing에 Stack을 이용하는 방법은 비교적 구식 방법이며, 근래에는 Register를 이용하는 방법이 일반적이다. |
Step 2 | - \(\texttt{read()}\) 가 요구하는 Argument 중 \(\texttt{&buffer}\)를 Stack에 Push한다. |
Step 3 | - \(\texttt{read()}\) 가 요구하는 Argument 중 \(\texttt{fd}\)(File Descriptor)를 Stack에 Push한다. |
Step 4 | - \(\texttt{read}\) Instruction을 실행한다. |
Step 5 | - 상수 \(\texttt{READ}\) 값을 Register에 저장한다. - 상수 \(\texttt{READ}\)는 \(\texttt{read()}\) 작업을 의미하는 값이다. - 그 이후, Trap 명령어를 실행한다. |
Step 6 | - Trap 명령어에 의해, Dispatch(Trap Handler)로 이동한다. |
Step 7 | - 각 System Call에 해당하는 Kernel Routine(함수)의 주솟값이 저장된 Table에서 수행할 함수를 찾는다. - 이 사례에서는, Table에서 \(\texttt{R_0}\) 레지스터에 있는 \(\texttt{READ}\) 값에 해당되는 함수를 수행하게 될 것이다. - 리눅스의 경우, \(\texttt{READ}\) 값에 해당되는 Kernel Routine은 \(\texttt{sys_read()}\)일 것이다. |
Step 8 | - Table에서 찾은 주소에 해당되는 System Call Handler를 수행시킨다. |
Step 9 | - Kernel Routine이 작업을 완료하면, \(\texttt{iret}\) 명령어를 통해 User 모드로 전환한다. |
Step 10 | |
Step 11 | - \(\texttt{read()}\) 함수가 사용한 Stack을 Pop시킨다. |
1.6.1 System Calls for Process Management
* Process Management System Calls (POSIX System)
System Call | Description |
pid = fork() | Create a child process identical to the parent |
pid = waitpid(pid, &statlocm options) | Wait for a child to terminate |
s = execve(name, argv, environp) | REplace a process' core image |
exit(status) | Terminate process execution and return status |
* File Management System Calls (POSIX System)
System Call | Description |
fd = open(file, how, ...) | Open a file for reading, writing, or both |
s = close(fd) | Close an open file |
n = read(fd, buffer, nbytes) | Read data from a file into a buffer |
n = write(fd, buffer, nbytes) | Write data from a buffer into a file |
position = lseek(fd, offset, whence) | Move the file pointer |
s = stat(name, &buf) | Get a file's status information |
* Directory and File System Management System Calls (POSIX System)
System Call | Description |
s = mkdir(name, mode) | Create a new directory |
s = rmdir(name) | Remove an empty directory |
s = link(name1, name2) | Create a new entry, name2, pointing to name1 |
s = unlink(name) | Remove a directory entry |
s = mount(special, name, flag) | Mount a file system |
s = umount(special) | Unmount a file system |
* Miscellaneous System Calls (POSIX System)
System Call | Description |
s = chdir(dirname) | Change the working directory |
s = chmod(name, mode) | Change a file's protection bits |
s = kill(pid, signal) | Send a signal to a process |
seconds = time(&seconds) | Get the elapsed time since Jan. 1, 1970 |
1.7 Operating system structure
* Monolithic Kernel
- 한 덩어리 형태의 OS이다. (Basic Structure)
- Microkernel과 상반되는 개념이다.
- 최상위의 System Call Dispatch Routine이 특정 System Call을 호출하면,
해당 System Call은 필요한 다른 Function을 호출해 나가는 계층적인 구조이다.
- 모든 Procedure가 Kernel 모드에서 작동된다.
- 모듈 간 호출 시간이 짧아, 성능이 우수하다.
- OS의 크기가 커, 이해하기 힘들고 유지 및 보수에 불리하다.
- 수정에 불리하다. (새로운 기능을 추가하고 다시 OS 전체를 컴파일해야 한다.)
- 또한, 시스템 모듈이 서로 독립되어 있지 않아 Reliability가 불량하다. (Error 발생 확률이 높다.)
- 일부에 Error가 발생되면, 모든 System이 Shut Down될 확률이 높다.
* Microkernel (μ-Kernel)
- 위 그림에서 회색 박스는 Microkernel 영역이며, OS는 Kernel부터 Servers Layer까지 이다.
- Monolithic Kernel과 달리, 최소한의 Procedure만 Kernel 모드에서 실행되도록 제한한다.
(OS의 일부분만 Kernel 모드에서 수행되도록 한다.)
- Kernel의 크기를 최소화함으로써 Bug를 줄일 수 있다.
- 나머지 Process는 모두 Regular Process로 구분된다.
(Protected Instruction이 요구되는 상황에서만 Kernel 모드로 전환된다.)
- 시스템 모듈이 서로 독립되어 있어 Reliability가 우수하다.
(Error가 발생돼도, 전체 System에는 영향이 안 갈 수 있다.)
- 수정이 용이하다. (새로운 기능을 추가하고자 하면, User 모드에서 수행되도록 구현하여 Add-On 시키면 그만이다.)
- User 모드의 Process들은 서로 직접적으로 정보교환을 할 수 없고 매번 Microkernel을 거쳐야 하기 때문에,
빈번히 발생되는 Crossing 연산으로 인해 Microkernel은 비교적 성능이 낮다.
Reference: Modern Operating Systems 4E
(Andrew S. Tanenbaum, Herbert Bos 저, PEARSON, 2015)