linux设备驱动归纳总结(四):1.进程管理的相关概念【转】
本文轉載自;http://blog.chinaunix.net/uid-25014876-id-64866.html
linux設備驅動歸納總結(四):1.進程管理的相關概念
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
接下來的幾節我會大概的講一下內核進程的一些概念,其實應該在學習系統編程時候就應該知道的。。我參照的書籍是《linux內核設計與實現》(第三版)。我會盡可能地跳開內核代碼,簡述一下原理。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
一、什么是進程
?
簡單的說,進程就是正在運行的程序,一個程序可以同時有多個進程。學過C語言都知道,程序運行時并不是只有代碼,還包含其他的資源,如打開的文件,信號,全局變量等等。我在《操作系統原理》中看過一個很生動很深刻的例子:一個人對照著菜譜做菜。在這例子中,人就是內核,菜譜就是程序,做菜的過程就是進程,而菜、鍋就是這個進程的資源。
內核為線程提供了兩種技術:虛擬處理器和虛擬內存。這就是說,每個進程都傻乎乎的認為自己獨占著CPU和享用這4G的內存,確不知道內核在背后調度進程和給每個進程4G的虛擬地址。
進程由fork創建,通過exit退出。
有人或許會問,那線程是什么?線程就是一種特殊的進程。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
二、進程是用什么結構體來維護
?
內核將所有的進程放在叫任務隊列(task list)的雙向循環鏈表中,鏈表中的每個項都是類型為task_struct、稱為進程描述符的結構。每個進程描述符包含著一個進程的所有信息,驅動開發中我用得最頻繁的有兩個成員,pid(進程標識值)和comm(當前進程的所執行的程序文件名稱)。
來張形象點的圖:
獲得當前正在進行的進程進程描述符也很簡單,使用全局項current就可以獲得。
/*4th_mutex/4th_mutex_1/1st/test.c*/
113 P_DEBUG("[%s]:pid[%d]\n", current->comm, current->pid);
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
三、進程的狀態
?
我只說5種,其中前兩種是之前在等待隊列的時候介紹過。
1)TASK_RUNNING(運行):該狀態出現在進程正在運行,或者已經放在運行隊列中等待執行(對應操作系統原理上所說的就緒狀態)。這里要注意的是等待執行和休眠是兩碼事。
2)TASK_INTERRUPTIBLE(可中斷休眠):這就是休眠狀態中一種,之所以說可中斷,就是說除了可以被其他進程從等待隊列喚醒以外,還可以接送到信號而喚醒,這是常用的休眠狀態。
3)TASK_UNINTERRUPTIBLE(不可中斷休眠):這就是休眠狀態的另一種,只能從等待隊列被喚醒。因為它如此霸道,所以很少有人使用。
4)TASK_ZOMBIE(僵死):這種情況出現在進程結束后,但父進程還有來回收該進程的進程描述符。
5)TASK_STOPPED(停止):一看就知道,進程停止執行。
?
來個圖來對照前四種狀態的轉換:
由上圖可以看到用戶空間的進程有fork()系統調用產生,如果運行途中沒有任何阻塞,它會在最后調用do_exit將進程的狀態轉為TASK_ZOMBIE。等待父進程來收尸。接下來就要簡單地說一下進程的創建和進程的終結。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
四、進程的創建
進程的創建一般分兩步:
?
1、調用fork():
在系統編程時,我們一般都是調用fork()來創建新的用戶進程。
先說一下傳統的forl()的實現,傳統的fork()被調用后,內核會拷貝父進程的所有資源給新建的子進程。要知道這是一個多愚蠢的操作,如果新建子進程是打算執行另一個新程序,之前的拷貝過程就白費了。
出于這樣的原因,linux的fork()有了寫時拷貝(copy-on-write)技術。從字面上就能理解意思,父進程創建子進程后,他給子進程創建一個文件描述符,并且與子進程以只讀方式共享原有的資源,只有在子進程或者父進程修改資源時,資源才會被復制。所以說,在不修改資源的情況下,fork()的實際開銷就兩樣:
1)復制父進程的頁表給子進程。大家應該都知道,linux內存管理使用的頁式管理,只要也就是說,只要把父進程的頁表復制給子進程,子進程就能在頁表中找到與父進程共享的4G虛擬地址了。
2)為子進程創建唯一的進程描述符。這個就不用解釋了,進程與進程描述符是一一對應的。
?
fork具體調用的什么函數我就不詳細說了,不過應該有這樣的一個概念:
fork->clone->do_fork()->copy_procrss:
fork()系統調用根據提供的參數調用clone(),然后clone()去調用do_fork(),其中do_fork中完成了創建的大部分操作,里面有一個主要的函數copy_process()。
?
2、調用exec():
一般的,創建的子進程都不是為了完成父進程中的任務,而是需要執行新的任務。exec()的作用就是讀取可執行文件并加載到地址空間開始運行,可以類比成命令”./xxxxx”。如果fork()后子進程調用exec()執行新的代碼,就不需要拷貝父進程的資源了。所以,一般fork()之后都是子進程先運行。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
五、進程的終結:
?
一般的,進程調用exit()結束進程。相應的,exit()是調用do_exit()進行刪除進程的資源和改變進程狀態等操作。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
六、什么是進程上下文:
?
用戶態的應用程序執行系統調用時,它就會陷入內核空間,此時,我們稱內核“代表進程執行”并處于進程上下文。簡單的說,以我們之前寫的驅動舉例,當應用成調用open,他就會陷入內核調用驅動函數中的test_open,此時內核就 處于進程上下文了。
值得一提的是,在進程上下文時,current始終有效,它還是指向應用層中的進程,所以在”1st”的例子中,tesp_open打印出來的進程號current->pid與應用層是一樣的。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
七、線程、進程和內核線程程又是怎么樣的關系:
?
線程,它是進程活動中的對象,最通俗的解釋,一個進程里面可以有一個或者多個線程,它們共同享用進程的資源。
內核線程,獨立運行在內核空間的標準進程,但沒有獨立的運行空間,只運行在內核空間,但和普通進程一樣被調度和搶占。
總的來說,線程(又叫用戶線程)和內核線程都是進程的特殊形式,它們的創建同樣也是通過調用clone()。它們和進程的最大區別在于它們沒有獨立的4G虛擬空間。
而線程和內核線程的區別就是:線程存在與用戶態,內核線程存在與內核態。
同時需要強調的是,進程是存在于用戶態的。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
八、總結:
?
今天只是介紹了進程的一些基本的概念,為以后的進程調度、并發、競態等理論打基礎。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
總結
以上是生活随笔為你收集整理的linux设备驱动归纳总结(四):1.进程管理的相关概念【转】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 扒取网站的源代码
- 下一篇: nginx+php-fpm动静分离