일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 백준
- 비트 코인
- Mist
- X보다 작은 수
- if문 사용해보기
- 단계별로 풀어보기
- 자바스크립트
- 시험 성적
- 더하기 사이클
- 이더리움
- 블록 체인
- 1065
- 10871
- 가상 화폐
- 1%d
- for문 사용해보기
- 함수 사용하기
- 알고리즘 문제풀이
- Baekjoon
- 10817
- 세 수
- 별 찍기 - 11
- Dapp
- 2448
- 그대로 출력하기
- Remix
- 솔리디티
- 평균은 넘겠지
- 1110
- 1546
- Today
- Total
블링블링 범블링
[OS 17장] 메모리 관리 - 페이징 본문
- 페이징(1) -
메모리의 낭비 공간인 hole을 최소한으로 만들기 위해 앞 장에서 많은 방법을 사용하였다. 최초 적합과 최적 적합을 통해 메모리의 공간에 적재하는 방식에 변화를 주었는데 이를 통해서도 메모리 공간의 1/3 정도가 낭비가 되었다. 이렇기 때문에 다른 방식인 Compaction이라는 방식을 사용하였으나 프로세스나 hole을 메모리 공간에서 이동시키기 위해서는 메모리 계산의 부담이 발생하기 때문에 힘들었다. 그래서 사용한 방식이 바로 페이징이다.
페이징은 프로세스를 일정 크기인 페이지로 잘라서 메모리에 적재하는 방식이다. 프로세스는 항상 연속해서 들어가야 한다는 생각을 통해 메모리 공간 활용에 있어서 앞장에서는 연속 메모리 할당에 초점을 맞추었다. 이런 생각부터 뒤집어서 프로세스를 일정한 단위로 잘라서 사용하자는 방식이 페이징이다. hole과 프로세스를 모두 특정 페이지 단위로 잘라서 메모리 공간을 관리한다. 하지만 프로세스를 자르게 되면 실행이 될까? 에 대한 궁금증이 생긴다. 그러면 프로세스를 자르게 되더라도 실행을 할 수 있게 하는 방식은 무엇이 있을까?
앞에서 설명한 방식을 한 번 떠올려보자. 메모리 공간을 할당할 때 코드에서 명명한 메모리 주소 위치와 다르게 임의로 프로세스를 메모리에 적재하였다. 이렇게 할 수 있었던 이유는 바로 MMU의 재배치 레지스터의 값을 바꾸어 CPU를 속일 수 있었기 때문이다. 그러면 이왕 속이게 되는 거 재배치 레지스터를 많이 두어 각각의 페이지 단위의 프로세스를 활용할 수 있게 속이는 것은 어떻게 생각하는가? 프로세스를 나눈 페이지마다 재배치 레지스터를 만들어 놓으면 CPU는 마치 프로세스가 연속된 메모리 공간에서 동작하고 있다고 생각하게 될 것이다. 왜냐하면 재배치 레지스터에서 메모리 적재 공간의 값을 더해주어 CPU를 속이기 때문이다.
프로세스를 자르는 단위는 페이지이다. 이에 동일한 크기로 메모리를 자른 것을 프레임이라고 한다. 같은 크기로 페이지와 프레임으로 잘라져 있기 때문에 페이지를 프레임에 할당하면 딱 맞아 떨어진다. 이 때 페이지를 관리하는 MMU는 페이지 테이블이 된다. 페이지 테이블 안에 있는 개수는 프로세스를 몇 등분하는가에 따라 결정된다.
CPU가 내는 주소는 논리 주소(Logical address)라고 한다. CPU에서 보낸 논리 주소는 MMU인 페이지 테이블을 통해 물리 주소(Physical address)로 바뀌어서 메모리에서 찾게 된다. 논리 주소는 2진수로 표현된 m개의 비트이다. 하위 n비트는 오프셋 또는 변위를 나타내고 상위의 m-n비트는 페이지 번호를 나타낸다. n비트는 페이지를 어떤 크기로 나누는 정도에 따라 다르게 된다. 예를 들어 16바이트로 페이지를 나눈다고 생각하면 n은 4비트가 된다. 2진수로 표현된 값이기 때문이다. 페이지 테이블에서 페이지 번호를 가져와서 해당하는 프레임 번호를 가져오게 된다. 페이지 번호는 페이지 테이블의 인덱스 값으로 인식한다. 페이지 번호에 해당하는 페이지 테이블의 값과 n비트의 값을 가진 물리 주소로 바뀌게 된다.
예를 들어 50번지라는 논리 주소를 물리 주소로 바꾸고 싶다고 한다고 가정해보자. 이때 페이지의 크기는 16바이트이다. 50번지를 2진수로 바꾸게 되면 110010이 된다. 이때 페이지의 크기가 16바이트이므로 하위 4비트는 n비트가 되고 앞의 두 비트가 페이지 번호를 나타내는 인덱스 값이 된다. 그러면 페이지 테이블에서 11에 해당하는 3의 값을 가진 페이지 번호로 가게 되어 페이지 테이블 값을 읽는다. 만약 이 때의 페이지 테이블 값이 8이라고 하면 8과 n비트를 합쳐서 물리 주소를 가지게 된다. 그 값은 10000010 이라는 메모리 공간의 주소로 가게 되는 것이다. 10진수로 바꾸게 되면 130번지가 된다. 다시 해석하게 되면 테이블 값의 8은 128번지를 의미하게 되고 여기서 하위 n비트의 변위 값에 의해 130번지에서 이 프로세스의 페이지가 동작하게 되는 것이다.
- 페이징(2) -
다중 프로그래밍 환경으로 바뀌면서 다양한 프로그램들이 메인 메모리 공간에 올라가서 프로세스로 되어 동작하기 시작했다. 프로그램들은 필요할 때 메인 메모리로 적재되고 사용이 다 되면 다시 하드디스크로 돌아가게 된다. 이런 작동이 반복되면서 메모리 공간에는 hole이라고 하는 빈 공간이 만들어지게 된다. 이런 빈 공간이 흩어져 있게 되어 총 합으로는 충분한 공간의 빈 메모리 공간이 있을지라도 사용할 수 없는 공간이 되어버린다. 왜냐하면 프로세스는 연속된 공간에서 존재해야 동작했기 때문이다. 이러한 문제를 외부 단편화라고 했다. 그리고 외부 단편화를 해결하고자 노력을 많이 하였다. 연속 메모리 할당을 하는 방식에 차이를 두기도 하고 compaction 기법을 사용하기도 하였다. 하지만 여전히 메모리 공간이 낭비가 되고 그렇지 않으면 고비용이 들게 되었다. 그래서 나온 해결 방법이 바로 페이징이다.
페이징은 프로세스가 연속된 메모리 공간에 할당되어야 한다는 이론을 부셔버린 개념이다. 프로세스를 무조건 메모리 공간에 연속해서 적재하는 것이 아니라 프로세스를 일정한 단위인 페이지로 쪼개어 적재를 하는 것이다. 물론 메모리의 영역도 프레임으로 쪼개어 준비를 시켜놓는다. 프레임과 페이지는 같은 크기를 가지게 한다. 그러면 어떠한 프레임 공간에 페이지를 넣었는지 기억을 하게 되면 메모리 공간을 다 활용할 수 있게 되는 것이다.
CPU가 동작을 할 때 메인 메모리에 주소를 요구한다. 모든 계산의 기반은 주소를 통해서 진행되기 때문이다. 그래서 프로세스가 코딩이 되는 과정에서 각각의 프로세스는 주소 기반으로 작성이 되어 지게 된다. 몇 번지의 메모리 공간 주소에 적재될 것인지 그 이후의 명령어들은 몇 번지에 들어가는지가 이미 지정이 되어 있다. CPU는 이를 이용하여 메모리 공간에 들어가서 찾게 된다. 하지만 메모리 공간은 항상 특정 프로세스를 위해 공간을 비워놓지 않는다. 만약 비워 놓게 된다면 메모리 공간의 크기보다 프로그램들의 크기들의 합이 크기 때문에 프로그램을 사용할 수 없을 것이다. 따라서 임의로 메모리 공간에 적재를 하고 MMU라는 도구를 사용하여 메모리 공간의 위치를 조절한다.
MMU는 CPU와 메인 메모리 사이에서 존재하는데 재배치 레지스터를 가지고 있다. 이는 논리 주소와 물리 주소의 변환을 주관한다. 논리 주소는 CPU가 요구하는 주소 값이고 물리 주소는 메인 메모리가 인식하여 찾는 주소 값이다. 메인 메모리는 임의의 공간에 프로그램의 적재하고 이 메모리 주소 값에 맞게 재배치 레지스터 값을 설정하여 CPU가 이 프로세스에 대한 논리 주소를 요구할 때 물리 주소로 바꾸어 맞는 메모리 공간 주소를 지칭하게 해준다.
페이징은 재배치 레지스터를 페이지의 수만큼 만드는 것이다. 프로세스를 페이지 단위로 자른 후 이에 해당하는 페이지마다 재배치 레지스터를 통해 적절한 위치에 있도록 만든다. 그러면 CPU가 논리 주소를 요구할 때 논리 주소를 분석해서 적절한 물리 주소로 갈 수 있게 해준다. 프로세스가 메인 메모리에서 페이지 단위로 나뉘어 있지만 CPU는 항상 같은 논리 주소를 요구하므로 프로세스가 연속된 것처럼 보이게 된다. 이 과정에서 MMU는 페이지 테이블을 가지게 된다.
예시를 통해 주소 변환을 하는 과정을 살펴보자. 자세한 계산 방법은 앞 장을 참고하면 좋을 것이다.(운영체제 17장) 페이지 사이즈가 4바이트이고 페이지 테이블에 페이지 번호 당 5, 6, 1, 2를 가진다고 한다. 그러면 페이지 번호가 0번이면 5로 1번이면 6으로 가는 것으로 인식하면 된다. CPU가 논리 주소로 13번지를 요구한다면 메인 메모리의 어디의 물리 주소 위치로 가야하는가? 13을 이진수로 표현하면 1101이 된다. 이 주소 값에서 페이지 사이즈가 4바이트이므로 변위는 하위 2비트가 된다. 그러므로 나머지 상위 2비트는 페이지 번호가 된다. 페이지 번호가 11이므로 3이 된다. 그러므로 3에 해당하는 2라는 물리 주소 값으로 가게 된다. 2는 이진수로 10이고 하위 비트 01을 붙여 물리 주소가 1001로 바뀌게 된다. 따라서 9번지에 실제 프로세스의 페이지가 위치하게 되는 것이다.
하지만 페이징 과정을 진행하면 내부 단편화가 발생할 수 있다. 페이징은 프로세스를 특정 단위인 페이지 단위로 나누어 주고 된다. 하지만 프로세스의 크기가 페이지 크기의 배수가 아니라면 마지막 프로세스의 페이지는 한 프레임을 다 채울 수 없다. 따라서 이런 공간이 메모리 안에서 빈 공간으로 남아 낭비되게 된다. 이런 문제를 내부 단편화라고 한다. 예를 들어 프로세스가 15바이트인데 페이지를 4바이트 단위로 나눈다고 생각해보자. 그러면 3묶음의 4바이트 페이지가 나오지만 마지막 페이지는 3바이트의 크기가 만들어 진다. 프레임은 4바이트 크기인데 3바이트 크기의 페이지가 들어오면 1바이트의 메모리 낭비가 생기게 되는 것이다. 하지만 외부 단편화에 비해 낭비되는 내부 단편화의 메모리 공간이 매우 미비하다.
- 페이징(3) -
모든 프로그램들은 실행되기 전에 하드디스크에 보관된다. 그리고 사용을 하고자 하는 프로그램들이 메인 메모리에 적재되어서 CPU를 할당받아 기능을 수행하게 된다. 처음에 메인 메모리에 프로세스의 적재를 배울 때에는 프로세스들이 연속적으로 하나씩 메모리에 적재되는 것으로 알고 있었다. 하지만 이런 방법은 메모리의 공간을 매우 낭비시켜 효율을 떨어뜨린다는 것을 배웠다. 이를 해결하기 위해 프로세스를 페이지라는 단위로 나누어서 메모리에 적재하는 방법인 페이징 기법을 사용하였다. 재배치 레지스터를 사용하여 페이지 테이블에 맞는 주소로 논리 주소를 물리 주소로 변환시켜 프로세스의 메모리 주소 위치를 파악하는 방법이다.
운영체제는 프로세스 관리와 메모리 관리 외에도 다양한 일을 수행한다. 그 중에서 보호라는 기능도 수행을 하는데 해킹 등을 방지하는 것을 말한다. 그런데 메모리 관리를 하면서 왜 보호에 대해서 이야기를 하는가? 모든 프로세스는 CPU에서 요구하는 논리 주소에 의해 기능을 수행한다. 다른 말로 모든 주소는 페이지 테이블을 경유해서 메인 메모리로 들어가게 된다. 따라서 페이지 테이블에서 메모리에 대한 해킹을 할 수 없도록 장치를 만들어 놓으면 보호의 기능이 매우 높아질 것이다. 이렇게 하는 방법이 바로 페이지 테이블 엔트리마다 r, w, x 비트를 두는 방식이다. 3개의 비트를 통해서 해당 페이지에 대한 접근 제어를 가능하게 만들어 준다. 특정 비트를 가지면 내용을 읽을 수만 있고 다른 비트를 가지만 내용을 읽고 쓰는 것을 모두 가능하게 한다. 따라서 해커가 읽을 수만 있는 비트를 가진 프로세스에 접근해서 내용을 수정하려고하면 바로 프로세스를 종료하도록 만들어 보호의 기능을 수행하는 것이다.
메모리를 활용하면 공유의 기능을 수행할 수 있다. 다중 프로그래밍을 지원하는 환경에서 메인 메모리에는 다양한 프로세스들이 동시에 올라와서 동작을 할 수 있다. 그런데 같은 프로그램을 지원하는 여러 가지 프로세스들이 올라오면 어떻게 될까? 예를 들면 한글 오피스를 3개의 파일을 실행시킨다고 생각해보자. 같은 프로그램인데 각각 다른 작업을 수행하기 위해 복수 개의 프로세스가 올라올 수 있다. 하지만 같은 프로그램이기 때문에 복수 개의 프로세스는 공통된 기능을 지원받을 수 있을 것이다. 그런데 메인 메모리에 적재될 때 각각 따로 프로그램이 지원하는 기능을 메모리에 올린다면 메모리의 낭비가 될 것이다. 이런 경우를 위해 운영체제는 code의 영역에는 공유가 가능하게 만들었다. code 영역은 읽는 것만 가능하고 수정하는 것이 불가능하다. 따라서 임계구역 문제가 발생하지 않는다. 페이징 기법을 통해 프로세스의 부분이 페이지의 단위로 나뉘어져 있으므로 code를 이용하는 페이지를 따로 공통으로 사용하는 메모리 주소에 넣는 방법을 수행한다. 만약 같은 프로그램의 복수 개의 프로세스가 올라올 경우 공통으로 사용하는 코드부분을 특정 메모리에 하나만 적재시킨다. 그리고 프로세스의 페이지 테이블에서 코드 영역으로 가는 부분을 같은 곳을 가리키게 하여 각각 따로 사용을 하지만 메모리에는 하나의 공간만 차지하게 만드는 것이다. 물론 CPU는 프로세스들이 연속된 환경에서 움직이고 있다고 생각할 것이다.
'Technology > 오퍼레이팅 시스템' 카테고리의 다른 글
[OS 19장] 메모리 관리 - 가상 메모리 (0) | 2018.04.17 |
---|---|
[OS 18장] 메모리 관리 - 세그멘테이션(Segmentation) (0) | 2018.04.17 |
[OS 16장] 메모리 관리 - 연속 메모리 할당 (0) | 2018.04.17 |
[OS 15장] 메모리 관리 - 메모리 낭비 방지 (0) | 2018.04.17 |
[OS 14장] 메모리 관리 - 주기억장치(메인 메모리) 개요 (0) | 2018.04.17 |