이 글은 CS:app 1장을 끝까지 다 읽고 정리한 글이다.
이전 글들을 같이 봐야 이해될 수 있는 내용이 있으니 이전 글을 읽고 보길 바란다.
1.5 캐시 메모리(cache memory)
이전 글에서 프로그램의 실행 과정을 보면 불필요할 정도로 프로그램과 데이터의 복사, 이동이 많다.
이 복사 및 이동 과정들은 프로그램의 실제 작업을 느리게 하는 오버헤드로 이 과정을 줄이는게 프로그램 실행을 빠르게 할 수 있는 방법이 될 것이다.
저장 장치의 용량이 크면 물리적인 이유로 인해 작은 것보다 느린 속도를 갖게 된다.
ex1) 디스크 드라이브가 메인 메모리보다 1000배 크지만, 1 워드를 읽는 속도는 10000000(천만)배 오래 걸릴 수 있다.
ex2) 레지스터 파일은 수 백 byte의 정보를 저장하지만, 메인 메모리는 십억 개의 byte를 저장한다. 그러나 프로세서는 레지스터 파일의 데이터를 읽는 데 메모리보다 100배 가까이 빨리 읽을 수 있다.
이러한 프로세스-메모리 간 격차에 대응하기 위해 작고 빠른 캐시 메모리(이후 캐시)를 고안했다.
이 캐시는 단기간에 필요로로 할 가능성이 높은 정보를 임시로 저장할 목적으로 사용된다.
캐시의 종류는 L1 cache, L2 cache, 성능이 좋은 시스템의 경우 L3 cache 까지 존재하는데,
속도 측면에서는 L1 > L2 > L3 순으로 L1이 가장 빠르고, 크기는 L1 < L2 < L3 순으로 L3가 가장 크다.
(프로세서가 접근할 때 L2가 L1보다 5배 느리지만, 메인 메모리보다 5 ~ 10배 빠르고, L1의 크기는 수천 byte인 반면, L2는 수백 KB ~ 수 MB 이다.)
L1 캐시는 프로세서 안에 위치하여 더 빠르게 갖다 쓸 수 있게 만들었고, L2는 프로세서 밖에 있는 대신 프로세서와 전용 버스를 통해 연결된다.
L1, L2 캐시는 SRAM(Static Random Access Memory)라는 하드웨어 기술을 이용해서 구현되었다.
캐시 시스템의 아이디어는 프로그램이 지엽적인 영역의 코드와 데이터를 엑세스하는 경향인 지역성(locality)을 활용하여 크고 빠른 메모리 효과를 얻는 것이다.
(자주 사용할 확률이 높은 데이터를 캐시에 보관해 더 빠르게 작업 수행할 수 있게 한다.)
이때 지역성은 시간적 지역성(Temporal Locality)와 공간적 지역성(Spatial Locality)로 나뉘게 된다.
· 시간적 지역성 : 한 번 접근된 데이터는 가까운 미래에 다시 접근될 가능성이 높다는 원리. 예를 들어, 루프 내에서 반복적으로 사용되는 변수는 시간적 지역성의 좋은 예이다.
· 공간적 지역성 : 메모리의 특정 주소에 접근한 후, 그 주변 주소에 있는 데이터에 접근될 가능성이 높다는 원리. 이는 배열이나 연속적인 메모리 블록에 접근할 때 종종 발생한다.
캐시 메모리는 이러한 지역성 원리를 활용하여 자주 사용되거나 연속적으로 사용될 가능성이 높은 데이터를 미리 캐시에 저장한다. 이로 인해 CPU는 필요한 데이터를 캐시에서 빠르게 찾을 수 있다.
1.6
모든 컴퓨터 시스템의 저장 장치들은 다음 그림과 같은 메모리 계층 구조로 되어있다.
이 계층에서 위에 있을 수록 크기가 작고, 빠르고, 비트 당 가격이 비싸다.
아래로 갈수록 위와 반대로 크고, 느리고, 비트 당 가격이 싸다.
메모리 계층 구조의 주요 아이디어는 한 레벨의 저장 장치가 다음 하위 레벨의 캐시 역할을 한다는 것이다.
1.7 OS(Operating System) 운영체제
운영체제는 하드웨어와 소프트웨어 사이에 위치한 소프트웨어 계층이다.
따라서 응용 프로그램이 하드웨어를 제어하려면 반드시 운영체제를 통해야한다.
운영체제는 두가지 주요 목적을 가지고 있다.
1) 제멋대로 동작하는 응용 프로그램들이 하드웨어를 잘못 사용하는 것을 막는 것
2) 응용 프로그램들이 단순하고 균일한 메커니즘을 사용하여 복잡한 저수준 하드웨어 장치들을 조작할 수 있또록 하는 것
이 두가지 목적은 아래 그림과 같이 추상화를 통해 달성한다.
1.7.1 프로세스(Process)
프로그램을 실행하면 프로그램이 프로세서, 메인 메모리, 입출력 장치를 모두 독차지하는 것 처럼 보이고, 프로세서는 프로그램 내의 instruction들을 방해없이 순차적으로 실행하는 것처럼 보인다.
이러한 현상은 프로세스라는 개념에 의해 만들어진 것이다.
프로세스는 독립적으로 실행되는 프로그램의 인스턴스로, 자체적인 주소 공간, 메모리, 데이터 스택 및 다른 시스템 자원을 갖는다.
프로세스는 실행 중인 프로그램에 대한 운영체제의 추상화로, 쉽게 말하면 실행 중인 프로그램을 눈으로 볼 수 있게 나타낸 것이라고 보면 된다.
다수의 프로세스들은 동일한 시스템에서 '동시에(concurrently)' 실행될 수 있고, 각 프로세스들은 하드웨어를 배타적으로 사용하는 것처럼 느껴진다.
이는 한 프로세서가 프로세서들을 바꿔주며 교차 실행하며 가능한 것이다.
여기서 concurrently는 한 프로세스의 instruction들이 다른 프로세스의 것과 섞인다는 의미한다.
OS는 context switching 이라는 방식을 사용하여 프로세스들의 교차 실행을 수행한다.
OS가 프로세스가 실행하는데 필요한 모든 상태 정보(PC, 레지스터 파일, 메인 메모리의 현재 값 등)의 변화를 추적하는 데, 이 상태 정보를 context라고 한다.
OS가 현재 프로세스에서 다른 것으로 제어를 옮기려 할 때 현재 프로세스의 context를 저장하고 새 프로세스의 context를 복원시키는 context switching을 실행하여 제어권을 넘겨준다.
이 때, 새 프로세스는 중단했던 위치부터 실행된다.
다음 그림을 통해 context switching의 과정을 요약하겠다.(이후 뒷장에서 다룰 예정)
- 자원 공유 : 각 프로세스는 독립적인 메모리 공간과 시스템 자원을 가지므로, 프로세스간 자원 공유는 IPC(Inter-Process Communication) 메커니즘을 통해 이루어진다.
프로세스의 전환은 커널(kernel)에 의해 관리된다.
커널은 별도의 프로세스가 아닌, 모든 프로세스를 관리하기 위해 시스템이 이용하는 코드와 자료 구조의 집합이다.
응용 프로그램이 운영체제에서 해야하는 작업을 요청하면, 특정 'system call'을 실행해서 커널에 제어를 넘겨준다.
system call이란, 운영체제의 커널이 제공하는 서비스에 대해, 응용 프로그램의 요청에 따라 커널에 접근하기 위한 인터페이스이다. C나 C++ 같은 고급 언어로 작성된 프로그램에 경우 직접 시스템 호출을 할 수 없기 때문에 고급 API를 통해 시스템 호출에 접근하게 하는 방법이다.
즉, system call을 호출하지 않으면 시스템에 접근할 방법이 없다.
1.7.2 쓰레드(Thread)
쓰레드는 프로세스 내부의 실행 흐름 단위로, 프로세스의 자원과 주소 공간을 공유하면서 실행된다.
최근의 시스템에서 프로세스는 쓰레드라는 다수의 실행 유닛으로 구성되어있다.
각각의 쓰레드는 해당 프로세스의 context에서 실행되며, 동일한 코드와 전역 데이터를 공유한다.
쓰레드는 다수의 프로세서들에서보다 데이터의 공유가 쉽고, 프로세스보다 더 효율적이다.
- 자원 공유 : 같은 프로세스 내의 쓰레드들은 위 그림과 같이 코드, 데이터 및 시스템 자원을 공유하고, 수행하는 내용이 다른 내용들은 공유하지 않는다.
1.7.3 가상 메모리(Virtual Memory)
가상 메모리는 각 프로세스들이 메인 메모리 전체를 독점적으로 사용하고 있는 것 같은 느낌을 제공하는 추상화이다.
책에서 이 글이 가장 이해가 안갔는데 자세히 말하자면, 실제 물리적 메모리에 프로세스를 그대로 올리면 차지하는 크기가 너무 크기 때문에 공통되는 부분은 하나만 올리고 각각 공유하지 않는 부분들만 따로 올린다.
이때, 각각의 프로세스들에 대해 가상 메모리라는 가상의 유닛을 구성하는데, 이 유닛에 물리적 메모리에 올라가 있는 공유하는 데이터, 공유하지 않는 데이터를 올려 실제 사용자가 보기에는 메모리에서 해당 프로세스만 다루는 것처럼 느끼게 하는 것이다.
주소 공간의 최상위 영역은 모드 프로세스들이 공통으로 사용하는 OS의 코드와 데이터를 위한 공간이다.
하위 영역은 사용자 프로세스의 코드와 데이터를 저장한다.
· 프로그램 코드와 데이터
코드는 모든 프로세스들이 같은 고정 주소에서 시작하고, 다음에 C 전역 변수에 대응되는 데이터 위치들이 따라온다.(크기가 고정되어있다.)
· 힙(heap)
위의 것 다음으로 런타임 힙이 따라온다.
힙은 malloc, free를 호출하면서 동적으로 크기가 늘었다 줄었다 한다.
· 공유 라이브러리
C 표준 라이브러리나 수학 라이브러리 같은 공유 라이브러리의 코드와 데이터를 저장하는 영역이다.
· 스택(stack)
컴파일러가 함수 호출을 구현하기 위해 사용되는 영역으로, 함수 호출 시 크기가 커졌다가 리턴될 때 줄어든다.(heap과 같이 동적으로 크기가 변한다)
· 커널 가상메모리
가장 위 공간은 항상 커널이 차지한다.
응용 프로그램은 이 영역의 내용을 읽거나 쓰는 것이 금지되어 있다.
커널 내 함수를 직접 호출하는 것도 불가하다.(커널을 호출해야 수행이 가능하다.)
1.7.4 파일(files)
파일은 연속된 바이트들이다.
모든 입출력 장치는 파일로 모델링 되고, 시스템의 모든 입출력은 유닉스 I / O라는 systemm call 들을 이용하여 파일을 읽고 쓰는 형태로 이루어진다.
1.8 네트워크
네트워크도 다음 그림과 같이 또 다른 입출력 장치로 볼 수 있다.
시스템이 메인 메모리로부터 네트워크 어댑터로 데이터를 복사할 때, 데이터는 로컬 디스크 드라이브 대신 네트워크를 통해 다른 컴퓨터로 이동한다.
반대는 시스템이 네트워크 어댑터로 받은 데이터를 읽어서 메인 메모리에 복사하는 과정으로 이루어진다.
1.9.1 Amdahl's law (암달의 법칙)
암달의 법칙은 수식은 복잡하지만 아이디어는 간단하다.
아무리 코어 수를 늘리고 쓰레드 수를 늘리는 등 시스템의 주요 부분에 대해서 기능 향상을 시켜도 속도 향상은 그 효과를 그대로 보지 못한다는 것이 메인 아이디어이다.
전체 시스템을 상당히 빠르게 하기 위해서는 전체 시스템의 매우 큰 부분의 성능을 개선해야한다.
순차적으로 실행되야하는 작업의 비율을 F, 프로세서의 개수를 N이라고 했을 때, 이 수식 만큼의 양까지만 속도를 증가시킬 수 있다는 의미이다.
1.9.2 동시성과 병렬성
동시성(concurrency)는 single core 환경과 multi core 환경에서의 의미가 다르다.
- Single core에서의 동시성은 쓰레드의 수행이 계속해서 교차되는 방식으로 진행된다는 의미이다.
- Multi core에서의 동시성은 위의 내용을 포함하고 쓰레드가 병렬적으로 실행 가능하다는 의미이다.
따라서 Multi Core에서는 병렬성이라는 단어도 존재하게 되는데 병렬성(Parallelism)이란 시스템이 동시에 한 개 이상의 작업을 수행할 수 있게 된다는 의미이다.
다음 그림을 보면 이해가 빠를 것이다.
자세한 내용은 뒤에서 나온다지만, 키워드만 나온거 치고는 더 깊고 많은 개념을 알고 있지 않으면 이해하지 못할 내용들이 너무 많았던 것 같다.
그래서 나는 추가 자료 조사를 하고, 이전에 공부했던 전공 자료를 찾아보고 많은 사람들과 논의하여 '아, 이 수준이면 겉핥기에서 이해가 되긴 했다.' 정도가 된 것이지 책만 봐서는 전혀 이해가 안 될 확률이 높다.
특히 가상 메모리는 Page 등 추가 개념이 필요하기에 더 이해가 어려운게 당연하다.
앞으로 책을 읽어가며 더 자세한 내용을 다뤄보도록 하겠다.
'TIL & WIL' 카테고리의 다른 글
[TIL] 크래프톤 정글 3주차 CS:app 4(메모리에서의 정보 접근) (0) | 2024.04.14 |
---|---|
[TIL] 크래프톤 정글 2주차 - 자료구조(B-Tree) (0) | 2024.04.01 |
[TIL] 크래프톤 정글 1주차 CS:app 2(C 프로그램 컴파일 이후 실행 과정) (0) | 2024.03.27 |
[TIL] 크래프톤 정글 1주차 CS:app1(컴퓨터 시스템과 C프로그램의 컴파일 과정) (0) | 2024.03.26 |
[TIL] 크래프톤 정글 1주차 파이썬의 특징과 객체 지향 언어의 특징에 관하여 (1) | 2024.03.26 |