[0]

리눅스 커널에서 모든 유저 프로세스는 가상 메모리 공간을 할당 받는다. 가상 메모리 덕분에, 각 프로세스들은 자신들이 모든 메모리 공간을 '독점'하는 것처럼 사용할 수 있다. 하지만 실제로 물리 메모리를 각 프로세스가 독점할 수 는 없기 때문에, 커널에서는 이런 유저 프로세스의 메모리를 관리할 필요가 있다. 이를 위헤 커널에서는 mm_struct라는 memory descriptor를 각 테스크마다 할당하여 관리한다. 또한 가상 메모리 공간의 다양한 영역(코드 영역, 데이터 영역 등)을 관리하기 위해 vm_area_struct(VMA) 구조체가 영역마다 할당되어 mm_struct에 저장된다. 

 

[1]

우선 각 테스크의 정보를 저장하는 task_struct에서 mm_struct 멤버를 찾아보자.

struct task_struct {
	...
    struct mm_struct	*mm;
    struct mm_struct	*active_mm;
    ...
}

task당 하나의 mm_struct가 mm이라는 멤버로 할당되는 것을 확인할 수 있다. 이때, mm은 모든 테스크마다 unique하지는 않다. 하나의 유저 프로세스에 여러 개의 스레드가 생성된다면, 해당 스레드는 메모리 공간을 공유하므로, mm이 같은 값을 가지게 된다. Task가 스케쥴링되게 되면, mm이 가르키는 mm_struct가 로드 되어 active_mm에 저장된다. 단, 이때 커널 스레드는 mm이 NULL이므로(유저 레벨 메모리 공간이 없으므로) 커널의 메모리 공간이 acive_mm에 로딩된다.

 

[2]

이제 실제 코드로 mm_struct를 분석해보자. 커널버전은 5.7이고, 일부의 멤버만 남겨놨다. 전체 mm_struct는 include/linux/mm_types.h에서 찾을 수 있다.

struct mm_struct {
	struct {
		struct vm_area_struct *mmap;		/* list of VMAs */
		struct rb_root mm_rb;
		
		/**
		 * @mm_users: The number of users including userspace.
		 *
		 * Use mmget()/mmget_not_zero()/mmput() to modify. When this
		 * drops to 0 (i.e. when the task exits and there are no other
		 * temporary reference holders), we also release a reference on
		 * @mm_count (which may then free the &struct mm_struct if
		 * @mm_count also drops to 0).
		 */
		atomic_t mm_users;

		/**
		 * @mm_count: The number of references to &struct mm_struct
		 * (@mm_users count as 1).
		 *
		 * Use mmgrab()/mmdrop() to modify. When this drops to 0, the
		 * &struct mm_struct is freed.
		 */
		atomic_t mm_count;

		unsigned long total_vm;	   /* Total pages mapped */
		unsigned long locked_vm;   /* Pages that have PG_mlocked set */
		atomic64_t    pinned_vm;   /* Refcount permanently increased */
		unsigned long data_vm;	   /* VM_WRITE & ~VM_SHARED & ~VM_STACK */
		unsigned long exec_vm;	   /* VM_EXEC & ~VM_WRITE & ~VM_STACK */
		unsigned long stack_vm;	   /* VM_STACK */
		unsigned long def_flags;

		spinlock_t arg_lock; /* protect the below fields */
		unsigned long start_code, end_code, start_data, end_data;
		unsigned long start_brk, brk, start_stack;
		unsigned long arg_start, arg_end, env_start, env_end;


	} __randomize_layout;

	/*
	 * The mm_cpumask needs to be at the end of mm_struct, because it
	 * is dynamically sized based on nr_cpu_ids.
	 */
	unsigned long cpu_bitmap[];
};
  • mm_users : mm_struct의 주소 공간을 사용중인 task를 나타낸다. 만약 하나의 프로세스에 대해 메모리를 공유하는 스레드가 3개라면, mm_users는 3이 된다.
  • mm_count : mm_struct의 참조 카운트. 0이 되면 mm_struct가 free된다.
  • mmap : 가상 메모리 영역의 리스트. vm_area_struct에 대해서는 아래에서 다룬다.
  • mm_rb : 마찬가지로 가상 메모리 영역을 저장한다. mmap의 원소와 완전히 동일하지만 red-black tree 구조이다. mmap은 VMA를 순회할때 사용되고, mm_rb는 VMA를 검색할 때 사용된다.
  • pgd : 가상메모리 -> 물리메로리 매핑에 사용되는 페이지 테이블의 주소.
  • start_code / end_code : code영역의 시작 주소와 끝 주소. 유사하게, data영역이나(start_data) heap영역(start_brk)도 다른 멤버로 선언되어있다.

[3]

vma_area_struct 역시 mm_struct와 같은 파일에 존재한다. 

/*
 * This struct describes a virtual memory area. There is one of these
 * per VM-area/task. A VM area is any part of the process virtual memory
 * space that has a special rule for the page-fault handlers (ie a shared
 * library, the executable area etc).
 */
struct vm_area_struct {
	/* The first cache line has the info for VMA tree walking. */

	unsigned long vm_start;		/* Our start address within vm_mm. */
	unsigned long vm_end;		/* The first byte after our end address
					   within vm_mm. */

	/* linked list of VM areas per task, sorted by address */
	struct vm_area_struct *vm_next, *vm_prev;

	struct rb_node vm_rb;

	/*
	 * Largest free memory gap in bytes to the left of this VMA.
	 * Either between this VMA and vma->vm_prev, or between one of the
	 * VMAs below us in the VMA rbtree and its ->vm_prev. This helps
	 * get_unmapped_area find a free area of the right size.
	 */
	unsigned long rb_subtree_gap;

	/* Second cache line starts here. */

	struct mm_struct *vm_mm;	/* The address space we belong to. */

	/*
	 * Access permissions of this VMA.
	 * See vmf_insert_mixed_prot() for discussion.
	 */
	pgprot_t vm_page_prot;
	unsigned long vm_flags;		/* Flags, see mm.h. */

	/*
	 * For areas with an address space and backing store,
	 * linkage into the address_space->i_mmap interval tree.
	 */
	struct {
		struct rb_node rb;
		unsigned long rb_subtree_last;
	} shared;

	/*
	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
	 * list, after a COW of one of the file pages.	A MAP_SHARED vma
	 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
	 * or brk vma (with NULL file) can only be in an anon_vma list.
	 */
	struct list_head anon_vma_chain; /* Serialized by mmap_sem &
					  * page_table_lock */
	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */

	/* Function pointers to deal with this struct. */
	const struct vm_operations_struct *vm_ops;

	/* Information about our backing store: */
	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
					   units */
	struct file * vm_file;		/* File we map to (can be NULL). */
	void * vm_private_data;		/* was vm_pte (shared mem) */

#ifdef CONFIG_SWAP
	atomic_long_t swap_readahead_info;
#endif
#ifndef CONFIG_MMU
	struct vm_region *vm_region;	/* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
#endif
	struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;

대부분의 멤버들이 주석과 이름을 충분히 활용도를 유추할 수 있다. VMA 하나가, 프로세스의 메모리 공간(code 영역, 데이터 영역 등)을 나타낸다는 점을 상기하면서, 중요한 멤버 몇개만 살펴보자.

 

  • vm_start, vm_end : VMA 영역의 시작 주소와 끝 주소. vm_end - vm_start가 해당 VMA의 사이즈를 나타낸다.
  • vm_prev, vm_next : 다음과 이전 VMA 구조체를 가르키는 포인터. 더블 링크드 리스트 형태로 되어있고, vm_start로 소팅되어 있다.
  • vm_mm : 해당 VMA가 속해있는 mm_struct
  • vm_flags : 해당 메모리 영역의 속성을 나타내는 플래그
  • vm_ops : VMA 영역에 대한 opeartions들을 나타내는 function pointer들
  • vm_file : mmap을 사용한 경우 해당하는 파일, null일 경우 이 VMA는 mmap으로 사용되고 있지 않음.

 

[참고]

esos.hanyang.ac.kr/tc/2015gradproject2/i/entry/2

 

http://esos.hanyang.ac.kr/tc/2015gradproject2/i/entry/2

신건수 컴퓨터 공학부 2010004207 1.    목표 -      리눅스 메모리 관리 기법을 이해한다. -      가상 메모리와 물리 메모리를 이해한다 2.    메모리 관리를 해야하는 이유 태스크를 실행 되기 �

esos.hanyang.ac.kr

Robert Love, Linux Kernel Development 3rd Edition.

+ Recent posts