메모리는 각각 주소가 할당된 일련의 바이트들로 구성된다. CPU는 PC가 지시하는 대로 메모리로 다음 수행할 명령어를 가져온다.
프로그램에 따라 3가지 경우 중 하나가 적용됨.
실행되지 않은 프로세스를 뺐다가 그 자리에 실행되는 프로세스를 넣는 방식, 제한된 메모리를 사용하기 위함이다. 제한된 메모리에서 많은 응용 프로그램을 실행시키기위해 필요하다.
주된 swap 시간은 프로세스를 옮기는 시간
프로세스를 swap in할 때 같은 주소로 가져와야 하는가 ?
Load time binding만 지원한다면 같은 주소 그렇지 않다면 어디든 상관없다.
main memory에 OS가 있고 다른 공간은 swapping에 사용
100MB process, 50MB/sec transfer rate일 때
swapping time에 비해 context switch time은 작은 시간이다. 그렇기에 swapping time을 줄이는게 관건 !
게임팩을 살 때 최소 권장사항이 이런 swapping이 많이 발생하지 않고 게임이 실행될 수 있게 하는 환경을 말한다. (권장사항을 만족하지 않아도 실행되지만...swapping이 미친듯이 일어날 것이다 !)
또한 작업 관리자에서 메모리 사용량이 넘치는 프로그램을 종료하면 swapping time을 대폭 줄이기에 빨라지는 것도 이제 이해할 수 있을 것이다.
Pending I/O : bus 구조에서 발생
iOS는 background에서 오랫동안 사용하지 않은 프로세스나 메모리를 많이 차지하는 프로세스를 알아서 종료해주고 Android는 사용자가 언제든 실행할 수 있도록 state만 저장해놓는다. state를 저장해놓았기에 이전 상태에서 재시작이 가능하다.
왜 mobile system에서는 swapping을 지원하지 않을까 ?
모바일 장치들은 영구 저장장치로 공간(물리적)을 많이 차지하는 하드디스크 보다 플래시 메모리를 사용하기 때문이다.
어떻게 할당할 것인가? 연속적인 공간을 핟당해준다. (contiguous allocation)
OS는 0부터 시작해서 연속적으로 공간을 할당받고 나머지는 사용자 공간을 위해서 할당된다. 이를 다시 표현하자면, OS는 low memory(0부터니까)에 사용자 프로세스는 high memory에 할당된다.
단점은 많은 fragmentation이 발생 !!
Relocation register
메모리 사이의 빈 공간들을 memory hole이라고 한다. (말그대로 비어있으니 구멍난...)
→연속적으로 할당된 공간들이 적재되고 제거되다 보면 구멍이 생김
프로그램이 hole에 들어갈 때를 생각해보자. hole이 1개라면 크기만 비교해서 넣을 수 있으면 넣으면 된다. 하지만 2개 이상이라면 ?...
hole이 각각 100, 80으로 2개가 존재하고 넣을 프로그램이 30이면 ?
정답은 OS 마음이다.
OS는 어떤 정책에 따라 이를 수행한다. hole도 링크드 리스트로 구현되어 있다는 것을 생각하며 정책을 바라보자. 넣을 수 있는 hole은 프로그램 크기보다 당연히 커야한다.
조각이 많이난 상태라서 contiguous allocation이 불가능한 상태
분명히 공간들이 있는데, 이 공간들은 각각 너무 작아
Windows에서 "디스크 조각 모음"이 바로 이런 것을 수행하는 것이다. 작은 공간들을 모아서 연속적인 공간으로 만드는 작업으로 compaction(압축)이라고 한다. compaction을 통해 external fragmentation(외부 단편화)을 해결할 수 있지만 compaction이 항상 가능한 것은 아니다. 또한 가능한다고 해도 비용이 매우 많이 든다.
Compaction은 재할당이 동적으로 수행되고 실행 시간에 수행되어야 가능하다.
외부 단편화를 해결할 수 있는 다른 방법은 한 프로세스의 논리 주소 공간을 연속적인 공간이 아닌 여러 개의 비연속적인 공간에 할당하는 방법이다. 이에 해당하는 방법 2가지를 살펴보자.
논리 주소 공간은 segment들(프로그램이 갖고 있는 특성의 조각들)의 집합으로 이루어진다.
사용자 공간에서 여러 개의 segment로 나누고 각 segment를 메모리 공간에 재할당하는 방식이다.
MMU의 역할이 중요해진다. → segment table
Segment table을 만들어 이를 통해 할당이 수행된다. 그렇기에 이 table이 어디에 있는지 시작 주소와 끝 주소를 가리키는 register가 필요하다. 이 register가 바로 Segment-table base register(시작 주소)와 Segment-table length register(끝 주소)이다.
메모리에 두 번 접근해야되기 때문에 기존의 MMU보다 느리고 스압 아웃되는 다양한 크기의 segment를 예비 저장장치에 저장해야 하는 문제가 생긴다. (이런 문제는 paging에서 해결)
예비 저장장치 역시 main memory와 같은 단편화 문제가 있으며 접근 속도가 느리기에 압축 하는 것이 불가능하다.
Segmentation은 (compaction을 통해) external fragment를 어느정도 해결할 수는 있고 internal fragment를 완전히 해결한다. 또한 논리적인 내용 단위로 나뉘어져 보호와 공유 측면에서 좋다.
모든 프로그램이 같은 크기가 아니기에 불가능하다. 이를 해결하는 방법이 paging이다.
하나의 프로그램을 같은 크기의 chunk로 나눈다. 메모리 또한 같은 크기의 chunck로 나눈다.
그러면 메모리의 모든 slot을 활용 가능하기에 external fragment가 완벽히 해결된다.
물리적 주소를 동일하게 나눈 하나의 부분을 frame이라고 한다. 총 몇 개의 frame이 존재하고 몇 개의 frame을 사용하고 있는지만 알면 된다. (좀 더 단순해짐)
frame 크기는 보통 4KB이다. (이럴 때 프로그램 최소 크기는 4KB)
논리적 주소를 동일하게 나눈 하나의 부분을 page라고 한다. 논리적 주소를 물리적 주소 mapping(상응)하기 위해 page table을 만든다. backing store도 page단위로 나누어진다.
하지만 항상 frame의 정수 배로 할당되기 때문에 internal fragment는 아직 남게된다.
결국 세그먼트로 나누어 보호와 공유 등의 권한을 지정할 수 있고 이를 다시 paging하는 방식으로 내부, 외부 단편화를 해결할 수 있다. (단, 세그먼트 테이블, 페이지 테이블 모두 존재하므로 속도 면에서 조금 떨어질 수 있다.)
논리적 주소의 크기와 page 크기가 다음과 같을 때
m = 4, n =2라고 가정해보자.
page size = 2048 bytes, process size = 72766 bytes 라고 가정하자.
그러면 72766/2048 = 35.530으로 35 pages + 1086 bytes가 나온다. 1086 bytes를 위해 page를 하나 더 추가해야 한다는 점에 주목하자. 그래서 총 page의 개수는 36이고 internal fragmentation이 발생한다는 것을 알 수 있다.
internal fragmentation = 2048-1086 = 962 bytes
그러면 page를 작게 해주면 되지 않을까 ? → 최선이 아님
page table은 메모리에 있는데, page 크기가 줄어들 수록 이 page table은 늘어나 overhead가 발생할 수 있다. 오히려 page table은 줄여 검색 속도를 빠르게, overhead를 줄이도록 하는게 좋을 수도 있다.
위에서 설명했던대로 PTBR, PTLR이 필요하다고 했지만... 문제 (메모리에 2번 접근해야 한다 : page table에 접근하기 위해 메모리 접근[1], 실제 주소 계산해서 메모리에 접근[2] )
TLB를 통해 이를 어느정도 해소하면서 구현이 가능하다. TLB는 일종의 캐시로, Translation Look-aside Buffer 의 약어이다.
TLB 내의 항목은 <key(page number), value(frame number)>의 쌍으로 구성된다. TLB에 요청이 들어오면 동시에 여러 개의 key를 비교하여 해당되는 value를 알려준다. page table의 일부를 메모리에 저장하지 않고 접근이 빠른 cache(CPU 근처에 있음)인 TLB에 저장한다. 그렇기에 상당히 제한적이다. 자주 쓰이는, 자주 쓰일 것 같은 page number를 TLB에 저장한다.
그런 page number를 잘 판단하는게 중요하다. 만약 TLB가 가득 차 있다면 교체될 항목도 잘 선택해야 한다. 보통 중요 커널 코드 항목들이 TLB에 고정된다.
page가 어떤 프로세스에 할당 되어있는지 모르기에 정보를 비우고(TLB flush) 다시 채워넣는 작업을 수행해야 한다. 이런 작업은 context switching이 자주 발생하는 문제를 야기한다.
이를 위해 프로세스를 식별할 수 있는 ASIDs(Address-Space Identifiers)가 TLB에 저장된다. 이 덕분에 한 TLB 안에 여러 프로세스들의 정보를 동시에 함께 보관할 수 있어 flush 될 필요가 없어진다.
TLB hit 비율이 늘어날 수록(cache에서 memory로 이동하는 횟수) 접근이 빠르다. TLB hit 비율은 접근하려는 메모리의 page 번호가 TLB에서 발견되는 비율이다.
TLB 크기를 키우는 것은 한계가 있기에 최근에는 multi-layer(다중 계층)을 구현하기도 한다. 앱실론: TLB에 접근하는데 걸리는 시간
각 page에는 protection bits를 통해 메모리 보호가 이뤄진다. 이 bits는 보통 page table에 속하며 page가 read-only나 read-write와 같은 메타 data를 저장한다.
page table에 valid-invalid bit가 있는데 이는 해당 page가 프로세스의 유효한 page인지 무효한 page인지(잘못된 주소 참조일 때)에 대한 정보를 담고 있다.
PTLR(Page Table Length Register)은 page table의 크기를 나타내기 위해 몇몇 시스템에서 사용된다. PTLR은 프로세스가 제시한 주소가 유효한지 확인하기 위해 모든 논리 주소 값이 PTLR 값과 비교된다.
재진입 가능 코드(reentrant or pure code)라면 프로세스들은 이 코드 부분을 서로 공유할 수 있다. 물론 동기화 문제가 발생하지 않도록 각각의 프로세서들은 레지스터들의 복사 값과 data를 따로 data 저장소에 저장한다.
코드는 page table에 mapping, data는 서로 다른 page frame에 mapping하는 방식이다.
pages를 공유하면 필요한 공간이 줄어들어 메모리 효율성이 높아진다.
각 프로세스의 shared data에 같은 page table을 가지면 공유가능하며 각 프로세스마다 독립적인 data도 존재할 수 있다.
page table은 앞으로 컴퓨터가 더 큰 공간을 가질수록 상당히 커질 것이다. 이럴 경우 모든 page table을 main memory에서 연속적으로 할당하는 것은 무리가 있다. 그렇기에 page table을 여러 개로 나누는 방법이 존재한다.
page table 자체가 paging 되는 것을 말한다.
outer page table이 있고 이곳에 있는 값을 통해 메모리의 다른 위치로 이동하는데, 그곳은 page table이 있는 곳이다.
pros : 연속적으로 큰 공간이 아니라 메모리 관리가 용이하다.
cons : page table에 두 번 접근해야 한다.
총 접근은 3번(outer page, page of page table, physical memory)
outer page는 page table에 대한 valid-invalid, page of page table은 page에 대한 valid-invalid가 존재
64bit machine이면 page offset 12 나머지 52... 총 6번 접근해야 한다. 64 bit system은 관리가 어렵다.
접근 횟수를 줄이기위해 4KB가 아닌 8KB로 2^10이 아닌 2^13로 시도할 수 있지만 너무 많은 메모리 접근을 요하기에 이 방법을 적용하기는 힘들다.
주소 공간이 32비트보다 커지면 논리 주소를 hash로 사용하는 hashed page table을 주로 사용한다.
Hashed Page Table을 만들고 hash function으로 접근횟수를 많이 줄일 수 있다.
사실 프로세스 하나당 page table이 존재하기에 이런 문제들이 발생했던 것 !!!
그럼 이 frame은 어떤 프로세스의 몇 번 page인지 역으로 접근하면 안되나 ?
기존 방법들의 가장 큰 단점은 page table 크기이다. 그 크기로 인해 엄청난 메모리 공간을 점유할 수 있게 된다. 이를 해결하는 방법이 Inverted Page Table(역 페이지 테이블)이다.
역 페이지 테이블에서는 메모리 프레임마다 한 항목씩 할당한다. (항목 : frame에 할당된 page 주소, page를 소유하고 있는 process id)
이렇게 되면 시스템에서는 단 하나의 page table만이 존재하게 되고, table 내 각 항목은 메모리 한 프레임씩을 가리키게 된다.
pros
cons