5장. 컴퓨터 아키텍처와 운영체제 - 컴퓨터는 어떻게 프로그램과 메모리를 조직적으로 관리할까
컴퓨터 아키텍처: 컴퓨터의 여러 구성요소를 배치하는 방법
- 기본적인 구조 요소들
1980년대에 만들어진 멀티프로세서시스템은 단일 CPU보다 훨씬 더 좋은 성능을 얻어내기 위한 방법이다. 하지만 이 방법은 쉽지 않았다. 여러 CPU를 활용할 수 있도록 프로그램을 병렬화하는 문제는 일반적인 경우에 풀 수 없는 문제다. 하지만 그래픽 사용자 인터페이스(GUI)를 제공하는 초기에 여러 프로그램을 동시에 실행할 수 있다는 점이 이점이었다.
반도체 회로 크기가 줄어들면서 비용이 낮아졌다. CPU를 더 빠르게 만듦으로써 더 나은 성능을 달성했다. 하지만 기계가 빨라지며 전력을 많이 소모하고, 회로 크기가 줄어드는데 기계가 빨라지니 전력을 더 많이 소모했다. 단위 면적당 열 발생이 많아졌다. 때문에 2000년경에는 전력 장벽에 부딪혔다. 열로 인해 회로를 이루는 물질의 녹는점 이상으로 온도가 올라가는 것을 방지하면서 회로를 소형화 고성능화하기가 어려워졌다.
메모리와 I/O가 프로세스 코어와 같은 패키지에 들어있지 않으면 마이크로프로세서라고 부른다. 반면 모든 요소를 한 칩 안에 패키징하면 마이크로컴퓨터라고 부른다. 이 용어들은 모호하다. 마이크로컨트롤러라고 부르기도 한다.
추가로 단일 칩 시스템(SoC)가 있다. 더 복잡한 마이크로컴퓨터를 뜻한다. 상대적으로 간단한 I/O 외에도 WiFi 회로 등의 복잡한 장치가 더 들어있다. 핸드폰 등에서 볼 수 있다. 원하는 대로 커스텀화가 가능한 FPGA를 제공하는 SoC도 있다.
- 스택
함수가 자기 자신을 호출하는 것, 재귀에 대한 설명
트리: 수학에서는 유향 비순환 그래프(DAG, directed acyclic graph)라고 부른다. 각 노드에서 가지가 4개 뻗어나가는 것을 쿼드트리라고 한다. 쿼드트리는 공간 데이터 구조에 속한다.
포스(forth)나 포스트스크립트(PostScript) 같은 몇몇 언어와 옛날 HP 계산기 몇 가지는 스택 기반 언어다. 그런데 스택은 컴퓨터 언어에만 한정되지 않는다. 한국어나 일본어는 스택 기반 언어다. 명사를 스택에 넣고 그다음에 오는 동사는 스택에 있는 명사에 작용한다.
- 인터럽트
실행 중인 프로그램을 잠깐 중단시켜서 주의를 기울여야 하는 외부의 요소에 대응할 수 있게 만들 방법이 필요하다. 실행 장치에 새로운 하드웨어 기능을 추가한다.
인터럽트는 시스템은 적절한 신호가 들어오면 CPU 실행을 잠깐 중단시킬 수 있는 핀이나 전기 연결을 포함한다.
CPU가 주의를 기울여야 하는 주변장치는 인터럽트 요청을 생성한다. 프로세서는 현재 실행 중인 명령어를 끝까지 실행한다. 그 후 프로세스는 현재 실행 중인 프로그램을 잠시 중단시키고 인터럽트 핸들러라는 전혀 다른 프로그램을 실행하기 시작한다. 인터럽트 핸들러가 필요한 작업을 다 마치고 나면 원래 실행 중이던 프로그램이 중단된 위치부터 다시 실행을 계속한다.
인터럽트에서 고려해야 할 요소는 첫째로 응답시간이다. 인터럽트 처리를 정해진 시간 안에 끝내야 한다.
둘째는 현재 상태 저장이다. 인터럽트가 걸린 시점에 실행 중이던 프로그램이 레지스터에 어떤 값을 저장하고 있었다면, 인터럽트 핸들러는 그 레지스터를 저장했다가 나중에 원래 프로그램으로 돌아오기 전에 레지스터값을 복구해줘야 한다.
인터럽트 시스템은 서비스 후 돌아올 프로그램 위치를 스택에 저장한다. 자신이 덮어쓸 레지스터를 모두 저장해야 할 책임이 있다. 이렇게 하면 인터럽트 핸들러가 저장해야 하는 요소를 최소화 -> 빠르게 서비스할 수 있다.
컴퓨터는 어떻게 인터럽트를 찾나? 저장하는 메모리 주소가 존재한다. 이 주소에 여러 인터럽트 벡터가 들어 있고, 각 벡터는 핸들러 주소를 지정한다. 벡터는 포인터일 뿐이다.
- 메모리 관리 장치(MMU, memory management unit)
MMU는 가상 주소와 물리 주소를 구분한다. MMU의 가상 주소 범위는 물리적 메모리 주소보다 큰 경우가 많다.
가상 메모리가 연속적인 것처럼 보이지만, 실제 물리 메모리상의 위치는 굳이 연속적일 필요가 없다. 심지어 프로그램이 실행되는 도중에 프로그램이 위치한 물리적 메모리 주소가 바뀔 수도 있다. 또 공유 메모리 기능을 제공할 수도 있다.
전체 페이지 테이블 항목은 주 메모리에 저장되거나 부족할 경우 디스크에 저장된다. MMU는 페이지 테이블 항목 중 일부를 필요할 때만 자신의 페이지 테이블로 읽어들인다.
프로그램이 물리적 메모리에 연관되지 않은 주소에 접근하면 페이지 폴트 예외가 발생한다. OS는 실행중인 프로그램을 중단시키는 대신 MMU가 추가 메모리를 할당하게 해서 스택 공간을 늘리고 사용자 프로그램 실행을 계속할 수 있다.
- 가상 메모리
MMU가 프로그램의 가상 주소를 물리 메모리 주소로 변환해준다는 사실을 살펴봤다. 페이지 폴트 메커니즘으로 인해 프로그램은 필요한 만큼 많은 메모리가 있다고 생각할 수 있다. 요청받은 메모리가 물리적 메모리 크기보다 크다면, 디스크로 옮긴다(스왑 아웃). 스왑 아웃한 페이지에 프로그램이 접근하면 OS는 필요한 메모리 공간을 확보하고 디스크에서 메모리로 다시 페이지를 불러들인다(스왑 인). 이를 요구불 페이징이라고 한다.
이는 시스템 성능이 크게 저하되지만 느리더라도 프로그램을 실행하는 것이 낫다. LRU알고리즘이 사용된다.
- 시스템 공간과 사용자 공간
사용자 프로그램으로부터 운영체제를 보호하고, 사용자 프로그램을 다른 사용자 프로그램으로부터 보호하기 위해 분리한다.
사용자 프로그램이 MMU 등의 요소에 접근할 수 없기에 운영체제가 프로그램에 대한 자원 할당을 전적으로 제어할 수 있다.(하드웨어 예외는 오직 시스템 공간에서만 처리된다.)
시스템 공간에서 실행되는 프로그램에 손을 대기 위해서는 아주 뛰어난 프로그래머가 될 필요가 있다.
- 메모리 계층과 성능
과거에는 CPU와 메모리가 같은 속도로 작업했고 컴퓨터 세계는 평온했다. 하지만 CPU는 빨라지고 메모리는 상대적으로 느렸다. 빠른 CPU가 느린 메모리를 기다리느라고 아무 일도 하지 않는 경우를 줄이려고 한다.
레지스터: 냉장고 -> 자주 냉장고 문을 열고 물건을 하나만 꺼낸다.
주 메모리: 가게 -> 가끔 장바구니를 챙겨 가게에 가서 물건을 좀 더 많이 담아 온다.
대용량 저장장치: 창고 -> 아주 가끔 창고에 트럭을 끌고 가서 물건을 와장창 실어온다.
... 근데 이 비유가 뒤에 나오는 메모리 설명이랑 무슨 상관인지 솔직히 모르겠다 ㅠ ㅠ
DRAM은 행으로 읽을 때보다 열로 읽을 때 더 빠르다. 프로그램이 작동하는 방식을 관찰하면, 분기가 없는 경우 프로그램 메모리를 순서대로 읽어온다. 프로그램에 사용하는 데이터는 한데 모여 있는 경우가 많다.
이런 현상을 활용해 CPU 메모리 컨트롤러 하드웨어는 메모리에서 연속된 열에 있는 데이터를 한꺼번에 가져온다.
- 코프로세서
몇 가지 연산을 코프로세서라는 더 단순한 회로에 위임하면 프로세서 코어가 일반적인 연산에 활용할 수 있는 공간을 더 확보할 수 있다. 과거에는 한 칩 안에 모든 연산 회로를 넣기 어려웠기 때문에 코프로세서가 쓰였다. 요즘은 그래픽 처리 등 여러 가지 기능을 담당하는 코프로세서가 한 칩에 같이 들어가 있는 경우가 많다.
주 메모리에서 일을 한다. 그리고 메모리와 디스크 사이에 데이터 복사가 일어난다. 연산을 한 결과를 복사해온다. 일부 코프로세서는 다른 일은 처리하지 않고 데이터 복사만 담당한다. '여기서 이만큼의 데이터를 저쪽으로 복사하고, 다 끝나면 나한테 알려줘', CPU는 귀찮은 일을 많이 떠넘기고 유용한 연산을 더 많이 처리한다. 이를 직접 메모리 접근이라고 한다.
- 메모리상의 데이터 배치
프로그램을 작성할 때 얼마나 많은 메모리가 필요한지 알고 있는 데이터를 정적데이터라고 한다. 반면에 프로그램을 실행하기 전에는 크기를 알 수 없는 데이터를 동적데이터라고 한다. 동적 데이터는 힙에 쌓인다.
- 프로그램 실행
본격적인 프로그램은 여러 조각으로 이뤄진다. 프로그램을 여러 조각으로 나누면 이를 엮거나 연결하는 방법이 필요하다. 링커라는 프로그램을 사용해 연결하여 실행할 수 있다.
과거에는 라이브러리를 프로그램의 나머지 부분과 직접 연결해 실행 파일을 만들었다. (정적 링크)
하지만 여러 프로그램이 똑같은 라이브러리를 사용하는 경우가 많다. 여러 실행 파일에 같은 라이브러리 코드가 반복적으로 들어가서 메모리를 낭비한다.
때문에 공유 라이브러리를 사용하여 MMU가 여러 프로그램이 같은 라이브러리를 공유할 수 있게 해준다. (동적 링크)
프로그램에는 진입점이 있다. 첫 번째 명령어가 위치한 주소를 뜻한다.
하지만 실제로는 프로그램을 이루는 모든 부분이 하나로 합쳐져서 실행파일을 이룰 때 런타임 라이브러리가 추가된다. 이 런타임 라이브러리에 있는 명령어가 먼저 실행되고 나중에 진입점의 명령어가 실행된다.
런타임 라이브러리는 스택과 힙 영역, 정적 데이터의 초깃값을 설정한다.
이런 저런 일들에 치여 이 책을 이제야 다시 펼치게 됐다.
근데 이번 5장, 페이지 수가 적어서 금방 정리할 줄 알았는데 내용이 =_=
단순하지 않았다. 메모리 사용을 개선하는 일은 아주 복잡하고 심오하군
그래서 책 내용을 제대로 다 이해했는지 모르겠지만..
일단은 뒷 부분 계속 읽어가보고 또 모르겠으면 다시 돌아와야겠다.
'full of life > book' 카테고리의 다른 글
블록체인이 세상을 바꾼다 - 2 탈중앙화 금융 디파이(DeFi) (0) | 2023.06.04 |
---|---|
블록체인이 세상을 바꾼다 - 1 가상화폐는 뭘까 (0) | 2023.05.19 |
[한 권으로 읽는 컴퓨터 구조와 프로그래밍] 조너선 스타인하트 - 4 (0) | 2021.08.20 |
[한 권으로 읽는 컴퓨터 구조와 프로그래밍] 조너선 스타인하트 - 3 (0) | 2021.08.12 |
[한 권으로 읽는 컴퓨터 구조와 프로그래밍] 조너선 스타인하트 - 2 (0) | 2021.08.01 |