[OS] 요구 페이징
1. 물리 메모리의 한계
- 64비트 CPU의 물리 메모리 최대량: 16EB
- 실체 컴퓨터에 설치되는 물리 메모리: 8~32GB
- 왜? 비용 때문에 일부밖에 설치 못한다.
1.1. 도출되는 질문들
- 물리 메모리의 크기 한계에서 비롯된 2가지 질문
- 설치된 물리 메모리보다 큰 프로세스를 실행시킬 수 있는가?
- 프로세스들을 합친 크기가 설치된 물리 메모리보다 클 때 이들을 실행시킬 수 있는가?
- 질문에 숨어있는 전제
- 프로세스 전체가 물리 메모리에 적재 되어야 실행이 가능한가?
- 당장 실행에 필요한 프로세스의 일부 메모리만 적재한 채 실행 시킬 수는 없는가? (Overlay)
- Locality의 개념
2. 가상 메모리
- 물리 메모리 한계 극복 해결책
- 물리 메모리를 디스크 공간으로 확장
- 물리 메모리에서 하드디스크로 연장
- 프로세스를 물리 메모리와 하드 디스크(보조기억장치)에 나누어 저장
- 프로세스나 사용자가 프로세스를 실행하기에 충분히 큰 메모리가 있다고 착각하게 만드는 메모리 관리 기술
2.1. 스와핑(swapping)
- 메모리 부족할 때, 실행에 불필요한 부분은 하드 디스크로 이동(스왑 아웃)
- 실행에 필요할 떄, 하드 디스크로부터 물리 메모리로 이동(스왑 인)
2.2. 개념
- 운영체제는 물리 메모리 영역을 하드 디스크까지 연장
- 프로세스를 물리 메모리와 하드 디스크에 나누어 저장
- 물리 메모리 한계 극복
- 프로세스의 실행 시 프로세스 전체가 물리 메모리에 적재되어 있을 필요 없음
- 운영체제는 물리 메모리의 빈 영역이 부족하게 되면,
- 물리 메모리 일부분을 하드 디스크로 옮겨 물리 메모리의 빈 영역 확보
- 이 방식으로 많은 프로세스를 메모리 적재 가능
- 다중프로그래밍 정도 높임 → CPU 활용율와 처리율 높임
- 물리 메모리를 확장하여 사용하는 디스크 영역: 스왑 영역
- 스왑-아웃 : 물리 메모리의 일부를 스왑 영역으로 옮기는 작업
- 스왑-인 : 스왑 영역에서 물리 메모리로 가지고 오는 작업
- 사용자는 컴퓨터 시스템에 무한대의 메모리가 있는 것으로 착각
- 큰 프로그램을 작성하는데 부담 없음.
- 여러 개의 프로그램을 실행시키는 데 부담 없음
- 가상 메모리는 운영체제마다 구현 방법이 다름
2.3. 메모리에 대한 각 시스템들의 관계
- 사용자, 프로세스
- 무한대에 가까운 큰 메모리를 사용하며, 0번지부터 연속되어 프로세스가 적재되는 것으로 생각
- 가상의 전체 메모리가 있다고 생각함: 가상메모리
- Process의 logical한 공간과 실제 physical memory와 분리 시키기 위한 개념
- 운영체제
- 프로세스를 물리 메모리와 하드디스크에 분산 저장, 프로세스의 일부만 물리 메모리에 적재
- 프로세스를 최대한 많이 올리려 하기 때문에, 프로세스를 최대한 잘게 쪼개려고 시도함
- 디스크
- 물리 메모리에 빈 공간이 부족하여 메모리 일부를 스왑 영역에 저장
- 없는 메모리가 있는 메모리 처럼 사용하게 해줌: 가상메모리
2.4. 연관되는 질문들
- 메모리가 커진 효과라는 건 알겠어요. 그럼 페이지 테이블은 어떻게 되나요?
- 가상 메모리에 대한 페이지 테이블
- 어쨋든 실제 메모리에 올라와야하는데…! 만약 실제 메모리에 없는 애를 찾으면 어찌되요?
- 페이지 폴트
- 애초에 메모리가 부족해서 가상메모리 쓰는거라면서요? 그러면 어떻게 가져와요?
- → 페이지 할당
- 그럼 누굴 뺄건간요?
- → 페이지 교체 알고리즘
- 가져다 두는 건 어찌하는데요?
- → 스왑영역
- 뭐 swap-in / swap-out 하겠죠. 그런데 이거도 비용이 비싼거 아니에요?
- → 스레싱 문제
3. 가상 메모리를 만드는 방법
3.1. Demand paging(요구 페이징)
- 페이징 기법을 토대로 프로세스의 일부 페이지들만 메모리에 할당하고, 페이지가 필요할 때 메모리를 할당받고 페이지를 적재시키기는 메모리 관리 기법
- 현재 실행에 필요한 일부 페이지만 메모리에 적재하고 나머지는 하드 디스크에 두고, 특정 페이지가 필요할 때 메모리에 적재하는 방식
- 요구 페이징 = 페이징 + Swapping
- 프로세스의 페이지가 있는 디스크 영역 = 스왑 영역 + 실행 파일
3.2. Swap area
- 스왑 영역
- 메모리가 부족할 때, 메모리를 비우고 페이지를 저장해두는 하드 디스크의 영역
- 리눅스 : 디스크 내 특별한 위치, 혹은 스왑 파티션에 구성
- Windows : C:/pagefile.sys 파일
3.3. 페이지 테이블 엔트리
- 페이지 번호
- 플래그 비트
- access
- 페이지가 메모리에 올라온 후 사용한 적이 있는지 여부
- modified (dirty bit)
- 해당 페이지가 수정되었는지 여부 (c.f., 캐시)
- 이 비트가 1이면, 해당 페이지가 프레임에 적재된 이후 수정되었음, 나중에 쫓겨날 때 스왑-아웃
- valid (presence)
- 해당 페이지가 물리 메모리에 있는지 여부
- 이 비트가 1이면, 해당 페이지가 프레임 번호의 메모리에 있음, 0이면 디스크의 블록 번호에 있음.
- rwx
- 읽기, 쓰기, 실행 비트 : 페이지에 대한 읽기 권한, 쓰기 권한, 실행 권한을 나타내는 비트
- access
- 프레임 번호
3.4. 가상 메모리와 페이지 테이블
4. Page fault
- 페이지 폴트
- 메모리 참조를 하려하는데 물리 메모리에 원하는 페이지가 없는 경우
- CPU가 액세스하려는 페이지가 물리 메모리에 없을 때, 페이지 폴트 발생
- 페이지 폴트가 일어나면 빈 프레임을 할당하고 스왑 영역이나 실행 파일로부터 페이지 적재
- 페이지 폴트(부재)
- 요구 페이징에서 가장 중요한 상황
- CPU가 발생시킨 가상 주소의 페이지가 메모리 프레임에 없는 상황
- MMU가 가상 주소를 물리 주소로 바뀌는 과정에서 발생
- 페이지 폴트 처리과정
- 프로세스가 페이지 n을 요청할 때, 페이지 테이블의 유효 비트가 0이면 페이지 폴트가 발생
- MMU는 PTE에 기재된 블록번호에 접근하여, 해당 데이터를 메모리의 빈 프레임으로 가져옴
- 비어 있는 공간이 없다? → 페이지 교체 알고리즘 동작
- 해당 프레임으로 접근하여 해당 데이터를 프로세스에 넘김
- 유효비트를 1로 업데이트하고, 프레임 번호도 업데이트.
4.1. 가용 프레임 리스트
- 비어있는 프레임은 OS가 리스트 형태로 관리함
- Zero fill-on-demend
- 메모리에 쓰레기 값 남지 않게 하는 것
- 보안을 위해 할당시에는 값들을 0으로 초기화
- 잘 쓰이지는 않음(메모리에 가용 영역이 있는 경우가 드묾)
4.2. 예제
- main() 함수와 전역 변수 n을 가진 C 프로그램
- 전역 변수 n의 가상 주소는 0x11111234, 페이지 번호는 0x11111 이라 가정
- n = 10 의 컴파일된 기계어 코드
mov eax, 10 ; eax 레지스터에 10 저장
mov [11111234], eax ; eax 값을 0x1111234 번지에 저장
- [111111234]에 접근할 때, page fault가 발생한다면?
5. 프로세스의 실행
- 운영체제는 첫 페이지만 물리 메모리에 적재, 실행 중 다음 페이지가 필요하면 그때 적재!
- 어떤 운영체제의 경우 첫 페이지도 물리 메모리에 적재하지 않고 실행시키고 첫 페이지가 필요할 때 그 때부터 적재시키는 방법 사용
5.1. 예제
- 100개 이상의 페이지로 구성된 C프로그램
- 프로세스 실행
- 프로세스 실행을 시작할 떄 첫 페이지를 메모리에 적재
- 페이지 폴트를 통해 실행파일로부터 페이지들 적재
- 메모리가 부족하면 스왑-아웃/스왑-인
- 스왑-아웃된 페이지 100의 스왑-인 후 n++ 실행
- 수정된 페이지를 스왑 영역에 쓰기
- 시간이 흐르고 운영체제가 빈 프레임을 만들기 위해 페이지 30을 희생 페이지로 선택하였다면, 다음과 같이 처리
- 페이지 테이블 항목에 M비트=1이라면, 운영체제는 페이지 30이 들어 있는 프레임을 스왑-영역에 다시 기록
- 하지만, M비트=0이라면(적재된 후 수정되지 않음) 스왑 영역에 저장할 필요가 없음. 그냥 페이지 30이 있었던 프레임에 다른 프로세스의 페이지를 적재하면 됨.
5.2. Page fault 해결
6. fork() 다시보기
- 프로세스는 부모 프로세스의 fork() 시스템 호출로 생성
- fork()를 사용하는 이유 중 하나는…
- “매번 프로세스를 처음부터 만드는게 너무 무거워요! 있는거 복사해서 하는게 간편해요!”
- 질문: 있는거 복사하는건 시간 오래걸리는 작업아닌가?
- 만약에 프로세스가 4GB를 정말 다쓴다면… → 이것의 fork()는 4GB를 복사해야한다. → 낭비낭비낭비!
- 많은 응용프로그램들이, fork() 후 생성된 자식프로세스가 exec()를 호출하여 곧 바로 다른 프로그램을 실행하도록 작성되는 경우가 많다…
- int pid= fork(); if(!pid) execlp(“/bin/ls”, “ls”, NULL);
- execlp()에 의해 자식 프로세스의 메모리가 모두 반환되고 실행파일 ls로부터 새로 페이지 적재
- 낭비가 심해도 너무 심하다!
6.1. COW, Copy-on-write
- 쓰기 시 복사
- 자식 프로세스를 위해 부모 프로세스의 페이지 테이블만 복사
- 자식 프로세스는 초기에 부모 프로세스의 메모리 프레임을 완전 공유
- 자식 프로세스의 페이지 테이블 항목에 ‘쓰기시 복사’ 표시
- 자식이나 부모 중 누군가 페이지를 수정할 때, 그때 새로운 프레임 할당 받아 내용을 수정하고 프레임 번호 업데이트!
- 즉, 실제론 페이지 테이블만 복사가 일어나는 것만으로 새로운 프로세스가 하나 생성!
6.1.1. 장점
- 프로세스 생성 시간 절약
- 부모 프로세스의 페이지 테이블만 복사하여 자식 프로세스를 만들기 때문에 프로세스 생성이 매우 빠름
- fork() 후 exec()으로 인한 괜한 복사 시간의 낭비 없음
- 메모리 절약
- 부모와 자식 프로세스가 둘 다 읽기만 하는 페이지는 새로운 프레임을 할당할 필요가 없으므로 메모리 절약
- 예) 프로세스의 코드 페이지. 코드와 같이 읽기 용 페이지 프레임은 자동 공유
7. 생각해 볼 문제
- Page 가 물리 메모리에 없다? → Page fault
- Page fault가 생기면? → 디스크에서 가져와야지.
- ㅇㅋ. 가져는 왔다. 이제 메모리 어디에 둘 것인가?
- 메모리가 비어있으면 → 문제 없다
- 메모리가 꽉 차있으면?
- 누군간 방빼야지! → 누구를? ⇒ 페이지 교체 문제
- 앞으로 페이지 폴트가 덜 나게는 못할까? ⇒ 프레임 할당 문제
- 요걸 잘 해결하면, 요구 페이징의 성능이 향상됩니다! 페이지 폴트를 줄여서!
이 포스팅은 작성자의 CC BY-NC 4.0 라이선스를 준수합니다.