[운영체제] 3. 메모리 관리(3/5)

3.3.3 페이징 속도 향상 

페이징 복습

MMU라는게 씨피유 안에 들어있는데, cpu에서 가상 주소를 내보내주면 mmu가 물리 주소로 바꾸어서 접근하게 됨. mmu안에 CLB존재. 그 밑엔 캐시 존재

페이지 테이블 엔트리 복습

페이지 테이블 엔트리만 보고는 안 쓰는걸 알 수 없음 -> 레퍼런스와 모디파이드 비트가 그래서 존재하는 것.

그리고 caching disabled field는 이게 새팅이 되면 이 페이지 프레임 내에 저장된 데이터를 cpu내 캐시에 캐시하지 말라는 것.

 

 

3.3.4 대용량 메모리를 위한 페이지 테이블

 

페이징 문제 2  : 페이지 테이블이 굉장히 크다 (가상 주소공간이 커지면)

해결 1 : 멀티 레벨 페이징 : 모든 페이지 테이블을 항상 메모리에 유지할 필요가 없다!

 

페이지 테이블 크기를 4mb라고 한다면, 옵셋이 12bit 를 쓰고 있음 -> 2^12.

나머지 20비트는 페이지 번호를 가리키게 되고, 2^20만큼의 페이지 테이블 엔트리가 필요함

엔트리 하나의 사이즈가 4B라고 할 수도 있고, 이런저런 비트를 추가해야 하니까 8byte로 만들 수 있어야 한다.

32bit cpu의 경우, 각 프로세스 마다 8mb의 페이지 테이블이 필요하다.

32bit의 가상 주소 공간은 4gb(2^32), 이 공간을 4KB의 페이지 크기로 분할하는 경우, 2^32/4KB = 2^20개의 페이지 생성.

-> 페이지 테이블 엔트리는 보통 4바이트, 페이지 테이블의 크기는 8바이트라고 하면 8MB가 됨. -> 프로세스가 많아지면, too big

 

안 쓰는 공간은 페이지 테이블을 만들지 말자? 불가능.

1. cpu내부에는 Pagetable address register가 있는데, 페이지 테이블이 존재하는 메모리 주소를 기록한다.

그리고 가상주의 앞부분, offset을 제외한 부분이 페이지 번호. 즉 배열의 index역할을 한다.

-> 중간에 mapping을 하지 않게 되면, 정상적으로 위치를 찾아갈 수 없게 된다.

2. process table을 보면 맨 윗쪽은 운영체제가 mapping되어야 하기에 비어있으면 안된다.

 

따라서 이걸 2단계로 나누면, 페이지 테이블 엔트리에서 옵셋을 제외한 나머지 20비트를 반으로 쪼갠다.

따라서 탑레벨에 1024개의 엔트리가 필요, 각각 하나의 해시테이블이 2단계를 가리키게 되는데 얘들도 1024개가 맵핑되어있다.

페이지 테이블은 오히려 늘어나게 되었다. 4gb다 쓰는 프로세스의 경우엔 손해지만,

거의 모든 프로세스는 주소공간을 다 사용하지 않기 때문에 탑레벨 페이지(위의 예시로는 8MB)만으로 표현할 수 있게 됨.

 

snd level page table은 전부 존재할 필요가 없다 : 실제 사용하는 부분만 top-level에 연결되어 있기 때문.

-> 페이지 테이블 사이즈를 줄일 수 있음.

 

페이지 테이블에 접근 1, 실제 주소 변환하여 물리 메모리 접근 2 이렇게 2회에서 3회가 됨

-> 다단계로 갈수록 hit를 잘 시켜야 한다. 64비트면 virtual address space가 2^64, 2단계는 부족해서 4단계정도 씀.

 

 

inverted page table : 역페이지. 반대로 간다. 실제 설치되어있는 메모리 위치를 페이지 단위로 나눔. 물리 메모리를 기준으로 페이지 테이블을 구성한다.

 

피지컬 메모리가 있으면, 이걸 4k단위로 쪼갰다고 하자. 이 페이지 테이블에는 프로세스의 페이지 번호를 적어야 한다 -> 이 페이지 번호를 가지고 물리 메모리 주소를 바꿀꺼니까.

또한 각 프로세스마다 address space는 0~2의 32제곱까지. 그럼 각 프로세스마다 페이지 번호가 중복되지만, 고유의 process id가 있기 때문에 이 아이디와 페이지 번호를 inverted page table entry에 적어두고 구성을 한다.

실제 프로세스가 실행되는 시점에 가상 주소가 page번호와 offset으로 나오고, 이걸 물리 페이지 프로그램으로 바꾸어야 한다.

 

logical address와 이 로지컬 어드레스를 만들어낸 pid. 그리고 페이지 번호를 가지고 페이지 테이블을 찾는다.

-> 특정 페이지 테이블 엔트리가 찾아짐 -> 이 위치 인덱스 번호가 피지컬 프레임 번호가 됨

-> 뒤에 offset을 더하면 이제 실제 피지컬 메모리의 주소를 알 수 있음.

따라서 inverted page table의 entry에 기록되는게 pid와 페이지 번호다. 기억하고

 

그런데, 가상 주소 공간이 물리 주소 공간에 비해 상당히 클 경우, 변환이 복잡해진다.

메모리 참조시 마다 역 페이지 테이블 전체를 참조해야 함 ->  TLB사용. 

 

전통적인 페이지 테이블과 인버티드 페이지 테이블을 비교 해보자.

좌측, 64bit cpu라면 page size가 4KB일 때, 2^52만큼의 페이지 테이블 엔트리가 필요해진다 -> 소요가 많아짐 -> 멀티 레벨 페이지로 해결.

인버티드 페이지 테이블의 경우 물리 메모리를 기준으로 페이지 테이블을 구성하니까 페이지 크기가 4KB, 2^12. 물리 메모리가 1GB라면

2^18개의 해시테이블을 구성한다. ( 2^12 * 2^18 = 1GB(2^30))

이 해시 인덱스를 만들 때 해시 함수를 쓰는데, 함수의 input으로 이전 슬라이드의 pid하고 p를 주면 인덱스가 만들어지고, 기록된 페이지 프레임 번호가 해당 pid와 p에 맵핑 된 물리메모리 주소가 된다.

 

해시 테이블은 hash collision(해시 충돌, 서로 다른 입력값이 동일한 해시 값을 가질 때)

-> 체이닝 기법 사용 : 동일한 해시 값을 가지는 모든 키를 연결 리스트로 관리

-> 운영체제는 index를 쫓아가서 거기에 기록된 물리 페이지 번호를 찾아 채워주고 다시 닫음.

 

사용자 영역이 맵핑되고, heap, stack 영역에 존재하는게 있다. user program 실행하다가 파일 읽어주세요 하면 system call이 일어나서

운영체제 코드가 실행되고, -> 다시 user program으로 돌아간다.

 

위 이미지를 보면, 진한 부분이 physical memory에 비치된 코드 모양.프로세스a의 페이지 테이블은 가상 주소공간이 실제 피지컬 메모리에 mapping 되어 있는 것을 볼 수 있다.

페이지 테이블 상단을 보면 운영체제가 mapping되어있는데, 과거 리눅스는 상위 1기가였음. 일반 프로세스가 나머지 3기가.

실제 피지컬 메모리는 그렇지 않을 수 있다. a와b는 공유하고 있고, 각자 필요한 부분만 맵핑되어 물리 메모리로 로드되어 실행된다.

 

A와 B둘 다 C로 작성된 프로그램이라 하면, 라이브러리 코드가 메모리에 로드되어 있을 것이고, 그 부분을 프로세스가 가리키고 있다.

운영체제 코드가 끝나면 다시 프로세스로 돌아온다. 그럼 운영체제 코드는? 스스로 필요하면 실행시켜서 쓰고 돌아온다.

운영체제는 하나의 프로세스가 아니라, 자신의 일부라고 생각한다 <- 핵심.