《奔跑吧linux內核》3.1筆記,不足之處還望大家批評指正
進程是Linux內核最基本的抽象之一,它是處於執行期的程序。它不僅局限於一段可執行代碼(代碼段),還包括進程需要的其他資源。在Linux內核中常被稱作任務。
線程被稱為輕量級進程,是操作系統調度的最小單元,通常一個進程可以擁有多個線程。
進程和線程的區別在於進程擁有獨立的資源空間,而線程則共享進程的資源空間。
問題一:在內核中如何獲取當前進程的task_struct數據結構?
內核有一個常用的常量current用於獲取當前進程task_struct數據結構,它利用了內核棧的特性。首先通過sp寄存器獲取當前內核棧的地址,對齊后獲取struct thread_info數據結構指針,最后通過thread_info->task成員獲取task_struct數據結構。圖1為linux內核棧的結構圖。
圖1 內核棧
問題二:下面程序會打印幾個“_”?
int main(void){
int i;
for(i=0; i<2; i++){
fork();
printf("_\n");}
wait(NULL);wait(NULL);
return 0;}
答案是6個“_”,具體思路如圖2所示。(i=0,調用一次fork后,父進程a創建子進程b,此后a和b進行打印,打印兩個“_”;后i=1,a和b均調用fork,a創建子進程a_1,b創建子進程b_1,4個進程執行打印操作,打印出四個“_”;i=2,返回)
圖2 fork解題思路
問題三:用戶空間進程的頁表是什么時候分配的,其中一級頁表什么時候分配?二級頁表呢?
(此問有點疑問,暫且認為一級頁表為頁目錄項(pgd),二級頁表為也表項(pte))
對於內核來說,進程的“鼻祖”是idle進程,稱為swapper進程;對於用戶空間來說,進程“鼻祖”是init進程,所有用戶空間進程都由init進程創建或派生。
在mm_init()函數中,首先給新進程的mm_struct數據結構進行初始化,然后對mm_users,mm_count進行初始化,設置進程空間地址讀寫信號量,設置保護進程頁表的spinlock鎖,最后調用pgd_alloc()函數進行pgd頁表的分配工作。在pgd_alloc()函數中,調用pte_alloc_map()函數進行第0,第1個頁表的分配,此后在dup_mmap()函數中將父進程所有的VMA對應的pte頁表項復制到子進程對應的pte頁表項中。
問題四:請簡述fork,vfork和clone之間的區別?
fork,vfork,clone的實現都是通過調用do_fork()函數實現的,只是函數調用不一樣。
fork函數實現:do_fork(SIGCHLD,0,0,NULL,NULL);只使用SIGCHLD標志位,在子進程終止后發送SIGCHLD信號通知父進程。fork是重量級調用,為子進程建立了一個基於父進程的完整副本,然后子進程基於此運行。為了減少工作量采用寫時復制技術(COW),子進程只復制父進程的頁表,不復制頁面內容。當子進程需要寫入新內容時,才觸發寫時復制機制,為子進程創建一個副本。
vfork函數實現:do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,0,0,NULL,NULL);它比fork多了兩個標志位,分別為CLONE_VFORK和CLONE_VM。CLONE_VFORK表示父進程會被掛起,直至子進程釋放虛擬內存資源。CLONE_VM表示父子進程運行在相同的內存空間中。
clone函數實現:do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);clone用於創建線程,並且參數通過寄存器從用戶空間傳遞下來,通常會指定新的棧地址(newsp)。