가상 메모리란? 프로세스 전체가 메모리에 올라오지 않고 실행이 가능하게 하는 기법이다. 이 기법은 물리 메모리보다 더 큰 양을 가질 수 있다.
프로그래머의 관점의 논리 메모리를 물리 메모리로 부터 분리하면 메인 메모리는 균일한 크기의 저장공간인 엄청 큰 배열로 추상화 된다.
장점 :
단점 :
9장까지에서는 코드는 반드시 물리 메모리에 존재하고, 이에 가장 쉬운 방법이 전체 프로세스가 메모리에 올라가는 것이었다.
물론 dynamic loading이 어느정도 완화시켜 주지만 이 방법은 프로그래머에게 주의와 추가작업을 요구한다.
가상 메모리의 구현을 가능하게 하는 경우들
프로그램을 일부분만 메모리에 올려놓게 된다면 다음과 같은 장점이 있다.
논리적 메모리의 구조는 위 그림과 같다. 힙, 스택 사이의 공간을 sparse 주소 공간이라고 하는데, 이 공백은 힙, 스택이 확장되거나 동적으로 라이브러리를 링크할때 사용된다.
그 외에도 가상 메모리는 둘 이상의 프로세스가 파일, 메모리를 공유할 수 있게 한다. (페이지 공유)
보조 저장장치(백업) -> 메모리로 실행프로그램 적재하기
- 전체 프로그램 물리 메모리에 적재
-> 아까 말했던 모든 옵션 등이 모두 물리 메모리에 적재됨.
- 요구페이징 기법 - 필요한 페이지만 적재
-> 프로그램 실행 중 필요할 때에만 페이지에 적대됨.
그림 10.4) 만약 B, D같이 메모리에 없는 페이지를 접근한다면? page-fault 트랩이 발생해 처리된다.
page-fault 발견-해결 과정
프로세스에 대한 내부 테이블을 검사해 그 메모리 비트가 유효/무효인지 알아낸다.(페이징 하드웨어가 페이지 테이블로 주소변환 중 발견, 트랩 걸기)
무표한 페이지라면 그 프로세스는 중단된다. 실제로는 있는데 메모리에 없다면(무효비트) 보조 저장장치로 가져와야한다.
빈 공간, free frame을 찾는다.
보조 저장장치에 이 프레임에 이 페이지를 달라고 요청한다
보조 읽기가 끝나면 페이지 테이블을 갱신하고 프로세스 내부 테이블을 고친다.
트랩에 의해 중단되었던 명령어를 다시 수행한다.
극단적으로 모든 페이지가 하나도 없어도 수행할 수 있다. pure demand paging 은 어떤 페이지가 필요해지기 전까지 그 페이지를 메모리로 적재하지 않는다.
요구 페이징은 특히 locality of reference 덕분에 만족할만한 성능을 보인다. (한 명령어에서 여러 페이지를 참조하는 경우가 잘 없다.)
요구 페이징을 위한 필수적인 요구 사항은 페이지 부재 오류 처리 후에 명령어 처리를 다시 시작할 수 있어야 한다.
C=A+B
1. 명령어(ADD) 인출
2. A인출
3. B인출4. A+B
5. C에 저장
이 외에도 블록들이 겹치거나, 페이지 경계에서 두 페이지에 걸치는 블록 등에서 문제가 발생한다.
해결책 :
1. 마이크로코드 : 미리 페이지 폴트 가능성이 있다면 트랩 발생시켜 이동하기
2. 임시 레지스터 : 이동때문에 이전 내용의 값을 보존했다가 복구
페이지 폴트가 일어날 때 운영체제는 요청된 페이지를 보조저장장치 -> 메인 메모리로 가져온다. 이 작업을 수용하기 위해 가용프레임 풀인 가용 프레임 리스트를 유지하는데, 프로젝트의 스택, 힙이 확장될때도 가용 프레임이 할당된다. 운영체제는 이때 zero-fill-on-demand기법으로 할당한다. 이 방법은 할당되기 전 프레임을 모두 0으로 채운다.(보안을 위함.)
가용 프레임이 요구되면 가용 프레임 리스트 크기는 하나씩 줄어든다. 만약 리스트 크기가 0이 된다면? (10.4절에서 이어짐..)
아휴..
...
끝이안난다..
그렇다면 페이지 폴트 시간은 얼마나 걸리는가? 다음을 고려해라
요약하자면
1. 인터럽트 처리
2. 페이지 읽기(백업 장치의 종류별로 다르다)
3. 프로세스 재시작
즉, 실제 접근 시간은 페이지 폴트율(page fault rate)에 비례한다. 따라서 페이지 폴트율을 낮게 유지하는 것이 상당히 중요하다 !!
요구 페이징의 또 다른 특성인 스압 공간의 관리를 알아보자.
칸이 부족해서 생략하기로 했다..
fork()를 통해 프로세스 생성 시 10.2에서 나온 첫 요구 페이징도 생략할 수 있다. (이게 뭐게요?)
그렇다면 새롭게 할당할 페이지 수도 최소화시킬수 있겠지?
잠깐!! 근데 fork()가 어떻게 작동하더라..
copy-on-write
자식이 시작하면 당분간 부모 페이지를 같이 쓴다. 부모/자식이 write를 시도할 떄 그 페이지의 copy를 만든다
그림10.7, 8) 물리 메모리 상태
이렇게 하면 수정하는 페이지에 대해서만 자식용copy가 생기고, 페이지 수를 아낄 수 있다.
시스템 메모리는 프로그램 페이지 저장만 하는 것이 아니다. I/O버퍼, 프로세스등등..다중 프레임 정도를 올리다 보면 메모리 과할당(over-allocating)이 발생한다.
또, 여러 프로세스가 프레임 풀 보다 더 큰 수의 프레임을 요구할 수도 있다.
그림에서 보이는 ? 물음표. 운영체제는 트랩이 발생해 페이지 위치를 백업에서 찾았지만 가용프레임이 더이상 없음을 발견했다.
이때에 해결방법은?
그렇다면... 페이지 스와핑 + 페이징 교체 하기
현재에서 안쓰고 있는 프레임은 비워버리자!
위 과정에서 디스크는 두번 접근된다.
1. 비우는 프레임을 백업에 저장
2. 가져올 프레임을 읽음
이때 1번은 modify bit(또는dirty bit(를 하드웨어에 적어놓아 감소시킬 수 있다. -> 변경 되지 않았으면 디스크에 저장안함. 그럼 2번할거 1번만 하니까 페이지 폴트 시간이 줄어든다.
요구 페이징의 기본은 페이지 교체이다. 따라서 요구 페이징 시스템은 두가지 중요한 문제를 해결해야 하는데, 바로 프레임 할당 알고리즘과 페이지 교체 알고리즘이다.
가장 간단한 페이지 교체 알고리즘.
큐로 페이지를 관리한다.
여기서 퀴즈!! 다음 참조열에서 프레임이 3개일때 페이지 폴트는 몇 번 발생할까?
.
.
.
.
.
.
.
정답 :
성능이 더 안좋아 질 수 도 있는 이유 :
프레임이 4개일때 -> 10번의 페이지 폴트
프레임이 3개일때 -> 9번의 폴트
으잉?? 분명히 프레임이 늘어나면 페이지 폴트가 일어난다고 했는데!! 이 현상을 Belady의 모순이라고 한다.
앞으로 가장 오랫동안 사용되지 않을 페이지를 찾아 교체하라
장점 : 프레임 수가 고정된 경우 가장 낮은 페이지 폴트율을 보장한다.
단점 : 구현이 어렵다. (7이 18회 후 사용될걸 내가 어떻게 알아?) -> 실제 사용보단 비교 연구 목적을 위해 사용한다.
least-recently-user 알고리즘
.3 방법을 근사해보자.. 최근의 과거를 가까운 미래의 근사치로 본다면(이 말이제일 모순이지..) 가장 오랜 기간동안 사용되지 않은 페이지를 교체할 수 있다.
페이지마다 마지막 사용 시간을 유지한다. 페이지 교체시에는 가장 오랫동안 사용되지 않은 페이지를 선택한다.
장점 : 물론, 예측이 정확하진 않지만 큐보다는 낫다.
덤 : 하드웨어의 지원이 필요하다(계수기나 스택)
+) 스택의 경우 : 최근의 사용된 페이지 번호가 top에 들어온다.
.4에서 하드웨어 지원이 안될때 쓰는 방법.
참조비트 라는걸 만들어서 일정시간동안 참조되는 페이지는 하드웨어가 1로 세팅한다. 사용순서는 몰라도 한동안 어떤 페이지가 사용되고, 사용되지 않았는지 알 수 있다.
자세하게는
사실.. 이 방법은 둘다 잘 안쓰인다. 구현하는데 시간이 많이 드는데 딱히 최적도 아님.. 왜만들었지
어?이해못했다
어 ? 어어 ??