第三章 進程管理
1. fork系統調用從內核返回兩次: 一次返回到子進程,一次返回到父進程2. task_struct結構是用slab分配器分配的,2.6以前的是放在內核棧的棧底的;所有進程的task_struct連在一起組成了一個雙向鏈表3. 2.6內核的內核棧底放的是thread_info結構,其中有指向task_struct的指針;4. current宏可以找到當前進程的task_struct;X86是通過先找到thread_info結構,而PPC是有專門的寄存器存當前task_struct(R2)5. 內核棧大小一般是8KB6. 進程的五種狀態:TASK_RUNNING, TASK_INTERRUPTABLE, TASK_UNINTERRUPTABLE(處于改狀態的進程不能被kill,因為它可能正在等待很關鍵的數據,或者持有了信號量等), TASK_TRACED(被其他進程跟蹤狀態,具體狀態表現不明), TASK_STOPPED(收到SIG_STOP信號,停止進程,相當于暫停進程,但是也可以恢復)7. 運行上下文分為“進程上下文”和“中斷上下文”。系統調用時內核代表進程在進程上下文中執行代碼,這時current宏是有效的,指向進程的task_struct,而且系統調用時內核使用的頁表是用戶態進程的頁表;而在中斷上下文內核不代表任何進程執行代碼,而是執行一個中斷處理程序,不會有進程去干預這些中斷上下文,所以此時不存在進程上下文。8. 系統調用在陷入內核的瞬間應該是在中斷上下文的,因為是軟中斷,只是陷入內核后又用了進程上下文9. 在每個進程的task_struct結構中,有一個parent指針指向其父進程,有一個鏈表表示其所有的子進程,用這樣的結構構成了整個系統進程關系樹。10. 內核的雙向列表專用結構struct list_head11. 進程創建分為兩個步驟:fork和exec,fork用來創建進程的結構,通過寫時復制,父子進程共享進程空間(頁表),父進程和子進程的區別僅僅是PID,PPID,某些資源,統計量(task_struct的結構); exec讀出程序代碼并執行之。通過寫時復制,只有在需要寫入進程地址空間時,才為子進程創建自己的進程地址空間。*** fork的開銷其實就是復制父進程的頁表 和 為子進程分配task_struct 結構fork的過程: fork() -> clone() -> do_fork() -> copy_PRocess()copy_process()的過程:a) 為子進程分配內核棧,創建thread_info結構,創建一份與父進程相同的task_struct結構b) 更改thread_info ,task_struct結構中的部分字段,將子進程與父進程區分開來c) 將子進程的狀態設置為TASK_UNINTERRUPTIBLEd) 為子進程分配一個可用的PID(alloc_pid())e) 拷貝或共享父進程打開的文件,信號處理函數,進程地址空間等f) 設置子進程狀態為RUNNINGg) 返回一個指向子進程的指針一般系統會優先喚醒子進程,因為如果優先喚醒父進程,父進程就有可能會寫入,這樣會觸發寫時復制,而子進程一般會調用exec12. vforkvfork保證父進程在創建子進程后被阻塞,除非子進程執行了exec,或者子進程退出vfork在fork有寫時復制功能后的好處只有一個,就是:vfork不用拷貝子進程的頁表13. 線程線程在linux內核中的實現就是一個進程,只是線程會與別的線程共享進程地址空間,共享信號等等創建一個有四個線程的進程,會有四個進程被創建(四個內核棧和四個task_struct),只要指明這些task_struct中共享同一個進程地址空間即可線程的創建和進程的創建的代碼上的對比:線程創建: clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0)進程創建: clone(SIG_CHILD, 0)從線程創建的代碼也可以看出:線程創建時是共享父進程的 進程地址空間(CLONE_VM), 打開的文件(CLONE_FILES),文件系統信息(CLONE_FS), 信號處理函數(CLONE_SIGHAND)14. 內核線程內核進程需要在后臺執行一些操作,所以需要創建一些內核線程(kernel thread)內核線程和普通線程的主要區別是: 內核線程沒有獨立的地址空間(task_struct中的mm指針是NULL),共享使用內核態的頁表; 內核線程只在內核運行,從來不會調度到用戶態;內核線程和普通線程的相同點有: 同樣有狀態,被調度,也可以被搶占有哪些內核線程:flush,ksoftirqd等, 用ps -ef可以查看,其中CMD欄是[]擴起來的都是內核線程創建方法: kthread_create(); wake_up_process()用來喚醒創建的線程; kthread_run()可以創建并使之運行; kthread_stop()停止內核線程15. 孤兒進程如果父進程比子進程先執行完,父進程要在線程組中找一個新的進程作為子進程的父進程,或者找init進程作為子進程的父進程16. 進程消亡進程消亡時是通過exit()來實現的,進行exit之后內核依然會保留其task_struct結構,直到其父進程調用wait()或waitpid()回收
9. 中斷處理程序只能在一個CPU上運行
目前只有兩個子系統直接使用軟中斷:網絡子系統和SCSI子系統; tasklet也是用軟中斷實現的
注冊軟中斷處理程序:open_softirq(softirq_no, softirq_handler);raise_softirq(softirq_no);軟中斷不能睡眠,能被中斷處理程序搶占。如果同一個軟中斷在它被執行的同時再次被觸發了,那么在其他CPU上可以同時執行其處理程序,這意味著要在軟中斷的上下文要采用鎖來保護,但是如果加鎖的話,使用軟中斷的意義就不大了。所以軟中斷中一般使用的是單處理器數據(僅屬于某一個處理器的數據)。所以軟中斷作為BH用的比較少,一般采用tasklet。4. tasklettasklet也是用軟中斷實現的,有兩個軟中斷和tasklet有關:HI_SOFTIRQ, TASKLET_SOFTIRQtasklet有兩個單處理器數據結構: tasklet_vec 和 tasklet_hi_vectasklet 可以保證同一時間里給定類別中只有一個tasklet會被執行,不同類別的tasklet可以同時執行,所以使用tasklet可以不用過多的考慮鎖的問題tasklet的使用:聲明:(name是tasklet的類別)DECLARE_TASKLET(name, func, data);DECLARE_TASKLET_DISABLED(name, funct, data); //聲明默認disable的tasklettasklet處理程序的格式:void tasklet_handler(unsigned long data)調度自己定義的tasklettasklet_schedule(&my_tasklet);啟用和禁用tasklettasklet_disable(&my_tasklet); // 如果指定的tasklet正在執行,則等到執行結束再返回tasklet_enable(&my_tasklet);一個tasklet總在調度它的CPU上執行,這是希望能更好的利用處理器的高速緩存一個tasklet只要在一個CPU上被執行了,就不會同時在另一個CPU上執行它,(因為有可能一個tasklet在執行的時候,中斷處理程序在另一個CPU上又激活了這個tasklet,這樣在另一個CPU上如果發現這個tasklet正在被執行,便不會再執行了)5. 軟中斷調度時機和ksoftirqd/n內核線程由于軟中斷可以被自行重新觸發,所以如果軟中斷中不斷觸發軟中斷,而且軟中斷立即被檢查執行,那么就會導致系統的CPU被軟中斷占用的過多。另一個方案是:自行重新觸發的軟中斷并不馬上被檢查執行,而是在下一次中斷處理程序返回后檢查執行。ksoftirqd/n是在每個cpu上都有一個的內核線程,只要有未處理的軟中斷,在空閑CPU上就會被調度執行。6. 工作隊列工作隊列把工作交給一個內核線程去執行,總是在進程上下文中執行。而軟中斷和tasklet可能在中斷上下文執行,也可能在進程上下文執行。因為總是在進程上下文執行,所以工作隊列可以重新調度甚至睡眠。如果下半部中要允許重新調度,那么可以使用工作隊列。工作者線程: events/nworker_thread()是核心函數:
下面是run_workqueue()
workqueue_struct, cpu_workqueue_struct, 以及work_struct之間的關系
工作隊列的使用:DECLARE_WORK(name, void (*func) (void *), void *data); //靜態創建INIT_WORK(struct work_struct *work, void (*func) (void *), void *data); //動態創建工作隊列處理函數void work_handler(void * data);調度工作執行schedule_work(&work);schedule_delayed_work(&work, delay);問題:每個CPU上都有一個events/n內核線程,那么schedule_work(&work)時是把work掛到哪個CPU上的events list上的呢?7. 禁止下半部local_bh_disable()local_bh_enable()這兩個函數可以嵌套,即disable幾次就需要enable幾次;這是因為這兩個函數使用了preempt_count(搶占計數)來記錄引用計數
6. 死鎖死鎖的原因:多個進程,多個資源;多個進程分別持有了一部分資源,又要請求其它資源;結果互相等待。7. 鎖的爭用和加鎖粒度高度爭用的鎖容易造成系統的瓶頸加鎖粒度:細粒度的鎖可以降低鎖的爭用
5. 完成變量思想和信號量類似,只是一種針對更簡單問題的一種解決方案init_completion(struct completion *);wait_for_completion()complete()6. 大內核鎖lock_kernel()unlock_kernel()盡量少用7. 順序鎖特定情況下使用,一般是寫很少,讀很多的時候,而且數據操作簡單時較好8. 禁止搶占preempt_disable()preempt_enable()preempt_enable_no_resched()preempt_count()9. 內存屏障由于編譯器會進行讀寫重排序,所以加入內存屏障來確保讀寫順序在SMP上常見,多個CPU操作同一個數據時,可能一個CPU似乎已經寫入了,但是另一個CPU讀出的還是原來的值,常常出現在多線程共享數據而又沒有加鎖的情況下rmb() 確保跨越rmb()的讀操作不會被重排序wmb() 確保跨越wmb()的寫操作不會被重排序mb() 確保跨越mb()的讀寫操作都不會被重排序smp_rmb() / smp_wmb() / smp_mb()

2. kmalloc和slab

kmalloc是基于slab分配器實現的。slab分配器主要解決的是分配不規則字節內存造成的內存碎片的,還有加快內存的分配和釋放時間kmem_cache_create()3. vmallocvmalloc和kmalloc類似,都是分配物理內存,但是kmalloc分配的物理內存和虛擬內存都一定是連續的;而vmalloc分配的虛擬內存是連續的,物理內存可能不連續另外,kmalloc的性能更好,因為vmalloc必須建立專門的頁表項,而kmalloc因為是直接映射的,所以無需頁表(TLB:緩存虛擬地址到物理地址的高速緩存)所以vmalloc在內核中一般只是在要分配大塊內存時使用,例如加載模塊時vfree(void *)是否vmalloc分配的內存4. 棧上的內存管理32位和64位體系結構的頁面大小分別是4KB和8KB,一般內核進程的棧是2頁,所以一般是8KB和16KB千萬不要在內核棧上面分配大量數據,容易造成棧溢出5. 高端內存的映射使用alloc_pages()分配的高端內存返回的是page*結構,因為高端內存沒有直接的邏輯地址映射,所以要建立頁表來映射永久映射: kmap/kunmap (可能睡眠)臨時映射: (在不能睡眠的情況下使用) kmap_atomic/kunmap_atomic (其實是有一組保留的映射地址)6.分配函數的選擇一般用kmalloc和_get_free_pages如果要從高端內存分配,用alloc_pages + kmap如果分配的內存較大,而且無需物理上連續,就用vmalloc如果要做分配管理,請用slab分配器
2. Unix的四種和文件系統相關的傳統抽象概念文件,目錄項,索引節點(inode,存儲文件的元數據),mount節點另外,文件系統的控制信息被放在了超級塊中===摘抄===大部分UNIX文件系統種類具有類似的通用結構,即使細節有些變化。其中心概念是超級塊superblock, i節點inode, 數據塊data block,目錄塊directory block, 。其中超級塊中包含了關于該硬盤或分區上的文件系統的整體信息,如文件系統的大小(其準確信息依賴文件系統)等。 i節點包括除了名字外的一個文件的所有信息,名字與i節點數目一起存在目錄中,目錄條目包括文件名和文件的i節點數目。 i節點包括幾個數據塊的數目,用于存儲文件的數據。 i節點中只有少量數據塊數的空間,如果需要更多,會動態分配指向數據塊的指針空間。這些動態分配的塊是間接塊;為了找到數據塊,這名字指出它必須先找到間接塊的號碼。=========3. 超級塊超級塊記錄文件系統基本信息超級塊的操作主要是CRUD inode4. inodeinode存放的是文件或目錄的所有信息:幾個時間,引用計數,uid,gid,文件大小,權限一個inode就代表了一個文件:可以是普通文件,也可以是管道,塊設備,字符設備等inode的操作:CRUD文件,修改權限,truncate,mkdir, mknod, rename等等通過inode可以尋找到dentry對象5. dentry目錄項對象目錄和普通文件都是一個目錄項對象主要用來解析文件路徑目錄項結構中維護了整個文件目錄樹dentry的操作:判斷目錄對象是否有效,比較文件名,查找文件等6. 文件對象文件對象存放的是文件信息:文件路徑,文件操作file_Operations, 文件offset,頁高速緩存地址文件的操作: read/write/lseek/ioctl/fsync/open/close/mmap...
新聞熱點
疑難解答