2.1 Operating-System Services
- OS는 프로그램 실행 환경을 제공한다.
물론 OS마다 제공하는 서비스는 다르지만 공통된 부류가 있다는 것을 발견할 수 있다.
User Interface(UI)
: 거의 모든 OS에서는 UI를 제공하는데, 다양한 형태로 제공될 수 있다.
- Graphic User Interface(GUI) : 마우스, 키보드
- Touch Screen Interface : 손가락으로 touch, swiping
- Command Line Interface(CLI)
Program Execution
: 시스템은 program을 memory에 load하여 execution할 수 있어야 한다.
program은 정상적이든, 비정상적이든(오류 표시) execution을 끝낼 수 있어야 한다.
I/O Operation
: 수행 중인 program은 입출력을 요구할 수 있다.
OS는 User를 대신하여 입출력 장치를 제어하기 때문에 OS가 입출력 수행의 수단을 제공해야 한다.
File System Manipulation
: OS는 program으로 하여금 파일을 읽고, 쓰고, 생성하고, 삭제하고, 찾을 수 있고, 파일정보를 열거할 수 있어야 한다.
또한 디렉터리의 접근을 허가하거나 거부할 수 있게 해야 한다.
Communication
: 한 process가 다른 process와 정보를 교환해야 할 상황이 있다.
이러한 통신을 수행하는 두 가지 중요한 방법이 있다.
- 동일한 컴퓨터에서 수행되고 있는 process들끼리의 통신
- network에 의해 서로 다른 computer 상에서 수행되는 process들끼리의 통신
통신은 Shared Memory(공유 메모리), Message Passing(메시지 전달) 기법으로 구현될 수 있다.
Error Detection
: OS는 모든 가능한 error를 항상 의식하고 있어야 한다.
오류는 CPU, memory HW(정전 등), I/O Device(parity, network fail, printer 종이 부족 등) 또는 User Program(overflow, 불법적 메모리 위치에 접근)에서 일어날 수 있다.
Resource Allocation
: 다수의 Process가 동시에 실행될 때, 그들 각각에 Resource를 Allocation해주어야 한다.
OS는 CPU Scheduling이 CPU의 속도, 우선순위의 process, CPU의 처리 Core의 개수와 다른 요인들을 고려해야 한다.
Logging(기록 작성)
: 우리는 어떤 program이 어떤 resource를 얼마나 많이 사용하는지 추적하기를 원한다.
Protection and Security
2.2 User and Operating-System Interface
Command Interpreter
Shell
은 Kernel과 User 사이에 위치하여 User가 명령어를 입력하면
해당 명령어를 해석하여 실행하는 Command Interpreter
를 호출하는 프로그램이다.
즉, Shell은 Command interpreter를 포함하는 프로그램으로서
User와 OS 간의 Interface 역할을 한다.
Command Interpreter
- Sometimes implemented in kernel : 대부분은 kernel 안에 포함돼있다. (내부명령)
- Sometimes by systems program : 하나의 특수한 프로그램처럼
OS를 만든 회사에서 실행파일 형태로 만들어서 제공해준다. (외부 명령어)
- 내부명령 : kernel이 동작하면 언제든지 동작
- 외부명령 : 해당 실행파일이 있어야만 동작
- 운영체제마다 application으로 shell을 제공한다.
- Windows의 power shell
- Linux의 bourne shell, C shell, korn shell, ..
- Unix, Linux, macOS 등에서 기본적으로 제공되는 Shell 중 하나가 bash이다.
Graphical User Interface (GUI)
2.3 System Calls
-
System Call
: OS에서 제공하는 서비스에 대한 interface.
특정 low level 작업(ex. HW를 직접 접근해야 하는 작업)은
assembly 언어로 만들어지고,
보통 C와 C++언어로 작성된 함수 형태로 제공된다.
-
example> 다음은 System Call이 사용되는 예시이다.
API
-
System Call 예시에서와 같이, 간단한 program이라도 OS의 기능을 아주 많이 사용하게 된다.
== 보통 초당 수천 개의 system call을 수행하게 된다.
-
API를 구성하는 함수들은 프로그래머를 대신하여 실제 system call을 호출한다.
즉, API
는 프로그래머가 System Call을 잘 쓸 수 있도록 만들어 놓은 Programming Interface이다.
-
API는 각 함수에 전달되어야 할 매개변수와 반환 값을 포함하여
프로그래머가 사용 가능한 함수의 집합을 명시한다.
프로그래머가 System Call보다 API를 선호하는 이유
-
프로그램의 호환성과 관련이 있다.
API에 따라 program을 설계하는 프로그래머는 자신의 program이
같은 API를 지원하는 어느 시스템에서건 compile되고 실행된다는 것을 기대할 수 있다.
System Call은 사용되는 시스템에 따라 다른 방법으로 발생한다.
-
게다가 실제 System Call은 좀 더 자세한 명세가 필요하고 program 상에서 작업하기가 API보다 훨씬 더 어렵다. (system call보다 쓰기 편하다)
Standard C Library(API) Example
- User가 program을 실행하면, API(Stand C Library)가 내려가서 System Call을 호출함
- System Call은 User mode를 Kernel Mode로 변환하는 방법.
- System Call을 사용하기 좋게 만들어 놓은 함수들의 모음인 API다.
System Call Implementation
- System Call은 10H function 이라고 불림.
- System Call을 호출하면, OS Kernel 내의 해당 기능을 불러내서,
해당 기능을 수행하고, 끝나면 다시 원래 호출된 자리로 복귀하는 방식.
➡️ interrupt와 SW적으로는 동일한 기능.
But. Interrupt는 외부에서 호출, System Call은 내부에서 호출
System Call Parameter Passing
- System Call은 컴퓨터(=특정 운영체제)에 따라 방법이 서로 다를 수 있다.
➡️ parameter가 복잡하게 길 수도 있고, 묵시적으로 정해질 수도 있다.
- example : 입력을 받아들이기 위해 입력소스로 사용될 파일이나 장치,
읽어 들인 데이터를 저장할 메모리 버퍼의 주소 등과 길이를 명시할 필요가 있다.
- parameter를 User나 program에 의해 OS에게 넘겨준다.
- 넘겨주는 방법은 크게 3가지가 있다.
1. Paramter를 CPU 내 register에 전달하는 방법.
2. register보다 Parameter가 더 많은 경우는
memory 내의 block이나 table에 저장되고,
block의 주소가 register 내에 Parameter로 전달된다.
3. Parameter는 program에 의해 Stack에 Push되고, OS에 의해 Pop된다.
➡️ register가 memory보다 더 빠르기 때문에
Parameter 개수가 적으면(parameter를 register에 전달하면) program이 빠르고,
Parameter 개수가 많으면(parameter를 memory에 전달하면) program이 느려진다.
- OS는 전달받은 Parameter를 갖고, 정해진 위치에 결과값을 돌려준다.
Types of System Call
- Process Control
- File Manipulation
- Device Management
- Information Maintenance
- Communication
- Protection
➡️ 이 중에서 1. Process Control 을 상세하게 공부할 것임.
Process Control
Program
: Disk에 저장되어 있는 것
Process
: 실행되기 위해 memory에 올라와 있는 것.
Process Contorl
- end, abort
- load ,execute
- create process, terminate process
- get process attributes, set process attributes
- wait for time
- wait event, signal event
- allocate and free memory
process는 수행을 정상적으로(end()) 또는 비정상적으로(abort()) 멈출 수 있어야 한다.
만약 process에서 비정상으로 중지하기 위해 system call이 호출되거나 프로그램에 문제가 발생하여 trap을 유발한 경우,
Memory Dump(memory에 현재 하고 있는 모든 것을 저장)가 행해지고,
error message가 생성된다. (가장 심한 error가 발생하면, Blue Screen)
이 Dump는 특별한 log file이나 disk에 기록되고 문제의 원인을 결정하기 위해 Debugger에 의해 검사될 수 있다.
Process Example : Single Tasking(MS-DOS)
- 한 번에 하나의 process만 처리하는 간단한 OS라면,
할당하고 하나씩 처리하면 된다.
- process가 없는 경우는 (a)이다.
process가 생성되면, (b)와 같이 free memory 공간에 할당되어 process를 처리한다.
(b)의 process가 끝나면, 다시 (a) 형태로 돌아간다.
Process Example : Multi Tasking(FreeBSD)
-
free memory 구역 곳곳에 process가 여러 개가 들어간다.
그 중에 하나씩 선택하여 실행을 하게 된다.
-
(Chapter 3.에서 공부할 내용)
실제 OS가 쓰는 process는 kernel이 유일하다.
실행해야 될 Process가 여러 개 있다면,
kernel이 fork(분기)하여, 쓸 수 있는 여러 process를 생성한다.
만들어진 process들은 exec()을 통해 memory에 load된다.
process들이 끝나면 exit()을 통해 호출한 process에게 status code를 돌려 준다.
➡️ Kernel은 Kernel 대로 일을 하고,
Kernel에 의해 만들어진 process들은 끝나면 회수된다.
2.4 System Services
- system service는 program 형태로 대부분 제공.
(OS의 필수적 기능을 program 형태로 제공하는 것,)
- File manipulation
- Status information
- Programming Language Support
- Program Loading and Execution
- Communications
- Background Services
- Application Programs
Application은 OS마다 다르게 만들어진다.
OS마다 각각이 기능하는 system call이 다 다르기 때문.
System Call이 다르면 OS를 달리 써야 한다는 뜻.
소스레벨에서 동작하는 것이라면, 구조에 따라 호환이 될 수도 있음.
(interpreter에서 동작하는 Python 이나 matlab과 같은 것들은 OS가 달라도
interpreter 위에서 돌아가는 것이라 소스레벨에선 호환 가능, 바이너리 레벨에선 호환 X)
2.5 Linkers and Loaders
- .c file 을 생성
- compile : .c file가 문법적으로 맞는지 검사
.c를 compile하여 .o file이 만들어짐.
- linking : 여러 .o file을 묶는다
.o file을 linking하여 .exe file이 만들어짐
- .exe file에 있는 Loader가 자기의 정보를 OS에 던져주고 요청한다.
- OS는 그 정보(program, data)를 받아서 main memory에 각각 load시킨다.
요즘 Windows의 경우는 DLL(Dynamically Linked Libraries, 동적 라이브러리)를 지원한다.
DLL
의 장점은 실행 파일에서 사용되지 않을 수 있는 library를 link하고 load하지 않아도 된다는 것이다. (library는 조건부로 link되며 program 실행 시간에 필요한 경우에만 load한다.)
여러 process가 동적으로 link된 library를 공유할 수 있어, 메모리 사용이 크게 절약될 수 있다.
(ex. printf를 쓰겠다. 쓸 수 있는 정보, 방법만 남겨놓고 호출할 때만 연결하여 쓰겠다.)
2.6 Why Applications Are Operating-System Specific
-
기본적으로 한 OS에서 compile된 Acpplication은 다른 OS에서 실행할 수 없다. 각 OS마다 제공하는 고유한 System Call이 있기 때문이다.
-
그러나 동일한 Application을 다른 OS에서 사용했던 경험이 있을 것인데, 이건 어떻게 가능한가?
-
Application은 OS마다 Interpreter가 제공되는
Interpreter Language(Python, Ruby, ..)로 작성될 수 있다.
Interpreter는 소스 프로그램의 각 라인을 읽고, 상응하는 기계어 명령을 실행하고,
해당 OS의 System Call을 호출한다.
(Interpreter에서 동작하는 Python이나 Matlab과 같은 language는 OS가 달라도
소스레벨에서는 호환 가능, 바이너리 레벨에서는 호환 불가)
-
Application은 실행 중인 Application을 포함하고 있는 Virtual Machine을 가진 Language로 작성될 수 있다. (ex. Java)
-
Application 개발자는 컴파일러가 기기 및 OS 고유의 이진 파일을 생성하는 표준 언어 또는 API를 사용할 수 있다.
이처럼 API는 Application 수준에서 특정 기능을 지정한다.
Architecture 수준에서 이진 코드의 여러 구성요소가
주어진 Architecture에서 특정 OS와 상호 작용할 수 있는 방법을 정의하는 데
ABI(Application Binary Interface)가 사용된다.
- ABI : ABI는 바이너리 코드에서 사용되는 함수 호출 방법, 매개 변수 전달 방법, 데이터 타입, 바이트 순서 등과 같은 다양한 세부 사항들을 명시한다.
따라서 ABI는 서로 다른 소프트웨어 컴포넌트들이 서로 호환되도록 보장하며,
이를 통해 서로 다른 언어나 운영 체제에서 작성된 코드가 상호작용할 수 있도록 한다.
2.7 Operating-System Design and Implementation
2.8 Operating-System Structure
- 현대의 OS와 같이 크고 복잡한 시스템은 적절하게 동작하고 쉽게 변경될 수 있으려면 신중히 제작되어야 한다.
- Monolithic Structure
- Layered Approach
- Microkernel
- Modules
- Hybrid Systems
Monolithic Structure
Monolithic Structure == Simple Structure
- OS도, Application도 HW(ROM, BIOS)를 직접 건들 수 있음.
- HW가 제공하는 system call도 있고 OS가 제공하는 system call도 있다.
- embedded OS가 이러한 방식.
Layered Approach
Layered Approach
- OS는 여러 개의 layer로 나누어 진다.
layer를 나눠놓고, 각각이 할 일을 정해둔다.
- layer를 나누는 일은 어렵기 때문에 잘 쓰지 않는다.
Microkernel
Microkernel
- kernel이 가져야 하는 최소한의 기능을 남겨두고,
나머지 서비스들은 별도의 주소 공간에 존재하는 User Mode 수준의 program으로 구현하여 OS를 구성하는 방법.
- kernel이 작기 때문에 다루기 쉽고,
kernel에 필요한 코드가 적어 새로운 서비스는 User Space에 추가되며, Kernel을 변경할 필요가 없다.
- 서비스 대부분이 User process로 수행되기 때문에 보안성과 신뢰성이 높다.
만약 서비스가 잘못되더라도, OS에 다른 부분은 아무런 영향을 받지 않는다.
Modules
Modules
- OS의 기능을 여러가지 module로 나누어 필요한 것만 링크하여 사용하는 방식이다.
- kernel은 핵심 서비스를 제공하고,
다른 서비스들은 kernel이 실행되는 동안 동적으로 구현하는 것이다,
서비스를 동적으로 링크하는 것은 새로운 기능을 직접 kernel에 추가하는 것보다 효율적이다.
Hybrid Systems
Hybrid Systems
: 하나의 방식에 얽매이지 않고 여러 방식을 조합해서 쓰는 방식.
- UI는 어떤 버전, kernel은 어떤 버전, 서비스의 버전, .. 이런 식으로 여러 방식을 혼용
2.9 Building and Booting an Operating System
System Build
- 일반적으로 OS는 구매 시 이미 설치되어 있다.
하지만 사전 설치된 OS를 교체하거나 부가의 OS를 추가한다고 가정할 때,
OS를 처음부터 building
하는 과정은 다음과 같다.
- 운영체제 소스 코드를 작성
- 운영체제가 실행될 시스템의 운영체제를 구성
- 운영체제를 compile
- 운영체제를 설치
- 컴퓨터와 새 운영체제 부팅.
System Boot
- HW는 kernel의 위치 또는 kernel을 load하는 방법을 어떻게 알 수 있을까?
kernel을 load하여 컴퓨터를 시작하는 과정을 booting
이라고 한다.
대부분의 시스템에서 booting 과정은 다음과 같다.
- 전원을 켠다.
- BIOS (Basic Input/Output System)가 실행되며 하드웨어를 초기화하고
POST (Power-On Self Test)를 수행하여 시스템의 상태를 검사한다.
- BIOS는 부팅 디바이스 (보통 HDD나 SSD)에서 부트로더(부트매니저)를 찾아 실행시킨다.
- 부트로더는 운영체제 커널을 메모리에 로드하고 실행시킨다.
- 커널이 실행되면 하드웨어를 초기화하고 필요한 드라이버들을 로드하여 운영체제가 사용할 수 있는 상태로 만든다.
- 루트 파일 시스템이 마운트되며 운영체제가 완전히 로드된다.
2.10 Operating-System Debugging
2.11 Summary
- OS는 사용자 및 프로그램에 서비스를 제고하여 프로그램 실행 환경을 제공한다.
- OS와 상호작용하기 위해 세 가지 주요 접근 방식은 (1) Command Interpreter, (2) GUI, (3) touch screen interface 이다.
- System Call은 OS에서 제공하는 서비스에 대한 interface를 제공한다.
프로그래머는 System Call 서비스에 access하기 위해 System Call의 API를 사용한다.
- 표준 C library는 UNIX 및 Linux 시스템을 위한 System Call Interface를 제공한다.
- OS에는 사용자에게 유틸리티를 제공하는 시스템 프로그램 모음도 포함되어 있다.
- linker는 재배치 가능한 여러 object module을 하나의 이진 실행 파일로 결합한다.
loader는 실행 파일을 메모리에 load하여 사용 가능한 CPU에서 실행할 수 있게 된다.
- Application이 OS에 따라 달라지는 이유는 몇가지 있다.
- 프로그램 실행 파일에 대한 서로 다른 이진 형식
- CPU에 따라 다른 명령 집합 및 OS마다 다른 System Call
- Boot Loader는 OS를 memory에 load하고 초기화를 수행하며 시스템 실행을 시작한다.
Practice Exercises