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

1. 在執行系統調用時,內核一般返回0表示成功,負數表示失敗。用戶態的包裝函數將內核返回的錯誤碼取負,設置到errno中。

2. 對於沒有實現的系統調用,sys_call_table中會存放sys_ni_syscall函數,返回-ENOSYS。

3. 進入系統調用有兩種方法:

(1) int 0x80

(2) sysenter

   退出也有兩種方式:

        (1) iret

(2) sysexit

4. int 0x80 方式:

         當系統調用結束後,將返回值寫入eax所在的內核棧中,之後內核會檢查thread_info中的flags,看是否有重新調用等額外的工作要做。最後恢復用戶態的寄存器,然後通過

iret返回用戶態。

5. sysenter 方式:

       當調用sysenter時,必須填充三個寄存器 (1) SYSENTER_CS_MSR, 內核代碼段選擇子 (2) SYSENTER_EIP_MSR,內核入口地址 (3)SYSENTER_ESP_MSR, 

       內核棧指針。

6.  內核的堆棧段和數據段是共用的,並且緊挨着內核代碼段。 

7. enable_sep_cpu在內核初始化的過程中初始化將__KERNEL_CS寫入SYSENTER_CS_MSR寄存器,將sysenter_entry函數的線性地址寫入SYSENTER_CS_EIP寄存器,

  將當前cpu TSS的末尾地址寫入SYSENTER_CS_ESP中。之後發生系統調用時,切換到內核態後,系統調用處理函數讀取esp,然後獲取TSS 中esp0的地址,再從esp0中

  讀取真正的內核棧地址。

8.爲了解決兩種調用方式的兼容問題,在內核初始化過程中sysenter_setup函數創建了一個頁,叫vsyscall page。裏面包含有一個小的ELF共享庫。當進程通過execve系統調用 來起動一個elf程序時,vsyscall page中的代碼就會動態第鏈接到進程的地址空間。vsyscall page中的代碼會選擇最優的方式發起系統調用。

9. vsyscall page 中定義了__kernel_vsyscall和 SYSENTER_RETURN,根據是否支持sysenter來實現該函數。用戶態的庫發起系統調用時最終會調用__kernel_vsyscall, 退出系統調用時切換回用戶態後從SYSENTER_RETURN 開始執行。

10. 系統調用時,參數會先被保存到cpu寄存器中,然後切換到內核態後,從寄存器中複製到內核棧中,供內核的C函數使用。參數不能超過6個,大於6個的話,需要傳遞棧指針。

11. 在進行參數檢查時,內核只需要確定用戶傳遞的地址是否屬於用戶態的地址範圍。更精細的檢查會在頁故障中進行。

12. 內核通過函數access_ok(verify_area)來檢查地址是否合法,該函數內部會檢查地址是否越過addr_limit.seg,這個值一般是0xbfffffff。通過get_fs 和set_fs 可以更改這個值。

13. 發生頁故障的三種情況:

(1)內核去訪問一個屬於該進程的頁,但是該頁面不存在或者是隻讀,內核必須分配一個新頁。

        (2)內核去訪問一個屬於該進程的頁,但是該頁對應的頁表不存在,內核必須建立頁表。

        (3)內核bug或者是硬件的錯誤,內核會oops。

        (4)內核在處理系統調用過程中,去讀去用戶穿過來的地址,但是該地址並不在進程的地址空間內。

14. 由於內核訪問用戶空間的函數比小少,因此可以將這些函數的指令地址記錄在一個異常表中,當發生頁故障時,只要檢查出錯的指令是否在異常表中,就可以確定是否是由於系統調用參數異常導致。

15. exception_table_entry結構中包含一個指令的地址和fixup代碼的地址。

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