[운영체제] 3. 메모리 관리(6/6) segmentation~

프로그램은 논리적인 조각으로 구성되어있고, 이 조각들을 세그먼트라고 부른다.

이 세그먼트 단위로 물리 메모리를 매핑하여 가상메모리 기법 사용.

소스텍스트(소스코드)와 같은 것들이 하나의 가상주소를 구성

-> 각각 물리 메모리에 매핑시켜 필요한 세그먼트만 물리 메모리로 로드시켜 프로세스 실행 : 세그멘테이션 기법

 

 

논리적으로 펼치면 이런 형태가 됨.

세그먼트 번호와 offset을 통해서 주소를 만들어 냄.

5개의 세그먼트가 있음 -> 3비트 필요. '매핑의 단위가 페이지가 아닌 세그먼트 단위다'를 기억.

 

세그먼트 단위는 프로그램의 크기와 상이 : 가변

-> 문제 발생 : 메모리에 공간이 없을 때 덜 사용하는 세그멘트를 쫓아내고 필요한 세그먼트를 로드하게 됨.

-> 가상메모리 기법 자체가 물리메모리보다 큰 프로그램을 실행시키기 위함이었는데, 다시 단편화가 발생

-> paging과 섞어서 사용

 

세그멘테이션과 (구)페이징을 비교하자면.

프로시저(코드)하고 데이터가 구분되고, 부분적으로 보호가 가능한가?

-> 세그먼트 단위로 세그멘트 테이블에 보호 비트 설정 BUT 페이징은 NO. 현재는 페이지 테이블 엔트리에 rwx권한을 주어 보호하는 기능이 추가됨.

 

사용자 간 프로시저(코드) 공유가 가능한가?

-> 세그멘테이션은 당연히 Yes. (구)페이징은 No. 공유 라이브러리를 보면 페이징 시스템에서 프로세스마다 접근 가능 -> 요즘엔 가능. Yes

 

size 동적 변환 -> 세그멘테이션만 Yes

 

 

a~e는 aging과정.

1. seg 1을 쫓아내고 7 load. 3K만큼 단편화 발생

2. seg 4를 쫓아내고 5 load. 3K만큼 단편화 발생

3. 6번 로드시키려는데 필요한게 4K -> 3을 쫓아내고 6을 씀 -> 최종적으로 총 10K 단편화

4. 사용할 수 없는 공간이 10K -> 컴팩션(e) -> 총 13K를 복사 : 비용이 큼

-> 순수 세그멘테이션 기법만으로는 부족하다 -> 페이징 기법 같이 사용

 

세그먼트를 코드 데이터 힙 스택 이렇게 나눔 : 각각의 세그먼트, 이를 페이지 단위로 쪼갬

-> 필요 세그먼트를 찾은 후 특정 위치를 페이지 단위로 쪼개어 필요한 페이지만 메모리에 로드

 

주소를 세그먼트 번호, 페이지 번호, 옵셋으로 구성

-> 가상주소의 앞 3비트를 seg num으로, 각각의 페이지로 나뉘이지니 페이지 num, 마지막 offset.

주소지정을 쉽게 하기 위해 비트를 나눔 : 64byte를 표현하려면 6비트 필요.

 

멀티레벨 페이징과 비교하자면,

CPU는 virtual address만 참조 -> 가상주소를  mmu를 통해 물리 주소로 변환

-> CPU의 가상주소는 s,p,o로 구분되는데 페이지의 크기가 보통 4kb로 구성(1024 word)

되었으니까 4kb는 뺴고 나머지만 페이지 프레임 번호로 가진다.

 

seg num 3비트. CPU에 STBR(segment table base register)이 있는데, 메모리에 세그먼트 테이블이 시작하는 위치를 알려주고 있음.

-> 2단계 페이지 테이블과 동일 : 굳이 세그멘테이션을 쓸 필요가 없다!

페이지 테이블을 공유함으로써 보호나 데이터공유가 가능해짐.

 

 

MULTICS에서는 세그먼트를 나눔. 나뉘어진 세그먼트를 매핑하는 세그먼트 테이블이 존재.

-> 각각에 segment descriptor이 존재 : 36 비트 사용

각각 0~6 세그먼트가 가리키는 데로 쫓아가면 세그먼트와 페이징을 동시에 사용

-> 세그먼트 테이블에서 페이지테이블 찾고, 페이지 테이블에서 옵셋 쫓아가서 physical frame을 찾아야 함

-> 세그먼트 테이블 엔트리에 적힌게 페이지 테이블의 시작 주소, 페이지 테이블 엔트리에 물리 프레임 번호가 적힘.

 

 

하위 9비트는 페이지 크기, 메모리 내 존재 여부, 보호비트 등을 나타냄.

중요한건 테이블 시작 주소. 18바이트고, 세그먼트는 가변인데 비해 페이지는 고정 크기

-> 세그멘트의 길이 정보가 필요해짐 : 가변이기에 그걸 넘어서 참조 불가 -> 엔트리엔 페이지 테이블 시작 주소와 세그먼트 정보 존재.

 

 

메모리 참조가 발생 : 세그먼트 디스크립터를 찾음(주소에 있는 세그먼트 번호를 통해) -> 세그먼트 페이지 테이블이 메모리에 없으면 segment fault -> 해당 페이지를 로드하고 주소변환, 물리 페이지 프레임 주소에 offset을 더하여 실제 피지컬 메모리 주소 생성(MULTICS 운영체제가 처리.)

 

 

당시 실제 가상 주소. 18개의 seg num, 6개의 page num, 10개의 offset.

-> 당시 페이지 크기가 1kb, 10bit로 페이지 단위를 쪼갬.

 

주소변환의 과정.

seg num을 통해 세그먼트 선택 -> offset 찾으면 페이지 테이블의 시작주소 확인 -> 페이지 프레임(시작 페이지)를 확인 -> offset을 통해 떨어진 곳에 가면 데이터 확인 가능 : 총 3단계.

주소 변환을 위해 TLB를 사용 : 변환된 데이터, 주소 결과값을 TLB에 넣음 -> miss시 table에서 구한 후 가장 오래 사용하지 않는 정보를 TLB에서 교체

 

 

펜티엄. 펜티엄에서는 selector라고 하는데, 이게 MULTICS에서는 segment.

16비트로 되어 있고, 13비트가 인덱스이고 2는 특권모드, 하나는 GDT(global discriptor table, 시스템에 딱 하나) or LDT(local discriptor table)

 

 

<디스크럽터의 구조>

셀렉터를 통해 세그먼트가 선택이 되고, 선택된 세그먼트는 32비트로 구성됨.

시작 Base 주소와  Limit로 나뉨.

 

 

세그먼트가 메모리에 있고, 오프셋이 세그먼트 크기보다 적다면 세그먼트의 베이스 필드의 값과 와 offset을 더해서 32비트의 linear address를 만듦.

virtual 하고 logical은 CPU가 바라보는 주소, Linear address는 프로세스가 바라보는 주소 공간. 

당연히 CPU의 bit 개수 만큼의 크기를 가지며, 이 주소로 바뀐 다음 페이징을 함.

-> 리니어 주소는 세그먼트 주소의 일정 부분을 기리키니까 이 일부를 페이징 테이블을 통해 물리 메모리 주소로 변환.

 

우리는 offset만으로 주소를 계산하고 싶어(세그맨테이션이 필요 없으며 하나의 32비트 주소공간에 페이징으로 충분해) -> 세그멘테이션을 무효화 해야함

->모든 세그먼트 레지스터는 같은 셀렉터로 초기화가 되고, 이 셀렉터가 가리키는 base address를 0으로, 리미트를 f로 -> 동일한 하나의 세그먼트를 사용하게 되고, 베이스가 0이니까 그냥 offset이 그대로 선형 주소가 됨 :   이걸로 페이징.

인텔CPU에서 세그멘테이션을 무효화 하기 위해 셀렉터와 베이스어드레스, 리미트를 000~ fff~로 채워서 페이징을 구현 한다. (64비트로 넘어와서는 페이지테이블이 너무 커서 5단계로 하는중)

 

 

 

이 어드레스를 10 10 12로 나누어 2단계 페이징을 함.

dir은 페이지 디렉토리를 인덱싱하여 발견된 엔트리는 대응되는 페이지 테이블의 시작주소를 가리킴.

page frame 시작 주소에 offset이 더해져서 실제 물리 주소를 얻을 수 있음.

dir과 page를 합쳐서 페이지 프레임을 가리키기 위해 사용.

-> TLB에 매핑.

 

Top, middle이 있는데. 첫번쨰 페이지 테이블의 시작주소와 offset으로 두번째 페이지 테이블을 찾아간 후, 두번째 테이블의  시작주소 옵셋을 더해서  실제 주소를 찾아감.

-> 역시 세그멘테이션 기법이 필요가 없어짐. But 하드웨어에는 남아있다.

 

 

 

권한을 의미하는데.

0은 커널, 1은 시스템콜, 2는 공유 라이브러리, 3은 사용자 프로그램

-> 이런식으로 select(segment) table에 bit를 설정함. 리눅스는 lv 0과 3만 사용.