커널에서 사용하는 메모리는 크게 연속적인 메모리 공간과 비연속적인 메모리 할당으로 나누어진다. 연속메모리는 kmalloc()이나 __get_free_pages() 계열의 함수를 통해서 획득할 수 있으며, virt_to_page()나 __pa() 매크로를 통해 쉽게 물리적인 메모리에 대한 정보를 얻을 수 있는 형태이다. 물리적 메모리의 주소를 얻을 수 있는 __pa() 매크로를 보면 다음과 같이 구성되어 있다.
125 #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
여기서 PAGE_OFFSET은 x86의 경우 3G로서 커널의 시작 주소를 나타낸다. 즉, 커널의 시작 주소 부분만이 더해져서 가상 주소가 만들어진 것이 연속적인 메모리의 특징이다.
반면, 비연속적인 메모리는 메모리 요청에 대해서 여기저기 산재해 있는 page들을 묶어서 하나의 가상 주소로 보여주는 형태이기 때문에 가상 주소로부터 물리적인 주소를 바로 알아낼 수 없다.(물론, page table 항목을 조사하면 알수는 있다.)
비연속적인 메모리를 사용하면 외부 단편화에 의해서 사용할 수 없었던 메모리를 사용할 수 있게 된다는 장점이 있다. 즉, 물리적으로 흩어져 있어서 __get_free_pages() 계열의 함수로는 얻을 수 없었던 메모리이지만, 비연속적인 메모리 할당 방식을 사용하면 얻을 수 있다는 것이다. 또한가지 장점은 HighMemory를 접근하기 위한 방법으로도 사용할 수 있다는 점이다.
단점으로는, DMA 연산등에 사용할 수 없다는 점과 page table에 대한 잦은 변화에 따른 속도 저하가 발생한다는 점이다. DMA연산에서는 CPU를 거치지 않기 때문에 물리적인 주소를 제공해야 하는데, page들이 여기저기 흩어져 있기 때문에 주소 변환 작업을 하지 않는 DMA에서는 처리가 불가능하다. 또한, 메모리를 할당하고 해제할 때마다 여기 저기 흩어져 있는 페이지들을 모아서 사용하기 때문에 페이지 테이블에 대한 업데이트가 자주 발생하며, TLB의 캐쉬 정보를 사용하는 효율성이 떨어질 수 도 있다.
PAGE_OFFSET : 커널 가상 메모리의 시작 주소
high_memory : 가상 메모리중 연속적 메모리 공간으로 사용할 수 있는 부분으로 PAGE_OFFSET + 896MB임
VMALLOC_START : vmalloc() 함수에서 사용하는 가상 메모리 주소 공간의 시작 주소로서 high_memory + VMALLOC_OFFSET 위치임
PKMAP_BASE : High Memory를 매핑해서 사용하기 위한 용도의 공간
vm_struct : 비연속 메모리 공간에 대한 Descriptor
addr : 할당된 가상 주소에 대한 정보를 가지고 있음
size : 할당된 메모리의 크기
pages : 실제로 할당된 페이지들에 대한 디스크립터인 page 구조체들의 배열(page들은 물리적으로 연속적이지 않음)
nr_pages : 할당된 페이지의 갯수
phys_addr : 하드웨어 장치에 대한 I/O 공유 메모리를 매핑하기 위한 경우가 아니면 0
비연속 메모리 할당 : vmalloc()
vmalloc() 함수 코드 부분은 불필요한 코드 부분을 생략하기 위해서 2.4.20 버전의 코드를 가지고 설명하겠다.
비연속 메모리 공간을 확보하는 것은 크게 두 단계로 이루어진다.
첫번째는 비연속 메모리로 사용할 수 있는 가상 메모리 공간이 존재하는지 확인(get_vm_area 함수)
다음으로는 할당된 공간에 대해서 실제 물리 메모리 할당 및 페이지 테이블 업데이트
get_vm_area() 함수에서는 VMALLOC_START 부터 시작해서 VMALLOC_END 까지의 사이 중에서 사용 가능한 가상 메모리가 남아 있는지 확인한다. 현재 할당된 메모리들은 vmlist 로부터 단일 링크드 리스트 형태로 연결되어 있으므로 그것을 따라 가면서 확인한다.
vmalloc()에서 사용하는 구조체인 vm_struct 를 만들어야 하는데, 이때 kmalloc() 함수를 이용한다(176번째 줄). 이때, 두번째 인자로 GFP_KERNEL을 사용하는데, 이 때문에 vmalloc() 함수는 메모리 할당 중 블럭킹이 되면 안되는 인터럽트 컨텍스트등에서는 사용하면 안된다.
get_vm_area()에서 리턴된 정보는 단지 사용 가능한 메모리 영역에 대한 부분이고, 실제로 이를 기반으로 페이지를 확보해야 한다. get_vm_area()에서 정상적으로 메모리 주소를 반환했다 하더라도 물리적인 메모리가 존재하지 않는다면 실제 메모리 확보는 이루어지지 않게 된다. vmalloc_area_pages()는 실제 물리적인 메모리를 확보하는 함수이다.
먼저 커널과 연관된 Page Global Directory 테이블중 address에 대응하는 위치를 얻어온다. 다음에는 해당 address에 대응한 pmd를 얻어오며, 없을 경우 새로 확보한다 (2.6의 경우에는 pmd에 앞서 pud에 대한 부분을 같은 방식으로 확보한다).
다음으로 alloc_area_pmd()에서는 Page TAble Entry에 대한 정보를 검색하며 없으면 새로 할당한다 (pte_alloc 함수). 마지막으로 alloc_area_pte()에서는 실제 사용할 메모리에 대한 부분을 확보한다. 이를 위해서 107번째 줄과 같이 alloc_page() 함수를 호출해서 한개의 페이지를 확보하고, 이에 대한 정보를 pte 부분에 업데이트 시킨다(113번째 줄).
이것을 전체 메모리에 대해서 반복하기 때문에, 실제로 확보되는 물리적인 메모리는 페이지 단위로 떨어져 있을 수 있다.
비연속 메모리 공간의 반납 : vfree()
vfree() 함수는 vmalloc()이나 vmalloc32(), vunmap() 함수등에 의해서 확보된 메모리를 반납할 때 사용된다.
vmalloc()에 비해서 훨씬 간단한 구조를 가지고 있다. 주어진 addr에 대해서 기본적인 검사를 수행한 후 vmlist에서 시작되는 전체 비연속 메모리 공간에 대한 구조체들을 조사해서 addr로 시작하는 것을 찾는다.
발견된 vm_struct에 대해서 vmfree_area_pages()를 호출해서 할당되었던 물리 메모리를 해제하고 나서 vm_struct 자체에 대한 공간(kmalloc()으로 할당되었음)도 반납한다.
vmfree_area_pages()에서는 커널에 대한 Page Global Directory에 관련된 Page Middle Directory를 찾으며(2.6은 이것 저것 Page Upper Directory를 먼저 처리한다), 다시 Page Table Entry에 대한 처리를 위해 free_area_pte() 함수를 호출한다. pte를 이용해 연관된 물리 메모리를 찾은 후 (pte_page 함수 사용), 해당 페이지가 사용 가능하며, 제거될 수 있는 형태인지(!PageReserved)를 확인 한 후 __free_Page 함수를 불러 메모리를 반납한다. 이 작업을 반납해야 할 전체 메모리에 대해서 반복 수행한다.
125 #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
여기서 PAGE_OFFSET은 x86의 경우 3G로서 커널의 시작 주소를 나타낸다. 즉, 커널의 시작 주소 부분만이 더해져서 가상 주소가 만들어진 것이 연속적인 메모리의 특징이다.
반면, 비연속적인 메모리는 메모리 요청에 대해서 여기저기 산재해 있는 page들을 묶어서 하나의 가상 주소로 보여주는 형태이기 때문에 가상 주소로부터 물리적인 주소를 바로 알아낼 수 없다.(물론, page table 항목을 조사하면 알수는 있다.)
비연속적인 메모리를 사용하면 외부 단편화에 의해서 사용할 수 없었던 메모리를 사용할 수 있게 된다는 장점이 있다. 즉, 물리적으로 흩어져 있어서 __get_free_pages() 계열의 함수로는 얻을 수 없었던 메모리이지만, 비연속적인 메모리 할당 방식을 사용하면 얻을 수 있다는 것이다. 또한가지 장점은 HighMemory를 접근하기 위한 방법으로도 사용할 수 있다는 점이다.
단점으로는, DMA 연산등에 사용할 수 없다는 점과 page table에 대한 잦은 변화에 따른 속도 저하가 발생한다는 점이다. DMA연산에서는 CPU를 거치지 않기 때문에 물리적인 주소를 제공해야 하는데, page들이 여기저기 흩어져 있기 때문에 주소 변환 작업을 하지 않는 DMA에서는 처리가 불가능하다. 또한, 메모리를 할당하고 해제할 때마다 여기 저기 흩어져 있는 페이지들을 모아서 사용하기 때문에 페이지 테이블에 대한 업데이트가 자주 발생하며, TLB의 캐쉬 정보를 사용하는 효율성이 떨어질 수 도 있다.
PAGE_OFFSET : 커널 가상 메모리의 시작 주소
high_memory : 가상 메모리중 연속적 메모리 공간으로 사용할 수 있는 부분으로 PAGE_OFFSET + 896MB임
VMALLOC_START : vmalloc() 함수에서 사용하는 가상 메모리 주소 공간의 시작 주소로서 high_memory + VMALLOC_OFFSET 위치임
82 #define VMALLOC_OFFSET (8*1024*1024)
83 #define VMALLOC_START (((unsigned long) high_memory + vmalloc_earlyreserve + \
84 2*VMALLOC_OFFSET-1) & ~(VMALLOC_OFFSET-1))
85 #ifdef CONFIG_HIGHMEM
86 # define VMALLOC_END (PKMAP_BASE-2*PAGE_SIZE)
87 #else
88 # define VMALLOC_END (FIXADDR_START-2*PAGE_SIZE)
89 #endif
83 #define VMALLOC_START (((unsigned long) high_memory + vmalloc_earlyreserve + \
84 2*VMALLOC_OFFSET-1) & ~(VMALLOC_OFFSET-1))
85 #ifdef CONFIG_HIGHMEM
86 # define VMALLOC_END (PKMAP_BASE-2*PAGE_SIZE)
87 #else
88 # define VMALLOC_END (FIXADDR_START-2*PAGE_SIZE)
89 #endif
PKMAP_BASE : High Memory를 매핑해서 사용하기 위한 용도의 공간
vm_struct : 비연속 메모리 공간에 대한 Descriptor
25 struct vm_struct {
26 /* keep next,addr,size together to speedup lookups */
27 struct vm_struct *ne
xt;
28 void *addr;
29 unsigned long size;
30 unsigned long flags;
31 struct page **pages;
32 unsigned int nr_pages;
33 unsigned long phys_addr;
34 };
26 /* keep next,addr,size together to speedup lookups */
27 struct vm_struct *ne
xt;
28 void *addr;
29 unsigned long size;
30 unsigned long flags;
31 struct page **pages;
32 unsigned int nr_pages;
33 unsigned long phys_addr;
34 };
addr : 할당된 가상 주소에 대한 정보를 가지고 있음
size : 할당된 메모리의 크기
pages : 실제로 할당된 페이지들에 대한 디스크립터인 page 구조체들의 배열(page들은 물리적으로 연속적이지 않음)
nr_pages : 할당된 페이지의 갯수
phys_addr : 하드웨어 장치에 대한 I/O 공유 메모리를 매핑하기 위한 경우가 아니면 0
비연속 메모리 할당 : vmalloc()
34 static inline void * vmalloc (unsigned long size)
35 {
36 return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
37 }
235 void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot)
236 {
237 void * addr;
238 struct vm_struct *area;
239
240 size = PAGE_ALIGN(size);
241 if (!size || (size >> PAGE_SHIFT) > num_physpages) {
242 BUG();
243 return NULL;
244 }
245 area = get_vm_area(size, VM_ALLOC);
246 if (!area)
247 return NULL;
248 addr = area->addr;
249 if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, gfp_mask, prot)) {
250 vfree(addr);
251 return NULL;
252 }
253 return addr;
254 }
255
35 {
36 return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
37 }
235 void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot)
236 {
237 void * addr;
238 struct vm_struct *area;
239
240 size = PAGE_ALIGN(size);
241 if (!size || (size >> PAGE_SHIFT) > num_physpages) {
242 BUG();
243 return NULL;
244 }
245 area = get_vm_area(size, VM_ALLOC);
246 if (!area)
247 return NULL;
248 addr = area->addr;
249 if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, gfp_mask, prot)) {
250 vfree(addr);
251 return NULL;
252 }
253 return addr;
254 }
255
vmalloc() 함수 코드 부분은 불필요한 코드 부분을 생략하기 위해서 2.4.20 버전의 코드를 가지고 설명하겠다.
비연속 메모리 공간을 확보하는 것은 크게 두 단계로 이루어진다.
첫번째는 비연속 메모리로 사용할 수 있는 가상 메모리 공간이 존재하는지 확인(get_vm_area 함수)
다음으로는 할당된 공간에 대해서 실제 물리 메모리 할당 및 페이지 테이블 업데이트
171 struct vm_struct * get_vm_area(unsigned long size, unsigned long flags)
172 {
173 unsigned long addr;
174 struct vm_struct **p, *tmp, *area;
175
176 area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
177 if (!area)
178 return NULL;
179
180 size += PAGE_SIZE;
181 if (!size) {
182 kfree (area);
183 return NULL;
184 }
185
186 addr = VMALLOC_START;
187 write_lock(&vmlist_lock);
188 for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
189 if ((size + addr) < addr)
190 goto out;
191 if (size + addr <= (unsigned long) tmp->addr)
192 break;
193 addr = tmp->size + (unsigned long) tmp->addr;
194 if (addr > VMALLOC_END-size)
195 goto out;
196 }
197 area->flags = flags;
198 area->addr = (void *)addr;
199 area->size = size;
200 area->next = *p;
201 *p = area;
202 write_unlock(&vmlist_lock);
203 return area;
204
205 out:
206 write_unlock(&vmlist_lock);
207 kfree(area);
208 return NULL;
209 }
172 {
173 unsigned long addr;
174 struct vm_struct **p, *tmp, *area;
175
176 area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
177 if (!area)
178 return NULL;
179
180 size += PAGE_SIZE;
181 if (!size) {
182 kfree (area);
183 return NULL;
184 }
185
186 addr = VMALLOC_START;
187 write_lock(&vmlist_lock);
188 for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
189 if ((size + addr) < addr)
190 goto out;
191 if (size + addr <= (unsigned long) tmp->addr)
192 break;
193 addr = tmp->size + (unsigned long) tmp->addr;
194 if (addr > VMALLOC_END-size)
195 goto out;
196 }
197 area->flags = flags;
198 area->addr = (void *)addr;
199 area->size = size;
200 area->next = *p;
201 *p = area;
202 write_unlock(&vmlist_lock);
203 return area;
204
205 out:
206 write_unlock(&vmlist_lock);
207 kfree(area);
208 return NULL;
209 }
get_vm_area() 함수에서는 VMALLOC_START 부터 시작해서 VMALLOC_END 까지의 사이 중에서 사용 가능한 가상 메모리가 남아 있는지 확인한다. 현재 할당된 메모리들은 vmlist 로부터 단일 링크드 리스트 형태로 연결되어 있으므로 그것을 따라 가면서 확인한다.
vmalloc()에서 사용하는 구조체인 vm_struct 를 만들어야 하는데, 이때 kmalloc() 함수를 이용한다(176번째 줄). 이때, 두번째 인자로 GFP_KERNEL을 사용하는데, 이 때문에 vmalloc() 함수는 메모리 할당 중 블럭킹이 되면 안되는 인터럽트 컨텍스트등에서는 사용하면 안된다.
140 inline int vmalloc_area_pages (unsigned long address, unsigned long size,
141 int gfp_mask, pgprot_t prot)
142 {
143 pgd_t * dir;
144 unsigned long end = address + size;
145 int ret;
146
147 dir = pgd_offset_k(address);
148 spin_lock(&init_mm.page_table_lock);
149 do {
150 pmd_t *pmd;
151
152 pmd = pmd_alloc(&init_mm, dir, address);
153 ret = -ENOMEM;
154 if (!pmd)
155 break;
156
157 ret = -ENOMEM;
158 if (alloc_area_pmd(pmd, address, end - address, gfp_mask, prot))
159 break;
160
161 address = (address + PGDIR_SIZE) & PGDIR_MASK;
162 dir++;
163
164 ret = 0;
165 } while (address && (address < end));
166 spin_unlock(&init_mm.page_table_lock);
167 flush_cache_all();
168 return ret;
169 }
141 int gfp_mask, pgprot_t prot)
142 {
143 pgd_t * dir;
144 unsigned long end = address + size;
145 int ret;
146
147 dir = pgd_offset_k(address);
148 spin_lock(&init_mm.page_table_lock);
149 do {
150 pmd_t *pmd;
151
152 pmd = pmd_alloc(&init_mm, dir, address);
153 ret = -ENOMEM;
154 if (!pmd)
155 break;
156
157 ret = -ENOMEM;
158 if (alloc_area_pmd(pmd, address, end - address, gfp_mask, prot))
159 break;
160
161 address = (address + PGDIR_SIZE) & PGDIR_MASK;
162 dir++;
163
164 ret = 0;
165 } while (address && (address < end));
166 spin_unlock(&init_mm.page_table_lock);
167 flush_cache_all();
168 return ret;
169 }
get_vm_area()에서 리턴된 정보는 단지 사용 가능한 메모리 영역에 대한 부분이고, 실제로 이를 기반으로 페이지를 확보해야 한다. get_vm_area()에서 정상적으로 메모리 주소를 반환했다 하더라도 물리적인 메모리가 존재하지 않는다면 실제 메모리 확보는 이루어지지 않게 된다. vmalloc_area_pages()는 실제 물리적인 메모리를 확보하는 함수이다.
먼저 커널과 연관된 Page Global Directory 테이블중 address에 대응하는 위치를 얻어온다. 다음에는 해당 address에 대응한 pmd를 얻어오며, 없을 경우 새로 확보한다 (2.6의 경우에는 pmd에 앞서 pud에 대한 부분을 같은 방식으로 확보한다).
120 static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, int gfp_mask, pgprot_t prot)
121 {
122 unsigned long end;
123
124 address &= ~PGDIR_MASK;
125 end = address + size;
126 if (end > PGDIR_SIZE)
127 end = PGDIR_SIZE;
128 do {
129 pte_t * pte = pte_alloc(&init_mm, pmd, address);
130 if (!pte)
131 return -ENOMEM;
132 if (alloc_area_pte(pte, address, end - address, gfp_mask, prot))
133 return -ENOMEM;
134 address = (address + PMD_SIZE) & PMD_MASK;
135 pmd++;
136 } while (address < end);
137 return 0;
138 }
95 static inline int alloc_area_pte (pte_t * pte, unsigned long address,
96 unsigned long size, int gfp_mask, pgprot_t prot)
97 {
98 unsigned long end;
99
100 address &= ~PMD_MASK;
101 end = address + size;
102 if (end > PMD_SIZE)
103 end = PMD_SIZE;
104 do {
105 struct page * page;
106 spin_unlock(&init_mm.page_table_lock);
107 page = alloc_page(gfp_mask);
108 spin_lock(&init_mm.page_table_lock);
109 if (!pte_none(*pte))
110 printk(KERN_ERR "alloc_area_pte: page already exists\n");
111 if (!page)
112 return -ENOMEM;
113 set_pte(pte, mk_pte(page, prot));
114 address += PAGE_SIZE;
115 pte++;
116 } while (address < end);
117 return 0;
118 }
121 {
122 unsigned long end;
123
124 address &= ~PGDIR_MASK;
125 end = address + size;
126 if (end > PGDIR_SIZE)
127 end = PGDIR_SIZE;
128 do {
129 pte_t * pte = pte_alloc(&init_mm, pmd, address);
130 if (!pte)
131 return -ENOMEM;
132 if (alloc_area_pte(pte, address, end - address, gfp_mask, prot))
133 return -ENOMEM;
134 address = (address + PMD_SIZE) & PMD_MASK;
135 pmd++;
136 } while (address < end);
137 return 0;
138 }
95 static inline int alloc_area_pte (pte_t * pte, unsigned long address,
96 unsigned long size, int gfp_mask, pgprot_t prot)
97 {
98 unsigned long end;
99
100 address &= ~PMD_MASK;
101 end = address + size;
102 if (end > PMD_SIZE)
103 end = PMD_SIZE;
104 do {
105 struct page * page;
106 spin_unlock(&init_mm.page_table_lock);
107 page = alloc_page(gfp_mask);
108 spin_lock(&init_mm.page_table_lock);
109 if (!pte_none(*pte))
110 printk(KERN_ERR "alloc_area_pte: page already exists\n");
111 if (!page)
112 return -ENOMEM;
113 set_pte(pte, mk_pte(page, prot));
114 address += PAGE_SIZE;
115 pte++;
116 } while (address < end);
117 return 0;
118 }
다음으로 alloc_area_pmd()에서는 Page TAble Entry에 대한 정보를 검색하며 없으면 새로 할당한다 (pte_alloc 함수). 마지막으로 alloc_area_pte()에서는 실제 사용할 메모리에 대한 부분을 확보한다. 이를 위해서 107번째 줄과 같이 alloc_page() 함수를 호출해서 한개의 페이지를 확보하고, 이에 대한 정보를 pte 부분에 업데이트 시킨다(113번째 줄).
이것을 전체 메모리에 대해서 반복하기 때문에, 실제로 확보되는 물리적인 메모리는 페이지 단위로 떨어져 있을 수 있다.
비연속 메모리 공간의 반납 : vfree()
vfree() 함수는 vmalloc()이나 vmalloc32(), vunmap() 함수등에 의해서 확보된 메모리를 반납할 때 사용된다.
211 void vfree(void * addr)
212 {
213 struct vm_struct **p, *tmp;
214
215 if (!addr)
216 return;
217 if ((PAGE_SIZE-1) & (unsigned long) addr) {
218 printk(KERN_ERR "Trying to vfree() bad address (%p)\n", addr);
219 return;
220 }
221 write_lock(&vmlist_lock);
222 for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
223 if (tmp->addr == addr) {
224 *p = tmp->next;
225 vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
226 write_unlock(&vmlist_lock);
227 kfree(tmp);
228 return;
229 }
230 }
231 write_unlock(&vmlist_lock);
232 printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n", addr);
233 }
212 {
213 struct vm_struct **p, *tmp;
214
215 if (!addr)
216 return;
217 if ((PAGE_SIZE-1) & (unsigned long) addr) {
218 printk(KERN_ERR "Trying to vfree() bad address (%p)\n", addr);
219 return;
220 }
221 write_lock(&vmlist_lock);
222 for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
223 if (tmp->addr == addr) {
224 *p = tmp->next;
225 vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
226 write_unlock(&vmlist_lock);
227 kfree(tmp);
228 return;
229 }
230 }
231 write_unlock(&vmlist_lock);
232 printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n", addr);
233 }
vmalloc()에 비해서 훨씬 간단한 구조를 가지고 있다. 주어진 addr에 대해서 기본적인 검사를 수행한 후 vmlist에서 시작되는 전체 비연속 메모리 공간에 대한 구조체들을 조사해서 addr로 시작하는 것을 찾는다.
발견된 vm_struct에 대해서 vmfree_area_pages()를 호출해서 할당되었던 물리 메모리를 해제하고 나서 vm_struct 자체에 대한 공간(kmalloc()으로 할당되었음)도 반납한다.
80 void vmfree_area_pages(unsigned long address, unsigned long size)
81 {
82 pgd_t * dir;
83 unsigned long end = address + size;
84
85 dir = pgd_offset_k(address);
86 flush_cache_all();
87 do {
88 free_area_pmd(dir, address, end - address);
89 address = (address + PGDIR_SIZE) & PGDIR_MASK;
90 dir++;
91 } while (address && (address < end));
92 flush_tlb_all();
93 }
56 static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size)
57 {
58 pmd_t * pmd;
59 unsigned long end;
60
61 if (pgd_none(*dir))
62 return;
63 if (pgd_bad(*dir)) {
64 pgd_ERROR(*dir);
65 pgd_clear(dir);
66 return;
67 }
68 pmd = pmd_offset(dir, address);
69 address &= ~PGDIR_MASK;
70 end = address + size;
71 if (end > PGDIR_SIZE)
72 end = PGDIR_SIZE;
73 do {
74 free_area_pte(pmd, address, end - address);
75 address = (address + PMD_SIZE) & PMD_MASK;
76 pmd++;
77 } while (address < end);
78 }
22 static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
23 {
24 pte_t * pte;
25 unsigned long end;
26
27 if (pmd_none(*pmd))
28 return;
29 if (pmd_bad(*pmd)) {
30 pmd_ERROR(*pmd);
31 pmd_clear(pmd);
32 return;
33 }
34 pte = pte_offset(pmd, address);
35 address &= ~PMD_MASK;
36 end = address + size;
37 if (end > PMD_SIZE)
38 end = PMD_SIZE;
39 do {
40 pte_t page;
41 page = ptep_get_and_clear(pte);
42 address += PAGE_SIZE;
43 pte++;
44 if (pte_none(page))
45 continue;
46 if (pte_present(page)) {
47 struct page *ptpage = pte_page(page);
48 if (VALID_PAGE(ptpage) && (!PageReserved(ptpage)))
49 __free_page(ptpage);
50 continue;
51 }
52 printk(KERN_CRIT "Whee.. Swapped out page in kernel page table\n");
53 } while (address < end);
54 }
81 {
82 pgd_t * dir;
83 unsigned long end = address + size;
84
85 dir = pgd_offset_k(address);
86 flush_cache_all();
87 do {
88 free_area_pmd(dir, address, end - address);
89 address = (address + PGDIR_SIZE) & PGDIR_MASK;
90 dir++;
91 } while (address && (address < end));
92 flush_tlb_all();
93 }
56 static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size)
57 {
58 pmd_t * pmd;
59 unsigned long end;
60
61 if (pgd_none(*dir))
62 return;
63 if (pgd_bad(*dir)) {
64 pgd_ERROR(*dir);
65 pgd_clear(dir);
66 return;
67 }
68 pmd = pmd_offset(dir, address);
69 address &= ~PGDIR_MASK;
70 end = address + size;
71 if (end > PGDIR_SIZE)
72 end = PGDIR_SIZE;
73 do {
74 free_area_pte(pmd, address, end - address);
75 address = (address + PMD_SIZE) & PMD_MASK;
76 pmd++;
77 } while (address < end);
78 }
22 static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
23 {
24 pte_t * pte;
25 unsigned long end;
26
27 if (pmd_none(*pmd))
28 return;
29 if (pmd_bad(*pmd)) {
30 pmd_ERROR(*pmd);
31 pmd_clear(pmd);
32 return;
33 }
34 pte = pte_offset(pmd, address);
35 address &= ~PMD_MASK;
36 end = address + size;
37 if (end > PMD_SIZE)
38 end = PMD_SIZE;
39 do {
40 pte_t page;
41 page = ptep_get_and_clear(pte);
42 address += PAGE_SIZE;
43 pte++;
44 if (pte_none(page))
45 continue;
46 if (pte_present(page)) {
47 struct page *ptpage = pte_page(page);
48 if (VALID_PAGE(ptpage) && (!PageReserved(ptpage)))
49 __free_page(ptpage);
50 continue;
51 }
52 printk(KERN_CRIT "Whee.. Swapped out page in kernel page table\n");
53 } while (address < end);
54 }
vmfree_area_pages()에서는 커널에 대한 Page Global Directory에 관련된 Page Middle Directory를 찾으며(2.6은 이것 저것 Page Upper Directory를 먼저 처리한다), 다시 Page Table Entry에 대한 처리를 위해 free_area_pte() 함수를 호출한다. pte를 이용해 연관된 물리 메모리를 찾은 후 (pte_page 함수 사용), 해당 페이지가 사용 가능하며, 제거될 수 있는 형태인지(!PageReserved)를 확인 한 후 __free_Page 함수를 불러 메모리를 반납한다. 이 작업을 반납해야 할 전체 메모리에 대해서 반복 수행한다.
'Linux > Memory' 카테고리의 다른 글
| 사용자 메모리 공간 (0) | 2007/10/08 |
|---|---|
| 비연속 메모리 영역 관리 (0) | 2007/10/08 |
이올린에 북마크하기
이올린에 추천하기

