정리 노트

운영 체제를 공부할 때 기본적으로 알아야 할 3가지 본문

개념 정리/운영체제

운영 체제를 공부할 때 기본적으로 알아야 할 3가지

꿈만 꾸는 학부생 2023. 6. 29. 00:15
728x90

이 포스트는 국민대학교 소프트웨어학부 '운영체제' 강의를 듣고 요약하는 포스트입니다. 원하시는 정보가 없을 수도 있습니다. 이 점 유의 바랍니다. 오류 지적은 매우 환영합니다!


이번 포스트에서는 운영 체제를 공부한다면 기본적으로 알아야 할 3가지에 대해 소개하는 느낌으로 적었습니다.

1. 가상화(Virtualizing)

1-1. CPU 가상화(Virtualizing CPU)

아래에 C 언어로 작성한 간단한 코드가 있습니다.

// Source: https://github.com/remzi-arpacidusseau/ostep-code/blob/master/intro/cpu.c
#include <stdio.h>
#include <stdlib.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) {
	printf("%s\n", str);
	Spin(1);
    }
    return 0;
}

이 코드는 파라미터가 주어졌을 때, 파라미터 값을 1초마다 출력하는 코드입니다. 이 코드는 외부에서 중지시키지 않는 이상 무한히 진행됩니다.

이러한 코드에 파라미터들을 달리 해서("A" ~ "U")(= 같은 동작을 하는 프로세스를 여러 개 만들어서) 한 번에 같이 돌린다면 어떻게 될까요? 터미널에 돌려보면 아래와 같이 나옵니다. 결과는 너무 길어서 파라미터가 "A" ~ "D"까지인 것처럼만 잘라서 작성했습니다.

A
D
B
C
D
B
A
C
D
A
B
C
.
.
.

마치 4개의 프로세스가 동시에 진행되는 것처럼 결과가 동시에 출력되는 것을 확인할 수 있습니다. 현재 제 노트북의 CPU의 코어 수가 16개인데 이 개수로 커버할 수 없는 프로세스의 양입니다. 그렇지만 마치 CPU의 코어 수가 21인 것처럼, 한 번에 같이 실행되는 것 같이 보이는데 이러한 것을 CPU를 가상화했다고 합니다.

출처: https://pages.cs.wisc.edu/~remzi/OSTEP/intro.pdf, 4페이지
(전략)
Turning a single CPU (or a small set of them) into a seemingly infinite number of CPUs and thus allowing many programs to seemingly run at once is what we call virtualizing the CPU,
(후략)

1-2. 메모리 가상화(Virtualizing Memory)

C 언어로 작성한 또 다른 코드가 있습니다.

// Source: https://github.com/remzi-arpacidusseau/ostep-code/blob/master/intro/mem.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "common.h"

int main(int argc, char *argv[]) {
    if (argc != 2) { 
	fprintf(stderr, "usage: mem <value>\n"); 
	exit(1); 
    } 
    int *p; 
    p = malloc(sizeof(int));
    assert(p != NULL);
    printf("(%d) addr pointed to by p: %p\n", (int) getpid(), p);
    *p = atoi(argv[1]); // assign value to addr stored in p
    while (1) {
	Spin(1);
	*p = *p + 1;
	printf("(%d) value of p: %d\n", getpid(), *p);
    }
    return 0;
}

이 코드는 파라미터가 주어졌을 때, 정수형 포인터가 가리키는 곳에 저장하고 1초마다 이 값을 1씩 증가시켜 증가시킨 결과를 출력하는 코드입니다. 이 코드도 외부에서 중지시키지 않는 이상 무한히 진행됩니다.

만약 파라미터 값까지 똑같은(0으로 파라미터를 설정합시다.) 프로세스를 2개를 같이 돌리면 어떻게 될까요? 터미널에 돌려보면 아래와 같이 나옵니다.

(14143) addr pointed to by p: 0x5555555592a0
(14142) addr pointed to by p: 0x5555555592a0
(14143) value of p: 1
(14142) value of p: 1
(14143) value of p: 2
(14142) value of p: 2
(14143) value of p: 3
(14142) value of p: 3
(14143) value of p: 4
(14142) value of p: 4
.
.
.

결과를 보면 서로 다른 프로세스의 포인터가 같은 메모리의 위치를 가리키고 있습니다. 하지만 각 프로세스의 출력 값을 보면 포인터가 가리키는 정수의 값이 1씩 증가하고 있음을 확인할 수 있습니다. 메모리는 1개만 존재하지만 마치 메모리가 각 프로세스의 수만큼 존재하는 것처럼 보이게 되는데 이러한 것을 메모리를 가상화했다고 합니다. 이때 각 프로세스는 각자의 private virutal address space(private 가상 주소 공간 또는 주소 공간)를 가지게 된다고 표현합니다. 따라서 위의 결과에서 나오는 메모리 주소는 실제 물리적 메모리의 주소가 아니라, 가상화된 메모리의 주소라 판단할 수 있습니다.

출처: https://pages.cs.wisc.edu/~remzi/OSTEP/intro.pdf, 6페이지
(전략)
It is as if each running program has its own private memory, instead of sharing the same physical memory with other running programs.
Indeed, that is exactly what is happening here as the OS is virtualizing memory. Each process accesses its own private virtual address space (sometimes just called its address space), which the OS somehow maps onto the physical memory of the machine.
(후략)

우분투를 기준으로 위와 같은 결과를 확인하기 위해서는 address-space randomization을 비활성화해야 합니다. 활성화 상태에서 실행할 경우, 각 프로세스마다 포인터가 다른 메모리 주소를 가리킵니다.

출처: https://pages.cs.wisc.edu/~remzi/OSTEP/intro.pdf, 6페이지의 5번 각주
For this example to work, you need to make sure address-space randomization is dis- abled; randomization, as it turns out, can be a good defense against certain kinds of security flaws. Read more about it on your own, especially if you want to learn how to break into computer systems via stack-smashing attacks. Not that we would recommend such a thing...

활성화된 상태가 메모리 보안 상 좋습니다만 원하는 결과를 확인하기 위해 일단 아래의 명령어를 통해 비활성화합시다.

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

다시 활성화하려면 아래의 명령어를 실행하면 됩니다.

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

2. 동시성(Concurrency)

// Source: https://github.com/remzi-arpacidusseau/ostep-code/blob/master/intro/threads.c
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "common_threads.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 <loops>\n"); 
	exit(1); 
    } 
    loops = atoi(argv[1]);
    pthread_t p1, p2;
    printf("Initial value : %d\n", counter);
    Pthread_create(&p1, NULL, worker, NULL);    // worker 함수 일을 하는 thread 생성
    Pthread_create(&p2, NULL, worker, NULL);    // worker 함수 일을 하는 thread 생성
    Pthread_join(p1, NULL);
    Pthread_join(p2, NULL);
    printf("Final value   : %d\n", counter);
    return 0;
}

이 코드는 loop의 횟수를 파라미터로 주면, worker 함수의 일을 하는 두 개의 thread를 만들고 각 thread는 메모리에 저장된 카운터의 값을 loop 수만큼 증가시키는 일을 합니다. Thread가 만들어지면 프로세스 안에서 새로운 실행 흐름이 만들어지고, 각 실행 흐름마다 스택 영역이 할당됩니다. 메모리의 나머지 영역(Code(작성한 코드 텍스트), Data(인스턴스가 1개인 변수들을 저장한 곳), Heap 영역(malloc 등을 통해 동적 할당된 것들을 저장한 곳))은 thread들이 같이 사용합니다.

이 코드를 실행할 때 'Final value'의 값은 loop으로 준 수의 2배로 기대할 수 있습니다. 실제로 loop을 100000으로 줄 때 결과 값으로 200000이 나옵니다. 하지만 가끔, 200000이 나오지 않을 때가 있습니다.

DeviceName:codePath$ ./thread 100000
Initial value : 0
Final value   : 200000
DeviceName:codePath$ ./thread 100000
Initial value : 0
Final value   : 95165
DeviceName:codePath$ ./thread 100000
Initial value : 0
Final value   : 104947

이러한 결과가 나오는 이유는, 카운터를 증가하는 코드 라인은 1줄이지만 실제로 이 코드는 atomically 하지 않기 때문입니다. 실제로는 3줄로 나눌 수 있습니다.

  1. 메모리에서 counter 값을 가져온다.
  2. 값을 1 증가시킨다.
  3. 증가시킨 값을 메모리에 저장한다.

이러한 원인으로 인해 생기는 문제가 바로 동시성 문제입니다.

출처: https://pages.cs.wisc.edu/~remzi/OSTEP/intro.pdf, 9페이지
(전략)
Because these three instructions do not execute atomically (all at once), strange things can happen. It is this problem of concurrency that we will address in great detail in the second part of this book.
(후략)

3. 영속성(Persistence)

SSD와 같은 드라이브에 저장된 내용은 전원의 유무와 상관없이 영구적으로 남아있기 때문에 괜찮지만, 컴퓨터나 노트북의 메모리는 RAM이기 때문에 전원이 꺼지면 메모리에 저장했던 변수들의 내용이 다 없어집니다. 이렇기 때문에 프로그램 실행 중 갑자기 전원이 꺼져 변수가 꼬이는 상황을 대비해 데이터를 영구적으로 저장할 수 있어야 합니다. 영구적으로 저장할 수 있다면, 프로세스들이 저장한 데이터를 가져다가 사용할 수 있을 것입니다.

출처: https://pages.cs.wisc.edu/~remzi/OSTEP/intro.pdf, 9페이지
(전략)
In system memory, data can be easily lost, as devices such as DRAM store values in a volatile manner; when power goes away or the system crashes, any data in memory is lost. Thus, we need hardware and software to be able to store data persistently;
(중략)
... the OS does not create a private, virtualized disk for each application. Rather, it is assumed that often times, users will want to share information that is in files.
(후략)

운영 체제 안에서 디스크를 관리하는 소프트웨어를 파일 시스템(file system)이라 부릅니다.

출처: https://pages.cs.wisc.edu/~remzi/OSTEP/intro.pdf, 9페이지
(전략)
The software in the operating system that usually manages the disk is called the file system; it is thus responsible for storing any files the user creates in a reliable and efficient manner on the disks of the system.
(후략)

프로그램에서 디스크와의 I/O 요청을 할 때, system call들(open, write, close 등)을 사용합니다. 운영 체제에서 이 system call들을 제공해 쉽게 저장 장치에 접근할 수 있게 됩니다. System call들은 파일 시스템으로 routing 되어 디스크에 I/O 작업을 진행할 수 있습니다.

참고 사이트

- 우분투에서 address-space randomization 활성화 / 비활성화 방법

 

How can I temporarily disable ASLR (Address space layout randomization)?

I am using Ubuntu 12.04 32-bits, for some experiment I need to disable ASLR; how do I accomplish that? What should I do after that to enable ASLR again?

askubuntu.com

 

728x90