본문 바로가기

Computer Science/OS

1. 운영체제 개요

1. 운영체제 개요: Concurrency, Persistence

프로그램은 매우 단순한 일을 한다:

  1. 명령어를 메모리에서 Fetch하고,
  2. Decode하고 (무슨 명령어인지 파악),
  3. Data를 얻고 (연산),
  4. Execute한다.

OS는 여러 프로그램을 쉽게 실행하게끔 하고, 프로그램 간의 메모리 공유, 장치와 상호작용할 수 있게 한다.

System Call을 사용해 OS에 원하는 작업을 수행시킬 수 있다.

OS는 Virtualization을 통해 다음을 가능하게끔 한다:

  • CPU 공유
  • Memory 공유
  • Disk 공유

CPU 가상화

A program that loops and prints (cpu.c)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <assert.h>
#include "common.h"  // Contains Spin() function

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); // Repeatedly checks the time and returns once it has run for a second
        printf("%s\n", str);
    }
    return 0;
}

실행 예시

하나의 프로세서만 가지고 있음에도 불구하고, 4개의 프로그램 모두 동시에 실행되는 것처럼 보인다.

Memory 가상화

실제 물리 메모리는 byte로 배열되어 있다. 프로그램은 실행 중 자신의 모든 자료들을 메모리에 유지하고 Read (load), Write (store) 하는데, 각 프로그램은 각자 고유의 메모리 영역을 가지고 있는 것처럼 보인다.

// 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: allocate memory
    assert(p != NULL);
    printf("(%d) address of p: %08x\n", getpid(), (unsigned) p);  // a2: print out the address of the memory

    *p = 0;  // a3: put zero into the first slot of the memory
    while (1) {
        Spin(1);
        *p = *p + 1;
        printf("(%d) p: %d\n", getpid(), *p);  // a4
    }
    return 0;
}

mem.c 여러 번 실행 결과

각 프로그램이 동일한 주소에 메모리가 할당된 것으로 나타내어진다.

Virtual Address Space

각 프로세스는 자신만의 Virtual Address Space를 가진다.

한 프로그램의 메모리 연산은 다른 프로세스와 독립적이며, 서로의 공간에 영향을 주지 않는다.

병행성 (Concurrency)

운영체제는 여러 프로세스를 실행시켜 한 번에 많은 일을 한다. 멀티스레드 프로그램도 동일한 문제가 있다.

  • main 프로그램은 pthread_create()로 두 개의 thread를 생성한다.

A Multi-threaded Program (thread.c)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "common.h"

volatile int counter = 0;  // 공유 변수
int loops;

void *worker(void *arg) {
    for (int 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;
}

loops

counter++ 는 세 개의 명령어로 구성된다.

  1. Memory -> register load
  2. Add
  3. Register -> memory store

이렇게 세 명령어가 Atomically하게 실행되지 않기 때문에 문제가 발생한다. (Critical Section 문제) OS는 이 문제에 해결방법을 제시한다.

Persistence (영속성)

DRAM과 같은 장치는 data를 volatile (휘발성) 하게 저장하므로, 전원이 꺼지거나 system crash가 발생하면 메모리의 data가 손실될 수 있다. -> 영속적 저장 필요.

  • HW, SW 필요:
  • H/W: I/O devices (HDD, SSD, …etc)
  • S/W: File System

File System은 사용자가 생성한 file을 디스크에 안전하고 효율적으로 저장해야 한다.

  • 파일 생성 예제: /tmp/file에 "hello world" 문자열을 저장하는 프로그램
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

int main(int argc, char *argv[]) {
    int fd = open("/tmp/file", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
    assert(fd >= 0);
    int rc = write(fd, "hello world\n", 13);
    assert(rc == 13);
    close(fd);
    return 0;
}
  • open(), write(), close() 등의 System call들은 OS에서 File System과 분리된 부분으로 전달된다.
  • Data가 어디에 저장될지 결정되고, device driver를 통해 저장 장치에 기록된다.
  • Crash handling을 위해 Journaling, Copy-On-Write와 같은 복잡한 쓰기 방법을 사용한다.

OS Design Goals

  • Abstraction
  • High Performance
  • Protection between programs
  • Reliability
  • Energy, Security, Mobility…