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

NO IMAGE

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,而不考慮檔案對映。