A Dialogue on the Book
운영체제 수업을 듣고 공부한 내용을 정리하려 한다. 2년 전에 들었는데 정리는 이제야 한다..
어색한 구어체, 오타, 오/의역이 있을 수 있다.
pdf 원문을 읽을 것을 추천하며, 여기에서 무료로 배포한다.
제목에 있는 Three easy pieces는 Virtualization(가상화), Concurrency(동시성), Persistence(영속성)을 뜻한다.
이 세 가지 관점에서 OS가 어떻게 구현되었는지 쉽게 설명해 준다는 내용이다.
Ch.2에서 OS에 대한 전반적인 소개를 한다. Ch.3 ~ Ch.24는 Vritualization, Ch.25 ~ Ch.34는 Concurrency, Ch.35 ~ Ch.50은 Persistence를 다룬다.
Introduction to Operating Systems
운영체제가 여러 동작을 할 수 있도록 해주는 중요한 기능 중 하나는 가상화(virtualization)다. OS가 프로세서나 메모리, 디스크와 같은 물리적 자원을 잘 사용할 수 있도록 해준다. 때문에 OS를 virtual machine이라고 부르기도 한다.
이런 동작을 위해 OS는 호출 가능한 인터페이스(API)를 제공할 필요가 있다. OS는 여러 system call을 제공하며, 이 때문에 OS가 standard library를 제공한다고 말하기도 한다.
가상화로 인해 여러 프로그램이 동시에 실행되고, 동시에 같은 메모리(공유 메모리)에 접근하거나 주변기기에 접근할 수도 있다. CPU, 메모리, 디스크 등도 모두 자원에 속하고, OS가 이들을 관리하므로 OS가 resource manager라고 불리기도 한다.
Virtualizing the CPU
다음 간단한 코드를 살펴보자.
Simple Example: Code That Loops and Prints
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <assert.h> #include "common.h" int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "usage: cpu <string>\n"); exit(1); } char *str = argv[1]; while (1) { Spin(1); printf("%s\n", str); } return 0; }
Spin()을 이용해 1초간 대기 후 argv[1]를 화면에 반복해서 출력한다.
하나의 프로세서에서 위 코드를 네 번 동시에 실행해보자.
output
./cpu A & ; ./cpu B & ; ./cpu C & ; ./cpu D & [1] 7353 [2] 7354 [3] 7355 [4] 7356 A B D C A B D C ...
그러면 이 4개의 프로그램이 동시에 실행되는 것처럼 보인다. 이처럼 실제로는 하나의 프로세서에서의 동작 때문에 마치 프로세서가 무한히 있는 것처럼 느껴지는 착각을 느낄 수 있다. 우리는 이것을 CPU의 가상화라고 부른다.
그러면 OS는 어떤 프로그램을 먼저 실행시켜야 할지 어떻게 결정할까? 이는 OS의 정책(policy)에 달려있다. 이 정책은 상황에 따라 달라지는데 더 구체적인 상황에서는 매커니즘(mechanism)을 따른다.
Virtualizing Memory
이번에는 메모리를 고려해보자. 현대 컴퓨터의 물리적 메모리(physical memory)는 단순히 byte들의 배열이다. 따라서 데이터를 읽거나(read) 쓰기(write)위해서는 해당 주소(address)를 이용해 접근해야 한다.
다음 코드를 살펴보자.
A Program that Accesses Memory
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include "common.h" int main(int argc, char *argv[]) { int *p = malloc(sizeof(int)); assert(p != NULL); printf("(%d) address of p: %08x\n", getpid(), (unsigned) p); *p=0; while (1) { Spin(1); *p = *p + 1; printf("(%d) p: %d\n", getpid(), *p); } return 0; }
&를 이용해 백그라운드에서 동시에 실행시킨다면 아래와 같은 결과가 출력될 것이다.
output
prompt> ./mem &; ./mem & [1] 24113 [2] 24114 (24113) memory address of p: 00200000 (24114) memory address of p: 00200000 (24113) p: 1 (24114) p: 1 (24114) p: 2 (24113) p: 2 (24113) p: 3 (24114) p: 3 (24113) p: 4 (24114) p: 4
서로 다른 두 프로세스가 같은 주소값(0x00200000)을 출력하고 있다. 이것은 마치 각 프로세스가 물리적 메모리를 공유하지 않고, 고유한 메모리를 가지고 있는 것처럼 보인다.
이것은 OS가 메모리를 가상화(virtualizing memory) 했기 때문이다. 각 프로세스는 가상 주소 공간(virtual address space) 또는 간단히 주소 공간(address space)라고 부르는 고유한 메모리를 갖는다. 이 주소는 OS가 물리적 주소와 매핑(mapping)해준 것이다. 실행 중인 프로그램이 사용하는 주소 공간은 다른 프로세스나 OS가 영향을 미치지 않는다.
이것이 본 책에서 다룰 첫 번째 테마인 가상화(virtualization)다.
Concurrency
다음 주요 테마는 동시성(concurrency)이다. concurrency의 문제는 더 이상 OS 자체에 국한되지 않고, 현대의 멀티-스레드(multi-thread) 프로그램에서 동일하게 나타난다.
다음 코드를 살펴보자.
A Multi-threaded Program
#include <stdio.h> #include <stdlib.h> #include "common.h" volatile int counter = 0; int loops; void *worker(void *arg) { int i; for (i = 0; i < loops; i++) { counter++; } return NULL; } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "usage: threads <value>\n"); exit(1); } loops = atoi(argv[1]); pthread_t p1, p2; printf("Initial value : %d\n", counter); Pthread_create(&p1, NULL, worker, NULL); Pthread_create(&p2, NULL, worker, NULL); Pthread_join(p1, NULL); Pthread_join(p2, NULL); printf("Final value : %d\n", counter); return 0; }
메인 프로그램이 Pthread_create() 함수를 이용해 두 스레드를 생성한다. 그러면 동시에 두 개 이상의 스레드가 한 메모리 공간에서 활성화될 수 있다. 생성된 두 스레드는 worker()에서 loop을 돌면서 counter 값을 증가시킬 것이다.
위 코드에서 loop을 반복할 횟수를 크게 준다면 다음과 같은 결과가 나올 것이다.
output
$ ./thread 100000 Initial value : 0 Final value : 143012 $ ./thread 100000 Initial value : 0 Final value : 137298
예상한 값 200000에 못 미치는 값이 될뿐더러, 심지어는 프로그램의 실행마다 결과가 달라진다.
이런 비정상적인 결과가 나오는 것은 세부적으로 어떤 명령어(instruction)이 실행되었는지에 달려있다. 위 코드에서 공유된 변수인 counter를 증가시킬 때 사실은 세 가지 instruction이 수행되었다.
- 메모리에서 counter의 값을 레지스터로 load 한다.
- 그 값을 증가시킨다.
- 증가한 값을 다시 메모리로 store 한다.
이 세 instruction들이 동시에 수행(atomically)되지 않기 때문에 이상한 결과가 나온 것이다. 이것이 본 책에서 두 번째로 다룰 concurrency의 문제다.
Persistence
세 번째 주요 테마는 영속성(persistence)이다.
시스템 메모리는 휘발성(volatile) 메모리인 DRAM에 저장되기 때문에 데이터를 쉽게 잃을 수 있다. 따라서 전원 공급이 중단되거나 시스템 crash가 발생해도 데이터를 지속적으로(persistently) 잃지 않기 위한 하드웨어가 필요하다.
하드웨어는 일종의 input/output 또는 I/O 장치 형태로 제공된다. 현대 시스템에서 수명이 긴 저장소로는 HDD나 SSD가 있다. 이런 디스크를 관리하는 OS의 소프트웨어를 파일 시스템(file system)이라고 하며, 사용자의 파일을 효율적이고 안정적으로 관리한다.
Design Goals
OS는 CPU, 메모리, 디스크와 같은 물리적 자원을 관리하고 가상화 한다. 그리고 concurrency와 관련된 문제들을 다룬다. 또한 파일을 지속적으로(persistently) 저장할 수 있도록 해준다.
이런 시스템을 설계하기 위해서는 몇 가지 목표가 있으며, 그 목표들을 위한 trade-off가 필요하다.
가장 기본이 되는 목표는 추상화(abstraction)다. 이 외에도 overhead를 최소화하여 성능(performance)를 늘리거나, 고립(isolation)을 통한 보호(protection)가 있다. 부수적으로 reliability, energy-efficiency, security, mobility 등도 OS의 목표 중 하나이다.
Uploaded by Notion2Tistory v1.1.0