9.1 배경
- 컴퓨터 시스템에서 메모리는 핵심적인 역할
- CPU는 프로그램 카운터 값을 기반으로 메모리에서 명령어를 가져와 실행한다.
- 메모리는 주소의 연속적인 배열로 구성되며, CPU는 명령어 실행 과정에서 추가적인 메모리 접근을 수행할 수 있다.
9.1.1 기본 하드웨어
- CPU가 직접 접근할 수 있는 저장소는 레지스터와 메인 메모리이다.
- 하지만 메모리는 레지스터보다 접근 속도가 느려, CPU는 데이터를 가져오는 동안 대기해야 한다. 이를 해결하기 위해 캐시 메모리를 CPU와 주 메모리 사이에 배치해 성능을 향상시킨다.
- 운영체제와 사용자 프로세스를 보호하기 위해 기본 레지스터(Base Register)와 한계 레지스터(Limit Register)를 사용한다.
- 기본 레지스터는 접근 가능한 메모리의 최소 주소를, 한계 레지스터는 최대 범위를 정의하며, 사용자 모드에서의 불법적인 접근을 방지한다.
9.1.2 주소 바인딩
프로그램은 실행되기 전에 메모리에 적재되며, 실행 중에는 명령어와 데이터를 메모리에서 불러온다. 주소 바인딩은 다음 세 가지 방식으로 수행될 수 있다.
- 컴파일 시간 바인딩: 프로그램이 특정 주소에 고정적으로 배치될 것이 확실하면 컴파일 시점에 절대 주소로 변환된다.
- 로딩 시간 바인딩: 실행 위치가 컴파일 시점에 결정되지 않았다면 재배치 가능 코드(relocatable code)가 생성되며, 실행 전 적절한 주소에 배치된다.
- 실행 시간 바인딩: 실행 도중 프로세스가 이동할 수 있다면, 실행 중에 동적으로 주소 변환이 이루어진다. 이를 위해 추가적인 하드웨어 지원이 필요하다. 대부분의 운영체제는 실행 시간 바인딩을 사용한다.
9.1.3 논리 주소와 물리 주소 공간
- 논리 주소(Logical Address): CPU가 생성하는 주소로, 사용자 프로그램이 인식하는 주소이다.
- 물리 주소(Physical Address): 실제 메모리에서 사용되는 주소이다.
- 실행 시간 바인딩을 사용하는 경우 논리 주소와 물리 주소가 다를 수 있으며, 논리 주소는 가상 주소(Virtual Address)라고도 불린다.
- 논리 주소를 물리 주소로 변환하는 작업은 메모리 관리 장치(MMU, Memory Management Unit)가 수행한다.
- MMU는 실행 중 생성되는 모든 주소에 재배치 레지스터(Relocation Register) 값을 더해 물리 주소로 변환한다.
9.1.4 동적 로딩(Dynamic Loading)
프로그램 실행을 위해 모든 코드와 데이터를 메모리에 올릴 필요 없이, 필요한 루틴만 메모리에 로드하는 방식이다.
- 장점: 메모리 공간을 효율적으로 사용 가능하며, 사용되지 않는 코드(예: 오류 처리 루틴 등)는 로드되지 않아 전체 프로그램 크기를 줄일 수 있다.
- 동적 로딩은 운영체제의 특별한 지원 없이도 프로그래머가 직접 구현할 수 있다.
9.1.5 동적 링킹과 공유 라이브러리
동적 링크 라이브러리(DLL, Dynamically Linked Library)는 프로그램 실행 시 필요한 라이브러리를 동적으로 연결하는 방식이다.
- 정적 링킹(Static Linking): 컴파일 시 모든 라이브러리를 프로그램에 포함하여 실행 파일 크기가 커진다.
- 동적 링킹(Dynamic Linking): 프로그램 실행 중 필요한 라이브러리를 불러와 메모리 절약 및 코드 공유가 가능하다.
- 운영체제는 공유 라이브러리를 한 번만 로드하고, 여러 프로세스가 이를 공유하도록 관리할 수 있다.
- 라이브러리 업데이트가 쉬우며, 새로운 버전이 배포되면 프로그램을 다시 컴파일하지 않아도 자동으로 새로운 버전을 사용할 수 있다.
9.2 연속 메모리 할당 (Contiguous Memory Allocation)
운영체제는 메인 메모리를 운영체제 자체와 사용자 프로세스 간에 효율적으로 할당해야 한다. 연속 메모리 할당 방식에서는 각 프로세스가 연속된 메모리 공간을 차지한다.
9.2.1 메모리 보호 (Memory Protection)
운영체제와 다른 프로세스의 메모리를 보호하기 위해 재배치 레지스터(relocation register) 와 제한 레지스터(limit register) 를 사용한다.
- 재배치 레지스터: 프로세스가 접근할 수 있는 메모리의 최소 물리 주소를 저장
- 제한 레지스터: 논리 주소 범위를 지정하여 프로세스가 할당된 영역을 벗어나지 않도록 함
- CPU가 새로운 프로세스를 실행할 때, 이 레지스터 값을 설정하여 불법적인 메모리 접근을 방지할 수 있음
- 동적으로 운영체제 크기를 변경할 수 있는 장점이 있음 (예: 필요할 때만 장치 드라이버를 로드)
9.2.2 메모리 할당 (Memory Allocation)
연속 메모리 할당에서 가장 기본적인 방법은 가변 크기 파티션(variable-partition) 방식 이다.
- 초기에는 전체 메모리가 하나의 큰 블록(=홀)으로 존재
- 프로세스가 들어오면 필요한 만큼 메모리를 할당하고, 나머지 공간은 다시 사용 가능한 블록으로 남김
- 프로세스가 종료되면 메모리를 반환하며, 인접한 빈 공간이 있을 경우 병합 가능
- 효율적인 메모리 할당을 위해 동적 저장소 할당(Dynamic Storage Allocation) 문제 가 발생
대표적인 메모리 할당 전략
- 최초 적합(First Fit): 첫 번째로 발견한 충분한 크기의 블록에 할당
- 최적 적합(Best Fit): 가장 작은 크기의 적합한 블록을 찾아 할당 (남는 공간 최소화)
- 최악 적합(Worst Fit): 가장 큰 블록을 찾아 할당 (큰 블록이 남아 더 유용할 가능성 있음)
성능 비교
- 최초 적합(First Fit) 과 최적 적합(Best Fit) 이 최악 적합(Worst Fit) 보다 성능이 우수함
- 최초 적합은 빠르지만, 최적 적합은 메모리 사용률이 더 높을 수도 있음
9.2.3 단편화(Fragmentation)
프로세스가 메모리를 할당받고 해제하는 과정에서 단편화(Fragmentation) 문제가 발생할 수 있음.
- 외부 단편화(External Fragmentation)
- 사용 가능한 전체 메모리는 충분하지만, 비연속적으로 조각나 있어 실제 할당이 어려운 상황
- 50% 법칙(50-percent rule): 전체 할당된 블록의 약 50%가 외부 단편화로 인해 낭비됨
- 해결 방법: 압축(Compaction) 을 통해 모든 프로세스를 한쪽으로 이동하여 연속된 큰 블록을 만듦 (비용이 많이 듦)
- 내부 단편화(Internal Fragmentation)
- 요청된 크기보다 더 큰 블록을 할당했을 때 남는 공간이 낭비되는 현상
- 해결 방법: 고정 크기 블록을 사용하여 미리 정해진 크기로 메모리를 할당
해결 방법
- 압축(Compaction): 메모리를 재배치하여 단편화를 줄임 (단, 실행 중인 프로세스의 주소 재조정이 필요)
- 페이징(Paging) 기법 사용: 논리 주소 공간을 연속적이지 않게 관리하여 단편화 문제 해결
9.3 페이징 (Paging)
- 페이징(Paging)은 프로세스의 물리적 주소 공간을 비연속적으로 할당할 수 있도록 하는 메모리 관리 기법이다.
- 페이징은 외부 단편화(External Fragmentation) 문제를 해결하고, 메모리를 압축(Compaction) 할 필요가 없다는 장점이 있다.
9.3.1 기본적인 방법 (Basic Method)
페이징을 구현하는 기본적인 방법은 다음과 같다.
- 물리적 메모리(Physical Memory)를 고정 크기 블록(Frames)으로 나눈다.
- 논리적 메모리(Logical Memory)도 페이지(Pages)라는 동일한 크기의 블록으로 나눈다.
- 실행할 프로세스의 페이지를 백업 저장소(Backing Store) 또는 파일 시스템에서 메모리 프레임으로 로드한다.
이 방식을 사용하면 논리적 주소 공간과 물리적 주소 공간이 완전히 분리된다. 예를 들어, 시스템의 실제 물리적 메모리가 2⁶⁴ 바이트보다 적더라도, 논리적 주소 공간을 64비트로 설정하여 큰 주소 공간을 활용할 수 있다.
논리 주소(Logical Address) 변환 과정
CPU가 생성하는 모든 주소는 페이지 번호(Page Number, p)와 페이지 오프셋(Page Offset, d)로 나뉜다.
- 페이지 번호(p): 프로세스의 페이지 테이블(Page Table)에서 해당 페이지가 저장된 프레임 번호(Frame Number, f)를 찾는 데 사용된다.
- 오프셋(d): 프레임 내에서 실제 데이터가 저장된 위치를 나타낸다.
이 과정을 통해 논리 주소(Logical Address) → 물리 주소(Physical Address) 변환이 이루어진다.
- CPU는 논리 주소의 페이지 번호(p)를 추출하여 페이지 테이블을 참조한다.
- 페이지 테이블에서 해당 페이지의 프레임 번호(f)를 찾는다.
- 논리 주소의 페이지 번호(p)를 물리 주소의 프레임 번호(f)로 대체한다.
- 페이지 오프셋(d)는 그대로 유지되며, 최종적으로 프레임 번호와 오프셋을 결합하여 물리 주소를 생성한다.
페이지 크기(Page Size)와 주소 변환
페이징에서 중요한 개념은 페이지 크기(Page Size)이다. 페이지 크기는 하드웨어에 의해 결정되며, 2의 제곱수(예: 4KB, 8KB, 1GB 등)로 설정된다.
논리 주소 공간이 2^m 크기이고, 페이지 크기가 2^n 바이트라면:
- 상위 (m-n) 비트 → 페이지 번호(p)
- 하위 n 비트 → 페이지 오프셋(d)
이를 통해 간단하게 논리 주소 → 물리 주소 변환이 가능하다.
예를 들어, 페이지 크기가 4바이트(2²)이고, 물리 메모리가 32바이트(8페이지)라고 가정하자.
- 논리 주소 0 → 페이지 0, 오프셋 0 → 페이지 테이블에서 페이지 0이 프레임 5에 매핑됨 → 물리 주소 20 [(5×4) + 0]
- 논리 주소 3 → 페이지 0, 오프셋 3 → 페이지 0이 프레임 5에 매핑됨 → 물리 주소 23 [(5×4) + 3]
- 논리 주소 4 → 페이지 1, 오프셋 0 → 페이지 1이 프레임 6에 매핑됨 → 물리 주소 24 [(6×4) + 0]
- 논리 주소 13 → 페이지 3, 오프셋 1 → 페이지 3이 프레임 2에 매핑됨 → 물리 주소 9 [(2×4) + 1]
운영체제의 메모리 관리
운영체제는 프레임 테이블(Frame Table)을 유지하여 메모리 상태를 관리한다.
- 프레임 테이블:
- 각 물리적 프레임이 사용 중인지, 비어 있는지 기록
- 사용 중이라면 어떤 프로세스의 페이지가 저장되어 있는지 표시
운영체제는 프로세스별 페이지 테이블도 유지해야 한다.
- 운영체제가 수행하는 작업
- 프로세스가 실행되면, 페이지 테이블을 로드하여 논리 주소를 변환
- 논리 주소가 물리 주소로 변환될 때, 페이지 테이블을 참조하여 올바른 프레임을 찾아야 함
- 운영체제의 메모리 할당 정책에 따라, 페이지를 메모리에 적절히 배치
페이징은 CPU에서 실행되는 문맥 전환(Context Switching) 시간을 증가시킬 수 있다. 왜냐하면 프로세스가 전환될 때마다 페이지 테이블도 함께 변경해야 하기 때문이다.
9.3.2 하드웨어 지원 (Hardware Support)
하드웨어는 페이지 테이블을 관리하고 주소 변환을 수행하는 데 도움을 주며, 이를 통해 프로세스 간 빠른 컨텍스트 스위칭과 효율적인 메모리 접근이 가능하다.
9.3.2.1 변환 색인 버퍼 (TLB, Translation Look-aside Buffer)
페이지 테이블과 하드웨어 지원
운영체제에서 가상 메모리를 사용할 경우, 각 프로세스는 개별적인 페이지 테이블을 가짐. 따라서 프로세스가 실행될 때, 운영체제는 해당 프로세스의 페이지 테이블을 로드해야 합니다.
- 페이지 테이블은 프로세스 컨트롤 블록(PCB)에 저장됨
- 프로세스가 실행될 때, PCB에 저장된 페이지 테이블 주소를 페이지 테이블 베이스 레지스터 (PTBR) 에 로드.
- CPU 스케줄러가 새로운 프로세스를 실행할 때, PTBR을 업데이트하여 해당 프로세스의 주소 공간을 참조하도록 함.
- 하드웨어 지원 방식
- 전용 레지스터에 페이지 테이블을 저장 → 속도는 빠르지만, 레지스터 크기가 제한적이므로 큰 페이지 테이블을 저장하기 어려움.
- 페이지 테이블을 메모리에 저장하고, PTBR을 이용해 참조 → 메모리 접근이 필요하지만, 컨텍스트 스위치 시간이 단축됨.
- 페이지 테이블을 메모리에 저장하면 메모리 접근이 2배 느려지는 문제 발생.
변환 색인 버퍼 (TLB, Translation Look-aside Buffer)
메모리에 저장된 페이지 테이블을 접근하는 방식은 속도가 느려지는 문제가 있음. 이를 해결하기 위해 TLB (Translation Look-aside Buffer) 라는 하드웨어 캐시를 사용함.
- TLB는 고속 캐시 역할을 하여 페이지 테이블 접근 속도를 향상
- 페이지 테이블의 일부를 저장하는 작은 크기의 고속 메모리
- CPU가 주소를 변환할 때, TLB를 먼저 조회
- TLB에 존재하면 (TLB hit) → 즉시 물리 주소 반환 (빠름).
- TLB에 없으면 (TLB miss) → 일반적인 페이지 테이블 조회 진행 후, 변환된 주소를 TLB에 추가.
TLB 성능 향상 기법
- ASID (Address Space Identifier): 프로세스별 ID를 저장하여 컨텍스트 스위치 시 TLB 초기화를 방지.
- TLB 교체 정책: LRU(가장 오래된 항목 제거), 랜덤, 라운드 로빈 방식 사용.
- 다중 레벨 TLB (L1, L2 TLB): 최신 CPU에서 성능 개선을 위해 사용.
TLB 히트율과 성능
- TLB 히트율이 높을수록 주소 변환 속도가 빨라짐.
- TLB 히트율 80% → 평균 메모리 접근 시간 12ns.
- TLB 히트율 99% → 평균 메모리 접근 시간 10.1ns (거의 성능 저하 없음).
9.3.3 보호 (Protection)
메모리 보호
- 페이지 테이블의 보호 비트를 이용하여 접근 권한 설정 (읽기 전용, 읽기-쓰기, 실행 전용 등).
- 읽기 전용 페이지에 쓰기 시도 시 운영체제로 트랩 (메모리 보호 오류 발생).
- 유효-무효 비트를 사용하여 프로세스의 논리 주소 공간에 포함된 페이지인지 확인.
- 유효 비트가 무효 (invalid)이면 잘못된 메모리 접근으로 간주하고 OS가 처리.
- 내부 단편화 문제 발생 가능: 페이지 크기 단위로 보호하므로, 마지막 페이지 일부가 유효하지 않을 수 있음.
페이지 테이블 크기 최적화
- 대부분의 프로세스는 전체 주소 공간을 사용하지 않음.
- 페이지 테이블 길이 레지스터 (PTLR) 를 이용해 유효한 페이지 테이블 크기를 저장, 불필요한 메모리 낭비 방지.
9.3.4 공유 페이지 (Shared Pages)
페이지 공유의 장점
- 여러 프로세스가 공통 라이브러리 (예: C 표준 라이브러리 libc)를 공유하면 메모리 절약 가능.
- 예시: 40개의 프로세스가 각각 2MB 크기의 libc를 로드하면 80MB 필요하지만, 공유하면 단 2MB만 사용.
재진입 가능 코드 (Reentrant Code)
- 자기 수정이 없는 코드 → 여러 프로세스가 동시에 실행 가능.
- 코드 부분은 공유하되, 각 프로세스는 별도의 데이터 영역을 가짐.
운영체제의 역할
- 공유 코드를 보호하기 위해 운영체제가 읽기 전용으로 설정.
- 윈도우 시스템, 데이터베이스 시스템 등도 공유 페이지 기법으로 메모리 효율을 높일 수 있음.
9.4 페이지 테이블의 구조 (Structure of the Page Table)
페이지 테이블 최적화 필요성
- 현대 시스템은 32비트~64비트 논리 주소 공간을 지원 → 페이지 테이블 크기가 매우 커짐.
- 예시: 32비트 시스템에서 4KB 페이지 사용 시, 100만 개 이상의 엔트리 (4MB 필요) → 메모리 낭비 발생.
- 해결책: 페이지 테이블을 계층 구조로 나누어 관리.
9.4.1 계층적 페이징 (Hierarchical Paging)
이단계 페이징 (Two-Level Paging)
- 페이지 테이블 자체를 페이지 단위로 나누어 관리 (예: 2단계 구조).
- 논리 주소를 세 부분으로 나눔:
- p1 (외부 페이지 테이블 인덱스)
- p2 (내부 페이지 테이블 인덱스)
- d (페이지 내 오프셋)
- 주소 변환 과정에서 외부 → 내부 페이지 테이블 순서로 참조 → 순차 매핑 (Forward-Mapped Page Table).
9.4.2 해시 페이지 테이블 (Hashed Page Tables)
해시를 이용한 주소 변환
- 32비트 이상의 주소 공간을 효과적으로 관리하기 위해 해시 페이지 테이블을 사용.
- 가상 페이지 번호를 해시 함수로 변환하여 테이블에서 검색.
- 해시 충돌이 발생하면 연결 리스트 (Linked List) 구조로 관리.
- 검색 과정:
- 가상 페이지 번호를 해시하여 테이블에서 검색.
- 일치하는 가상 페이지 번호를 찾으면 해당 프레임 번호를 사용해 물리 주소 생성.
- 일치하는 항목이 없으면 연결 리스트를 순차 검색.
9.4.3 역방향 페이지 테이블 (Inverted Page Tables)
- 각 물리 메모리 프레임에 하나의 엔트리만 유지 (즉, 전체 시스템에 하나의 페이지 테이블만 존재).
- 각 엔트리 = (프로세스 ID, 가상 페이지 번호) 로 구성.
- 특정 가상 주소를 찾을 때, 역방향 테이블을 검색하여 물리 주소를 매핑.
단점: 검색 속도 문제
- 역방향 페이지 테이블은 물리 주소 기준으로 정렬 → 가상 주소 검색 시 전체 테이블 탐색 필요 → 비효율적.
- 해결책: 해시 테이블과 함께 사용하여 검색 속도 개선.
9.5 스와핑 (Swapping)
- 운영체제가 프로세스를 메모리에서 백업 스토리지(보조 저장장치)로 이동했다가 다시 불러오는 기법.
- 실제 물리 메모리보다 더 많은 프로세스를 실행할 수 있어 다중 프로그래밍을 증가시킴.
9.5.1 표준 스와핑 (Standard Swapping)
개념
- 프로세스 전체를 보조 저장장치로 이동했다가 다시 메모리로 로드하는 방식.
- UNIX와 같은 전통적인 운영체제에서 사용되었음.
장점
- 비활성 프로세스를 스와핑하여 활성 프로세스에 더 많은 메모리를 제공.
- 물리 메모리를 초과하는 다중 프로그래밍 가능.
단점
- 전체 프로세스를 이동해야 하므로 속도가 느림.
- 멀티스레드 프로세스의 경우, 모든 스레드의 데이터 구조도 함께 스와핑해야 함.
- 현대 운영체제에서는 잘 사용되지 않음 (예외: Solaris는 극단적인 경우에만 사용).
9.5.2 Swapping with Paging
개념
- 표준 스와핑 대신 프로세스의 개별 페이지 단위로 이동하는 방식.
- "스와핑"이라는 용어는 이제 보통 이 방식을 의미.
과정
- 페이지 아웃(Page Out): 메모리에서 특정 페이지를 보조 저장장치로 이동.
- 페이지 인(Page In): 다시 필요할 때 보조 저장장치에서 페이지를 불러옴.
장점
- 프로세스 전체를 이동하는 부담 없이 필요한 페이지만 이동 가능 → 속도 향상.
- 메모리 오버헤드 감소 (적은 메모리로 많은 프로세스 실행 가능).
- 가상 메모리와 효과적으로 결합
사용 예시
- Linux, Windows 등 대부분의 현대 운영체제에서 사용.
'운영체제 공룡책' 카테고리의 다른 글
[운영체제 공룡책] 11장 Mass -Storage Structure (0) | 2025.03.19 |
---|---|
[운영체제 공룡책] 10장 가상 메모리 (0) | 2025.03.18 |
[운영체제 공룡책] 8장 교착상태 (1) | 2025.01.25 |
[운영체제 공룡책] 7장 동기화 예제 (0) | 2025.01.15 |
[운영체제 공룡책] 6장 Synchronization Tools(동기화 도구) (1) | 2025.01.02 |