링크 ux 커 널 분석 프로 세 스 주소 공간

본 고 는 주로 Liux 커 널 에서 프로 세 스 주소 공간의 데이터 구조 에 대한 설명 을 소개 하 는데 mm 를 포함한다.struct/vm_area_struct。프로 세 스 선형 주소 구간 의 분배 프로 세 스 를 설명 하고 해당 하 는 소스 코드 를 설명 했다. 
커 널 의 함 수 는 상당히 직접적 이 고 적절 한 방식 으로 동적 메모 리 를 얻 습 니 다.사용자 상태 프로 세 스에 메모 리 를 할당 할 때 상황 이 완전히 달 라 졌 다.프로 세 스 가 동적 메모리 에 대한 요청 은 긴박 하지 않 은 것 으로 여 겨 집 니 다. 일반적으로 커 널 은 사용자 상태 프로 세 스에 메모 리 를 할당 하 는 것 을 최대한 늦 춥 니 다.사용자 프로 세 스 가 신뢰 할 수 없 기 때문에 커 널 은 사용자 상태 프로 세 스 로 인 한 모든 주소 지정 오 류 를 수시로 준비 해 야 합 니 다.사용자 상태 프로 세 스 가 동적 메모 리 를 요청 할 때 요청 한 페이지 상 자 를 얻 지 못 하고 새로운 선형 주소 구간 에 대한 사용권 만 얻 습 니 다. 이 선형 주소 구간 은 프로 세 스 주소 공간의 일부분 이 됩 니 다.
프로 세 스 주소 공간 은 프로 세 스 가 사용 할 수 있 는 모든 선형 주소 로 구성 되 어 있 습 니 다.커 널 은 일부 스 레 드 주소 구간 을 추가 하거나 삭제 함으로써 프로 세 스 의 주소 공간 을 동적 으로 수정 할 수 있 습 니 다.커 널 은 이른바 선형 제거 자원 을 통 해 선형 주소 구간 을 표시 하고 선형 구역 은 초기 선형 주소, 길이 와 일부 방문 권한 에 의 해 설명 된다.프로 세 스 가 새로운 선형 구역 의 전형 적 인 상황 을 얻 었 습 니 다:
1. 그러나 사용자 가 콘 솔 에 명령 을 입력 할 때 셸 프로 세 스 는 이 명령 을 수행 하기 위해 새 프로 세 스 를 만 듭 니 다.그 결과 새로운 주소 공간 (즉 선형 구역) 이 새로운 프로 세 스에 분배 되 었 다.
2. 실행 중인 프로 세 스 가 완전히 다른 프로그램 을 불 러 오기 로 결정 할 수 있 습 니 다.이 때 프로 세 스 설명 자 는 변 하지 않 지만 이 프로그램 을 불 러 오기 전에 모든 선형 구역 이 풀 리 고 새로운 선형 구역 이 이 프로 세 스에 분 배 됩 니 다.
3. 실행 중인 프로 세 스 가 파일 에 메모리 이미 지 를 실행 할 수 있 습 니 다.
4. 프로 세 스 는 사용자 상태 스 택 에 데 이 터 를 계속 추가 할 수 있 습 니 다. 이미지 이 스 택 의 선형 구역 이 다 떨 어 질 때 까지 커 널 은 이 선형 구역 의 크기 를 확장 하기 로 결정 할 수 있 습 니 다.
5. 프로 세 스 는 다른 협력 프로 세 스 와 데 이 터 를 공유 하기 위해 IPC 공유 선형 구역 을 만 들 수 있 습 니 다.이 때 커 널 은 이 프로 세 스에 새로운 선형 구역 을 할당 하여 이 방안 을 실현 합 니 다.
6. 프로 세 스 는 malloc 와 같은 함 수 를 호출 하여 자신의 동적 더 미 를 확장 할 수 있 습 니 다.결 과 는 커 널 이 이 더미 에 분 배 된 선형 구역 으로 확장 하기 로 결정 할 수 있다 는 것 이다.
데이터 구조 설명
프로 세 스 설명자 taskstruct 의 mm 필드 는 프로 세 스 주소 공간 을 설명 합 니 다.
struct mm_struct {
	struct vm_area_struct * mmap;		/* list of VMAs */
	struct rb_root mm_rb;
	struct vm_area_struct * mmap_cache;	/* last find_vma result */
	unsigned long (*get_unmapped_area) (struct file *filp,
				unsigned long addr, unsigned long len,
				unsigned long pgoff, unsigned long flags);
	void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
	unsigned long mmap_base;		/* base of mmap area */
	unsigned long task_size;		/* size of task vm space */
	unsigned long cached_hole_size; 	/* if non-zero, the largest hole below free_area_cache */
	unsigned long free_area_cache;		/* first hole of size cached_hole_size or larger */
	pgd_t * pgd;
	atomic_t mm_users;			/* How many users with user space? */
	atomic_t mm_count;			/* How many references to "struct mm_struct" (users count as 1) */
	int map_count;				/* number of VMAs */
	struct rw_semaphore mmap_sem;
	spinlock_t page_table_lock;		/* Protects page tables and some counters */

	struct list_head mmlist;		/* List of maybe swapped mm's.	These are globally strung
						 * together off init_mm.mmlist, and are protected
						 * by mmlist_lock
						 */

	/* Special counters, in some configurations protected by the
	 * page_table_lock, in other configurations by being atomic.
	 */
	mm_counter_t _file_rss;
	mm_counter_t _anon_rss;

	unsigned long hiwater_rss;	/* High-watermark of RSS usage */
	unsigned long hiwater_vm;	/* High-water virtual memory usage */

	unsigned long total_vm, locked_vm, shared_vm, exec_vm;
	unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
	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;

	unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */

	struct linux_binfmt *binfmt;

	cpumask_t cpu_vm_mask;/*    TLB      */

	/* Architecture-specific MM context */
	mm_context_t context;

	/* Swap token stuff */
	/*
	 * Last value of global fault stamp as seen by this process.
	 * In other words, this value gives an indication of how long
	 * it has been since this task got the token.
	 * Look at mm/thrash.c
	 */
	unsigned int faultstamp;
	unsigned int token_priority;
	unsigned int last_interval;

	unsigned long flags; /* Must use atomic bitops to access the bits */

	struct core_state *core_state; /* coredumping support */
#ifdef CONFIG_AIO
	spinlock_t		ioctx_lock;
	struct hlist_head	ioctx_list;/*  IO     */
#endif
#ifdef CONFIG_MM_OWNER
	/*
	 * "owner" points to a task that is regarded as the canonical
	 * user/owner of this mm. All of the following must be true in
	 * order for it to be changed:
	 *
	 * current == mm->owner
	 * current->mm != mm
	 * new_owner->mm == mm
	 * new_owner->alloc_lock is held
	 */
	struct task_struct *owner;
#endif

#ifdef CONFIG_PROC_FS
	/* store ref to file /proc/<pid>/exe symlink points to */
	struct file *exe_file;
	unsigned long num_exe_file_vmas;
#endif
#ifdef CONFIG_MMU_NOTIFIER
	struct mmu_notifier_mm *mmu_notifier_mm;
#endif
};

mm 에 대하 여users 필드 와 mmcount 필드
mm_users 필드 저장 공유 mmstruct 데이터 구조의 경량급 프로 세 스 갯 수 입 니 다.mm_count 필드 는 메모리 설명자 의 메 인 카운터 입 니 다. mm 에 있 습 니 다.users 차 사용 카운터 의 모든 사용 자 는 mmcount 에서 한 단위 로 만 mmcount 가 점차 줄 어 들 때, 커 널 은 그 가 0 으로 변 했 는 지 확인 해 야 합 니 다. 만약 그렇다면, 이 메모리 설명 자 를 해제 해 야 합 니 다. 더 이상 사용자 가 그 를 사용 하지 않 기 때 문 입 니 다.
하나의 예 로 mm 해석users 와 mmcount 간 의 차이.두 경량급 프로 세 스 가 공유 하 는 메모리 설명 자 를 고려 합 니 다.그의 mmusers 필드 에 저 장 된 값 은 2 이 고 mmcount 필드 에 저 장 된 값 은 1 입 니 다.메모리 설명 자 를 긴 작업 중간 에 놓 지 않 으 면 mm 를 추가 해 야 합 니 다.users 필드 는 mm 가 아 닙 니 다.count 필드 의 값 입 니 다.최종 결 과 는 같다. 왜냐하면 mmusers 의 증가 로 mm 확보count 는 0 으로 변 하지 않 습 니 다. 이 메모리 설명 자 를 가 진 모든 경량급 프로 세 스 가 사망 하 더 라 도.
커 널 스 레 드 는 커 널 상태 에서 만 작 동 되 기 때문에 TASK 보다 낮 게 접근 하지 않 습 니 다.SIZE (PAGE OFFSET, 보통 0xc 000000) 의 주소 입 니 다. 일반 프로 세 스 와 달리 커 널 스 레 드 는 선형 구역 을 사용 하지 않 기 때문에 메모리 설명자 의 많은 필드 는 커 널 스 레 드 에 의미 가 없습니다. 커 널 스 레 드 를 만 들 때 커 널 스 레 드 의 active mm 는 부모 프로 세 스 의 mm 를 공유 하지만 mm 의 일부 데이터 와 변수 만 사용 합 니 다.
선형 영역
Liux 는 vm area struct 의 대상 을 통 해 선형 구역 을 실현 합 니 다. 필드 는?
/*
 * This struct defines a memory VMM 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 {
	struct mm_struct * vm_mm;	/* The address space we belong to. */
	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;

	pgprot_t vm_page_prot;		/* Access permissions of this VMA. */
	unsigned long vm_flags;		/* Flags, see mm.h. */

	struct rb_node vm_rb;

	/*
	 * For areas with an address space and backing store,
	 * linkage into the address_space->i_mmap prio tree, or
	 * linkage to the list of like vmas hanging off its node, or
	 * linkage of vma in the address_space->i_mmap_nonlinear list.
	 */
	union {
		struct {
			struct list_head list;
			void *parent;	/* aligns with prio_tree_node parent */
			struct vm_area_struct *head;
		} vm_set;

		struct raw_prio_tree_node prio_tree_node;
	} 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_node;	/* Serialized by anon_vma->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, *not* PAGE_CACHE_SIZE */
	struct file * vm_file;		/* File we map to (can be NULL). */
	void * vm_private_data;		/* was vm_pte (shared mem) */
	unsigned long vm_truncate_count;/* truncate_count or restart_addr */

#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
};

프로 세 스 가 가지 고 있 는 선형 구역 은 겹 치지 않 으 며, 커 널 은 새로운 분 배 된 선형 구역 을 인접 한 기 존 선형 구역 과 합 치 려 고 노력 합 니 다. 만약 두 인접 지역 의 접근 권한 이 일치 하면 그들 을 합 칠 수 있 습 니 다.
조작 하 다.
선형 영역 처리
우 리 는 자주 사용 하 는 find vma 함 수 를 들 었 습 니 다. rb 트 리 에서 지정 한 선형 구간 을 찾 습 니 다. 다른 함 수 는 더 이상 예 를 들 지 않 습 니 다.
/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
//deal with searching the virtual address space for mapped and free regions.
//The two parameters are the top-level mm_struct that is to be searched and the address the caller is interested in
struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
{
//Defaults to returning NULL for address not found.
	struct vm_area_struct *vma = NULL;
//Makes sure the caller does not try to search a bogus mm.
	if (mm) {
		/* Check the cache first. */
		/* (Cache hit rate is typically around 35%.) */
		//mmap_cache has the result of the last call to find_vma().
		//This has a chance of not having to search at all through the red-black tree
		vma = mm->mmap_cache;
		//If it is a valid VMA that is being examined, this checks to see if the address being searched is contained within it. If it is, 
		//the VMA was the mmap_cache one, so it can be returned. Otherwise, the tree is searched.
		if (!(vma && vma->vm_end > addr && vma->vm_start <= addr)) {
//Starts at the root of the tree.
			struct rb_node * rb_node;

			rb_node = mm->mm_rb.rb_node;
			vma = NULL;
//This block is the tree walk.
			while (rb_node) {
				struct vm_area_struct * vma_tmp;
//The macro, as the name suggests, returns the VMA that this tree node points to.
				vma_tmp = rb_entry(rb_node,
						struct vm_area_struct, vm_rb);
//Checks if the next node is traversed by the left or right leaf
				if (vma_tmp->vm_end > addr) {
					vma = vma_tmp;
//If the current VMA is what is required, this exits the while loop
					if (vma_tmp->vm_start <= addr)
						break;
					rb_node = rb_node->rb_left;
				} else
					rb_node = rb_node->rb_right;
			}
			//If the VMA is valid, this sets the mmap_cache for the next call to find_vma().
			if (vma)
				mm->mmap_cache = vma;
		}
	}
//Returns the VMA that contains the address or, as a side effect of the tree walk, 
//returns the VMA that is closest to the requested address.
	return vma;
}

분배 선형 주소 구간
do mmap 함 수 는 현재 프로 세 스 를 위해 새로운 선형 구역 을 만 들 고 초기 화 합 니 다. 단, 분배 에 성공 하면 이 새로운 선형 구역 을 프로 세 스 가 있 는 다른 선형 구역 과 통합 할 수 있 습 니 다.
/*                ,
  ,      ,           
             ;
file offset:                  
          file      offset
addr:                 
     ;
len:       ;
prot:                ,
    、  ;
flag:           
*/
static inline unsigned long do_mmap(struct file *file, unsigned long addr,
	unsigned long len, unsigned long prot,
	unsigned long flag, unsigned long offset)
{
	unsigned long ret = -EINVAL;
	/* offset           */
	if ((offset + PAGE_ALIGN(len)) < offset)
		goto out;
	if (!(offset & ~PAGE_MASK))
		ret = do_mmap_pgoff(file, addr, len, prot, flag, offset >> PAGE_SHIFT);
out:
	return ret;
}

도 mmap pgoff 함수 가 하 는 실제 작업 을 봅 니 다.
unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
			unsigned long len, unsigned long prot,
			unsigned long flags, unsigned long pgoff)
{
	struct mm_struct * mm = current->mm;
	struct inode *inode;
	unsigned int vm_flags;
	int error;
	unsigned long reqprot = prot;
	/*             ,     
	       */
	/*
	 * Does the application expect PROT_READ to imply PROT_EXEC?
	 *
	 * (the exception is when the underlying filesystem is noexec
	 *  mounted, in which case we dont add PROT_EXEC.)
	 */
	if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
		if (!(file && (file->f_path.mnt->mnt_flags & MNT_NOEXEC)))
			prot |= PROT_EXEC;

	if (!len)
		return -EINVAL;

	if (!(flags & MAP_FIXED))
		addr = round_hint_to_min(addr);

	error = arch_mmap_check(addr, len, flags);
	if (error)
		return error;

	/* Careful about overflows.. */
	len = PAGE_ALIGN(len);
	if (!len || len > TASK_SIZE)
		return -ENOMEM;

	/* offset overflow? */
	if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)
               return -EOVERFLOW;

	/* Too many mappings? */
	if (mm->map_count > sysctl_max_map_count)
		return -ENOMEM;

	if (flags & MAP_HUGETLB) {
		struct user_struct *user = NULL;
		if (file)
			return -EINVAL;

		/*
		 * VM_NORESERVE is used because the reservations will be
		 * taken when vm_ops->mmap() is called
		 * A dummy user value is used because we are not locking
		 * memory so no accounting is necessary
		 */
		len = ALIGN(len, huge_page_size(&default_hstate));
		file = hugetlb_file_setup(HUGETLB_ANON_FILE, len, VM_NORESERVE,
						&user, HUGETLB_ANONHUGE_INODE);
		if (IS_ERR(file))
			return PTR_ERR(file);
	}

	/* Obtain the address to map to. we verify (or select) it and ensure
	 * that it represents a valid section of the address space.
	 */
	 /*             */
	addr = get_unmapped_area(file, addr, len, pgoff, flags);
	if (addr & ~PAGE_MASK)
		return addr;

	/* Do simple checking here so the lower-level routines won't have
	 * to. we assume access permissions have been handled by the open
	 * of the memory object, so we don't do any here.
	 */
	 /*      prot flags         
	              */
	vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |
			mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;

	if (flags & MAP_LOCKED)
		if (!can_do_mlock())
			return -EPERM;

	/* mlock MCL_FUTURE? */
	if (vm_flags & VM_LOCKED) {
		unsigned long locked, lock_limit;
		locked = len >> PAGE_SHIFT;
		locked += mm->locked_vm;
		lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
		lock_limit >>= PAGE_SHIFT;
		if (locked > lock_limit && !capable(CAP_IPC_LOCK))
			return -EAGAIN;
	}

	inode = file ? file->f_path.dentry->d_inode : NULL;

	if (file) {
		switch (flags & MAP_TYPE) {
		case MAP_SHARED:
			if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))
				return -EACCES;

			/*
			 * Make sure we don't allow writing to an append-only
			 * file..
			 */
			if (IS_APPEND(inode) && (file->f_mode & FMODE_WRITE))
				return -EACCES;

			/*
			 * Make sure there are no mandatory locks on the file.
			 */
			if (locks_verify_locked(inode))
				return -EAGAIN;

			vm_flags |= VM_SHARED | VM_MAYSHARE;
			if (!(file->f_mode & FMODE_WRITE))
				vm_flags &= ~(VM_MAYWRITE | VM_SHARED);

			/* fall through */
		case MAP_PRIVATE:
			if (!(file->f_mode & FMODE_READ))
				return -EACCES;
			if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) {
				if (vm_flags & VM_EXEC)
					return -EPERM;
				vm_flags &= ~VM_MAYEXEC;
			}

			if (!file->f_op || !file->f_op->mmap)
				return -ENODEV;
			break;

		default:
			return -EINVAL;
		}
	} else {
		switch (flags & MAP_TYPE) {
		case MAP_SHARED:
			/*
			 * Ignore pgoff.
			 */
			pgoff = 0;
			vm_flags |= VM_SHARED | VM_MAYSHARE;
			break;
		case MAP_PRIVATE:
			/*
			 * Set pgoff according to addr for anon_vma.
			 */
			pgoff = addr >> PAGE_SHIFT;
			break;
		default:
			return -EINVAL;
		}
	}

	error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
	if (error)
		return error;
	error = ima_file_mmap(file, prot);
	if (error)
		return error;
	/*    */
	return mmap_region(file, addr, len, flags, vm_flags, pgoff);
}

우리 get unmapped area 함수 가 새로운 선형 주소 구간 을 얻 었 습 니 다.
/*
The parameters passed are the following:

file The file or device being mapped

addr The requested address to map to

len The length of the mapping

pgoff The offset within the file being mapped

flags Protection flags
*/
//When a new area is to be memory mapped, a free region has to be found that is large enough to contain the new mapping.
/*                  
      ,              
               ,    
  (get_unmapped_area           
get_unmapped_area  )    */
unsigned long
get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
		unsigned long pgoff, unsigned long flags)
{
	unsigned long (*get_area)(struct file *, unsigned long,
				  unsigned long, unsigned long, unsigned long);

	get_area = current->mm->get_unmapped_area;
	if (file && file->f_op && file->f_op->get_unmapped_area)
		get_area = file->f_op->get_unmapped_area;
	addr = get_area(file, addr, len, pgoff, flags);/*       */
	if (IS_ERR_VALUE(addr))
		return addr;

	if (addr > TASK_SIZE - len)
		return -ENOMEM;
	if (addr & ~PAGE_MASK)
		return -EINVAL;
	/*x86 ia-32      */
	return arch_rebalance_pgtables(addr, len);
}

우 리 는 파일 을 사용 하지 않 는 하 나 를 보고 파일 과 관련 된 하 나 를 파일 시스템 에서 다시 분석 합 니 다.
메모리 와 관련 된 get unmapped area 함 수 를 다음 함수 에 설정 합 니 다.
/*
 * This function, called very early during the creation of a new
 * process VM image, sets up which VM layout function to use:
 */
void arch_pick_mmap_layout(struct mm_struct *mm)
{
	if (mmap_is_legacy()) {
		mm->mmap_base = mmap_legacy_base();
		mm->get_unmapped_area = arch_get_unmapped_area;
		mm->unmap_area = arch_unmap_area;
	} else {
		mm->mmap_base = mmap_base();
		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
		mm->unmap_area = arch_unmap_area_topdown;
	}
}

우 리 는 arch get unmmapped area 를 직접 보 았 습 니 다. 다른 하 나 는 비슷 합 니 다.
unsigned long
arch_get_unmapped_area(struct file *filp, unsigned long addr,
		unsigned long len, unsigned long pgoff, unsigned long flags)
{
	struct mm_struct *mm = current->mm;
	struct vm_area_struct *vma;
	unsigned long start_addr;

	if (len > TASK_SIZE)
		return -ENOMEM;

	if (flags & MAP_FIXED)
		return addr;

	if (addr) {
		addr = PAGE_ALIGN(addr);
		/*            */
		vma = find_vma(mm, addr);
		/*     ,           
		vma                   
		 vma (           vma  )*/				
		if (TASK_SIZE - len >= addr &&
		    (!vma || addr + len <= vma->vm_start))
			return addr;/*    */
	}
	/*      addr 0         */
	/*cached_hole_size   free_area_cache       
	     ,   free_area_cache    ,
	        */
	if (len > mm->cached_hole_size) {
	        start_addr = addr = mm->free_area_cache;
	} else {/*                   
			 */
	        start_addr = addr = TASK_UNMAPPED_BASE;
	        mm->cached_hole_size = 0;
	}

full_search:
	/*       addr   vma*/
	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
		/* At this point:  (!vma || addr < vma->vm_end). */
		if (TASK_SIZE - len < addr) {
			/*
			 * Start a new search - just in case we missed
			 * some holes.
			 */
			if (start_addr != TASK_UNMAPPED_BASE) {
				addr = TASK_UNMAPPED_BASE;
			        start_addr = addr;
				mm->cached_hole_size = 0;
				goto full_search;
			}
			return -ENOMEM;
		}
		/*        */
		if (!vma || addr + len <= vma->vm_start) {
			/*
			 * Remember the place where we stopped the search:
			 */
			mm->free_area_cache = addr + len;
			return addr;
		}/*  cached_hole_size,       cached_hole_size
		  ,    len           
		       ,            
		                    
		    ,             
		free_area_cache          */
		if (addr + mm->cached_hole_size < vma->vm_start)
		        mm->cached_hole_size = vma->vm_start - addr;
		addr = vma->vm_end;/*  addr           */
	}
}


이어서 위의 호출 mmap region 함수
unsigned long mmap_region(struct file *file, unsigned long addr,
			  unsigned long len, unsigned long flags,
			  unsigned int vm_flags, unsigned long pgoff)
{
	struct mm_struct *mm = current->mm;
	struct vm_area_struct *vma, *prev;
	int correct_wcount = 0;
	int error;
	struct rb_node **rb_link, *rb_parent;
	unsigned long charged = 0;
	struct inode *inode =  file ? file->f_path.dentry->d_inode : NULL;

	/* Clear old maps */
	error = -ENOMEM;
munmap_back:
	/*                  ,
	                */
	vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent);
	/*                 */
	if (vma && vma->vm_start < addr + len) {
		if (do_munmap(mm, addr, len))/*      */
			return -ENOMEM;
		goto munmap_back;
	}

	/* Check against address space limit. */
	/*                    
	      */
	if (!may_expand_vm(mm, len >> PAGE_SHIFT))
		return -ENOMEM;

	/*
	 * Set 'VM_NORESERVE' if we should not account for the
	 * memory use of this mapping.
	 */
	if ((flags & MAP_NORESERVE)) {
		/* We honor MAP_NORESERVE if allowed to overcommit */
		if (sysctl_overcommit_memory != OVERCOMMIT_NEVER)
			vm_flags |= VM_NORESERVE;

		/* hugetlb applies strict overcommit unless MAP_NORESERVE */
		if (file && is_file_hugepages(file))
			vm_flags |= VM_NORESERVE;
	}

	/*
	 * Private writable mapping: check memory availability
	 */
	if (accountable_mapping(file, vm_flags)) {
		charged = len >> PAGE_SHIFT;
		if (security_vm_enough_memory(charged))
			return -ENOMEM;
		vm_flags |= VM_ACCOUNT;
	}

	/*
	 * Can we just expand an old mapping?
	 */
	 /*                 */
	vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, NULL);
	if (vma)/*    */
		goto out;

	/*
	 * Determine the object being mapped and call the appropriate
	 * specific mapper. the address has already been validated, but
	 * not unmapped, but the maps are removed from the list.
	 */
	 /*                  
	     vma  */
	vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
	if (!vma) {
		error = -ENOMEM;
		goto unacct_error;
	}
	/*       */
	vma->vm_mm = mm;
	vma->vm_start = addr;
	vma->vm_end = addr + len;
	vma->vm_flags = vm_flags;
	vma->vm_page_prot = vm_get_page_prot(vm_flags);
	vma->vm_pgoff = pgoff;

	if (file) {
		error = -EINVAL;
		if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
			goto free_vma;
		if (vm_flags & VM_DENYWRITE) {
			error = deny_write_access(file);
			if (error)
				goto free_vma;
			correct_wcount = 1;
		}
		vma->vm_file = file;
		get_file(file);
		error = file->f_op->mmap(file, vma);
		if (error)
			goto unmap_and_free_vma;
		if (vm_flags & VM_EXECUTABLE)
			added_exe_file_vma(mm);

		/* Can addr have changed??
		 *
		 * Answer: Yes, several device drivers can do it in their
		 *         f_op->mmap method. -DaveM
		 */
		addr = vma->vm_start;
		pgoff = vma->vm_pgoff;
		vm_flags = vma->vm_flags;
		
	}
	/*             */
	else if (vm_flags & VM_SHARED) {
		/*   ,              */
		error = shmem_zero_setup(vma);
		if (error)
			goto free_vma;
	}

	if (vma_wants_writenotify(vma))
		vma->vm_page_prot = vm_get_page_prot(vm_flags & ~VM_SHARED);
	/*                 */
	vma_link(mm, vma, prev, rb_link, rb_parent);
	file = vma->vm_file;

	/* Once vma denies write, undo our temporary denial count */
	if (correct_wcount)
		atomic_inc(&inode->i_writecount);
out:
	perf_event_mmap(vma);
	/*  total_vm    */
	mm->total_vm += len >> PAGE_SHIFT;
	vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
	if (vm_flags & VM_LOCKED) {
		/*
		 * makes pages present; downgrades, drops, reacquires mmap_sem
		 */
		 /*           ,    
		   RAM */
		long nr_pages = mlock_vma_pages_range(vma, addr, addr + len);
		if (nr_pages < 0)
			return nr_pages;	/* vma gone! */
		mm->locked_vm += (len >> PAGE_SHIFT) - nr_pages;
	} else if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK))
		/*           */
		make_pages_present(addr, addr + len);
	return addr;/*        */

unmap_and_free_vma:
	if (correct_wcount)
		atomic_inc(&inode->i_writecount);
	vma->vm_file = NULL;
	fput(file);

	/* Undo any partial mapping done by a device driver. */
	unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
	charged = 0;
free_vma:
	kmem_cache_free(vm_area_cachep, vma);
unacct_error:
	if (charged)
		vm_unacct_memory(charged);
	return error;
}

여기 서 선형 주소 공간 을 분배 하면 끝 이 나 더 라 도 주 된 작업 은 주소 와 길이 에 따라 프로 세 스 주소 공간 에서 추가 되 지 않 은 선형 구간 을 찾 습 니 다. 이 구간 이 현재 프로 세 스 선형 주소 공간의 선형 구간 과 합 칠 수 있다 면 합 칩 니 다. 합 칠 수 없다 면 선형 구간 을 만 들 고 이 선형 구간 vma 를 삽입 합 니 다.프로 세 스 기 존의 선형 주소 공간 에 들 어가 서 그의 선형 주소 공간의 일부분 으로 합 니 다. 마지막 으로 선형 구간 에 실제 물리 페이지 를 분배 하고 기본 주 소 를 되 돌려 줍 니 다.

좋은 웹페이지 즐겨찾기