Chapter 9. Process Address Space

 

As seen in the previous chapter( this is 8)

 

a kernel function gets dyanamic memory in a fairly straightforward manner by invoking one of a variety of functions

 

  • Get_free_pages() or alloc_ages(): To get pages from the zoned page frame allocator(buddy system algorithm 
  • kmem_cache_alloc() or kmalloc(): To use the slab allocator for specialized or general-purpose objects
  • vmalloc() or vmalloc_32(): To get a noncontiguous memory area
  •  

 

이제 위 함수들을 이용해서 request가 정상적으로 처리가 된다면

각각의 함수들은 page descriptor address 또는 linear address를 return 한다.

이러한 반환되는 주소들을 이용해서 allocated dynamic memory area의 beginning을 identifying 할 수 있다.

 

위와 같은 simple approache들은 2개의 reason이 있다.

 

1) 커널은 운영체제에서 우선순위가 가장 높은 구성 요소이다. 커널 함수가 동적 메모리를 요청 하려면 반드시 정당한 이유가 있는 것이고, 이러한 요청을 더는 뒤로 미룰 수없다.

 

2) 커널은 자신을 신뢰한다. 모든 커널 함수는 오류가 없다고 가정하기 때문에 프로그래밍 오류로부터 보호할 필요가 없다.

 

When allocating memory to User Mode rpocesses, the situation in entirely different :

 

1) 프로세스꺼는 최대한 연기된다.

2) 사용자 프로그램은 cannot be trusted 이다. 따라서 kernel은 must be prepared to catch all addressing erros caused 해야 한다.

 

 

이장에서는 커널이 새로운 종류의 자원을 이용하여 프로세스에 동적 메모리를 할당하는 일을 연기할 수 있게 한다.

사용자 모드 프로세스가 동적 메모리를 요청하면 커널은 페이지 프레임을 추가로 할당해 주지 않고, 선형 주소의 새로운 범위를 사용할 수 있는 권할을 주는데, 이 범위는 프로세스 주소 공간의 일부가 된다.

 

이장에서 다루는 내용

 

  • 프로세스가 동적 메모리를 어떻게 바라보는지 관점에 대해서 설명한다.
  • 전체 메무리 구역에서 볼때 프로세스 주소 공간이 어떤 기본 구성 요소로 이루워저 있는지를 본다.
  • 프로세스에 페이지 프레임을 할당하는 일을 미룰 때 페이지 폴트 예외 핸들러의 역할을 살펴 본다.
  • 커널이 모든 프로세스 주소 공간을 만들고 삭제하는 방법을 설명 한다.
  • 주소 공간을 관리하는 API와 System call을 설명 한다.

User Mode의 Process가 동적 메모리를 요청하면 커널은 페이지 프레임을 추가로 할당해주지 않고, Linear address의 new range를 주게 된다. 이 범위는 프로세스 주소 공간의 일부가 된다.

이러한 구간을 가리켜 Memory Region이라고 한다.

 

즉, page frame들을 allocating하는 대신에 일단 range만 잡아주는 꼴이 된다.

 

9.1 The Process's Address Space

 

프로세스 address space는 프로세스가 사용할 수 있는 모든 linear address로 구성 된다.

이 주소공간은 서로 배타적이므로, 프로세스 끼리 절대로 관련성이 없다.

 

커널은 선형 주소 구간을 추가하거나 삭제하여 프로세스의 주소 공간을 동적으로 변경 할 수 있다.

 

커널은 프로세스가 현재 소유하는 메모리 구역을 반드시 파악하고 있어야 한다. 왜나하면 페이지 폴트에 대해서 핸들링을 해줘야 하기 때문이다.

 

Lenth of a memory region : The initial address and the lenth of a memroy region must be multipes of 4,096Byte

 

 

9.2 The Memory Descriptor

 

프로세스 주소 공간과 관련한 모든 정보는 "Memory Descriptor"라는 객체에 들어 있다.

이 Object는 mm_struct type이고, Process Descriptor의 mm filed를 가리킨다.

 

When to Get New Memory Regions

 

  1. 새로운 프로세스 생성

  2. exec 시스템 콜로 다른 프로그램을 load 한다.

  3. mmap() system call을 이용해서 "memory mapping"을 한다.  mmap() : 파일에 대한 메모리 mapping을 만들어 process address space를 enlarging 한다. 잘은 모르겠지만, 뭔가 딴거를 일단 process address space로 맵핑 시켜 버리는것 같다. 그래서 process의 주소를 키우는것 같다.

  4. Process의 User Mode stack 사이즈를 키운다.

  5. IPC를 위해 shared memory region을 생성 한다.

  6. heap 그러닌까, process's dyanamic area를 expand 한다. malloc을 이용해서

 

 

 

Porcess Descriptor로 접근하는 방법은 위 그림과 같다.

즉 task_struct의 mm필드로 가서 그 포인터가 지시하는 mm_struct를 가면

memory descriptor의 정보를 알 수 있다.

 

The Memory Descriptor

 

pgd: 페이지 전역 디렉터리를 가리키는 포인터

 

rss: 프로세스에 할당되어진 페이지 프레임의 수, 이것을 조사해보면 main memory가 얼마나 사용 되고 있는지를 알 수 있다.

 

total_vm: 프로세스 주소 공간의 크기(페이지의 수)

 

mmlist: 메모리 디스크립터 리스트에서 인접하는 항목을 가리키는 포인터

 

start_code / end_code: 실행 코드의 시작 주소와 끝 주소

 

brk: heap의 현재 끝 주소

 

mm_users: 2차 사용 횟수 라고 나와있는데 자세히 설명 하면, LightWeight process가 몇게가 mm_struct를 공유하고 있는지 그 숫자를 의미 하게 된다.

 

mm_count: 1차 사용 횟수

즉 이것의 의미는, 메모리 디스크립터의 1차 사용 횟수로 mm_users에 있는 모든 '사용자를 즉 Light-Weight process의 사용을 mm_count에서는 한 단위로 카운트 한다.

Kernel은 mm_count 필드를 감소킬 때마다 이 값이 0이 되는지 검사하여, 0이되면 더는 사용ㅎ지 않는 것이므로 메모리 디스크립터를 해지 한다.

 

예를 들어 설명,

 

LWP 1

LWP 2

위 2개의 프로세스가 mm_struct를 공유한다.

따라서 mm_users = 2, mm_conunt =1이다.

 

이떄 mm_struct를 kernel thread에게 빌려준다. 그러면 mm_count가 2로 증가한다.

이때 LWP1,2가 모두 종료되어도 mmcount는 1만 감소해서 1이 남기 때문에 mm_struct를 유지하게 된다.

물론 try_to_unuse() 함수를 이용해서 mm_count를 증가시켜도 동일한 결과를 얻는다.

 

 

Memory Regions 

 

리눅스 vm_area_struct 타임의 객체를 이용하여 메모리 구역(memory region)을 구현한다.

 

각각의 memory region descriptor는 linear address 구간을 identifiy할 수 있도록 한다.

이 strcut의 filed로는 다음의 것들이 있다.

vm_start: start adress

vm_end: end address

vm_mm : 해당 구역을 소유하는 메모리 디스크립터를 가리키는 포인터

프로세스의 mm_struct 즉, 프로세스가 가지고 있는 memory descriptor를 포인팅 한다.

 

프로세스들은 결고 서로의 memory region을 overlap하지 않는다.

 

 

 

위 그림에서 볼 수 있듯이, 새로운 선형 주소 범위를 프로세스의 주소 공간에 추가할 때 커널은 기존의 메모리 구역을 확장할 수 있는지 검사한다. (a 경우) 가능하다면 확장을 한다.

 

그렇지 않으면 b의 경우 처럼 새로운 메모리 구역을 생성 한다. (b의 경우)

 

마찬가지로 프로세스의 주소 공간에서 선형 주소 범위를 제거할 때 커널은 이에 영향을 받는 메모리 구역의 크기를 조정한다. (c의 경우)

 

어떤 경우에는 크기를 조정할 때 한 메모리 구역이 작은 구역 두개로 나뉘기도 한다. (d의 경우)

 

 

 

 

 

 

Memory Region Data Structures

 

위 그림은 프로세스의 address space와 memory desscriptor, memory region list 사이의 관계를 보여 준다.

 

커널이 자주 하는 일 중 하나는 특정 선형 주소를 포함하는 메모리 구역을 찾는 것이다. 리스트는 정렬 되어 있으므로 메모리 구역의 끝이 지정한 선형 주소 이후에 있는 구역을 찾는 즉시 검색을 종료 한다.

 

 

이러한 리스트 구조는 일반적인 상황에서는 큰 문제가 없지만,

 

객체지향 데이터베이스, malloc() 사용과 관련된 특수한 디버거처럼 수백 내지 수천 구역을 소유하는 큰 응용 프로그램에서는 메모리 구역 리스트를 관리하는 것은 매우 비효율 적이고, 메모리와 관련한 시스템 콜의 성능은 참을 수 없을 만큼 떨어 진다.

 

그래서 리눅스 2.6은 Red_Black Tree라는 자료구조에 Memory descriptor를 저장 한다.

여기서 Node는 보통 left child와 Right child이라는 두 child를 가진다.

 

Read Black tree는 다음과 같은 네가지 규칙을 준수 한다.

 

1) 모든 노드는 빨강이나 검정 중 하나이다.

2) 트리의 Root는 검정이다.

3) Read node의 child는 black이다.

4) 어떤 Node에서 Leaf에 이르는 모든 길에는 같은 수의 black node가 있어야 한다. The number of black node를 셀 때 NULL pointer도 balck node로 계산 한다.

 

Theses four rules ensure that every red0black tree with n internal nodes has a height of at most

 

 

따라서 트리에서 필요한 Node를 찾는 시간은 트리 크기에 로그를 취한 값에 그대로 비례하기 때문에 매우 효율적이다.

 

 

Memory Descirptor 즉 mm_struct의 필드에는

mmap 필드가 있다. 이는 연결 리스트의 head를 포인팅 하게 된다. 모든 Memory region object는 list에서의 다음 object를 포인팅하기 위한 vm_next 필드를 가지고 있다.

 

mm_struct에는 또한 mm_rb 필드가 있다. 이것은 red_back tree의 root를 가리킨다. 모든 memory region object는 rb_node type의 vm_rb 필드에 node의 color와 함께 parent와 left child, right cild에 대한 pointer를 store한다.

 

일반적으로 특정 주소를 포함하는 구역을 찾을 때는 red-black tree를 사용하고, 전체 구역을 검색할 때 연결 리스트가 유용 하다.

 

 

Memory Region versus pages

 

 

 

Page: a set of linear addresses and the data contained in the set of linear addresses

e.g ) the page 0 : linear address interval ranging between 0 and 4095

 

Memory region: consists of a set of pages having consecutive page numbers(of linear addresses)

 

 

 

Memory Region Handling

 

do_mmap() : allocates(enlarge) a linear address interval, After a successful allocation, the memory region could be merged with other memory regions defined for the process.

 

do_munmap(): Delete a linear address interval from the address space of the current process, the intervall to be deleted does not usually correspond to a memory region

 

find_vma(): 지정한 주소에 가장 가까운 구역 찾기

 

find_vma_interseciton() : 지정한 구간과 겹치는 메모리 구역 찾기

 

get_unmapped_area() : 빈 주소 구간 찾기

 

insert_vm_struct() : 메모리 디스크립터 리스트에 구역 삽입

 

 

Page Falut Exception Handler

 

이것의 원인 2가지이다.

프로그래밍 오류

프로세스 주소 공간에 들어 있지만 아직 할당하지 않은 페이지를 참조해서 발생

 

do_page_fault()

page fault interrupt service routine

compares the linear address that caused the page fault against the memory regions of the current process.

 

 

 

 

Page Fault Exception handler

 

bad_area:

do_page_fault()함수가 실행 된다.

process address space에 들어있지 않으면 발생 한다.

사용자 모드에서 에러가 발생하면 current process에 SIGSEGV signal을 보낸다.

 

 

good_area:

 

do_page _falut()는 address가 프로세스의 주소 공간에 속하면 good_area 라벨에 있는 문장 부터 실행 한다.

쓰기 접근 때문에 예외가 발생하였다면 메모리 구역 쓰기 가능인지 검사한다. 쓰기가 가능하지 않으면 bad_area 코드로 점프하고, 쓰기가 가능하면 지역 변수 write를 1로 설정한다.

 

Demand paging

 

이것은 가능한 마지막 순간까지, 즉 프로세스가 램에 존재하지 않는 페이지 주소로 접근하여 페이지 폴트 예외가 발생하는 순간까지 페이지 프레임 할당을 연기하는 동적 메모리 할당 기법이다.

 

 

Copy on Write

 

페이지 프레임을 복사하는 대신에 부모 프로세스와 자식 프로세스 사이에 페이지 프레임을 공유하는 것이다.

그러나 공유하는 동안은 페이지 프레임의 내용을 바꿀 수 없다.

 

 

Process address Space의 Creating과 Deleting

 

clone() , fork(), vfork()

 

 

 

Managing the Heap

 

memory descirptor의 start_brk와 brk 필드가 각각 힙의 시작과 끝주소를 저장 한다.

 

다음 API는 프로세스가 동적 메모리를 요청하고 해지할 때 사용 한다.

 

malloc(size): 동적 메모리를 size바이트만큼 요청한다. 성공적으로 할당하면 시작 메모리 위치의 선형 주소를 반환 한다.

 

calloc(n, size) ; 크기가 size인 요소 n개로 구성된 배열을 요청 한다.

 

realloc

 

free

 

brk(addr): 직접 힙의 크기를 변경한다. addr 매개 변수는 current->mm->brk의 새로운 값을 지정하며, 결과 값은 메모리 구역의 새로운 끝 주소이다.(프로세스는 요청한 addr 값과 결과 값이 일치하는지 검사해야 한다.)

 

sbrk(incr): brk()와 비슷하지만 매개 변수 incr로 지정한 바이트만큼 힙의 크기를 늘리거나 줄인다는 차이가 있다.

 

 

 

 

 

 

 

'Computer Science > Linux Kernel' 카테고리의 다른 글

2장 메모리 주소 지정  (0) 2012.06.07
8장 메모리 관리  (1) 2012.06.07

+ Recent posts