Linux kernel physical memory allocator (Buddy) - Part 3


오늘은 할당에 이어 해제과정의 소스 분석을 해본다.


현재 쓰는 시점에서 lxr.linux.no 가 접속 되지 않아 함수에 link 를 붙이지 못했다.


Free 의 호출 순서는 아래의 그림과 같다.




free_pages() 는 parameter 로 virtual address 와 order 를 받게 된다. 이 virtual address 를 page 단위로 변환하고 order 는 size 에 준하여 계산하면 된다.


위의 그림에는 나와있지 않지만 free_pages() 와 __free_pages_ok() 함수가 호출 되는 과정에 free_hot_cold_page() 함수 가 있다. 이는 order 0 일 경우에만 동작하는데, part 2 에서 할당하는 과정에 order 0 일 때, per_cpu_pages 구조체에 있는 page 를 갖고오는 code를 기억할 것이다. 이런 경우(?)는 자세히 다루진 않겠지만 대략적으로 order 0의 cold 한 page 를 per_cpu_pages 구조체에 갖고 있다가 필요한 경우 가져다 쓰는 방식으로 하는 것이 아닐까 한다. 


암튼, __free_pages_ok() ==> free_one_page() ==> __free_one_page() 순으로 호출된다.


__free_one_page() 함수의 소스를 보자.

(한꺼번에 분석하지 않고 적당한 line에서 나눈 것이다.)


이 함수는 parameter 가 4개이다. 이 때까지 migratetype은 신경 안썼으니 pass 하자. 

첫번째 parameter 는 free_pages 로 넘어온 virtual address 를 page 로 변환한 주소를 넘겨 받았을 것이다.

그리고 해제 하려는 size 를 받았을 것이고 size 를 order(세번째 parameter) 로 변환하여 받는다.


line 16 에서 page_idx (page frame number) 를 구하는 공식이다. 

간략히 살펴 보면 __page_to_pfn(page) 를 보면 (page 주소 - mem_map) 을 한뒤 ARCH_PFN_OFFSET 을 더해주고 return 해준다. 


ARCH_PFN_OFFSET 은 대게 물리메모리 시작 주소가 0이 아닌 경우에 사용된다. 

(예를 들어 물리 메모리의 시작 번지가 8192 이고 그 시스템의 PAGE size가 4096 이라면 ARCH_PFN_OFFSET 은 2 가된다.) 여기서 mem_map 은 간략히 시스템이 갖고 있는 물리 메모리를 page 단위로 mapping 해놓은 map이다. 그렇다는 것은 mem_map(struct page* 형) 을 현재 page 주소값에다 빼주면 말 그대로 page 가 mem_map 으로 부터 몇 번째 있는 page 인지 알 수 있는 것이다.


현재 page_idx = PFN & ((1 << 11) -1); // 11은 MAX_ORDER 값이다.

다시 적으면 page_idx = PFN & 0x1FFFF 가 된다. pfn max 131071 인 듯.(정확히 왜 제한을 두는 것인지 확인 하지 못했음).


line 21 현재 order 에서 MAX_ORDER - 1까지 loop 을 수행하면서, 

line 22 __find_buddy_index() 를 통해 buddy page frame number를 구해 온다. 

part 2-1 을 보면 알겠지만, buddy 와 함체를 위해 찾는 것이다. 

현재 free 하려는 index 가 2 이고 order 가 1이라면, 2 ^ (1 << 1) = 0 이 된다. 즉 order 1(page 2개가 묶음)에서 2번 page frame의 buddy는 0번 page frame이다. 


line 23 page index(page frame number) 기준으로 page 구조체 주소를 얻어 올 수 있다. 현재 해제 하려는 기준의 page 구조체는 parameter 로 받아왔으니 buddy 의 page 구조체를 구하는 것은 index 를 더하고 빼면 나올 것이다.


line 24 page_is_buddy() 함수에서 몇 가지를 check 한다.

  a. buddy page 가 유효한 page 인지 확인

  b. 현재 해제 요청한 page 와 buddy 가 같은 zone 에 있는 지 확인.

  c. buddy 의 order 가 해제 요청한 page 의 order 와 같은지 비교 및 guard 상태인지 확인.

      guard 상태의 확인은 kernel option에서 CONFIG_DEBUG_PAGEALLOC 가 enable 되어 있다면 뭔가 정보를 확인하겠지만 아니라면 무조건 false 를 return 한다.


buddy 가 맞고, 같은 order 의 free_list 에 있다면,

line 35~42 free_area[order] 의 nr_free 값을 하나 줄이고 buddy 의 page 속성 중 lru list 에서만 제거한다. 

  merge 는 위에서 보듯이 order 1의 index 2 의 요청이면 buddy가 0번 page 일 테고, merge 가 되면 0번 page 가 index 로 된다.(합쳐 졌으니)

그리고 상위 order 로 이동하여 위의 작업을 다시 하고, buddy check 에서 buddy가 아니거나 없으면 그만 둔다. 이 while loop 에서 최대한 상위 order 로의 merge 가 완료 된 후, 다음에 설명할 code 에서 실제 free_area 구조체에 정보를 update 한다.


free_area 정보 update code.


사실 위의 while 문 내부에서 현재 해제 시도에서 최상위 merge 까지 했다면 line 22 에 list_add 의 단 한 줄이면 정리가 끝난다. line 9~20 에 있는 내용이 잘 이해가 되지 않는다. :( (이부분의 내용도 __free_one_page() 함수 내부이다.) 그래도 code의 내용만을 보자면, 현재 계산된 order 가 합쳐질 수 있는 최상의 order 가 맞는지 확인하고(order 9), pfn_valid_within() 함수로 pfn 이 zone 내부에 있는 지 확인하는 것인데, zone 내부에 memory hole을 포함하고 있지 않다면 무조건 1 을 return 한다. 

line 10 ~ 15 위의 while 문과 아주 비슷한 행동을 하는데, 실제로 상위 order 의 free_area 를 확인해서 뭔가 처리하는 code가 아닌 그냥 확인용(?) 같은 느낌이 든다. 

line 15 에서 page_is_buddy 로 현재 구해진 order 에 +1 하여 상위 order 에 page buddy 를 확인해서 buddy page 가 같은 order 에 있는 상태를 확인했음에도 line 16 에서 free_area는 order + 1이 아니라 order 를 갖고 와서 적용한다. 한번 고민을 해봐야 할 듯 한 code이다.


일단 line 22 를 통해 계산된 order 의 free_area[order].free_list 에 page 를 잘 붙여 준다.


여기까지 buddy 초기화/할당/해제 를 알아봤다.


다음에는 할당하는 과정에서 memory 가 부족했을 경우에 수행되는 code를 살펴 보고자 한다.



+ Recent posts