본문 바로가기

System/OS

[OS] Introduction, Properties

0. 들어가기 앞서

 

이 포스트는 Operating System 에 관한 기본 정보들을 복습하는 김에 겸사겸사 아카이브해놓은 정리글이다.

영어로 된 내용을 한글로 번역하며 필기하는 느낌으로, 코드나 내용의 유지보수는 아마도 하지 않을 예정.

 

코드는 주로 C로 작성하였으며,

참고자료는 Operating System: Three Easy Pieces v0.91 by Arpaci-Dusseau 이고

레퍼런스는 울 학교 교수님 강의자료다.

연세대학교 송진호 교수님 사랑해요.

 

1. OS란?

 

(1) 컴퓨터의 구조

현대 컴퓨터 시스템의 구조

컴퓨터는 여러가지 부품으로 이루어지는데, 전력 공급을 위한 파워 서플라이를 제외하면 위와 같이 나타난다.

이 중에서 OS에 연관된 것을 살펴보면

1) 연산 장치 : CPU, GPU

2) 휘발성 저장 장치 : Cache, Memory

3) 비휘발성 저장 장치 : HDD/SSD

4) 기타 I/O : 키보드, 마우스 등등...

로 나타낼 수 있다.

 

사람들은

- 비휘발성 저장 장치에서 데이터를 꺼내와

- 휘발성 메모리에 프로그램을 여러개 올려 놓고

- CPU로 실행하면서

- I/O 장치로 이를 조정한다.

물론 다른 방식도 많겠지만 대부분 위의 순서로 진행이 되는데, OS는 이 전반에 걸쳐 필요한 하드웨어 조작을 코드 몇줄로 대신 해준다.

 

OS가 없다면... 우리가 하드 디스크에 프로그램 저장되어있는 장소를 수첩에 적어놓았다가, 필요할 때 어셈블리어로 꺼내와서 메모리에 넣어놓고, CPU에 명령을 직접 전달해준다음, I/O가 있을 때마다 장치 위치로 가서 실행한 뒤 다시 돌아와야한다.

 

어우... 생각만 해도 끔찍하다.

 

 

(2) OS의 정의, 효과

 

OS는 컴퓨터 하드웨어를 관리하는 소프트웨어의 일종으로, 

1) 사용자(user/program)과 하드웨어를 연결해주고

2) 하드웨어를 쉽게(ease) 사용하게 도와주고

3) 효율적으로(efficiently) 사용하게 도와주고

4) 안전하게(safe) 사용하게 도와준다.

 

이 때 3)의 efficiently는 개인적으로는 economically로 바꾸어서 설명하는게 좋을 듯 싶은데,

필연적으로 오버헤드가 많이 있을 수 밖에 없는 OS 시스템 구조상 동작 속도나 메모리공간 상에서는 원시 컴퓨터 관리 수준으로 하나하나 관리해주는 것보다 못한 경우가 많다.

단지 그 관리에 드는 노동의 값을 매우 극적으로 줄여주기 때문에 시간과 비용상에서 효율적이라고 한 것이니 사실상은 economical하다고 보면 된다.

 

 

(3) 역할(Role)

 

어떻게 해서 위의 효과를 나타내느냐?

 

1) 하드웨어 자원 관리(HW Resource Management)

프로그램이 동작하는 순서를 정해준다.

메모리 공간을 할당해준다.

인터럽트(주로 I/O)를 다루어준다.

 

2) 시스템 보호(System Protection)

프로그램의 비이상적 실행을 막는다. 주로 메모리공간 독립화를 이용해서 실현된다.

에러를 감지해준다. (Trap Handler)

 

 

2. OS의 특징(Properties)

 

(0) 개요

하드웨어 자원(CPU 사용률, 메모리 공간 등) 은 제한되어있는데, 우리는 여러 프로그램들을 한 번에 돌리기를 원한다.

각 프로그램들은 제한된 자원을 공유 하면서도 모두 자기 역할을 해야한다.

관리 가능한 선에서, 같이 돌아가면서도, 서로 데이터를 침범하지 않아야 한다는 점에서 OS는 꼭 필요하다.

 

 

(1) Virtualization(가상성)

Ctrl+Alt+Del을 누르고 프로세스 탭을 켜보자. 어떠한 프로그램도 하드웨어 자원을 100% 사용하지는 않는다. 하지만 OS는 개별 프로그램들에게 마치 모든 CPU자원을 독차지하고 있다는 듯한 환상(illusion)을 준다.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {

	int *data = (int*)malloc(1 * sizeof(int));
	*data = getpid();
    
	// Print memory address and its value.
	printf("data address = %p, value (pid) = %d\n", data, *data);
    
	free(data);
	return 0;
}

위 코드를 실행해보면 매 실행시마다 데이터가 저장된 주소값과 해당 프로세스의 id가 다르다는 사실을 알 수 있다.

 

#include <stdio.h>
#include <unistd.h>

int main(void) {
	int process_id = getpid(); // Get the process id #.
    
	for(unsigned i = 5; i > 0; i--) {
		printf("process #%d: waiting ... %d\n", process_id, i);
		sleep(1); // Each loop delays for a second.
	}
    
	printf("process #%d done\n", process_id);
	return 0;
}

위 코드를 program.c로 저장한 뒤 shell에서 다음과 같이 실행해보면 프로세스의 id가 매 초마다 순서에 관계없이 나타난다는 사실을 알 수 있다.

for i in {1..3}; do
	./program & # Run the program in the background.
done

 

위 두 사례에서 알 수 있는 사실은, 여러개의 프로세스들은 각자 개별 메모리공간을 가지고있으면서 데이터 간섭의 우려 없이 잘 동작한다는 것이다. 즉, 데이터가 물리적으로 메모리공간 어디에 위치해있는지 알 필요가 없도록 알아서 잘 해준다는 것이다.

 

 

(2) Concurrency(동시성)

 

1) Multi-Processing VS Multi-Threading

멀티 프로세싱과 멀티 스레딩의 차이를 알아보자.

Multi-processing vs Multi-threading

 멀티 프로세스(또는 멀티 프로그램)는 "하나의 컴퓨터에 여러 프로세스"가 돌아가고 있는 상태다. "동시(Concurrent)" 일 필요는 전혀 없다. 각 프로세스는 독립적인 메모리 공간을 별개로 가지고 있다.

 

 멀티 스레드는 "하나의 프로세스에 여러 스레드"가 돌아가고 있는 상태다. 하드웨어 자원의 가상화는 프로세스 단위로 이루어지므로, 스레드들은 메모리 공간을 공유한다. 이 경우 "동시(Concurrent)"라는 말을 사용한다.

 

 주의해야할 점은, Concurrency는 Simultaneous 와 어감이 조금 다르다는 점에 있다. 양손에 맥주를 들고 있을 때, 한꺼번에 입안에 들이붓는 것도 동시에(Simultaneously) 마신다고 하지만, 왼손 마시고 오른손 마시고 반복해도 동시에(Concurrently) 마신다고 하지 않는가? CPU 코어 하나가 한 번에 여러개의 동작을 하기는 힘들기 때문에(불가능하지는 않다) 컴퓨터에선 주로 후자를 쓴다. 뭐 그런것이다.

 

2) Atomicity & Semaphore

 

사실 Concurrency(동시성)은 위와 같은 애매모호한 정의가 아니다. 여러개의 스레드의 연산이 같은 데이터를 다룰때 그 정확성을 말하는 것이다.

 

이런 경우에 문제가 발생하는 건 매우 쉽게 떠올릴 수 있는데, 전역 변수 count 하나를 두 개의 스레드에서 다룬다고 생각해보자.

스레드 A 에서는 count+=1, B에서는 count+=2 한다고 했을 때, count는 어떤 값이 될까?

나아아중에 배우겠지만 1만 올라갈수도, 2가 올라갈 수도, 3이 올라갈 수도 있다.

와 같이 애매모호한 상황을 방지하는 방법이 다음에 나올 Atomicity와 Semaphore이다.

 

Atomicity는 스레드 A에서 참조하는 동안에는 스레드 B에서 참조하지 못하게 막는 방법 중 하나로,

끼어들 틈 없이 RMW(Read-Modify-Write) 과정을 한 번에 해버리면 된다는 무식하고도 효과적인 방법이다. 

 

Semaphore는 Modify 연산이 좀 길어서 Atomicity가 해결할 수 없을 때 많이 쓰는 방법이다.

- 중요한 자원에 접속하려고 할 때 flag를 먼저 체크해서,

- 가용 상태면, 남들이 못쓰게 비가용상태로 만든 다음, 다 쓰고나서 가용상태로 돌려놓고

- 비가용 상태면 가용상태 될 때 까지 대기

이런 알고리즘을 골자로 한다.

관련해서 flag를 여러개 쓰는 방법, 신박하게 대기하는 방법, flag 체크도 atomic하게 하는 방법 등등등 많은 것들이 있으니 나중에 살펴보도록 하자.

 

 

(3) Persistence(견고성)

 

저장장치의 속도, 용량, 가격은 상관관계가 있는데, RAM과 ROM의 속도, 용량 차이만 살펴보자.

RAM은 휘발성 메모리다. 우리가 아는 그 시금치 램은 DRAM이라고 하는데, 전원 끄면 내용물이 날아간다. 꽤 빠른 속도 대신 용량은 2020년 기준 ~16GB 정도인게 보통이다.

ROM은 비휘발성 메모리다. HDD, SSD가 여기에 속하는데, 속도는 좀 느리지만 용량은 SSD는 64GB~, HDD도 500GB~ 로 꽤 높다.

 

그러면 OS는 어디에 저장될까? 날아가면 안되니까 당연히 ROM에 저장된다.

ROM에 저장되었다가 부팅할 때 RAM으로 옮긴다음, 필요한 경우 캐시를 거쳐 CPU로 명령을 보낸다.

OS도 단순히 코드 덩어리이니 명령 덩어리이고, CPU로 가야 실행될 수 있으니까.

 

스마트폰같은 AP 탑재 제품이나 아두이노나 라즈베리파이같은 MCU탑재 제품도 여기서 벗어나지 못한다.

최근에 나온 라즈베리파이4를 살펴보면, microSD카드에 OS(라즈비안)을 저장해 놓았다가 전원이 인가되면 1GB LPDDR2 SDRAM으로 부트로더를 보내고, 부트로더의 명령에 따라 파일을 꺼내오면서 부팅을 한다.

물론 MCU개발보드쯤 가면 내부의 SRAM에 디버거로 부트로더를 넣어놓고 쓰기도 하는데, 이 경우에도 부트로더 저장부분을 ROM처럼 쓰는 것이니 달라지지 않는다.

 

이 이야기를 왜 이렇게 장황하게 했냐면, OS 코드는 캐싱해서 쓰는 몇가지 빼고는 결국 메모리에서 꺼내오는 것이란 말이고,

컴퓨터 구조상 비휘발성 메모리는 I/O로 처리되니, OS가 스스로 OS 자체도 관리하는 꼴이 된다는 말이다.

즉, OS는 파일(비휘발성 메모리에 저장된 데이터 덩어리)을 읽고 쓰는 작업을 할 수 있어야 하며, 이를 굉장히 안전하게 수행해야한다는 말이다.

 

(참고) ACID : OS가 아닌 곳에서도 위의 특성이 필요한 경우가 많다. 주로 통신의 경우에 그런데, 데이터를 주고 받는 과정 역시 OS와 유사하게 신뢰성이 매우 중요하기 때문이다.

 

'System > OS' 카테고리의 다른 글

[OS] Scheduling  (0) 2020.05.05
[OS] Process  (0) 2020.05.02