본 내용은 OSTEP 의 내용을 정리 및 요약한 내용입니다. 전문은 이 곳을 방문하시면 보실 수 있습니다.

02. Introduction to Operating Systems

  • 프로그램이 실행되면 무슨 일이 일어날까? : 가장 심플한 답은 아래와 같다.
    • instruction들을 수행한다.
    • 매초 마다 수백만회나, 프로세서들은 메모리에서 명령어를 가져오고(fetch), 이를 실행하고(execute), 한 명령어가 마무리 되면, 프로세서는 다음 명령어로 이동하며, 모든 작업이 마무리 될 때까지 계속 이어진다.
  • 우리는 이 교재를 통해 프로그램의 동작을 배울 것이고, 프로그램이 동작하는 동안 아주 많은 다른 거친 일들이 작동하고 있음을 배울 것이다. 또한 이것의 주요 목적은 어디까지나 시스템을 사용하기 쉽게 만드는 것에 있다.
  • 이러한 목적을 위한 소프트웨어의 몸통이 존재하고, 이 몸통은 프로그램들 사이에 메모리를 공유하거나, 프로그램과 장치 간의 상호작용을 가능케 하며, 여러 다른 재밌는 일들을 도와주는데 이를 우리는 운영체제operating system(OS)라고 한다. 운영체제는 따라서 정확하고 효율적으로 사용하기 쉬워야 한다는 기준을 갖고 시스템을 작동하게 만들 책임이 있다.
  • 운영체제가 작동하는 주요한 방식 중 하나로 일반적인 기술이며, 소위 가상화virtualization라고 부른다.
    • 물리적 자원(Physical resource)을 좀더 사용하기 쉽고, 관리하기 쉬우며, 강력한 도구로 가상희 형태로 만드는 것을 의미한다.
    • 이러한 기술적 기반 아래에 있다보니 운영체제를 때때로 가상머신virtual machine이라고 부르기도 한다.
  • 전형적인 OS의 경우, 대략 수백여개 정도의 시스템 콜이란 기능을 제공하며, 이는 여러 자원을 사용하는 기능들을 수행하기 위함이며, 이를 때론 “OS가 표준 라이브러리standard library를 어플리케이션에게 제공한다고 표현합니다. ”
  • 가상화는 많은 프로그램들이 실행하거나, 많은 프로그램들을 동시에 접근하거나, 장치에 접근하기를 허락해주기 때문에, 운영체제는 때론 자원관리자resource manager이라고도 불립니다.

2.1 Virtualizing The CPU

  • Figure 2.1 Simple Example: Code That Loops And Prints(cpu.c)
#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);
}
prompt> gcc -o cpu cpu.c -Wall
prompt> ./cpu "A"
A
A
A
A
^C
prompt>
  • 위 예시 코드는 1초가 경과 할 때까지 점검을 한 뒤, 1초가 지나면 사용자가 입력한 값을 입력해주는 단순한 프로그램이다. 이는 영원히 이어지지만, 컨트롤 + C를 통해, 중지할 수 있다.

  • Figure 2.2: Running Many Programs At Once

prompt> ./cpu A & ./cpu B & ./cpu C & ./cpu D &
[1] 7353
[2] 7354
[3] 7355
[4] 7356
A
B
D
C
A
B
D
C
A
...
  • 이제는 여러개를 한꺼번에 실행시켜 볼 수 있다. 이러면 다소 상황이 흥미로워 지는데, 비록 하나의 프로세서를 갖고 있지만, 어떻게인지 모든 4개의 프로그램은 거의 동시에 작동하는 거처럼 보여진다. 이는 왜, 어떻게 발생하는 것일까?
  • 여기서 운영체제, 하드웨어의 다소 도움과 함께, 우리가 눈에 보이는 환상을 만드는 역할을 한다는 것을 알 수 있다.
    • 단일 CPU를 마치 무한한 갯수의 CPU처럼 보이게 만들고, 많은 프로그램들이 이를 통해 즉시 돌아가는 것처럼 만든다. 이를 우리는 CPU 가상화virtualizing the CPU 라고 부른다.
    • 물론 이를 위해서는 운영체제와 프로그램 사에서 필요한 것을 소통하는 도구가 필요하다.(API)
    • 또한 복수의 프로그램을 즉시 실행하는 능력이 있다는 것을 깨달았기에, 우리는 다른 종류의 새로운 질문이 생길 수 있다. 이러한 질문들에 대해 답할 수 있는 것이 바로 운영체제의 정책a policy of the OS이다.
    • 그렇기에 이러한 운영체제 작동의 기초적인 메커니즘에 관해 배울것이고, 더 나아가 운영체제가 자원 관리자resource manager의 역할에 대해서도 배워볼 것입니다.

2.2 Virtualizing Memory

  • Figure 2.3 A Program That Accesses Memory(mem.c)
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "common.h"

int main(int argc, char *argv[])
{
	int *p = malloc (sizeof(int)); //a1
	assert(p != NULL);
	printf("(%d) address pointed to by p : %p\n", getpid(), p); //a2
	*p = 0; //a3
	while(1) {
		Spin(1);
		*p = *p + 1;
		printf("(%d) p: %d\n", getpid(), *p); //a4
	}
	return (0);
}
prompt> ./mem
(2134) address pointed to by p: 0x200000
(2134) p: 1
(2134) p: 2
(2134) p: 3
(2134) p: 4
(2134) p: 5
^C
prompt>
  • 현대의 기계에 의해 표지되는 물리 메모리의 모델은 매우 가단합니다. 메모리는 그저 바이트 배열일 뿐입니다.

    • 메모리를 읽기 위해선, 메모리 주소 값이 구체화되어야 거기에 저장된 데이터에 접근하는 것이 가능하다.
    • 메모리를 쓰거나 갱신을 하려면, 제공되는 주소에 쓰여진 데이터를 구체화해야 한다.
  • Figure 2.4: Running The Memory Program Multiple Times

prompt> ./mem &; ./mem &
[1] 24113
[2] 24114
(24113) address pointed to by p: 0x200000
(24114) address pointed to by p: 0x200000
(24113) p: 1
(24114) p: 1
(24114) p: 2
(24113) p: 2
(24113) p: 3
(24114) p: 3
(24113) p: 4
(24114) p: 4
...
  • 2.4 예시를 통해 복수의 동일 프로그램을 실행 시켰을 때, 동일한 주소 값(0x200000) 공간에 할당이 되고, 값도 독립적으로 변하는 것을 볼 수 있다. 이는 각 프로그램이 다른 프로그램과 공유하는 동일한 물리 메모리를 공유하는 것 대신에 private memory 를 소지하고 있음을 보여준다.
  • 운영체제 상에서 정확히 무엇이 일어났는가에 대해, 이것이 메모리 가상화virtualizing memory라고 한다. 각 프로세스들은 자신들의 사적 가상 주소 공간private virtual address space을 가지며, 이는 머신의 물리 메모리 상에 어딘가와 매핑이 된다.
  • 정확하게 어떻게 이렇게 수행되는 지는 이 책의 가상화 토픽의 최초 파트의 주제 다.

2.3 Concurrency

// Figure 2.5 : A Multi-thread Program(threads.c)
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "common_thread.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);
}
  • 본 교의 또다른 주제는 동시성에 대한 내용이다. 이는 하의 프로그램 상에서 즉시 여러가지 일들이 작업되는 상황에서 발생하며 대처해야하는 주요한 대상으로 간주 된다.
  • 실제로 모던 다중 스레드 프로그램들은 이러한 문제들을 가지며, 이에대한 예시가 2.5의 코드 내용이다.
  • 이 예시는 각 스레드가 시작되면, worker() 함수를 호출하며, loops 라는 변수의 값 안에서 counter 라는 전역 변수의 값을 증가 시키는 구조의 예시다.
  • 이걸 바라보면서, 우리는 간단히 스레드가 2개이니, 만약 loops = 1000 이면, counter = 2000 이 되리라 생각한다.
  • 하지만 정작 결과물은 전혀 다른 값이 나오게 된다.
prompt> ./thread 100000
Initial value : 0
Final value   : 143012
prompt> ./thread 100000
Initial value : 0
Final value   : 137298
// huh??
// what the??
  • 이런 이상한 현상이 일어나는 이유는, 명령들이 원자적atomically으로 작동하지 않기 때문이며, 이것이 동시성concurrency이 가지는 문제이며, 이 책의 두 번째 파트에서 집중적으로 다룰 내용이다.

2.4 Persistence

// Figure 2.6 : A Program That Does I/O(io.c)
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>

int main(int argc, char* argv[]) {
	int fd = open("/temp/file", O_WRONLY | O_CREAT | O_TRUNC. S_IRWXU);
	assert (fd > -1)
	int rc = write(fd, "hello world\n", 13);
	assert(rc == 13);
	close(fd);
	return 0;
}
  • 세번째 핵심 주제는 지속성persistence 이다.
  • 메모리 체계 상에서 데이터는 쉽게 소실 될 수 있다. DRAM은 값을 저장하지만 이는 한시적이며, 시스템의 문제나 전력 공급이 끊기면 데이터를 소실할 수 밖에 없고 그렇기에 이에 대응하여 데이터를 지속적으로 저장할 수 있는 하드웨어, 소프트웨어가 필요시 된다.
  • 하드웨어는 보통 입출력 단자들을 의미하며 하드드라이브, SSD 등이 이에 포함되며 소프트웨어는 디스크를 관리하는 파일 시스템file system 을 말합니다. 이 시스템은 사용자가 작성하는 어떤 파일이든 신뢰성과 효율성의 기준을 가지고 시스템의 디스크에 저장하는 역할을 해준다.
  • CPU나 메모리를 위해 운영체제에 의해 제공되는 추상화와는 다르게, 운영체제는 사적인 혹은 가상화된 디스크를 각 프로그램을 위해서 만들 수 없다. 이는 사용자가 정보를 공유하고, 여러 곳에서 사용하기를 원하기 때문이다.
  • 이러한 내용을 이해하기 위해 2.6과 같은 예시를 보면 된다. 여기서는 운영체제를 통해 3가지 주요 기능이 실행되는데,
    • 첫 번째는 파일을 열거나 만드는 작업
    • 두 번째는 파일에 어떤 데이터를 쓰는 작업
    • 세 번째는 지금까지 사용한 작업의 파일 종료하고, 더이상 데이터를 쓰지 않는 작업
    • 이러한 시스템 콜들은 파일 시스템이라고 불리는 운영체제의 일부분에서 관장한다.
  • 이러한 내용들에대해 제 3장에서, 지속성persistence 에 대해 파일시스템들에 대해 디테일하게 알아볼 것이다.

2.5 Design Goals

  • 이제 우리는 운영체제에 대한, 실제로 이것이 무엇인가에 대한 몇 가지 아이디어를 얻었다.
    • 우선 물리적 자원이 존재하여서, CPU, 메모리, 디스크등을 가지고 있으며 이를 가상화virtualization 한다.
    • 이러한 자원을 운영체제는 관리하며, 동시성과 관련된 다양한 이슈들을도 다룬다.
    • 그리고 운영체제는 지속적으로 파일을 저장한다. 그래서 그러한 파일들을 장기간 안정적이게 만들 수 있다.
  • 이러한 상황에서 우리는 그러한 시스템을 만들길 원하며, 우리의 이런 디자인과 목표를 돕기 위한 몇가지 골들을 가질 수 있으며, 이는 간혹 트레이드오프 사항으로 필요시 된다.
  • 이러한 시스템을 만들기 위한 가장 기초적인 골 중에 하나는 시스템을 편리하게 혹은 사용하기 쉽게 만들기 위하여 ‘추상화’abstraction라는 것을 만들어 내야 한다.
  • 또 다른 운영체제의 설계나 구현의 목표는 높은 성능을 보장해야 한다는 점이며, 달리 표현하면 운영체제의 오버헤드를 최소화 시켜야 한다는 것이다.
  • 또 다른 목표는 프로그램과 운영체제 사이에서 보호protection을 제공하는 것이다. 왜냐하면 다양한 프로그램이 동시에 운영체제 상에서 돌아갈 때, 하나의 프로그램의 오류로 운영체제 자체를 공격하거나, 안정성을 저해하는 것은 피해야 하는 것이다. 보호의 개념은 운영체제 상에서, 독립성isolation이라는 주요한 원리 아래의 핵심이라고 볼 수 있다.
    • 이 외에도 에너지 효율성, 보안성, 이식성mobility 등은 운영체제에 대한 다른 지향점들로 소개된다.

2.6 Some History

  • 소개글을 마치기 전에 현재까지 운영체제의 어떤 발전 궤적을 가졌는지에 대해 소개해본다.

Early Operating Systems: Just Libraries

  • 최초의 운영체제는 많은 일을 하진 않았다. 아주 기본적인 자주 사용하는 함수들의 라이브러리와 같은 역할 정도를 수행했다.
  • 이런 시스템이 사용되던 시대에는 보통 한번에 하나의 프로그램만 수행 되었으며 이는 인간인 오퍼레이터에 의해 통제 되었다. 당신이 생각하는 현대의 운영체제에서 행동하는 것들이 모두 오퍼레이터에 의해 수행되었다.
  • 이러한 형태의 연산은 batch 라는 처리 과정으로 알려졌는데, 수많은 작업이 설정되고 실행되는 것은 batch 안에서 동작했고, 이는 오퍼레이터(교환원)에 의해 수행되었다.

Beyond Libraries: Protection

  • 운영체제는 더 발전하게 되고, 보다 중심적 역할을 관리하는 머신화 된다. 여기서 특히나 중요한 측면은 운영체제로 돌아가는 프로그램에 대해선 다소 특별하게 구분지어야 할 필요를 느꼈다는 점이다. 그래서 파일 시스템을 구현하게 되는 등의 일이 일어난다.
  • 또한 이를 위해 시스템 콜 이라는 개념도 발명되었으며, 이 아이디어는 특수한 하드웨어 명령어 쌍과 운영체제 에 보다 형식적인 제어되는 프로세스로 변화하는 하드웨어 상태를 추가하는 것이었다.
  • 시스템 콜과 프로세져 콜 사이의 가장 큰 특징은 시스템 콜은 ‘제어권한’을 운영체제 전달할 수 있다는 것 하드웨어의 권한 수준을 올리는 것이 가능하다는 점이다.

The Era of Multiprogramming

  • 메인 프레임에서 연산되던 시대를 끝낸 운영체제는 바로 미니 컴퓨터의 도래에서 비롯되었다.
  • PDP와 같은 기기들의 발달로 많은 제조사들이 이러한 기기들을 개발해나갔다.
  • 가격면에서 하락하다는 주요 요인 덕분에 개발 활동은 증가하게 된다. 보다 더 많은 똑똑한 사람들이 컴퓨터 쪽으로 오게 되었고, 이럴 수록 컴퓨터 시스템은 흥미롭지만 아름다운 것들을 만들어내게 된다.
  • 특히나 장치의 자원 사용을 더 좋게 만들고 싶은 욕망으로 멀티 프로그래밍이 대세로 자리잡게 된다. 여기에는 특히나 입출력단자의 교환이 느리기 때문이다. 이를 기다리는 시간이 상당한 낭비가 되기 때문에 대신에 다른 작업을 돌리면 된다는 것에서 착안되어 시스템이 발전되어 왔다.
  • 멀티프로그래밍을 지원하기를 바라는 요구와 입출력 특성등이 겹쳐 지면서 점점 운영체제의 개념적 개발 상에서 혁신이 요구되어진다.
    • 메모리 보호
    • 동시성 문제
  • 이런 상황에서 가장 실질적인 진보라 칠 수 있는 주요한 내용이 바로 유닉스 운영체제의 등장이다.

The Modern Era

  • 미니 컴퓨터 시대를 넘어서 새로운 종류의 기기가 나타나게 되는데, 이는 더 싸고 더 빠르며 대량생산이 가능한 개인용 컴퓨터personal computer, 오늘날에 PC의 등장이다. 최초의 시작은 애플사의 초기 머신들이며, IBM PC 등이다. 이들은 점차 확장되어 갔고 컴퓨팅 을 더 낮은 가격에 그룹 당 미니컴퓨터 한대로 공유하는 대신 데스크톱 당 한대의 기기를 가지는 것을 가능케 한다.
  • 애매한 것은, 최초의 PC는 미니 컴퓨터 시대에서의 배운 점들을 잊어버린 듯 여러 문제들을 직면했었다는 것이다.

2.7 Summary

  • 운영체제에 대한 첫 소개를 해보았다. 오늘날의 운영체제는 시스템을 상대적으로 쉽게 사용하고, 가상으로 우리가 오늘날 사용하는 모든 운영체제는 이 책을 통해(OSTEP) 논의되어지는 개발 환경에 의해 영향을 받았다.
  • 애석하게도 시간적 제한 사항들로 인해, 이 책에서는 운영체제에 대해 다루지 못하는 부분도 있다. 예를 들면 네트워킹과 관련된 코드들이 그렇다. 이에 대해선 네트워크 클래스에서 배우기위해 남겨 두었다. 그래픽 관련 분야또한 그러하며, 마지막으로 몇가지 운영체제 책들은 보안에 관해서 아주 자세히 설명해 줄 것이다. 우리 역시 실행중인 프로그램과 그들의 파일들을 지키는 능력을 제공하는 것 사이에서 운영체제가 반드시 보호 기능이 있어야 한다는 사실을 알지만, 보안 코스 상에서 배우는 매우 깊은 보안 이슈들에 대해서는 다루지 않을 생각이다.
  • 그러나 본 교재는 많은 중요한 토픽들을 다루고 있다. CPU나 메모리의 가상화, 동시성, 장치와 파일 시스템을 경유하는 지속성 등이다. 걱정말아라! 여기엔 이야기할 것들이 꽤나많고 이는 상당히 멋진 것들이다. 당신은 이제 어떻게 컴퓨터 시스템이 실제로 작동하는지에 대한 시각을 얻게 될 것이다. 이제 배우러 가보자!