커널 메모리에 대한 관리와 사용자 프로세스에 대한 메모리 관리는 극명한 차이를 보인다.
커널 메모리의 경우에는 다음과 같은 특징으로 즉각적인 메모리 처리를 해준다.
- 커널은 운영체에의 가장 높은 우선순위 요소이다. 즉, 즉각적인 메모리 할당이 필요하다.
- 커널은 스스롤 믿는다. 커널 내부의 코드에 대해서 메모리 관리 부분에서 오류 처리를 하지 않음
반면에 사용자 모드 프로세스들을 위한 메모리 할당은 전혀 다른 상황이다.
- 동적 메모리 할당이 급하지 않다고 생각함. 메모리 할당을 최대한 미룬다
- 사용자 프로그램을 믿지 않기 때문에, 사용자 모드에서의 주소 접근에 대한 에러 처리를 항상 준비함
프로세스에 대한 주소 공간
각 프로세스에 대해서 메모리는 vm_area_struct라는 구조체 형태로 해서 할당된 메모리 블럭들을 관리한다. 각 메모리 블럭이 가리키는 가상 주소는 PAGE_SIZE(4096)의 배수 위치에서 시작해야 하며, 크기도 PAGE_SIZE의 배수이어야 한다.
새로운 메모리 공간 즉, vm_area_struct가 확보되는 경우들로는 다음과 같은 경우들이 있다.
- 콘솔에서 명령을 입력해서 새로운 프로세스를 만드는 경우
- 현재 수행중인 프로그램이 exec를 통해서 새로운 프로그램을 적재할 경우
- 파일에 대한 메모리 매핑을 수행할 경우
- 현재 할당되어 있는 스택양을 넘어서는 스택 사용이 있을 경우(재귀호출, 지역변수 선언...)
- 프로세스간 공유 메모리를 만들었을 경우
- malloc() 과 같은 함수를 사용해서 동적 영역(힙)을 증가시켰을 경우
위와 관련된 유저 모드에서의 메모리 관련 함수들은 다음과 같다.
brk(), execve(), _exit(), fork(), mmap(), mmap2(), mremap(), remap_file_pages(), munmap(), shmat(), shmdt()
위의 함수들 중에서 실질적인 메모리 할당과 연관된 함수들로는 brk()와 mmap() 함수 등이다. 하지만, 이 함수들도 물리적 메모리를 확보하지는 않으며, 단지 vm_area_struct 구조체를 새로 구성하는 일만 수행한다.
vm_area_struct는 나중에 실제 메모리를 할당하는 부분인 Page Fault Handler 부분에서 사용된다. 이것을 이용해 페이지 폴트 핸들러는 다음 두가지 상황을 파악하게 된다.
- 프로그래밍 오류에 의해서 발생한 경우
- 페이지가 확보되지 않아서 발생한 경우 (이 경우 정상적인 메모리 접근이므로 해당 메모리를 확보해준다)
메모리 디스크립터(mm_struct)
프로세스의 메모리에 대한 모든 정보를 관리하는 구조체로서 task_struct 구조체내에서 mm 필드가 보관하고 있다. mm_struct 구조체의 내용은 다음과 같다.
시스템에 존재하는 모든 mm_struct 들은 이중 링크드 리스트로 연결되어 있으며, 이에 대한 정보는 mm_struct 안의 mmlist 필드에 의해서 관리된다. 전체 리스트를 살펴보고자 한다면 init_mm 변수의 mmlist 필드를 사용하면 된다.
init_mm 변수는 0번 프로세스에 대한 메모리 정보를 관리하는 것으로서 처음 부팅시에 사용된다.
mm_struct 구조체를 확보하기 위해서는 mm_alloc() 함수가 사용된다. 이것은 실제로는 kmem_cache_alloc()를 호출하는 다음과 같은 코드로 구성되어 있다.
mm_struct 해제는 다음 함수를 사용한다.
mm_users 필드의 값을 하나 감소시키며, 이 값이 0인 경우에만 실제로 해당 구조체를 제거한다. 물론, 여기에서도 mm_count 필드를 다시 감소시키며, 이게 0이 되어야 실질적인 해제가 이루어진다.
커널 쓰레드에 대한 메모리 디스크립터
커널 쓰레드는 커널 모드에서 수행되기 때문에 TASK_SIZE 아래쪽 메모리를 접근할 일은 없다. 따라서, vm_area_struct 형태의 정보도 관리할 필요가 없다. 하지만, 불필요한 TLB와 캐쉬 플러쉬를 방지하기 위해서 커널 쓰레드인 경우에는 이전에 수행하던 유저모드 프로세스의 메모리 정보를 그대로 유지하게 된다.
유저 모드 프로세스에서는 mm과 active_mm이 모두 자신이 관리하는 mm_struct를 가리키고 있다. 하지만, 커널 쓰레드의 mm 필드는 NULL 이며, active_mm 필드가 이전 프로세스의 active_mm 값을 가지고 있게 된다. 이렇게 되면, 결국 이전에 마지막으로 수행된 유저 프로세스의 mm값을 가리키게 된다.
커널 메모리의 경우에는 다음과 같은 특징으로 즉각적인 메모리 처리를 해준다.
- 커널은 운영체에의 가장 높은 우선순위 요소이다. 즉, 즉각적인 메모리 할당이 필요하다.
- 커널은 스스롤 믿는다. 커널 내부의 코드에 대해서 메모리 관리 부분에서 오류 처리를 하지 않음
반면에 사용자 모드 프로세스들을 위한 메모리 할당은 전혀 다른 상황이다.
- 동적 메모리 할당이 급하지 않다고 생각함. 메모리 할당을 최대한 미룬다
- 사용자 프로그램을 믿지 않기 때문에, 사용자 모드에서의 주소 접근에 대한 에러 처리를 항상 준비함
프로세스에 대한 주소 공간
각 프로세스에 대해서 메모리는 vm_area_struct라는 구조체 형태로 해서 할당된 메모리 블럭들을 관리한다. 각 메모리 블럭이 가리키는 가상 주소는 PAGE_SIZE(4096)의 배수 위치에서 시작해야 하며, 크기도 PAGE_SIZE의 배수이어야 한다.
새로운 메모리 공간 즉, vm_area_struct가 확보되는 경우들로는 다음과 같은 경우들이 있다.
- 콘솔에서 명령을 입력해서 새로운 프로세스를 만드는 경우
- 현재 수행중인 프로그램이 exec를 통해서 새로운 프로그램을 적재할 경우
- 파일에 대한 메모리 매핑을 수행할 경우
- 현재 할당되어 있는 스택양을 넘어서는 스택 사용이 있을 경우(재귀호출, 지역변수 선언...)
- 프로세스간 공유 메모리를 만들었을 경우
- malloc() 과 같은 함수를 사용해서 동적 영역(힙)을 증가시켰을 경우
위와 관련된 유저 모드에서의 메모리 관련 함수들은 다음과 같다.
brk(), execve(), _exit(), fork(), mmap(), mmap2(), mremap(), remap_file_pages(), munmap(), shmat(), shmdt()
위의 함수들 중에서 실질적인 메모리 할당과 연관된 함수들로는 brk()와 mmap() 함수 등이다. 하지만, 이 함수들도 물리적 메모리를 확보하지는 않으며, 단지 vm_area_struct 구조체를 새로 구성하는 일만 수행한다.
vm_area_struct는 나중에 실제 메모리를 할당하는 부분인 Page Fault Handler 부분에서 사용된다. 이것을 이용해 페이지 폴트 핸들러는 다음 두가지 상황을 파악하게 된다.
- 프로그래밍 오류에 의해서 발생한 경우
- 페이지가 확보되지 않아서 발생한 경우 (이 경우 정상적인 메모리 접근이므로 해당 메모리를 확보해준다)
메모리 디스크립터(mm_struct)
프로세스의 메모리에 대한 모든 정보를 관리하는 구조체로서 task_struct 구조체내에서 mm 필드가 보관하고 있다. mm_struct 구조체의 내용은 다음과 같다.
310 struct mm_struct {
311 struct vm_area_struct * mmap; /* vm_area_struct 구조체 리스트의 첫 항목 */
312 struct rb_root mm_rb; /* red-black tree의 루트 노드 */
313 struct vm_area_struct * mmap_cache; /* 마지막으로 호출된 find_vma() 함수가 리턴한 값*/
314 unsigned long (*get_unmapped_area) (struct file *filp,
315 unsigned long addr, unsigned long len,
316 unsigned long pgoff, unsigned long flags);
317 void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
318 unsigned long mmap_base; /* base of mmap area */
319 unsigned long task_size; /* size of task vm space */
320 unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */
321 unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */
322 pgd_t * pgd;
323 atomic_t mm_users; /* How many users with user space? */
324 atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
325 int map_count; /* number of VMAs */
326 struct rw_semaphore mmap_sem;
327 spinlock_t page_table_lock; /* Protects page tables and some counters */
328
329 struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung
330 * together off init_mm.mmlist, and are protected
331 * by mmlist_lock
332 */
333
334 /* Special counters, in some configurations protected by the
335 * page_table_lock, in other configurations by being atomic.
336 */
337 mm_counter_t _file_rss;
338 mm_counter_t _anon_rss;
339
340 unsigned long hiwater_rss; /* High-watermark of RSS usage */
341 unsigned long hiwater_vm; /* High-water virtual memory usage */
342
343 unsigned long total_vm, locked_vm, shared_vm, exec_vm;
344 unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
345 unsigned long start_code, end_code, start_data, end_data;
346 unsigned long start_brk, brk, start_stack;
347 unsigned long arg_start, arg_end, env_start, env_end;
348
349 unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
350
351 cpumask_t cpu_vm_mask;
352
353 /* Architecture-specific MM context */
354 mm_context_t context;
355
356 /* Swap token stuff */
357 /*
358 * Last value of global fault stamp as seen by this process.
359 * In other words, this value gives an indication of how long
360 * it has been since this task got the token.
361 * Look at mm/thrash.c
362 */
363 unsigned int faultstamp;
364 unsigned int token_priority;
365 unsigned int last_interval;
366
367 unsigned char dumpable:2;
368
369 /* coredumping support */
370 int core_waiters;
371 struct completion *core_startup_done, core_done;
372
373 /* aio bits */
374 rwlock_t ioctx_list_lock;
375 struct kioctx *ioctx_list;
376 };
311 struct vm_area_struct * mmap; /* vm_area_struct 구조체 리스트의 첫 항목 */
312 struct rb_root mm_rb; /* red-black tree의 루트 노드 */
313 struct vm_area_struct * mmap_cache; /* 마지막으로 호출된 find_vma() 함수가 리턴한 값*/
314 unsigned long (*get_unmapped_area) (struct file *filp,
315 unsigned long addr, unsigned long len,
316 unsigned long pgoff, unsigned long flags);
317 void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
318 unsigned long mmap_base; /* base of mmap area */
319 unsigned long task_size; /* size of task vm space */
320 unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */
321 unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */
322 pgd_t * pgd;
323 atomic_t mm_users; /* How many users with user space? */
324 atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
325 int map_count; /* number of VMAs */
326 struct rw_semaphore mmap_sem;
327 spinlock_t page_table_lock; /* Protects page tables and some counters */
328
329 struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung
330 * together off init_mm.mmlist, and are protected
331 * by mmlist_lock
332 */
333
334 /* Special counters, in some configurations protected by the
335 * page_table_lock, in other configurations by being atomic.
336 */
337 mm_counter_t _file_rss;
338 mm_counter_t _anon_rss;
339
340 unsigned long hiwater_rss; /* High-watermark of RSS usage */
341 unsigned long hiwater_vm; /* High-water virtual memory usage */
342
343 unsigned long total_vm, locked_vm, shared_vm, exec_vm;
344 unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
345 unsigned long start_code, end_code, start_data, end_data;
346 unsigned long start_brk, brk, start_stack;
347 unsigned long arg_start, arg_end, env_start, env_end;
348
349 unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
350
351 cpumask_t cpu_vm_mask;
352
353 /* Architecture-specific MM context */
354 mm_context_t context;
355
356 /* Swap token stuff */
357 /*
358 * Last value of global fault stamp as seen by this process.
359 * In other words, this value gives an indication of how long
360 * it has been since this task got the token.
361 * Look at mm/thrash.c
362 */
363 unsigned int faultstamp;
364 unsigned int token_priority;
365 unsigned int last_interval;
366
367 unsigned char dumpable:2;
368
369 /* coredumping support */
370 int core_waiters;
371 struct completion *core_startup_done, core_done;
372
373 /* aio bits */
374 rwlock_t ioctx_list_lock;
375 struct kioctx *ioctx_list;
376 };
시스템에 존재하는 모든 mm_struct 들은 이중 링크드 리스트로 연결되어 있으며, 이에 대한 정보는 mm_struct 안의 mmlist 필드에 의해서 관리된다. 전체 리스트를 살펴보고자 한다면 init_mm 변수의 mmlist 필드를 사용하면 된다.
17 struct mm_struct init_mm = INIT_MM(init_mm);
47 #define INIT_MM(name) \
48 { \
49 .mm_rb = RB_ROOT, \
50 .pgd = swapper_pg_dir, \
51 .mm_users = ATOMIC_INIT(2), \
52 .mm_count = ATOMIC_INIT(1), \
53 .mmap_sem = __RWSEM_INITIALIZER(name.mmap_sem), \
54 .page_table_lock = __SPIN_LOCK_UNLOCKED(name.page_table_lock), \
55 .mmlist = LIST_HEAD_INIT(name.mmlist), \
56 .cpu_vm_mask = CPU_MASK_ALL, \
57 }
47 #define INIT_MM(name) \
48 { \
49 .mm_rb = RB_ROOT, \
50 .pgd = swapper_pg_dir, \
51 .mm_users = ATOMIC_INIT(2), \
52 .mm_count = ATOMIC_INIT(1), \
53 .mmap_sem = __RWSEM_INITIALIZER(name.mmap_sem), \
54 .page_table_lock = __SPIN_LOCK_UNLOCKED(name.page_table_lock), \
55 .mmlist = LIST_HEAD_INIT(name.mmlist), \
56 .cpu_vm_mask = CPU_MASK_ALL, \
57 }
init_mm 변수는 0번 프로세스에 대한 메모리 정보를 관리하는 것으로서 처음 부팅시에 사용된다.
mm_struct 구조체를 확보하기 위해서는 mm_alloc() 함수가 사용된다. 이것은 실제로는 kmem_cache_alloc()를 호출하는 다음과 같은 코드로 구성되어 있다.
355 struct mm_struct * mm_alloc(void)
356 {
357 struct mm_struct * mm;
358
359 mm = allocate_mm();
360 if (mm) {
361 memset(mm, 0, sizeof(*mm));
362 mm = mm_init(mm);
363 }
364 return mm;
365 }
323 #define allocate_mm() (kmem_cache_alloc(mm_cachep, GFP_KERNEL))
356 {
357 struct mm_struct * mm;
358
359 mm = allocate_mm();
360 if (mm) {
361 memset(mm, 0, sizeof(*mm));
362 mm = mm_init(mm);
363 }
364 return mm;
365 }
323 #define allocate_mm() (kmem_cache_alloc(mm_cachep, GFP_KERNEL))
mm_struct 해제는 다음 함수를 사용한다.
383 void mmput(struct mm_struct *mm)
384 {
385 might_sleep();
386
387 if (atomic_dec_and_test(&mm->mm_users)) {
388 exit_aio(mm);
389 exit_mmap(mm);
390 if (!list_empty(&mm->mmlist)) {
391 spin_lock(&mmlist_lock);
392 list_del(&mm->mmlist);
393 spin_unlock(&mmlist_lock);
394 }
395 put_swap_token(mm);
396 mmdrop(mm);
397 }
398 }
1381 static inline void mmdrop(struct mm_struct * mm)
1382 {
1383 if (atomic_dec_and_test(&mm->mm_count))
1384 __mmdrop(mm);
1385 }
1386
372 void fastcall __mmdrop(struct mm_struct *mm)
373 {
374 BUG_ON(mm == &init_mm);
375 mm_free_pgd(mm);
376 destroy_context(mm);
377 free_mm(mm);
378 }
324 #define free_mm(mm) (kmem_cache_free(mm_cachep, (mm)))
311 static inline void mm_free_pgd(struct mm_struct * mm)
312 {
313 pgd_free(mm->pgd);
314 }
384 {
385 might_sleep();
386
387 if (atomic_dec_and_test(&mm->mm_users)) {
388 exit_aio(mm);
389 exit_mmap(mm);
390 if (!list_empty(&mm->mmlist)) {
391 spin_lock(&mmlist_lock);
392 list_del(&mm->mmlist);
393 spin_unlock(&mmlist_lock);
394 }
395 put_swap_token(mm);
396 mmdrop(mm);
397 }
398 }
1381 static inline void mmdrop(struct mm_struct * mm)
1382 {
1383 if (atomic_dec_and_test(&mm->mm_count))
1384 __mmdrop(mm);
1385 }
1386
372 void fastcall __mmdrop(struct mm_struct *mm)
373 {
374 BUG_ON(mm == &init_mm);
375 mm_free_pgd(mm);
376 destroy_context(mm);
377 free_mm(mm);
378 }
324 #define free_mm(mm) (kmem_cache_free(mm_cachep, (mm)))
311 static inline void mm_free_pgd(struct mm_struct * mm)
312 {
313 pgd_free(mm->pgd);
314 }
mm_users 필드의 값을 하나 감소시키며, 이 값이 0인 경우에만 실제로 해당 구조체를 제거한다. 물론, 여기에서도 mm_count 필드를 다시 감소시키며, 이게 0이 되어야 실질적인 해제가 이루어진다.
커널 쓰레드에 대한 메모리 디스크립터
커널 쓰레드는 커널 모드에서 수행되기 때문에 TASK_SIZE 아래쪽 메모리를 접근할 일은 없다. 따라서, vm_area_struct 형태의 정보도 관리할 필요가 없다. 하지만, 불필요한 TLB와 캐쉬 플러쉬를 방지하기 위해서 커널 쓰레드인 경우에는 이전에 수행하던 유저모드 프로세스의 메모리 정보를 그대로 유지하게 된다.
유저 모드 프로세스에서는 mm과 active_mm이 모두 자신이 관리하는 mm_struct를 가리키고 있다. 하지만, 커널 쓰레드의 mm 필드는 NULL 이며, active_mm 필드가 이전 프로세스의 active_mm 값을 가지고 있게 된다. 이렇게 되면, 결국 이전에 마지막으로 수행된 유저 프로세스의 mm값을 가리키게 된다.
'Linux > Memory' 카테고리의 다른 글
| 사용자 메모리 공간 (0) | 2007/10/08 |
|---|---|
| 비연속 메모리 영역 관리 (0) | 2007/10/08 |
이올린에 북마크하기
이올린에 추천하기