天天看點

Linux核心設計與實作(13)--程序位址空間

上一節講了核心如何管理實體記憶體,其實核心除了管理本身的記憶體外,還必須管理使用者空間中程序的記憶體,這就是程序位址空間,也就是系統中每個使用者空間程序所看到的記憶體。

Linux采用虛拟記憶體技術,系統中所有程序之間以虛拟方式共享記憶體,對一個程序而言,可以通路整個系統的所有實體記憶體,其擁有位址空間也可以遠遠大于系統實體記憶體。

1.位址空間

程序位址空間由程序可尋址的虛拟記憶體組成。每個程序都有一個32bit或64bit的平坦位址空間,空間具體大小取決于體系結構。平坦(flat)指的是位址空間範圍是一個獨立的連續空間(比如,位址從0~2​32-1).一些作業系統提供了段位址空間,這種位址空間并不是一個獨立的線性區域,而是被分段的。現代采用虛拟記憶體的作業系統都使用平坦位址空間。

每個程序都有唯一的這種平坦位址空間,并且不同程序之間,彼此互不相幹,位址空間完全獨立。

盡管一個程序可以尋址4GB虛拟記憶體(32bit),但這并不代表它就有權通路所有虛拟記憶體,在位址空間中,更關心的是一些可以合法通路的虛拟記憶體的位址空間,這個空間稱為“記憶體區域(memory areas)”,程序隻能通路有效記憶體區域内的記憶體位址,每個記憶體區域也具有相關權限,如可讀、可寫,可執行屬性。如果一個程序通路了不在有效範圍的記憶體區域,或以不正确的方式通路了有效位址,核心就會終止該程序,并傳回“段錯誤”。

記憶體區域可以包含各種記憶體對象,比如:

代碼段(text section):可執行檔案代碼的記憶體映射

資料段(data section): 可執行檔案的已初始化全局變量的記憶體映射

bss段:包含未初始化全局變量,也就是bss段的零頁 (頁面中的資訊全部為0,可用于映射bss段等目的) 的記憶體映射

棧:使用者程序使用者空間的棧(不要和程序核心棧混淆,程序的核心棧獨立存在并且由核心維護)每個諸如C庫或動态連結程式等共享庫的代碼段、資料段和BSS也會被載入程序的位址空間。

任何記憶體映射檔案;

任何共享記憶體段;

任何匿名的記憶體映射,比如malloc()配置設定的記憶體;

程序位址空間中的任何有效位址都隻能位于唯一的區域,這些記憶體區域不能互相覆寫;在執行的程序中,每個不同的記憶體片段都對應一個獨立的記憶體區域:棧、對象代碼、全局變量、被映射的檔案等。

2.記憶體描述符

核心使用記憶體描述符來表示程序的位址空間,該結構體包含了和程序位址空間有關的官不資訊,mm_struct結構體,定義在

此處)折疊或打開

1. struct mm_struct {
2. struct vm_area_struct * mmap;        /* 記憶體區域連結清單list of VMAs */
3. struct rb_root mm_rb;                /* VMA形成的紅黑樹*/
4. struct vm_area_struct * mmap_cache;    /* 最近使用的記憶體區域last find_vma result */
5. #ifdef CONFIG_MMU
6. unsigned long (*get_unmapped_area) (struct file *filp,
7. unsigned long addr, unsigned long len,
8. unsigned long pgoff, unsigned long flags);
9. void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
10. #endif
11. unsigned long mmap_base;        /* base of mmap area */
12. unsigned long task_size;        /* size of task vm space */
13. unsigned long cached_hole_size;     /* if non-zero, the largest hole below free_area_cache */
14. unsigned long free_area_cache;        /* 位址空間第一個空洞first hole of size cached_hole_size or larger */
15. pgd_t * pgd; /*頁全局目錄*/
16. atomic_t mm_users;            /* 使用位址空間的使用者數How many users with user space? */
17. atomic_t mm_count;            /* 主使用計數器How many references to "struct mm_struct" (users count as 1) */
18. int map_count;                /* number of VMAs */
19. struct rw_semaphore mmap_sem; /*記憶體區域的信号量*/
20. spinlock_t page_table_lock;        /* 頁表鎖Protects page tables and some counters */
21. 
22. struct list_head mmlist;        /*所有mm_struct形成的雙向連結清單 List of maybe swapped mm's.    These are globally strung
23. * together off init_mm.mmlist, and are protected
24. * by mmlist_lock
25. */
26. 
27. 
28. unsigned long hiwater_rss;    /* High-watermark of RSS usage */
29. unsigned long hiwater_vm;    /* High-water virtual memory usage */
30. 
31. /*全部頁面數目,上鎖的頁面數目*/
32. unsigned long total_vm, locked_vm, shared_vm, exec_vm;
33. unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
34. /*代碼段的開始位址,結束位址;資料段的首位址和結束位址*/
35. unsigned long start_code, end_code, start_data, end_data;
36. /*堆的首位址,堆的尾位址*/
37. unsigned long start_brk, brk, start_stack;
38. /*指令行參數的首位址和結束位址,環境變量的首位址和結束位址*/
39. unsigned long arg_start, arg_end, env_start, env_end;
40. 
41. unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
42. 
43. /*
44. * Special counters, in some configurations protected by the
45. * page_table_lock, in other configurations by being atomic.
46. */
47. struct mm_rss_stat rss_stat;
48. 
49. struct linux_binfmt *binfmt;
50. 
51. cpumask_t cpu_vm_mask;
52. 
53. /* Architecture-specific MM context */
54. mm_context_t context;
55. 
56. /* Swap token stuff */
57. /*
58. * Last value of global fault stamp as seen by this process.
59. * In other words, this value gives an indication of how long
60. * it has been since this task got the token.
61. * Look at mm/thrash.c
62. */
63. unsigned int faultstamp;
64. unsigned int token_priority;
65. unsigned int last_interval;
66. 
67. unsigned long flags; /* Must use atomic bitops to access the bits */
68. 
69. struct core_state *core_state; /* coredumping support */
70. #ifdef CONFIG_AIO
71. spinlock_t        ioctx_lock;
72. struct hlist_head    ioctx_list;
73. #endif
74. #ifdef CONFIG_MM_OWNER
75. /*
76. * "owner" points to a task that is regarded as the canonical
77. * user/owner of this mm. All of the following must be true in
78. * order for it to be changed:
79. *
80. * current == mm->owner
81. * current->mm != mm
82. * new_owner->mm == mm
83. * new_owner->alloc_lock is held
84. */
85. struct task_struct *owner;
86. #endif
87. 
88. #ifdef CONFIG_PROC_FS
89. /* store ref to file /proc/<pid>/exe symlink points to */
90. struct file *exe_file;
91. unsigned long num_exe_file_vmas;
92. #endif
93. #ifdef CONFIG_MMU_NOTIFIER
94. struct mmu_notifier_mm *mmu_notifier_mm;
95. #endif
96. };      

mm_users域記錄正在使用該位址的程序數目,mm_count表示mm_struct結構體的主引用計數,當mm_users值減少為0時(所有使用該位址空間的線程都退出),mm_count變為0;當mm_count等于0,說明已經咩有人和指向該mm_stuct結構體的引用了,這時該結構體會被撤銷。

mmap和mm_rb描述同一個對象:該位址空間中的全部記憶體區域。Mmap以連結清單形式存放,mm_rb以紅-黑樹形式存放。核心通常會避免用兩種資料結構組織同一種資料,但此處這種備援派的上用場,mmap連結清單,利于簡單、高效地周遊所有元素;而mm_rb結構更适合搜尋指定的元素。覆寫樹上的連結清單并用這兩個結構體同時通路相同的資料集,有時候這種操作稱為線索樹。

所有mm_stuct都通過自身的mmlist域連結在一個雙向連結清單中,該連結清單首元素是init_mm記憶體描述符,它代表init程序的位址空間,另外注意,操作該連結清單是需要使用mmlist_lock來防止并發通路。

2.1 配置設定記憶體描述符

在程序的程序描述符task_struct中,mm域存放着該程序使用的記憶體描述符,是以current->mm指向目前程序的記憶體描述符。

fork()函數利用copy_mm()函數複制父程序的記憶體描述符,而子程序中的mm_struct結構體實際是通過檔案kernel/fork.c中的allocate_mm()宏從mm_cachep slab緩存中配置設定得到的。通常每個程序都有唯一的mm_struct結構體,即唯一的程序位址空間。

如果父程序希望和子程序共享位址空間,可以調用clone()時,設定CLONE_VM标志,這樣的程序稱作線程,Linux中所謂的線程和程序的本質差別,就是是否共享位址空間。

當CLONE_VM被指定後,核心就不再需要調用allocate_mm()函數,而僅僅需要在調用copy_mm()函數中将mm域指向其父程序的記憶體描述符就可以了:

此處)折疊或打開

1. if (clone_flags & CLONE_VM) {
2. //current 是父程序,而tsk在fork()指向期間是子程序
3. atomic_inc(¤t->mm->mm_users);
4. tsk->mm = current->mm;
5. }      

2.2 撤銷記憶體描述符

程序退出時,核心會調用exit_mm(),該函數執行一些正常撤銷工作,同時更新一些統計量。

該函數會調用mmput()減少記憶體描述符中的mm_users使用者基數,如果使用者計數降到0,将調用mmdrop()函數,減少mm_count使用計數。如果使用計數也等于零,說明記憶體描述符不再有任何使用者了,那麼調用free_mm()宏通過kmem_cache_free()将mm_struct結構體歸還到mm_cachep slab緩存中。

2.3 mm_struct與核心線程

核心線程沒有程序位址空間,也沒有相關的記憶體描述符,是以核心線程對應的程序描述符mm域為空,事實上,這也正是核心線程的真實含義—它們沒有使用者上下文。

為了避免核心線程為記憶體描述符和頁表浪費記憶體,也為了當新核心線程運作時,避免浪費處理器周期向新位址空間進行切換,核心線程将直接使用前一個程序的記憶體描述符。

當一個程序被排程時,該程序的mm域指向的位址空間被載入記憶體,程序描述符中的active_mm域會被更新,指向新的位址空間。核心線程沒有位址空間,mm域為NULL,于是,當一個核心線程被排程時,核心發現它的mm域為NULL,就會保留前一個程序的位址空間,随後更新核心線程的程序描述符的active_mm域,使其指向前一個程序的記憶體描述符。是以需要時,核心線程便可以使用前一個程序的頁表。

3.虛拟記憶體區域

記憶體區域由vm_area_struct結構體描述,定義在

中,記憶體區域在Linux核心中經常稱作虛拟記憶體區域(virtual memoryAreas, VMAs).

vm_area_struct描述了指定位址空間内連續區間上的一個獨立記憶體範圍,核心将每個記憶體區域作為一個單獨的記憶體對象管理,每個記憶體區域都擁有一緻的屬性,比如通路權限等,相應的操作也應該一緻。按這種方式,每個VMA就可以代表不同類型的記憶體區域(比如記憶體映射檔案或者程序使用者空間棧)。

此處)折疊或打開

1. /*
2. * This struct defines a memory VMM memory area. There is one of these
3. * per VM-area/task. A VM area is any part of the process virtual memory
4. * space that has a special rule for the page-fault handlers (ie a shared
5. * library, the executable area etc).
6. */
7. struct vm_area_struct {
8. struct mm_struct * vm_mm;    /* 相關的mm_struct結構體The address space we belong to. */
9. unsigned long vm_start;        /* 區間首位址Our start address within vm_mm. */
10. unsigned long vm_end;        /* 區間尾位址The first byte after our end address
11. within vm_mm. */
12. 
13. /* linked list of VM areas per task, sorted by address */
14. struct vm_area_struct *vm_next, *vm_prev; //VMA連結清單
15. 
16. pgprot_t vm_page_prot;        /* 通路控制權限Access permissions of this VMA. */
17. unsigned long vm_flags;        /* 标志:記憶體區域标志的資訊和行為Flags, see mm.h. */
18. struct rb_node vm_rb; //樹上該VMA的節點
19. 
20. /*
21. * For areas with an address space and backing store,
22. * linkage into the address_space->i_mmap prio tree, or
23. * linkage to the list of like vmas hanging off its node, or
24. * linkage of vma in the address_space->i_mmap_nonlinear list.
25. */
26. union {
27. struct {
28. struct list_head list;
29. void *parent;    /* aligns with prio_tree_node parent */
30. struct vm_area_struct *head;
31. } vm_set;
32. 
33. struct raw_prio_tree_node prio_tree_node;
34. } shared;
35. 
36. /*
37. * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
38. * list, after a COW of one of the file pages.    A MAP_SHARED vma
39. * can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack
40. * or brk vma (with NULL file) can only be in an anon_vma list.
41. */
42. struct list_head anon_vma_chain; /* Serialized by mmap_sem &
43. * page_table_lock */
44. struct anon_vma *anon_vma;    /* Serialized by page_table_lock */
45. 
46. /* Function pointers to deal with this struct. */
47. const struct vm_operations_struct *vm_ops; //相關的操作連結清單
48. 
49. /* Information about our backing store: */
50. unsigned long vm_pgoff;        /*檔案中偏移量 Offset (within vm_file) in PAGE_SIZE
51. units, *not* PAGE_CACHE_SIZE */
52. struct file * vm_file;        /* 被映射的檔案File we map to (can be NULL). */
53. void * vm_private_data;        /* 私有資料was vm_pte (shared mem) */
54. unsigned long vm_truncate_count;/* truncate_count or restart_addr */
55. 
56. #ifndef CONFIG_MMU
57. struct vm_region *vm_region;    /* NOMMU mapping region */
58. #endif
59. #ifdef CONFIG_NUMA
60. struct mempolicy *vm_policy;    /* NUMA policy for the VMA */
61. #endif
62. };      

每個記憶體描述符都對應于程序位址空間中的唯一區間,vm_end-vm_start大小就是記憶體區間的長度。在同一位址空間内的不同記憶體區間不能重疊。

vm_mm域指向和VMA相關的mm_struct結構體,每個VMA對其相關的mm_struct結構體來說都是唯一的,如果兩個線程共享一個位址空間,那麼它們也同時共享其中所有的vm_area_struct結構體。

3.1VMA标志

VMA是一種位标志,它包含在vm_flags域内,标志了記憶體區域所包含的頁面行為和資訊。VMA辨別反映了核心處理頁面所需遵守的行為準則,而不是硬體要求。vm_flags包含了記憶體區域中每個頁面的資訊或記憶體區域的整體資訊,而不是具體的獨立頁面。

幾個重要的标志(這些标志可以按需求組合):

VM_READ,VM_WRITE和VM_EXEC标志了區域中頁面的讀、寫和執行權限。

VM_SHARD:指明記憶體區域包含的映射是否可以在多程序間共享,

VM_IO:标志記憶體區域中按對裝置I/O空間的映射,該标志通常在裝置驅動程式執行mmap()函數進行I/O空間映射時才被設定。

VM_SEQ_READ:标志核心應用程式對映射内容執行有序的(線性和連續的)讀操作,這樣核心可以有選擇地執行預讀檔案。

3.2 VMA操作

Vm_area_struct結構體中的vm_ops域指向與指定記憶體區域相關的操作函數

此處)折疊或打開

1. struct vm_operations_struct {
2. //當指定的記憶體區域被加入到一個位址空間時,該函數被調用
3. void (*open)(struct vm_area_struct * area);
4. //當指定的記憶體區域從位址空間删除時,該函數調用
5. void (*close)(struct vm_area_struct * area);
6. //當沒有出現在屋裡記憶體中的頁面被通路時,該函數被頁面故障處理調用
7. int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf);
8. 
9. /* notification that a previously read-only page is about to become
10. * writable, if an error is returned it will cause a SIGBUS */
11. //當某個頁面為隻讀頁面時,該函數被頁面故障處理調用
12. int (*page_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf);
13. 
14. /* called by access_process_vm when get_user_pages() fails, typically
15. * for use by special VMAs that can switch between memory and hardware
16. */
17. //當get_user_pages()函數調用失敗時,該函數被access_process_vm()函數調用
18. int (*access)(struct vm_area_struct *vma, unsigned long addr,
19. void *buf, int len, int write);
20. …
21. };      

3.3 記憶體區域的樹型結構和記憶體區域的連結清單結構

mmap和mm_rb,獨立地指向與記憶體描述符相關的全體記憶體區域對象,它們包含完全相同的vm_area_struct結構體指針,僅僅方法不同。

mmap域使用單獨的連結清單連結所有的記憶體區域對象,每個vm_area_struct結構體通過自身vm_next域被連傳入連結表,mmap域指向連結清單中的一個記憶體區域,鍊中最後一個結構體指針指向空

mm_rb域使用紅-黑樹連結所有記憶體區域對象,mm_rb指向紅-黑樹根節點,位址空間中每個vm_area_struct通過自身的vm_rb連接配接到樹中。

連結清單用于需要周遊全部節點的時候,而紅黑樹适用于在位址空間中定位特定記憶體區域的時候,核心為了記憶體區域上的各種不同操作都能獲得高性能,是以同時使用了這兩種資料結構。

3.4 實際使用中的記憶體區域

可使用/proc檔案系統和pmap工具檢視給定程序的記憶體空間和其中所含的記憶體區域。

此處)折疊或打開

1. #include <stdio.h>
2. int main(void)
3. {
4. printf("hello,world!\r\n");
5. while (1);
6. return 0;
7. }      

該程式執行pid是2874,那麼

leon@ubuntu:~$ cat /proc/2874/maps

08048000-08049000 r-xp 00000000 08:01 131477     /home/leon/a.out

08049000-0804a000 r--p 00000000 08:01 131477     /home/leon/a.out

0804a000-0804b000 rw-p 00001000 08:01 131477     /home/leon/a.out

b7589000-b758a000 rw-p 00000000 00:00 0

b758a000-b772e000 r-xp 00000000 08:01 524970     /lib/i386-linux-gnu/libc-2.15.so

b772e000-b7730000 r--p 001a4000 08:01 524970     /lib/i386-linux-gnu/libc-2.15.so

b7730000-b7731000 rw-p 001a6000 08:01 524970     /lib/i386-linux-gnu/libc-2.15.so

b7731000-b7734000 rw-p 00000000 00:00 0

b7744000-b7747000 rw-p 00000000 00:00 0

b7747000-b7748000 r-xp 00000000 00:00 0          [vdso]

b7748000-b7768000 r-xp 00000000 08:01 524935     /lib/i386-linux-gnu/ld-2.15.so

b7768000-b7769000 r--p 0001f000 08:01 524935     /lib/i386-linux-gnu/ld-2.15.so

b7769000-b776a000 rw-p 00020000 08:01 524935     /lib/i386-linux-gnu/ld-2.15.so

bfb5b000-bfb7c000 rw-p 00000000 00:00 0          [stack]

每行資料格式如下:

記憶體位址開始-結束 通路權限 偏移 主裝置号:次裝置号 i節點 檔案

或者用pmap指令檢視

leon@ubuntu:~$ pmap 2874

2874:   ./a.out

08048000      4K r-x--  /home/leon/a.out

08049000      4K r----  /home/leon/a.out

0804a000      4K rw---  /home/leon/a.out

b7589000      4K rw---    [ anon ]

b758a000   1680K r-x--  /lib/i386-linux-gnu/libc-2.15.so

b772e000      8K r----  /lib/i386-linux-gnu/libc-2.15.so

b7730000      4K rw---  /lib/i386-linux-gnu/libc-2.15.so

b7731000     12K rw---    [ anon ]

b7744000     12K rw---    [ anon ]

b7747000      4K r-x--    [ anon ]

b7748000    128K r-x--  /lib/i386-linux-gnu/ld-2.15.so

b7768000      4K r----  /lib/i386-linux-gnu/ld-2.15.so

b7769000      4K rw---  /lib/i386-linux-gnu/ld-2.15.so

bfb5b000    132K rw---    [ stack ]

 total     2004K

分别表示程式和C庫的代碼段、資料段、bss段

程序全都位址空間大約2004KB,但隻有大概不到200KB的記憶體區域是可寫或私有的。如果一片記憶體範圍是共享的或不可寫的,那麼核心隻需要在記憶體中為檔案保留一份映射,比如C庫的代碼,隻讀入一次是安全的。

由于記憶體未被共享,是以隻要一有程序寫該處資料,那麼該處資料就将被拷貝出來(寫時拷貝),然後才被更新。

每個和程序相關的記憶體區域都對應于一個vm_area_strcut結構體。

4.操作記憶體區域

核心時常需要在某個記憶體區域上執行一些操作,這些操作非常頻繁,它們也是mmap()例程的基礎,為了友善這類對記憶體區域的操作,核心定義了許多輔助函數聲明在

4.1 查找一個給定的記憶體位址屬于哪一個記憶體區域: find_vma()

此處)折疊或打開

1. /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
2. struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
3. {
4. struct vm_area_struct *vma = NULL;
5. 
6. if (mm) {
7. /* Check the cache first. */
8. /* (Cache hit rate is typically around 35%.) */
9. vma = mm->mmap_cache;
10. if (!(vma && vma->vm_end > addr && vma->vm_start <= addr)) {
11. struct rb_node * rb_node;
12. 
13. rb_node = mm->mm_rb.rb_node;
14. vma = NULL;
15. 
16. while (rb_node) {
17. struct vm_area_struct * vma_tmp;
18. 
19. vma_tmp = rb_entry(rb_node,
20. struct vm_area_struct, vm_rb);
21. 
22. if (vma_tmp->vm_end > addr) {
23. vma = vma_tmp;
24. if (vma_tmp->vm_start <= addr)
25. break;
26. rb_node = rb_node->rb_left;
27. } else
28. rb_node = rb_node->rb_right;
29. }
30. if (vma)
31. mm->mmap_cache = vma;
32. }
33. }
34. return vma;
35. }      

該函數在指定位址空間中搜尋的一個vm_end大于addr的記憶體區域,這樣傳回的VMA首位址可能大于addr,是以指定的位址并不一定就包含在傳回的VMA中。

因為很有可能在執行某個VMA操作後,其他操作還會對該VMA進行操作,是以find_vma()函數傳回的結果被緩存在記憶體描述符的mmap_cache域中,實踐證明,被緩存的VMA有相當好的命中率(30~40%),檢查被緩存的VMA速度會很快,如果指定的位址不在緩存中,那麼必須搜尋和記憶體描述符相關的所有記憶體區域,這種搜尋通過紅黑樹進行。

4.2 查找第一個和指定位址區間相交的VMA:find_vma_intersection()

此處)折疊或打開

1. /* Look up the first VMA which intersects the interval start_addr..end_addr-1,
2. NULL if none. Assume start_addr < end_addr. */
3. static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
4. {
5. struct vm_area_struct * vma = find_vma(mm,start_addr);
6. 
7. if (vma && end_addr <= vma->vm_start)
8. vma = NULL;
9. return vma;
10. }      

mm:要搜尋的位址空間

start_addr:區間的起始位址

end_addr:區間尾位址

如果find_vma()傳回NULL,那麼find_vma_intersection()傳回NULL;

如果find_vma()傳回有效VMA,find_vma_intersection()隻有在該VMA的起始位置于給定的位址區間結束位置之前,才将其傳回,否者傳回NULL

5.mmap()和do_mmap():建立位址區間

核心使用do_mmap()函數建立一個新的線性位址區間。如果這個新的VMA與相鄰位址區間具有相同通路權限的話,将合并為一個VMA,如果不能合并,就确實需要建立一個新的VMA了。

無論如何,do_mmap()函數都會将一個位址區間加入到程序的位址空間中,無論是擴充已存在的記憶體區域還是建立一個新的區域。

中定義

static inline unsigned long do_mmap(struct file *file, unsigned long addr,

       unsigned long len, unsigned long prot,

       unsigned long flag, unsigned long offset)

該函數映射由file指定的檔案,具體映射從檔案偏移ofset開始,長度為len位元組。如果file參數是NULL并且offset是0,那麼代表這次映射沒有和檔案相關,這叫做匿名映射(anonymous mapping)。否則叫檔案映射(file-backed mapping)。

addr是可選參數,它指定搜尋空閑區域的起始位置。

prot參數指定記憶體區域中頁面的通路權限。

flag參數指定VMA标志,這些标志指定類型并改變映射的行為

如果系統調用do_mmap()的參數中有無效參數,它傳回一個負值;否者,就會在虛拟記憶體中配置設定額一個合适的新記憶體區域(有可能從slab中擷取)。

在使用者空間通過調用mmap()系統調用擷取核心do_mmap()的功能。

void *mmap2(void *addr,

size_t length,

int prot,

                     int flags,

int fd,

off_t pgoffset)

該系統調用是mmap()調用的第二種變種,是以起名為mmap2(),原始的mmap()方法的調用最後一個參數是位元組偏移量,而mmap2()使用頁面偏移量;mmap()調用由POSIX定義,C庫中任然作為mmap()方法使用,但新核心中已經沒有對應實作了,mmap()方法的調用是通過将位元組偏移轉化為頁面偏移,進而轉化為對mmap2()函數的調用來實作的。

6.mummap()和do_mummap():删除位址區間

do_mummap()從特定的程序位址空間中删除指定位址區間,定義在

int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)

從mm指定的使用者空間,删除從位址start開始,長度為len位元組的位址區間。

Itn munmap(void *start, size_t length)

該系統調用定義在檔案mm/mmap.c中,它是對do_mummap()函數的一個簡單封裝:

此處)折疊或打開

1. asmlinkage long sys_munmap(unsigned long addr,size_t len)
2. {
3. int ret;
4. struct mm_struct *mm;
5. 
6. mm = current->mm;
7. down_write(&mm->mmap_sem);
8. ret = do_munmap(mm,addr,len);
9. up_write(&mm->mmap_sem);
10. 
11. return ret;
12. }      

7.頁表

雖然應用程式操作的對象是對應虛拟記憶體,但處理器直接操作的卻是實體記憶體,當應用程式通路一個虛拟位址時,首先必須将虛拟位址轉化成實體位址,然後處理器才能解析位址通路請求。

Linux使用三級頁表管理完成位址轉換,可按需求在編譯簡化使用兩級,用三級是利用“最大公約數”的思想--- 一種設計簡單的體系結構。

每個程序都有自己的頁表(線程會共享頁表),記憶體描述符的pgd域指向的就是程序的頁全局目錄,注意,操作和檢索頁表時必須使用page_table_lock鎖,該鎖在相應程序的記憶體描述符中,防止競争條件。

頁表對應的結構體依賴于具體的體系結構,定義在

繼續閱讀