深入理解linux內核讀書筆記(第九章)

1. 內核信賴自己,但是對於用戶態的內存請求,內核會做必要的地址檢查,然後先給進程分配地址空間(線性地址),真正的物理內存分配推遲到必要的時候才進行。

2. 內核使用mm_struct 來描述用戶的地址空間信息,所有的mm_struct 是用雙向鏈表連接起來的,相鄰的mm_struct 在mmlist域中表示,鏈表中的第一個元素是init_mm中mmlist指向的進程0的mm_struct。

3. mm_users 域用來存儲共享內存描述符的線程數, mm_count域是mm_struct 的引用計數,每次mm_count減小時,內核都檢查其是否等於0,如果是的話,就釋放該描述符。

4. mm_alloc用來分配新的進程空間描述符,通過slab cache來分配,並且將mm_users和mm_count 都初始化爲1.

5. mmput將mm_users減小1,如果 等於0, 就釋放LDT,內存區域描述符和相關聯的頁表,並且調用mmdrop,mmdrop將mm_count 減1,如果等於0, 就釋放進程空間描述符。

6. 內核線程和普通進程不同,它不使用進程空間。對於TASK_SIZE以上的內存,所有的進程都是相同的,所以內核線程使用在它之前的進程頁表。

7. 對於普通進程,mm 和active_mm應該是相同的,但是對於內核線程,mm 爲NULL, active_mm指向之前進程的active_mm所指向的進程空間描述符。

8. 當內核更新TASK_SIZE之上的內存映射時(vmalloc,vfree),內核只更新主頁表,其他進程的頁表信息會通過page_fault處理函數來進行更新。

9. vm_area_struct 用來表示一個內存區域,範圍是[vm_start, vm_end),當添加或移除一個內存區域時,內核嘗試將權限相同的,地址連續的區域進行合併。起始地址和大小均爲頁對齊。

10. 一個進程的所有內存區域通過一個簡單鏈表按照地址的大小連接起來,vm_area_struct 中的vm_next域指向下一個內存區域,mm_struct 的mmap 指向該進程的第一個內存區域,map_count存儲進程擁有的內存區域個數。

11. linux 2.6中用了紅黑樹來快速查找某個內存區域,由mm_rb指向根節點。

12. vm_area_struct 中的vm_flags用來表示該內存區域的一些屬性,包括訪問權限,共享權限以及增長方式等信息。

13 初始的頁表flags存儲在vm_area_struct 中的vm_page_port字段中。

14. find_vma接收mm_struct和addr兩個參數,用來找到第一個vm_end大於addr的vm_area_struct的地址,沒有找到的話,返回NULL。

15. mm_struct中的mmap_cache指向該進程上次訪問的vm_area_struct , find_vma會首先檢查該內存區域是否包含addr,如果是,立即返回, 否則的話,通過紅黑樹來進行查找。

16. find_vma_intersection用來查找覆蓋一段地址的內存區域。

17. get_unmapped_area用來查找一個進程空間中指定長度區間的線性地址段,addr不爲空表示從addr開始查找,接下來根據是否是文件映射或者是匿名映射來分別調用相應的get_unmapped_area方法。

18. insert_vm_struct將一個vm_area_struct 插入到鏈表和紅黑樹中,如果是匿名映射的話,還要與anon_vma進行關聯。

19. mmap函數爲進程創建並且初始化一個新的內存區域,但是有可能和之前的區域合併。

20. get_user_pages 函數會遍歷一個指定的用戶內存區間,對於每一個頁,調用follow_page來檢查是否有相應的物理頁面,如果沒有的話,會調用handle_mm_fault來分配物理頁面並且建立頁表。

21. do_page_fault函數是x86平臺的頁故障異常處理函數,它接收pt_regs 指針和error_code。

22 error_code由3個bit組成:

   (1)bit 0如果置位,表示權限非法,否則,表示頁面不存在。

   (2)bit 1如果置位,表示是寫異常,否則,爲讀或執行異常。

   (3)bit 2如果置位,表示異常發生在用戶態,否則,發生在內核態。

23. 當頁故障發生時,發生故障的頁的線性地址放入了CR2寄存器。

24. in_atomic函數檢查內核是否在關鍵區內,如果是以下情況,則返回真:

   (1) 內核正在執行中斷處理函數或者延遲函數。

   (2)內核正在執行關鍵區的代碼,並且已經關閉內核搶佔。

25.  滿足in_atomic 或者是內核線程時, 缺頁會導致內核錯誤,產生oops。

26. 爲了讀取用戶進程的內存信息,需要獲取mm->mmap_sem,爲了防止造成死鎖,使用down_read_trylock,如果無法獲取鎖,該信號量可能是被別的系統調用佔用了,這種情況的話,就等待信號量被釋放,否則,就是內核bug,產生oops。

27.  對於異常的地址不在進程的vma中,還有一種額外的情況是由於push操作導致的棧增長,vm_end不變,vm_start 減小, 異常的地址應該只比sp小一點,否則的話,也是bug。確認是棧擴展的話,調用expand_stack, 改變vm_start的值,賦值爲 address & PAGE_MASK。

28.  如果是用戶態的進程不包含異常地址,進入bad_area函數,向用戶進程發送SIGSEGV信號。

29.  在訪問權限沒有問題的情況下,接下來調用handle_mm_fault函數來分配物理頁面。返回VM_FAULT_MINOR表示頁故障不會阻塞當前進程,VM_FAULT_MAJOR表示會導致當前進程阻塞。

30. handle_mm_fault 會先檢查相應的頁目錄,頁表是否存在,不存在的話,就分配。handle_pte_fault用於處理最後的頁表項,如果頁表項是空,就是demand paging;如果是隻讀,就是copy on write (COW)。

31.  (1) 如果pte_none返回1,頁表項全部爲0, 表示該頁從來沒被訪問或者爲一個線性文件映射,如果vm_ops不空,可能是文件映射,否則爲匿名映射。

       (2) 如果頁屬於一個非線性文件映射,P清零,D位置1。

         (3)    如果頁被訪問過,但是目前被置換到磁盤,P, D位都清零。

32.  do_anoymous_page 在處理讀請求時,將pte設置爲系統預先分配的0頁內存,權限爲只讀, 後續的寫操作會導致COW; 如果爲寫請求,則需要分配物理頁。

33. COW技術:page結構體的_count域表示共享該頁的進程數,當進程釋放page或者發生COW時,_count減1, 當_count爲-1時,真正釋放該頁。

34. 當handle_pte_fault發現P爲置1, 並且頁表項爲只讀,請求爲寫,此時調用do_wp_page,該函數來讀取_count等信息來決定是否執行COW。

35. 如果發生在內核空間,並且地址大於TASK_SIZE, 會進入vmalloc_fault, 將主內核頁表複製到用戶進程的頁表中。

36. linux線程是通過clone函數,傳入CLONE_VM參數來創建的,線程之間共享地址空間。

37. copy_mm用來創建新進程的地址空間,如果CLONE_VM設置了,就直接使用父進程的mm, 否則,需要調用dup_mm來複制父進程的mm,並且修改一些字段。dup_mm 會調用dup_mmap來複制內存區域和頁表。

38. 進程退出時,調用exit_mm來釋放進程空間, 首先會將mm_count加1, mmput最後會釋放LDT, vma和頁表。mm本身不會被銷燬,而是在schedule中的finish_task_switch中的mmdrop來進行銷燬。

39. mm->brk用來表示進程的堆頂,brk是一個系統調用,C庫中的堆操作都是通過brk和mmap來實現的。如果是縮減堆,則會調用do_munmap; 擴展堆的話,會調用do_brk, 該函數相當於一個只處理匿名內存區域的do_mmap,而不考慮文件映射。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章