본문 바로가기
CS/운영 체제🖥️

[OS] OS Overview (CPU가상화, 메모리 가상화, Concurrency, Persistence)

by Mintta 2023. 10. 23.

Virtualizing CPU

오늘날 하나의 컴퓨터에서 돌아가는 프로그램은 매우 많습니다. 지금 당장 이 글을 쓰면서 제 노트북에는 Xcode, Chrome, Notion, Discord, 카카오톡 등등 정말 수 많은 프로그램들이 실행되고 있습니다. 하지만 이런 프로세스들을 실행시킬 CPU는 물리적으로 하나입니다. 정말 좋은 컴퓨터들이 많으면 CPU가 1,2개 더 있을 수 있지만 보통 1개입니다. 보통 물리적인 CPU는 한개지만, 멀티 코어라는 개념을 통해서 멀티스레딩의 효용을 우리는 겪고 있습니다.

 

그럼에도 오늘날 우리가 실행하고 있는 프로그램들에 비해 CPU의 개수가 부족한 것은 사실입니다. 이것이 "CPU의 가상화"를 하게된 계기입니다. 마치 CPU가 여러개 있어서 여러개 프로그램들이 동시에 실행되는 것처럼 착각하게 만듭니다.

Virtual Memory

위 프로그램 실행 사진을 보면 두개의 프로세스(241113, 24114)에서 각각 변수가 p가 저장된 메모리 주소를 출력하고 있습니다.

그런데 같은 메모리 주소를 가르키고 있는 것을 알 수 있습니다. 아래 출력하는 실행흐름을 보면 분명히 독립적으로 움직이고 있는데 말이죠.

이것을 통해 메모리가 가상화되었다는 것을 알 수 있습니다.

 

프로그램이 실행될 때 프로세스를 형성하는데, 그 프로세스가 사용하는 메모리가 물리적인 메모리가 아니라, 가상화된 메모리입니다.

실제로 물리적인 메모리는 하나지만 이를 가상화하여 마치 프로세스가 자기 전용의 메모리를 소유한 것과 같은 착각을 불러일으킵니다.

Concurrency

병렬적으로 실행되는, 즉 여러개의 실행 흐름이 존재할 때를 기준으로 메모리 구조를 살펴봅시다.

 

실행흐름(스레드)마다 스택 영역은 따로 갖게 됩니다. 이 스택 영역을 Execution Stack이라고 합니다.

그렇다면 프로세스 안에서 스레드간에 메모리에서 공유하는 부분은 Text(Code), Data(+BSS), Heap 영역이 됩니다.

 

사진에서의 코드를 보면 각각의 스레드에서 counter를 +1 씩 증가시키는 일을 서로 협력하면서 하고 있습니다.

협력이라는 단어를 썼는데요, 이는 Counter 변수는 초기화된 변수로 Data영역에 할당되고 이는 모든 스레드에서 공유되는 영역이기 때문에 모든 곳에서 접근이 가능하기 때문입니다.

 

만약 loop에 대한 인자로 10만 을 주면 어떻게 될까요 ?

 

각각의 독립적인 실행흐름에서 1000씩 루프를 돌면서 Counter 변수를 +1 시키기 때문에 20만이 될 것 입니다 !

...

과연 그럴까요 ?

이해할 수 없는 결과

우리가 의도한 결과인 20만에는 전혀 못미칠 뿐더러 매번 실행할 때마다 예측할 수 없는 다른 결과값이 나오고 있습니다.

 

그 문제가 일어나고 있는 곳은 바로

 

counter++

 

이 코드입니다. 언뜻 보기에는 한줄의 코드같지만 사실 기계어 단계까지 간다면 전혀 그렇지 않습니다.

1. LOAD

2. ADD

3. STORE

약 3단계를 걸쳐서 진행되는 코드입니다. 

 

우리가 CPU에 있는 ALU장치를 통해서 연산을 하게 되는데, 이 때 연산을 하기 위해서는 메모리에 있는 값을 CPU내부로 가져와야합니다. 이 때 레지스터를 사용하게 될 것입니다. 이것이 LOAD입니다. 이 후 가져온 값을 이용해서 ADD연산을 하고 계산된 결과값을 다시 메모리에 STORE를 하는 과정을 거쳐서 counter++의 코드 한줄이 실행이 됩니다.

 

저 위의 과정들이 모두 atomic하다고 한다면 문제가 되지 않겠지만 그렇지 않은 것이 문제입니다.

P1이 LD, ADD를 통해서 101이라는 결과값을 만들어놓고 아직 STORE하지 않은 상황에서 P2도 마찬가지로 LD, ADD, 통해서 101이라는 결과값을 만들고 P2는 저장까지했다고 쳐봅시다.

P2는 101이라는 결과값을 저장할테고, 그 이후에 곧바로 P1도 101이라는 결과값을 저장할 것 입니다.

 

우리는 P1과 P2가 한번씩 실행되었다고 한다면 100에서 2를 더한 102의 결과값을 예상할 테지만 위의 상황에서는 101의 결과값이 나오게 되는 것 입니다. 이렇게 데이터를 경쟁적으로 바꾸려하는 상황을 data race라고 하고, 경쟁 조건 (race condition)이라고도 합니다. 이것이 Concurrency의 대표적인 문제입니다.

이것에 대한 해결방법은 뒤에서 질리도록 다룰테니 여기서는 더 이상 다루지는 않겠습니다 !

 

Persistence (영속성)

우리가 작성한 코드나 거기서 사용하는 변수 등은 모두 메인 메모리인 DRAM에 저장되게 됩니다.

이 때 메인 메모리는 휘발성의 성격을 가지고 있기 때문에, 컴퓨터 전원이 꺼지면 그 안에 있는 모든 정보가 사라지게 됩니다.

 

그렇기 때문에 전원을 꺼도 그 정보가 사라지지 않게끔 하기 위해서 HDD, SSD에 저장을 하게 됩니다.

이는 곧 영속성을 갖게끔 하는 행위입니다.

그러면 HDD에 쓸 때 어떻게 쓸까 ? 

파일로 저장하게 됩니다. 그리고 이 파일들을 찾아가고 컨트롤할 수 있게끔 파일 시스템을 구성합니다.

 


운체 스터디를 이제 본격적으로 시작하면서 블로그에 글로 정리하고자 합니다. 사실 지금까지 학교 전공 수업 이후에는 계속 WWDC공부나 Swift에 대해 공부를 하다보면 조금씩 운체 개념들을 필요로 해서 그때마다 계속 접하긴했는데 이번 기회로 쭉 정리를 하고 블로그에 계속해서 글로 남기고자 합니다 ! 

 

다음글

https://codingmon.tistory.com/37

 

[OS] Processes: Process, Process States, Process API

Program 소스프로그램에서 컴파일에서 얻은 실행 파일을 말하고, 이것은 HDD, SDD 저장장치에 저장됩니다. 저장장치에 저장되어있던 프로그램이 실행되려면 메인 메모리로 loading되어야합니다. 어째

codingmon.tistory.com

 

댓글