권영진 교수님 OS 강의
정리 이전
권영진 교수님께서 pdf를 주셔서
해당 pdf의 일부 이미지를 참고하였고,
정리 중 일부 내용을 추가하였다
운영체제를 배우는 이유?
사용자 어플리케이션 프로그램만 만들 것이라면
사실 OS를 공부하는 것에 의문을 느낄 수 있다
(직접적으로 OS 개발을 할 생각이 없는 경우에 특히!)
그래도 OS를 공부할 필요가 있다는 부분을 강조하고 싶다
예를 들어 내가 짠 소프트웨어 코드가 첫번째 호출과 두번째 호출에 따라
다른 성능을 가지는 이유는 무엇일까?
=> 두번째 호출의 경우
데이터가 이미 ‘캐시’에 존재할 가능성이 높기에(L1,L2,L3 등)
2번째 접근은 보다 빠를 수 있음
(참고로, ‘캐시’의 저장량보다 ‘높은’ 양의 데이터라도
Page Table이 갱신되었기에, Page Fault의 오버헤드가 일어나지 않기에
실질적으로 2번째 접근이 조금 더 빠르게 접근할 수 있음)
이외의 다양한 ‘현상’에 대하여,
OS를 공부하면 ‘왜’ 이런식으로 OS가 처리를 하는지에 대하여
파악할 수 있음
OS의 개념
운영체제 자체는 일종의 ‘추상화’된 개념이다
‘스케쥴’도 해주고, ‘File과 I/O 시스템’도 관리해주고
‘IPC(Process 간 통신)’ 도 해주고,
‘메모리(자원)’도 관리하고…
각각의 ‘추상화 된 작업’을 분리하고(Decompose),
(‘스케쥴링’, ‘IPC’, ‘자원 관리’… 등등)
이러한 일들을 ‘결합하여 처리’ 해주는 하나의 ‘시스템’을
OS로 정의한다
(Decompose and Divide Conquer 라는 ‘추상화’ 기법이라 하더라)
(이는 각자 다른 일을 하는 ‘모듈’을 하나로 합쳐
하나의 시스템을 구현한 것과 같다)
-
- 추상화? (Abstraction)
- ‘단순’하게 만들어 이해를 ‘쉽게’ 만든다
기본적으로 ‘복잡한’ 내용을 제거하여,
사용을 쉽게한다는 의미로
struct, class 등도 사실 이를 위한 내용이다
(ex : boss 라는 클래스를 만들 때,
boss의 ‘story concept’(이야기 설정)이라는 필드가 필요할 지
고민해볼만한 내용일듯 하다
기본적으로는 ‘복잡한’ 내용에 포함되어 ‘제거’될 수 있으나
현실에서 ‘보스’ 라는 개념에는 이야기 설정이 있을수도 있지 않은가?)
- 추상화? (Abstraction)
‘추상화’에서 돌아오자면
우리가 User Level에서 직접 ‘하드웨어’에 접근하는 것은
아주 비효율적이고 힘듦 작업이다
(‘어셈블리어’를 사용하고 싶다면 모를까?)
프로세스
OS 디자이너들이 생각하길
- 하드웨어를 세부적으로 처리하기를 원하는 사람이 없음
(프로그램 작성의 편의를 위함) - 하드웨어 리소스를 최대한 활용하기 위하여
OS는 여러 어플리케이션을 동시에 실행해야 함
(각 실행파일의 관리) - 각각의 응용 프로그램을 보호해야 함
(각 실행 파일의 보호)
그러므로
각 어플리케이션이 ‘혼자서’ 실행되도록 착각하도록
‘추상화’를 구축한다
(이를 ‘프로세스’라 한다)
OS 디자이너들은
하드웨어의 자원들을 각각 추상화하여
프로세스와 엮었는데
- CPU : Virtualizing CPU (가상 CPU)
- 메모리 : Virtual Address Space (가상 주소 공간)
- 저장소 : File(파일)
=> 이는 하나의 ‘기계’에서 오직 ‘하나’의 프로그램만 실행하는 것처럼 보이게 한다
다만 실제 사용하는 경우,
‘물리 주소’ 및 ‘디스크’에 접근해야 한다
(이 때, ‘시스템 콜’이 호출되어, OS가 그 일을 대신 처리한다)
(프로세스 자체를 만들어주며, 또한 가상 주소의 ‘layout’ 역시 만들어준다)
프로세스의 ‘가상 주소 공간’은
text, data, stack, heap 이 존재하며
이는 이전 포스팅에서 살펴보았기에
자세한 내용은 url을 남기려 한다
(https://hnjog.github.io/%ED%81%AC%EB%9E%98%ED%94%84%ED%86%A4%20%EC%A0%95%EA%B8%80/cs/%EA%B0%80%EC%83%81%EB%A9%94%EB%AA%A8%EB%A6%AC,%ED%8E%98%EC%9D%B4%EC%A7%95/)
운영체제가 파일을 열고,
메모리에 ‘가상 주소’(VA)에 맞게 올려주는 것을
‘loading’이라 한다
(어플리케이션이 ‘자신만의 공간’이라 여기도록 환경을 세팅)
주소 공간의 추상화
가상 주소 와 물리 주소와 매핑
(각각의 VA를 ‘일정한 단위’로 쪼개고,
이를 물리 주소로 옮긴 뒤, 옮긴 위치와 VA를 매핑한다)
이런 매핑 결과를 저장한 것이 바로 ‘변환 테이블’이다
주 매핑 방식으로는
페이징, 세그맨테이션, 세그멘티드 페이징 등이 존재한다
(보통 이 3가지를 사용한다 한다)
(참고로 페이지는 일종의 메모리 단위이며, ‘페이징’ 기법에 주로 사용되나
페이지라는 용어가 등장하여도, 반드시 ‘페이징’ 기법이라는 뜻은 아니다)
공통점이 있다면 매핑은
하나의 ‘가상 주소’를 받은 뒤
하나의 ‘물리 주소’로 접근한다
CPU <-> MMU (CPU가 VA 전달하고 MMU는 Table에서 메모리를 뒤지러 간다)
MMU <-> Memory(전달받은 VA에서 PA주소를 찾아 데이터를 반환)
조금 더 구체적으로는
MMU 내부에 ‘TLB’라는 작고 빠른 캐시 메모리를 가지고 있다
(Translation Lookaside Buffer)
그렇기에, CPU에서 MMU 내부의 TLB를 먼저 ‘체크’하여
캐시 존재 여부를 확인한다
(가상 주소의 ‘페이지’를 물리 주소의 ‘프레임’으로 변환한다)
(이 때, 존재하는 경우는 TLB 캐시 Hit 이기에, 이상적인 메모리 접근이 가능하다)
TLB 캐시가 적중하지 못한 경우,
커널 내부의 Page Table을 탐색하며
(보통 프로세스가 전환될때 PDBR(Page Directory Based Register)가
현재 프로세스의 Page Table을 가리키도록 세팅된다)
이 때, 물리 주소가 ‘메인 메모리’에 존재한다면
메인 메모리에 접근하되
만약, 메인 메모리에 존재하지 않는 경우,
(이는 보통 PTE의 ‘유효 비트’ 나 ‘물리 페이지 번호’를 체크)
Page Fault를 발생시키고,
비어있는 공간을 찾아 Disk 에서 주소를 찾아 메모리에 올리게 된다
(만약 비어있지 않다면, 페이지 교체 알고리즘을 통해 Swapping이 일어남)
(Page Replacement Policy 라고도 한다)
정리하자면
- CPU가 VA를 통해 특정한 물리 주소에 접근을 원함
- TLB 체크
- TLB 적중 시, 해당 정보를 통해 빠르게 물리 주소에 접근 가능
- TLB 미스 시, 2번으로
- TLB 적중 시, 해당 정보를 통해 빠르게 물리 주소에 접근 가능
- MMU가 PTE를 체크
- PTE를 돌면서, VA와 연관된 물리 주소를 검색한다
- 물리 주소가 메모리(RAM)에 존재한다면 해당 메모리에 접근
- 메모리에 존재하지 않다면 ‘Page Fault’ 발생!
- PTE를 돌면서, VA와 연관된 물리 주소를 검색한다
- Page Fault
- 디스크에 존재하는 데이터를 찾아 메모리의 빈 공간에 올린다
- 메모리에 빈 공간이 없는 경우, 페이지 교체 알고리즘에 따라
기존 페이지 중 하나를 골라 swap 영역(disk)으로 내리고
필요한 데이터를 메인 메모리로 올린다
(이 때, 기존 영역은 0으로 초기화 -> 각 프로세스 간의 보안을 위함)
- 디스크에 존재하는 데이터를 찾아 메모리의 빈 공간에 올린다
이러한 과정이 끝난 후,
TLB와 PTE 값이 업데이트 된다
(물론 너무 큰 값인 경우 TLB가 업데이트 되지 않을 수 있음) - TLB 체크
(참고로 D는 offset을 뜻하며,
이는 페이지 주소(프레임) 에서 얼마만큼 떨어진 데이터임을
나타낸다)
-
- TLB에 대한 TMI
- TLB의 구성요소는
‘가상 주소’ : 가상 페이지 번호
‘물리 주소’ : 물리 프레임 번호
‘유효 비트’ : 물리 메모리 적재 여부
로 구성되어 있음
TLB는 ‘너무 큰 정보’는 저장할 수 없는데,
이는 TLB가 처리하려는 양보다, 더 많은 주소 매핑을 요구하는
데이터인 경우를 말한다
- TLB에 대한 TMI
-
- Page Table에 대한 TMI
- Page Table은 ‘가상 메모리 주소’와 ‘실제 메모리 주소’ 간의 매핑 정보를 담는 테이블이다
Page Table Entry는 위의 테이블의 각각의 항목을 의미한다
PTE의 구성요소는
‘프레임 번호’ : 가상 페이지 번호와 매핑되는 물리 프레임의 번호
‘유효 비트’ : 물리 메모리 적재 여부
‘보호 비트’ : 페이지에 대한 접근 권한과 보호 수준을 나타냄
‘캐시 비트’ : 캐시 정책을 설정하는 비트
‘페이지 타입 비트’ : 페이지의 종류에 대한 정보 (코드, 데이터, 스택 인지의 여부)를 나타내는 비트
‘Dirty, Accessed’ 비트 : 수정 여부, 접근 여부를 나타내는 비트
- Page Table에 대한 TMI
이러한 기법에서 하드웨어는 어떠한 역할을 할까?
또한 OS가 하는 부분은 무엇일까?
하드웨어는 ‘MMU’를 이용하여
실제 물리 주소값을 변환하는 역할을 하며
OS는 PT의 값을 세팅해준다
(커널 영역에서 이루어지며, ‘매핑’을 해준다)
‘물리 주소’는 실제로 언제 할당될까?
(실제로 메인 메모리에 올라오게 되는 시점에 대하여)
=> ‘실제로 사용’될 때
다소 말장난 같지만,
이는 메인 메모리를 절약하여 성능을 향상하기 위한 것으로
‘실제 사용되는 시점’을 관측하여
그 전까지 해당 공간을 최대한 이용하는 방식이다
(이를 lazy loading이라 한다)
(tmi : demand zero memory는 페이지를 처음 할당할 때, ‘초기화’를 해주지 않음)
그렇다면 사용하는 시점을 ‘어떻게 알 수 있을까?’
바로 ‘Page Fault’이다
접근이 필요한 ‘가상 주소’가 메인 메모리에 올라오지 않은 상황이며
이는 ‘실제로 해당 주소가 사용’되는 상황이다
이 때, 프로그램이 잠시 멈추고 (Page Fault)
새로운 page table entry가 업데이트 되며,
메인 메모리에 데이터가 올라온 후, 다시 제어권이 프로그램으로 돌아간다
그렇기에,
‘Page Fault’가 발생한 경우
최대한 빨리 제어권을 돌려줘야 하며
이에 따라 Page Fault는 반드시 ‘최적화’되어야 한다
=> MMU가 존재하는 이유!
(소프트웨어에서 처리하기엔 너무 빈번하게 VA <-> PA 가 일어남)
2가지 메모리 타입
OS는 2가지 타입으로 메모리를 관리하는데
이는 각각
File-backed 와 Anonymous 이다
-
- File-backed Memory
- 파일 시스템에 연결된 파일을 메모리에 ‘매핑’하여 사용하는 메모리 유형
‘파일’을 읽거나 쓰는 데 사용되며, 해당 메모리 변경 시 실제 파일에 영향을 끼침
(다만, 이 경우 fsync 같은 시스템 콜이 호출되어야 disk에 영향을 준다)
(File-backed Page 라는 것은 ‘메모리’의 페이지 중 파일과 연관된 페이지를 뜻함)
(프로세스의 ‘Code’ 부분은 파일에 기반되기에 이 영역에 속한다)
- File-backed Memory
-
- Anonymous Memory
- 파일에 대한 매핑이 없이 할당된 메모리 유형
프로세스의 ‘스택’, ‘힙’, ‘데이터’ 등 ‘동적’으로 할당되는 영역에 사용된다
- Anonymous Memory
이 두 메모리에 대한 차이점은 ‘Page Fault’ 발생 시 더 부각된다
- File backed 의 경우,
OS는 ‘파일 시스템’을 통하여 페이지의 내용을 ‘disk’에서 찾아
메모리에 로드한다 - Anonymous의 경우,
OS는 ‘스왑 영역’ 이라는 ‘disk’의 일부 공간을 통해
메모리에서 스왑 영역으로 내리거나,
스왑 영역에서 메모리로 데이터를 올린다
Page Fault와 DMA
Page Fault가 발생하여
‘disk’에서 필요한 데이터를 ‘메인 메모리’로 옮길 필요가 있을때
DMA(Direct Memory Access)가 이용될 수 있다
이를 통해 CPU는 ‘페이지’를 요청하고
‘DMA’를 통해 CPU를 다시 거치지 않고
메인 메모리로 데이터를 옮긴다
CPU는 메모리를 다 옮겼다는 것을
컨트롤러가 보낸 ‘인터럽트’를 통해 확인할 수 있다
TMI : https://hnjog.github.io/%ED%81%AC%EB%9E%98%ED%94%84%ED%86%A4%20%EC%A0%95%EA%B8%80/cs/dma/
댓글남기기