进程基础
1.?進(jìn)程基礎(chǔ)
l?了解進(jìn)程的概念。
l?了解進(jìn)程和程序的區(qū)別
l?進(jìn)程的類型
l?進(jìn)程的運(yùn)行狀態(tài)以及之間的轉(zhuǎn)化
l?進(jìn)程的模式
1.1進(jìn)程的相關(guān)基本概念(P5)
回顧一下:我們可以把操作系統(tǒng)看做應(yīng)用程序和硬件之間的一層軟件。
1)?防止硬件被失控的應(yīng)用程序?yàn)E用。
2)?為應(yīng)用程序提供簡單一致的接口來訪問底層的功能各異的硬件。操作系統(tǒng)將前述的計(jì)算機(jī)系統(tǒng)抽象為幾個(gè)基本的抽象概念:
a)?進(jìn)程:為每個(gè)應(yīng)用程序虛擬獨(dú)占整個(gè)計(jì)算機(jī)系統(tǒng)。
b)?虛擬存儲(chǔ)器:為每個(gè)進(jìn)程提供了一個(gè)假象好像獨(dú)占內(nèi)存,而且提供了一個(gè)抽象的虛擬地址空間,使每個(gè)進(jìn)程看到的存儲(chǔ)器都是一致的。想想看如果沒有這種虛擬機(jī)制我們的鏈接器的實(shí)現(xiàn)會(huì)多么麻煩。
c)?文件:……
進(jìn)程和程序的區(qū)別:
l?程序是一個(gè)靜態(tài)的概念,可以理解為保存在磁盤上的包含了指令和數(shù)據(jù)的一個(gè)文件。
l?進(jìn)程它是動(dòng)態(tài)的,是操作系統(tǒng)的概念,是操作系統(tǒng)中程序執(zhí)行和資源管理的最小單位 。“進(jìn)程是一個(gè)程序的一次執(zhí)行的過程”:每當(dāng)我們執(zhí)行一個(gè)程序時(shí),對于操作系統(tǒng)來講就創(chuàng)建了一個(gè)進(jìn)程作為調(diào)度的基本單元,這個(gè)過程包括包括創(chuàng)建、調(diào)度和消亡;在這個(gè)過程中,伴隨著資源的分配和釋放。比如,linux的vi編輯器,它就是一段在linux下用于文本編輯的工具,那么它是一個(gè)程序,而我們在linux終端中,可以分別開啟兩個(gè)vi編輯器的進(jìn)程。
參考
http://wiki.linuxdeepin.com/index.php?title=%E7%A8%8B%E5%BA%8F%E4%B8%8E%E8%BF%9B%E7%A8%8B
簡單過一下ppt上的內(nèi)容后重點(diǎn)提一下進(jìn)程來自程序但又不同于程序。引出后面要重點(diǎn)講解的進(jìn)程和程序的差別。
1.2進(jìn)程與程序(P6)
針對p6的這個(gè)圖:
1.2.1 先講一下程序
對程序的正文段和數(shù)據(jù)段的解釋從elf格式上去理解。
程序可以理解為存放在某種存儲(chǔ)介質(zhì)上(一般可以理解為硬盤/flash等)的二進(jìn)制字節(jié)碼,在Linux上一般為ELF格式。
ELF格式(http://en.wikipedia.org/wiki/Executable_and_Linkable_Format)Executable and Linkable Format.
Linux系統(tǒng)上的可執(zhí)行文件,.o文件,共享庫,coredump文件都是ELF格式
給學(xué)生畫個(gè)ELF格式的大致圖理解一下。或者再用readelf 命令演示一下。
我們可以大致看到程序文件除了文件頭之外,一般由多個(gè)段section組成,主要有代碼段(text)和數(shù)據(jù)段(data)。
舉例:<<<<<< 主要用readelf看一下
可以通過objdump 或者readelf命令進(jìn)行讀取分析。
readelf –h filename // 查看文件頭
readelf –a filename // 查看所有內(nèi)容
objdump和readelf的使用區(qū)別:
http://blog.csdn.net/shenyan008/article/details/6889373
objdump和readelf都可以用來查看二進(jìn)制文件的一些內(nèi)部信息.?區(qū)別在于objdump借助BFD而更加通用一些,?可以應(yīng)付不同文件格式,?readelf則并不借助BFD,?而是直接讀取ELF格式文件的信息,?按readelf手冊頁上所說,?得到的信息也略細(xì)致一些。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1.2.2 再講一下進(jìn)程 (P7)
進(jìn)程是一個(gè)程序被OS加載到RAM中執(zhí)行后的一個(gè)完整的執(zhí)行環(huán)境。
舉例:<<< misc\?program_process.pptx?>>>
參考http://oss.org.cn/kernel-book/ch04/4.1.htm
(1)正文段(text):存放被執(zhí)行的機(jī)器指令。這個(gè)段是只讀的(所以,在這里不能寫自己能修改的代碼),它允許系統(tǒng)中正在運(yùn)行的兩個(gè)或多個(gè)進(jìn)程之間能夠共享這一代碼。例如,有幾個(gè)用戶都在使用文本編輯器,在內(nèi)存中僅需要該程序指令的一個(gè)副本,他們?nèi)脊蚕磉@一副本。
(2)用戶數(shù)據(jù)段(user segment):存放進(jìn)程在執(zhí)行時(shí)直接進(jìn)行操作的所有數(shù)據(jù),包括進(jìn)程使用的全部變量在內(nèi)。顯然,這里包含的信息可以被改變。雖然進(jìn)程之間可以共享正文段,但是每個(gè)進(jìn)程需要有它自己的專用用戶數(shù)據(jù)段。例如同時(shí)編輯文本的用戶,雖然運(yùn)行著同樣的程序__編輯器,但是每個(gè)用戶都有不同的數(shù)據(jù):正在編輯的文本。
(3)系統(tǒng)數(shù)據(jù)段(system segment):(課件上寫的堆棧段是錯(cuò)誤的)不要理解為ELF中的段,而應(yīng)該理解為Linux為每個(gè)進(jìn)程建立了task_struct數(shù)據(jù)結(jié)構(gòu)來容納這些控制信息。該段有效地存放程序運(yùn)行的環(huán)境。事實(shí)上,這正是程序和進(jìn)程的區(qū)別所在。如前所述,程序是由一組指令和數(shù)據(jù)組成的靜態(tài)事物,它們是進(jìn)程最初使用的正文段和用戶數(shù)據(jù)段。作為動(dòng)態(tài)事物,進(jìn)程是正文段、用戶數(shù)據(jù)段和系統(tǒng)數(shù)據(jù)段的信息的交叉綜合體,其中系統(tǒng)數(shù)據(jù)段是進(jìn)程實(shí)體最重要的一部分,之所以說它有效地存放程序運(yùn)行的環(huán)境,是因?yàn)檫@一部分存放有進(jìn)程的控制信息。系統(tǒng)中有許多進(jìn)程,操作系統(tǒng)要管理它們、調(diào)度它們運(yùn)行,就是通過這些控制信息。
1.2.3 task_struct:
內(nèi)核會(huì)創(chuàng)建一個(gè)叫進(jìn)程描述符task_struct的數(shù)據(jù)結(jié)構(gòu)對象來管理該對象。
可以提示讓學(xué)生思考并列出,打開文件演示一下幾個(gè)關(guān)鍵的成員給大家演示一下linux/sched.h
| state | 進(jìn)程的運(yùn)行狀態(tài)。參考sched.h中進(jìn)程狀態(tài)的宏定義。 |
| rt_priority; | 表示此進(jìn)程的運(yùn)行優(yōu)先級 |
| struct ??mm_struct *mm; | 該結(jié)構(gòu)體記錄了進(jìn)程內(nèi)存使用的相關(guān)情況,虛擬地址的概念可以提一下 |
| pid_t ??pid; | 進(jìn)程號,是進(jìn)程的唯一標(biāo)識 |
| pid_t ?tgid; | 線程組id號,Each thread belongs to a group and the PID of the first thread (also known as the group leader) created in a thread group is stored in a field called tgid (thread-group id) http://careers.directi.com/display/tu/Understanding+Processes+in+Linux |
| struct ?task_struct ?*real_parent; | real_parent是該進(jìn)程的"親生父親",不管其是否被"寄養(yǎng)" |
| struct ??task_struct ?*parent; | parent是該進(jìn)程現(xiàn)在的父進(jìn)程,有可能是"繼父" |
| struct ??list_head ???children; | 這里children指的是該進(jìn)程孩子的鏈表,可以得到所有孩子的進(jìn)程描述符 |
| struct ???list_head ???sibling; | 同理,sibling該進(jìn)程兄弟的鏈表,也就是其父進(jìn)程的所有孩子的鏈表 |
| struct ??task_struct ?*group_leader; | 這個(gè)是主線程的進(jìn)程描述符,也許你會(huì)奇怪,為什么線程用進(jìn)程描敘符表示,因?yàn)閘inux并沒有單獨(dú)實(shí)現(xiàn)線程的相關(guān)結(jié)構(gòu)體,只用一 ???????????個(gè)進(jìn)程來代替線程,然后對其做一些特殊的處理。 |
| struct ??list_head ??thread_group; | 這個(gè)是該進(jìn)程所有線程的鏈表 –?結(jié)合pid和tpid提一下線程和線程組的概念,后面講到線程時(shí)會(huì)再展開講 |
| struct fs_struct?*fs; | 它包含此進(jìn)程當(dāng)前工作目錄和根目錄、 ? |
| struct ?files_struct ?*files; | 打開的文件相關(guān)信息結(jié)構(gòu)體。f_mode字段描述該文件是以什么模式創(chuàng)建的:只讀、讀寫、還是只寫。f_pos保存文件中下一個(gè)讀或?qū)憣l(fā)生的位置 |
| struct ??signal_struct ?*signal; struct ??sigband_struct ?*sighand; | 信號相關(guān)信息的句柄 |
task_struct結(jié)構(gòu)體非常龐大,我們沒必要去了解它的所有字段,只需要對其中比較重要的字段加以關(guān)注就可以了。從上面的分析可以看出,一個(gè)進(jìn)程至少有一下東東
1、?進(jìn)程號(pid),就像我們的身份證ID一樣,每個(gè)人的都不一樣。進(jìn)程ID也是,是其唯一標(biāo)示。
2、?進(jìn)程的狀態(tài),標(biāo)識進(jìn)程是處于運(yùn)行態(tài),等待態(tài),停止態(tài),還是死亡態(tài)
3、?進(jìn)程的優(yōu)先級和時(shí)間片。用于OS的進(jìn)程調(diào)度.不同有優(yōu)先的進(jìn)程,被調(diào)度運(yùn)行的次序不一樣,一般是高優(yōu)先級的進(jìn)程先運(yùn)行。時(shí)間片標(biāo)識一個(gè)進(jìn)程將被處理器運(yùn)行的時(shí)間
4、?處理器相關(guān)上下文: 一個(gè)進(jìn)程可以被認(rèn)為是系統(tǒng)當(dāng)前狀態(tài)的總和。每當(dāng)一個(gè)進(jìn)程運(yùn)行時(shí),它要使用處理器的(PC,各種寄存器,堆棧指針,堆指針等),這是進(jìn)程的上下文(context)。并且,每當(dāng)一個(gè)進(jìn)程被暫停時(shí),所有的CPU相關(guān)上下文必須保存在該進(jìn)程的task_struct中。當(dāng)進(jìn)程被調(diào)度器重新啟動(dòng)時(shí)其上下文將從這里恢復(fù)。
5、?進(jìn)程運(yùn)行時(shí)使用的其他資源,比如虛擬內(nèi)存,Linux必須跟蹤內(nèi)存如何映射到系統(tǒng)物理內(nèi)存。打開的文件, 未決的信號。
6、?甚至還有進(jìn)程里的一個(gè)或者多個(gè)執(zhí)行線程等等信息。
總結(jié):所以我們可以認(rèn)為進(jìn)程這個(gè)對象就是CPU上正在執(zhí)行的程序代碼的實(shí)時(shí)結(jié)果(快照),內(nèi)核需要將這些信息組織起來作為程序執(zhí)行的上下文環(huán)境信息進(jìn)行有效的管理。
進(jìn)程的存儲(chǔ)器安排。
1.3 Linux系統(tǒng)中的進(jìn)程類型(P8)
參考:http://wiki.linuxdeepin.com/index.php?title=%E7%A8%8B%E5%BA%8F%E4%B8%8E%E8%BF%9B%E7%A8%8B
l?交互進(jìn)程是由一個(gè)Shell啟動(dòng)的進(jìn)程。交互進(jìn)程:既可以在前臺(tái)運(yùn)行,也可以在后臺(tái)運(yùn)行。
l?批處理進(jìn)程:?該類進(jìn)程不屬于某個(gè)終端,它被提交到一個(gè)隊(duì)列中以便順序執(zhí)行。
注:交互進(jìn)程和批處理進(jìn)程的概念主要是從進(jìn)程調(diào)度的角度去分類的。參考:
總結(jié):交互進(jìn)程和批處理進(jìn)程的概念我們并不需要特別地關(guān)注,需要知道的是交互型進(jìn)程主要與外界交互,響應(yīng)度要求較高,所以在進(jìn)程優(yōu)先級上需要高一些,而批處理類型的進(jìn)程則相反。一般開發(fā)人員只有確實(shí)需要在實(shí)際需要自己手動(dòng)調(diào)整系統(tǒng)時(shí)才會(huì)關(guān)注。了解一下就好了。
l?系統(tǒng)守護(hù)進(jìn)程:?該類進(jìn)程在后臺(tái)運(yùn)行。它一般在Linux啟動(dòng)時(shí)開始執(zhí)行,系統(tǒng)關(guān)閉時(shí)才結(jié)束。后面會(huì)專門介紹
1.4 進(jìn)程運(yùn)行狀態(tài)(P9~P10)
舉例 <<<<<<< 先看看進(jìn)程的狀態(tài)。
1)運(yùn)行top命令演示一下,注意看 第二行:Tasks: 153 total, ??1 running, 152 sleeping, ??0 stopped, ??0 zombie
“S”那一列顯示進(jìn)程的狀態(tài)變換
Ps命令也可以 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
| 狀態(tài) | 描述 |
| R (TASK_RUNNING),可執(zhí)行狀態(tài)。 | l?R包括了教材上顯示的: 2?“運(yùn)行態(tài)”就是R的就緒狀態(tài),進(jìn)程獲得了除CPU以外的所有資源 2?“擁有CPU”就是R的正在執(zhí)行狀態(tài)。 l?對應(yīng)R狀態(tài),這些進(jìn)程的task_struct結(jié)構(gòu)(進(jìn)程控制塊)被放入對應(yīng)CPU的可執(zhí)行隊(duì)列中(一個(gè)進(jìn)程最多只能出現(xiàn)在一個(gè)CPU的可執(zhí)行隊(duì)列中)。進(jìn)程調(diào)度器的任務(wù)就是一旦一個(gè)“擁有CPU”的進(jìn)程時(shí)間片用完, Linux 內(nèi)核的調(diào)度器會(huì)剝奪這個(gè)進(jìn)程對這個(gè)CPU的控制權(quán),并且從該CPU的可執(zhí)行隊(duì)列中選擇一個(gè)合適的進(jìn)程投入運(yùn)行(通過schedule())。所以重點(diǎn)是要知道處于R狀態(tài)的進(jìn)程并不一定在CPU上執(zhí)行。 很多操作系統(tǒng)教科書將正在CPU上執(zhí)行的進(jìn)程定義為RUNNING狀態(tài)、而將可執(zhí)行但是尚未被調(diào)度執(zhí)行的進(jìn)程定義為READY狀態(tài),這兩種狀態(tài)在linux下統(tǒng)一為 TASK_RUNNING狀態(tài)。 |
| S (TASK_INTERRUPTIBLE),可中斷的睡眠狀態(tài)。 | l?有時(shí)候,進(jìn)程需要等待直到某個(gè)特定的事件發(fā)生,例如設(shè)備初始化完成、I/O 操作完成或定時(shí)器到時(shí)等。在這種情況下,進(jìn)程task_struct結(jié)構(gòu)必須從可執(zhí)行隊(duì)列移出,加入到一個(gè)等待隊(duì)列中,這個(gè)時(shí)候進(jìn)程就進(jìn)入了睡眠狀態(tài)。所以所謂的睡眠就是進(jìn)程從可執(zhí)行隊(duì)列被移到另一個(gè)等待隊(duì)列中。 l?可中斷的睡眠狀態(tài)的進(jìn)程會(huì)睡眠直到某個(gè)條件變?yōu)檎?#xff0c;比如說產(chǎn)生一個(gè)硬件中斷、釋放進(jìn)程正在等待的系統(tǒng)資源或是傳遞一個(gè)信號都可以是喚醒進(jìn)程的條件。進(jìn)程被喚醒后又進(jìn)入R狀態(tài)并被放到可執(zhí)行隊(duì)列中等待被調(diào)度到CPU上執(zhí)行。注:這里的可中斷不僅僅是指被信號中斷。不要被后面TASK_UNINTERRUPTIBLE的不可被信號中斷所迷惑。 通過ps -aux命令我們會(huì)看到,一般情況下,進(jìn)程列表中的絕大多數(shù)進(jìn)程都處于TASK_INTERRUPTIBLE狀態(tài)(除非機(jī)器的負(fù)載很高)。畢竟CPU就這么一兩個(gè),進(jìn)程動(dòng)輒幾十上百個(gè),如果不是絕大多數(shù)進(jìn)程都在睡眠,CPU又怎么響應(yīng)得過來。 有關(guān)睡眠參考:http://blog.csdn.net/unbutun/article/details/5730089 |
| D (TASK_UNINTERRUPTIBLE),不可中斷的睡眠狀態(tài)。 | l?與TASK_INTERRUPTIBLE狀態(tài)類似,進(jìn)程處于睡眠狀態(tài),但是此刻進(jìn)程是不可中斷的。不可中斷,指的并不是CPU不響應(yīng)外部硬件的中斷,而是指進(jìn)程不響應(yīng)異步信號。絕大多數(shù)情況下,進(jìn)程處在睡眠狀態(tài)時(shí),總是應(yīng)該能夠響應(yīng)異步信號的。否則你將驚奇的發(fā)現(xiàn),kill -9竟然殺不死一個(gè)正在睡眠的進(jìn)程了!于是我們也很好理解,為什么ps命令看到的進(jìn)程幾乎不會(huì)出現(xiàn)TASK_UNINTERRUPTIBLE狀態(tài),而總是TASK_INTERRUPTIBLE狀態(tài)。 而TASK_UNINTERRUPTIBLE狀態(tài)存在的意義就在于,內(nèi)核的某些處理流程是不能被信號打斷的。如果響應(yīng)異步信號,程序的執(zhí)行流程中就會(huì)被插入一段用于處理異步信號的流程(這個(gè)插入的流程可能只存在于內(nèi)核態(tài),也可能延伸到用戶態(tài)),于是原有的流程就被中斷了。(參見《linux內(nèi)核異步中斷淺析》) 在進(jìn)程對某些硬件進(jìn)行操作時(shí)(比如進(jìn)程調(diào)用read系統(tǒng)調(diào)用對某個(gè)設(shè)備文件進(jìn)行讀操作,而read系統(tǒng)調(diào)用最終執(zhí)行到對應(yīng)設(shè)備驅(qū)動(dòng)的代碼,并與對應(yīng)的物理設(shè)備進(jìn)行交互),可能需要使用TASK_UNINTERRUPTIBLE狀態(tài)對進(jìn)程進(jìn)行保護(hù),以避免進(jìn)程與設(shè)備交互的過程被打斷,造成設(shè)備陷入不可控的狀態(tài)。這種情況下的TASK_UNINTERRUPTIBLE狀態(tài)總是非常短暫的,通過ps命令基本上不可能捕捉到。 以下例子暫時(shí)不演示!!! linux系統(tǒng)中也存在容易捕捉的TASK_UNINTERRUPTIBLE狀態(tài)。執(zhí)行vfork系統(tǒng)調(diào)用后,父進(jìn)程將進(jìn)入TASK_UNINTERRUPTIBLE狀態(tài),直到子進(jìn)程調(diào)用exit或exec(參見《神奇的vfork》)。 通過下面的代碼就能得到處于TASK_UNINTERRUPTIBLE狀態(tài)的進(jìn)程: #include ??void main() { ?if (!vfork()) sleep(100); ?} 編譯運(yùn)行,然后ps一下: kouu@kouu-one:~/test$ ps -ax | grep a\.out ?4371 pts/0 ???D+ ????0:00 ./a.out ?4372 pts/0 ???S+ ????0:00 ./a.out ?4374 pts/1 ???S+ ????0:00 grep a.out 然后我們可以試驗(yàn)一下TASK_UNINTERRUPTIBLE狀態(tài)的威力。不管kill還是kill -9,這個(gè)TASK_UNINTERRUPTIBLE狀態(tài)的父進(jìn)程依然屹立不倒。 |
| T (TASK_STOPPED or TASK_TRACED),暫停狀態(tài)或跟蹤狀態(tài)。 | 對于進(jìn)程本身來說,TASK_STOPPED和TASK_TRACED狀態(tài)很類似,都是表示進(jìn)程暫停下來。 l?何時(shí)進(jìn)入TASK_STOPPED:向進(jìn)程發(fā)送一個(gè)SIGSTOP信號,它就會(huì)因響應(yīng)該信號而進(jìn)入TASK_STOPPED狀態(tài)(除非該進(jìn)程本身處于TASK_UNINTERRUPTIBLE狀態(tài)而不響應(yīng)信號)。(SIGSTOP與SIGKILL信號一樣,是非常強(qiáng)制的。不允許用戶進(jìn)程通過signal系列的系統(tǒng)調(diào)用重新設(shè)置對應(yīng)的信號處理函數(shù)。)向進(jìn)程發(fā)送一個(gè)SIGCONT信號,可以讓其從TASK_STOPPED狀態(tài)恢復(fù)到TASK_RUNNING狀態(tài)。 l?何時(shí)進(jìn)入TASK_TRACED:當(dāng)進(jìn)程正在被跟蹤時(shí),它處于TASK_TRACED這個(gè)特殊的狀態(tài)。“正在被跟蹤”指的是進(jìn)程暫停下來,等待跟蹤它的進(jìn)程對它進(jìn)行操作。比如在gdb中對被跟蹤的進(jìn)程下一個(gè)斷點(diǎn),進(jìn)程在斷點(diǎn)處停下來的時(shí)候就處于TASK_TRACED狀態(tài)。而在其他時(shí)候,被跟蹤的進(jìn)程還是處于前面提到的那些狀態(tài)。 兩者的區(qū)別:TASK_TRACED狀態(tài)相當(dāng)于在TASK_STOPPED之上多了一層保護(hù),處于TASK_TRACED狀態(tài)的進(jìn)程不能響應(yīng)SIGCONT信號而被喚醒。只能等到調(diào)試進(jìn)程通過ptrace系統(tǒng)調(diào)用執(zhí)行PTRACE_CONT、PTRACE_DETACH等操作(通過ptrace系統(tǒng)調(diào)用的參數(shù)指定操作),或調(diào)試進(jìn)程退出,被調(diào)試的進(jìn)程才能恢復(fù)TASK_RUNNING狀態(tài)。 |
| Z (TASK_DEAD - EXIT_ZOMBIE),退出狀態(tài),進(jìn)程成為僵尸進(jìn)程。 | l?什么叫僵尸:進(jìn)程在退出的過程中,處于TASK_DEAD狀態(tài)。在這個(gè)退出過程中,進(jìn)程占有的所有資源將被回收,除了task_struct結(jié)構(gòu)(以及少數(shù)資源)以外。于是進(jìn)程就只剩下task_struct這么個(gè)空殼,但這個(gè)空殼上至少記錄了這個(gè)進(jìn)程退出時(shí)的原因status-退出碼以及一些統(tǒng)計(jì)信息,而linux內(nèi)核認(rèn)為這個(gè)退出碼很重要,如果子進(jìn)程結(jié)束后沒有向父進(jìn)程報(bào)告這個(gè)退出碼或者說父進(jìn)程沒有通過回收來獲取這個(gè)退出碼都是不可接受的,它會(huì)一直保留這個(gè)空殼。這個(gè)task_struct的空殼就形象地被稱為僵尸(a process that is dead already but does not disappear yet because it has not yet reported its exit status.)。 (當(dāng)然,內(nèi)核也可以將這些信息保存在別的地方,而將task_struct結(jié)構(gòu)釋放掉,以節(jié)省一些空間。但是使用task_struct結(jié)構(gòu)更為方便,因?yàn)樵趦?nèi)核中已經(jīng)建立了從pid到task_struct查找關(guān)系,還有進(jìn)程間的父子關(guān)系。釋放掉task_struct,則需要建立一些新的數(shù)據(jù)結(jié)構(gòu),以便讓父進(jìn)程找到它的子進(jìn)程的退出信息。) l?如何清除僵尸:有如下三種方法: 2?子進(jìn)程在退出的過程中,內(nèi)核會(huì)給其父進(jìn)程發(fā)送一個(gè)信號,通知父進(jìn)程來“收尸”。這個(gè)信號默認(rèn)是SIGCHLD。如果父進(jìn)程通過等待他(調(diào)用wait / waitpid)回收了這個(gè)僵尸進(jìn)程,那么內(nèi)核就會(huì)立即釋放掉task_struct,即僵尸很快就被清理了。回收的方式在wait部分我們后面介紹。 2?父進(jìn)程在創(chuàng)建子進(jìn)程之前通知內(nèi)核不想等待回收他,則內(nèi)核在子進(jìn)程結(jié)束后內(nèi)核直接將子進(jìn)程的僵尸回收掉。通知涉及信號,在信號處理時(shí)再講。 2?如果子進(jìn)程的父進(jìn)程在子進(jìn)程沒有結(jié)束之前先結(jié)束了,那么該進(jìn)程就不會(huì)變成僵尸進(jìn)程, 因?yàn)槊總€(gè)進(jìn)程結(jié)束的時(shí)候,系統(tǒng)都會(huì)掃描當(dāng)前系統(tǒng)中所運(yùn)行的所有進(jìn)程, 看有沒有哪個(gè)進(jìn)程是剛剛結(jié)束的這個(gè)進(jìn)程的子進(jìn)程,如果是的話,就由Init 來接管他,成為他的父進(jìn)程……。)而init成為子進(jìn)程的繼父進(jìn)程后就會(huì)在其結(jié)束時(shí)負(fù)責(zé)清理它。(linux系統(tǒng)啟動(dòng)后,第一個(gè)被創(chuàng)建的用戶態(tài)進(jìn)程就是init進(jìn)程。它有兩項(xiàng)使命:1、執(zhí)行系統(tǒng)初始化腳本,創(chuàng)建一系列的進(jìn)程(它們都是init進(jìn)程的子孫);2、在一個(gè)死循環(huán)中等待其子進(jìn)程的退出事件,并調(diào)用waitid系統(tǒng)調(diào)用來完成“收尸”工作;init進(jìn)程不會(huì)被暫停、也不會(huì)被殺死(這是由內(nèi)核來保證的)。它在等待子進(jìn)程退出的過程中處于TASK_INTERRUPTIBLE狀態(tài),“收尸”過程中則處于TASK_RUNNING狀態(tài)。) l?僵尸為何會(huì)長期存在:如果以上三種情況都未發(fā)生, 那么子進(jìn)程的僵尸就會(huì)一直存在(即task_struct這個(gè)結(jié)構(gòu)就一直占這內(nèi)核的內(nèi)存)。 舉例:制造一個(gè)EXIT_ZOMBIE狀態(tài)的進(jìn)程:<<<<<<<< samples\1.ProcessI\1.4-task_status\?task_Z.c 演示重點(diǎn): 1)?如何通過ps或者top命令查看僵尸進(jìn)程: 編譯運(yùn)行,然后ps一下:ps -ax | grep a.out 2)?演示清除僵尸進(jìn)程的第三種方法(其他兩種方法涉及wait和signal,都還沒有講),即殺死父進(jìn)程,因?yàn)?/span>父進(jìn)程不退出,這個(gè)僵尸狀態(tài)的子進(jìn)程就一直存在。>>>>>> l?僵尸出現(xiàn)后我們還有其他方法殺死它們嗎:同學(xué)可能會(huì)問,是否可以用kill來直接殺死僵尸呢?回答是不能,除非你舍得殺死parent。利用的原理就是讓父進(jìn)程先死,然后子進(jìn)程就被init繼養(yǎng),然后就會(huì)被init回收的原理。所以我們寫程序時(shí)要避免出現(xiàn)僵尸進(jìn)程,否則浪費(fèi)資源。(A zombie is already dead, so you cannot kill it. To clean up a zombie, it must be waited on by its parent, so killing the parent should work to eliminate the zombie. (After the parent dies, the zombie will be inherited by init, which will wait on it and clear its entry in the process table.) If your daemon is spwaning children that become zombies, you have a bug. Your daemon should notice when its children die and wait on them to determine their exit status.) |
| X (TASK_DEAD - EXIT_DEAD),退出狀態(tài),進(jìn)程即將被銷毀。 | 這個(gè)狀態(tài)沒有在我們的教材中顯示,不講了 而進(jìn)程在退出過程中也可能不會(huì)保留它的task_struct。比如這個(gè)進(jìn)程是多線程程序中被detach過的進(jìn)程(進(jìn)程?線程?參見《linux線程淺析》)。或者父進(jìn)程通過設(shè)置SIGCHLD信號的handler為SIG_IGN,顯式的忽略了SIGCHLD信號。(這是posix的規(guī)定,盡管子進(jìn)程的退出信號可以被設(shè)置為SIGCHLD以外的其他信號。) 此時(shí),進(jìn)程將被置于EXIT_DEAD退出狀態(tài),這意味著接下來的代碼立即就會(huì)將該進(jìn)程徹底釋放。所以EXIT_DEAD狀態(tài)是非常短暫的,幾乎不可能通過ps命令捕捉到。 |
參考:
1.?Linux進(jìn)程狀態(tài)解析之R、S、D、T、Z、X
http://blog.csdn.net/nilxin/article/details/7437671
2.?Linux進(jìn)程的狀態(tài)轉(zhuǎn)換圖
http://blog.csdn.net/mu0206mu/article/details/7348618
1.5?進(jìn)程的模式(P11~P12)
http://stackoverflow.com/questions/2479118/cpu-switches-from-user-mode-to-kernel-mode-what-exactly-does-it-do-how-does-i
進(jìn)程的執(zhí)行模式分為用戶模式和內(nèi)核模式。實(shí)際上嚴(yán)格說應(yīng)該是CPU的運(yùn)行模式,要么處于受信任的內(nèi)核模式,要么處于受限制的用戶模式。內(nèi)核模式的代碼可以無限制地訪問所有處理器指令集以及全部內(nèi)存和I/O空間。如果是運(yùn)行在用戶模式則受限,如果一個(gè)程序需要擁有內(nèi)核模式權(quán)限,必須通過系統(tǒng)調(diào)用切換到內(nèi)核模式執(zhí)行系統(tǒng)功能,操作系統(tǒng)通過這種方式來確保系統(tǒng)的安全和穩(wěn)定。進(jìn)程的執(zhí)行代碼路徑中通過調(diào)用一個(gè)系統(tǒng)調(diào)用以異常處理的方式陷入內(nèi)核空間。
l?用戶模式的代碼允許發(fā)生缺頁,而內(nèi)核模式的代碼則不允許。
l?在2.4和更早的內(nèi)核中,僅僅用戶模式的進(jìn)程可以被上下文切換出局,由其他進(jìn)程搶占。除非發(fā)生以下兩種情況,否則內(nèi)核模式代碼可以一直獨(dú)占CPU:(1) 它自愿放棄CPU;(2) 發(fā)生中斷或異常。2.6內(nèi)核引入了內(nèi)核搶占,大多數(shù)內(nèi)核模式的代碼也可以被搶占。
注:中斷和異常的區(qū)別:http://bbs.csdn.net/topics/60216500。8086/8088把中斷分為內(nèi)部中斷和外部中斷兩大類。為了支持多任務(wù)和虛擬存儲(chǔ)器等功能,80386把外部中斷稱為“中斷”,把內(nèi)部中斷稱為“異常”。與8086/8088一樣,80386通常在兩條指令之間響應(yīng)中斷或異常。80386最多處理256種中斷或異常。
2.進(jìn)程相關(guān)操作命令
重點(diǎn):
l?前臺(tái)運(yùn)行和后臺(tái)運(yùn)行的概念以及前后臺(tái)切換的命令
l?周期運(yùn)行的命令
l?查看進(jìn)程的命令
l?指定進(jìn)程運(yùn)行優(yōu)先級的命令
l?結(jié)束進(jìn)程的命令
2.1 Linux下的進(jìn)程管理(P13)
如何后臺(tái)運(yùn)行: &
?
在linux系統(tǒng)如果你想要讓自己設(shè)計(jì)的備份程序可以自動(dòng)在某個(gè)時(shí)間點(diǎn)開始在系統(tǒng)底下運(yùn)行,而不需要手動(dòng)來啟動(dòng)它,又該如何處置呢? 這些例行的工作可能又分為一次性定時(shí)工作與循環(huán)定時(shí)工作,在系統(tǒng)內(nèi)又是哪些服務(wù)在負(fù)責(zé)? 還有,如果你想要每年在老婆的生日前一天就發(fā)出一封信件提醒自己不要忘記,linux系統(tǒng)下該怎么做呢?
但是crontab 主要用來提交不斷循環(huán)執(zhí)行的job, ?而at 用來提交一段時(shí)間后執(zhí)行的job(執(zhí)行完就自動(dòng)刪除整個(gè)job)
舉例:<<<<
l?at命令
首先檢查atd服務(wù)有無開啟
在一個(gè)指定的時(shí)間執(zhí)行一個(gè)指定任務(wù),只能執(zhí)行一次,且需要開啟atd進(jìn)程(
ps?-ef?|?grep?atd查看,?
開啟用/etc/init.d/atd?start?or?restart;?
開機(jī)即啟動(dòng)則需要運(yùn)行 chkconfig?--level?2345?atd?on)
演示,定時(shí)在11:30am用ls列出當(dāng)前目錄內(nèi)容并寫入~/log文件
cd ~
at 11:30am today
at>ls > ~/t.log
at> <EOT> //按Ctl-D退出
l?cron命令
ps?-ef?|?grep?cron
參考http://88263188.blog.51cto.com/416252/291683
crontab -u //設(shè)定某個(gè)用戶的cron服務(wù),一般root用戶在執(zhí)行這個(gè)命令的時(shí)候需要此參數(shù)
crontab -l //列出某個(gè)用戶cron服務(wù)的詳細(xì)內(nèi)容
crontab -r //刪除某個(gè)用戶的cron服務(wù)
crontab -e //編輯某個(gè)用戶的cron服務(wù)
root查看自己的cron設(shè)置:crontab -u root -l
或者直接看自己名下的任務(wù): crontab -l
創(chuàng)建任務(wù):crontab -e
打開默認(rèn)編輯器編輯后保存退出即可
修改默認(rèn)編輯器可以使用:select-editor
如果使用vi,編輯后wq保存退出即可
編輯基本格式 :
* * * * * command
分 時(shí) 日 月 周 命令
第1列表示分鐘1~59 每分鐘用*或者 */1表示
第2列表示小時(shí)1~23(0表示0點(diǎn))
第3列表示日期1~31
第4列表示月份1~12
第5列標(biāo)識號星期0~6(0表示星期天)
第6列要運(yùn)行的命令
如果寫為*, 表示每X
如果想定義間隔,在X后加"/"和間隔的數(shù)字
每隔一分鐘打印一下系統(tǒng)時(shí)間
*/1 * * * * date >>?~/t.log???//>> means append
>>>>
2.2 調(diào)度進(jìn)程(P14)
對講過的命令ps,top, kill 可以提問學(xué)生
2.2.1 ps
實(shí)驗(yàn) <<<< 實(shí)驗(yàn)4.1, 學(xué)生自己熟悉一下
4.1?-不僅要熟悉ps命令方法,更重要的是可以了解Linux進(jìn)程的組成。
ps命令是Process?Status的縮寫
ps命令用來列出系統(tǒng)中當(dāng)前運(yùn)行的那些進(jìn)程。ps命令列出的是當(dāng)前那些進(jìn)程的快照,就是執(zhí)行ps命令的那個(gè)時(shí)刻的那些進(jìn)程,如果想要?jiǎng)討B(tài)的顯示進(jìn)程信息,就可以使用top命令。
Man 1 ps
ps:查看系統(tǒng)中的進(jìn)程,Linux中可以使用ps -aux查看所有進(jìn)程。其中PID代表進(jìn)程ID,TTY是該進(jìn)程是由哪個(gè)控制臺(tái)啟動(dòng)的,CMD則是命令。
如果想列出更詳細(xì)的信息,則可使用命令:“ps -auxw”。參數(shù)w表示加寬顯示的命令行,參數(shù)w可以寫多次,通常最多寫3次,表示加寬3次,這足以顯示很長的命令行了。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
2.2.2 top
top命令是Linux下常用的性能分析工具,能夠?qū)崟r(shí)顯示系統(tǒng)中各個(gè)進(jìn)程的資源占用狀況,類似于Windows的任務(wù)管理器。下面詳細(xì)介紹它的使用方法。
參考:linux top命令詳解
http://blog.csdn.net/sanshiqiduer/article/details/1933625
2.2.3 kill
kill命令的工作原理是,向Linux系統(tǒng)的內(nèi)核發(fā)送一個(gè)系統(tǒng)操作信號和某個(gè)程序的進(jìn)程標(biāo)識號,然后系統(tǒng)內(nèi)核就可以對進(jìn)程標(biāo)識號指定的進(jìn)程進(jìn)行操作。比如在top命令中,我們看到系統(tǒng)運(yùn)行許多進(jìn)程,有時(shí)就需要使用kill中止某些進(jìn)程來提高系統(tǒng)資源。
譬如:系統(tǒng)多個(gè)虛擬控制臺(tái)的作用是當(dāng)一個(gè)程序出錯(cuò)造成系統(tǒng)死鎖時(shí),可以切換到其它虛擬控制臺(tái)工作關(guān)閉這個(gè)程序。此時(shí)使用的命令就是kill,因?yàn)閗ill是大多數(shù)Shell內(nèi)部命令可以直接調(diào)用的。
Kill –l // 顯示所有kill可以發(fā)送的信號id
不多舉例了
2.2.4 nice和renice
nice是按照指定的優(yōu)先級執(zhí)行命令
renice更改正在運(yùn)行的進(jìn)程的優(yōu)先級
系統(tǒng)中運(yùn)行的每個(gè)進(jìn)程都有一個(gè)優(yōu)先級(亦稱“nice 值”),其范圍從 -20 (最高優(yōu)先級)到 19 (最低優(yōu)先級)。優(yōu)先級越高,其值越小。默認(rèn)情況下,進(jìn)程的優(yōu)先級是 0 (“基本”調(diào)度優(yōu)先級)。優(yōu)先級比較大的進(jìn)程(nice 值比較小,最低到 -20)相對優(yōu)先級比較小的進(jìn)程(直到 19)將比較頻繁地被調(diào)度運(yùn)行,因此就擁有更多的進(jìn)程周期。一般用戶只能降低它們自己進(jìn)程的優(yōu)先級別,并限于 0 到 19 之間。超級用戶(root)可以將任何進(jìn)程的優(yōu)先級設(shè)定為任何值。
舉例:<<<<
ps -l -p 2924
F S ??UID ??PID ?PPID ?C ?PRI ?NI???ADDR SZ ?WCHAN ?TTY ?????????TIME CMD
0 S ?1000 ?2924 ????1 ?0 ?80??0????- ???8854 poll_s?????? ???????00:00:01 notify-osd
PRI指的是內(nèi)核調(diào)度優(yōu)先級
NICE是進(jìn)程優(yōu)先級
top里的nice,ps -efl里的NI
renice +15?785?//加值,降低優(yōu)先級
nice -n 19?dd if=/dev/cdrom of=~/mdk1.iso
>>>>>
2.2.5 bg,fg
不能光講這兩個(gè),要結(jié)合其他幾個(gè)命令一塊講
l?jobs:查看當(dāng)前有多少在后臺(tái)運(yùn)行的命令
l?&:運(yùn)行一個(gè)程序時(shí),直接放到后臺(tái)
l?Ctrl-Z:將一個(gè)正在前臺(tái)執(zhí)行的進(jìn)程放到后臺(tái),并且暫停
l?fg:將后臺(tái)中的命令調(diào)至前臺(tái)繼續(xù)運(yùn)行。如果后臺(tái)中有多個(gè)命令,可以用 fg N將選中的命令調(diào)出,N是通過jobs命令查到的后臺(tái)正在執(zhí)行的命令的序號[N]?(不是pid)。也可以用%代替fg。
l?bg:將一個(gè)在后臺(tái)暫停的命令,變成繼續(xù)執(zhí)行。如果后臺(tái)中有多個(gè)命令,可以用bg N將選中的命令調(diào)出,N是通過jobs命令查到的后臺(tái)正在執(zhí)行的命令的序號(不是pid)
舉例:<<<<<
直接在后臺(tái)開始運(yùn)行一個(gè)命令
[maple@linux ~]$ ?ping baidu.com -a >/dev/null &
[1] 12879
顯示后臺(tái)命令運(yùn)行狀態(tài)
[maple@linux ~]$ jobs
[1]+ ?Running ????????????????ping baidu.com -a > /dev/null &
將前臺(tái)運(yùn)行進(jìn)程掛起放到后臺(tái)
[maple@linux ~]$ ping google.com -a >/dev/null
#Ctrl+Z
[2]+ ?Stopped ????????????????ping google.com -a > /dev/null
[maple@linux ~]$ jobs
[1]- ?Running ????????????????ping baidu.com -a > /dev/null &
[2]+ ?Stopped ????????????????ping google.com -a > /dev/null
注意沒有直接的命令可以將后臺(tái)running的進(jìn)程stop,必須要先把后臺(tái)的進(jìn)程通過fg調(diào)到前臺(tái),再用Ctrl+Z掛起后重新放回后臺(tái)
[maple@linux ~]$ fg 1
ping baidu.com -a > /dev/null
#Ctrl+Z
[1]+ ?Stopped ????????????????ping baidu.com -a > /dev/null
[maple@linux ~]$ jobs
[1]+ ?Stopped ????????????????ping baidu.com -a > /dev/null
[2]- ?Stopped ????????????????ping google.com -a > /dev/null
通過bg可以直接將后臺(tái)掛起的進(jìn)程恢復(fù)執(zhí)行
[maple@linux ~]$ bg 2
[2]- ping google.com -a > /dev/null &
[maple@linux ~]$ jobs
[1]+ ?Stopped ????????????????ping baidu.com -a > /dev/null
[2]- ?Running ????????????????ping google.com -a > /dev/null &
>>>>>
提高:(課上估計(jì)沒有太多時(shí)間講。讓學(xué)生自己回去看)
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
實(shí)驗(yàn)4.2:使用proc文件系統(tǒng)查看進(jìn)程信息
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
3.?進(jìn)程系統(tǒng)調(diào)用
重點(diǎn):
l?Fork函數(shù)的機(jī)制
l?Exec函數(shù)形式的區(qū)別
l?Exit和_exit的區(qū)別
l?Wait的作用,wait和waitpid的區(qū)別,如何避免僵尸進(jìn)程
3.1概述:和進(jìn)程相關(guān)的系統(tǒng)調(diào)用
Fork;Exec;Exit/_exit;Wait/waitpid
3.2進(jìn)程標(biāo)識(這個(gè)不講了)
和一個(gè)進(jìn)程相關(guān)的最重要的有6個(gè)標(biāo)識。和IO部分-文件與目錄章節(jié)的文件存取許可檢查相關(guān)的四個(gè)ID,授課時(shí)可以簡單回顧一下。
pid_t getpid(void);?Returns: process ID of calling process
pid_t getppid(void);Returns: parent process ID of calling process
uid_t getuid(void);Returns: real user ID of calling process
uid_t geteuid(void);Returns: effective user ID of calling process
gid_t getgid(void);Returns: real group ID of calling process
gid_t getegid(void);Returns: effective group ID of calling process
3.3 Fork
綜述:Unix的進(jìn)程創(chuàng)建很特別。其他的操作系統(tǒng)都提供了一步產(chǎn)生spawn進(jìn)程的機(jī)制,比如Windows里的CreateProcess。但Unix采用了與眾不同的實(shí)現(xiàn)方式,它把上述步驟分解到兩個(gè)單獨(dú)的函數(shù)中去執(zhí)行:fork和exec。一個(gè)現(xiàn)有進(jìn)程通過調(diào)用Fork拷貝當(dāng)前的進(jìn)程創(chuàng)建新進(jìn)程(由fork創(chuàng)建的新進(jìn)程被稱為子進(jìn)程(child process))。子進(jìn)程與父進(jìn)程的區(qū)別僅僅在于PID,PPID和某些資源&統(tǒng)計(jì)量(例如,掛起的信號)。然后由exec函數(shù)負(fù)責(zé)讀取可執(zhí)行文件并將其載入地址空間開始運(yùn)行。
3.3.1 fork函數(shù)說明(P16~P18)
3.3.1.1 如何理解fork被調(diào)用一次,但返回兩次這個(gè)概念。
提示:用叉子的概念來引導(dǎo)。先對著P16講一下函數(shù),再對照P17講下面的解釋。
在分叉點(diǎn)上發(fā)生了什么事情:內(nèi)核為我們拷貝創(chuàng)建了一個(gè)新的task_struct結(jié)構(gòu)。子進(jìn)程是父進(jìn)程的副本,它將獲得父進(jìn)程數(shù)據(jù)空間、堆、棧等資源的副本。注意,子進(jìn)程持有的是上述存儲(chǔ)空間的“副本”,這意味著父子進(jìn)程間不共享這些存儲(chǔ)空間。
由于在復(fù)制時(shí)復(fù)制了父進(jìn)程的堆棧段,所以兩個(gè)進(jìn)程都停留在fork函數(shù)中,等待返回。因此fork函數(shù)會(huì)返回兩次,一次是在父進(jìn)程中返回,另一次是在子進(jìn)程中返回,這兩次的返回值是不一樣的。過程可以參考一個(gè)圖。回顧原來講過的程序和進(jìn)程中,進(jìn)程的組成:代碼段,數(shù)據(jù)段,內(nèi)存空間的概念。文本段共享。函數(shù)返回時(shí)pid所在棧段被復(fù)制并填寫了不同的返回值。
fork創(chuàng)建的新進(jìn)程我們稱之為子進(jìn)程。fork被調(diào)用一次,但返回兩次。唯一的區(qū)別是在子進(jìn)程里返回值是0,而在父進(jìn)程里其返回值是子進(jìn)程的進(jìn)程ID。
舉例 <<< 課件P17 Fork的例子: samples\1.ProcessI\3.3-fork\fork1.c ?
演示過程:misc/fork.pptx
>>>>>>
舉例:<<<< 驗(yàn)證fork后子進(jìn)程持有的是上述存儲(chǔ)空間的“副本”,這意味著父子進(jìn)程間不共享這些存儲(chǔ)空間。
samples\1.ProcessI\3.3-fork?\ fork2.c
演示過程:misc\fork-sample.pptx
這個(gè)例子講解完后讓學(xué)生自己運(yùn)行體驗(yàn)一下fork的含義。
演示時(shí)有兩點(diǎn)需要說明:
1)?從上面可以看出,因?yàn)樽舆M(jìn)程和父進(jìn)程擁有獨(dú)立的物理內(nèi)存空間,所以當(dāng)子進(jìn)程對拷貝來的數(shù)據(jù)做修改的時(shí)候,并沒有影響到父進(jìn)程。
2)?復(fù)制的前后,其值的地址都是一樣的。為什么呢?子進(jìn)程拷貝的時(shí)候也拷貝了父進(jìn)程的虛擬內(nèi)存"頁",這樣他們的虛擬地址都一樣,但是對應(yīng)不同的物理內(nèi)存空間。示例代碼沒有打印出物理地址,所以無法生動(dòng)地展現(xiàn)這個(gè)現(xiàn)象,打印的只是虛擬地址。因?yàn)槲锢淼刂凡缓弥苯訌挠脩魬B(tài)訪問,除非我們在內(nèi)核寫驅(qū)動(dòng)幫助用戶態(tài)讀取。 http://www.it165.net/os/html/201205/2243.html
注:這個(gè)例子仿照了實(shí)驗(yàn)4.3,因?yàn)槲矣X得實(shí)驗(yàn)手冊上的例子不夠好,重新改了一個(gè)(出于簡化考慮,實(shí)驗(yàn)4.3(不準(zhǔn)備講解了)同時(shí)也沒有仿照網(wǎng)上的例子演示標(biāo)準(zhǔn)IO的緩存對fork的影響。http://blog.chinaunix.net/uid-26833883-id-3222794.html)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
3.3.1.2 使用fork函數(shù)的注意點(diǎn):
l?一個(gè)現(xiàn)存的進(jìn)程調(diào)用fork是unix內(nèi)核創(chuàng)建一個(gè)新進(jìn)程的唯一方法(除了內(nèi)核自舉過程中的進(jìn)程創(chuàng)建,比如init等)
l?注意在父進(jìn)程里保存好其獲得的子進(jìn)程的ID,因?yàn)槲覀儧]有其他的系統(tǒng)調(diào)用可以獲得一個(gè)父進(jìn)程的子進(jìn)程ID號。而對于子進(jìn)程來說卻可以通過getppid來獲取其父進(jìn)程的ID。進(jìn)程ID已經(jīng)被內(nèi)核保留,所以在用戶態(tài)我們可以使用0來判斷。
l?我們無法確定fork之后是子進(jìn)程先運(yùn)行還是父進(jìn)程先運(yùn)行,這依賴于系統(tǒng)的實(shí)現(xiàn)。所以在移植代碼的時(shí)候我們不應(yīng)該對此作出任何的假設(shè)。
3.3.2 fork的優(yōu)化歷史- P19
下面采用歷史發(fā)展的敘述方式給學(xué)生介紹一下fork的優(yōu)化歷史,是如何一步一步提高效率的。引入COW(Linux上fork)和vfork。對于vfork,根據(jù)LKD p28的說法,理想情況下,最好不要調(diào)用vfork。所以培訓(xùn)中簡單介紹一下vfork就可以了。重點(diǎn)還是講fork。
主要可以參考:Linux寫時(shí)拷貝技術(shù)(copy-on-write)
演示:misc\fork-cow.pptx
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/20/2601655.html
1) 經(jīng)典的fork實(shí)現(xiàn)
復(fù)制地址空間 –?進(jìn)程的頁表 (圖中的從左到右的箭頭)共享正文段;其他段立即復(fù)制一份
?
2)支持寫時(shí)復(fù)制的(Copy-On-Write COW技術(shù))的fork實(shí)現(xiàn)(目前Linux的實(shí)現(xiàn)方式)
傳統(tǒng)的fork直接把所有資源復(fù)制給新建的子進(jìn)程,這種實(shí)現(xiàn)過于簡單并且效率低下,因?yàn)樗截惖臄?shù)據(jù)也許并不共享,更糟的情況是,如果新進(jìn)程打算立即執(zhí)行一個(gè)新的程序,那么所有的拷貝工作都將前功盡棄。
內(nèi)核只為新生成的子進(jìn)程創(chuàng)建虛擬空間結(jié)構(gòu),它們來復(fù)制于父進(jìn)程的虛擬究竟結(jié)構(gòu),但是不為這些段分配物理內(nèi)存,它們共享父進(jìn)程的物理空間,當(dāng)父子進(jìn)程中有更改相應(yīng)段的行為發(fā)生時(shí),再為子進(jìn)程相應(yīng)的段分配物理空間。
寫時(shí)拷貝是一種可以推遲甚至免除拷貝數(shù)據(jù)的技術(shù)。內(nèi)核此時(shí)并不復(fù)制整個(gè)進(jìn)程地址空間,而是讓父進(jìn)程和子進(jìn)程共享同一個(gè)拷貝。只有在需要寫入的時(shí)候,數(shù)據(jù)才會(huì)被復(fù)制,從而使各個(gè)進(jìn)程擁有各自的拷貝。也就是說,資源的復(fù)制只有在需要寫入的時(shí)候才進(jìn)行,在此之前,只是以只讀方式共享。這種技術(shù)使地址空間上的頁的拷貝被推遲到實(shí)際發(fā)生寫入的時(shí)候。在頁根本不會(huì)被寫入的情況下—舉例來說,fork()后立即調(diào)用exec()—它們就無需復(fù)制了。fork()的實(shí)際開銷就是復(fù)制父進(jìn)程的頁表以及給子進(jìn)程創(chuàng)建惟一的進(jìn)程描述符。在一般情況下,進(jìn)程創(chuàng)建后都會(huì)馬上運(yùn)行一個(gè)可執(zhí)行的文件,這種優(yōu)化可以避免拷貝大量根本就不會(huì)被使用的數(shù)據(jù)(地址空間里常常包含數(shù)十兆的數(shù)據(jù))。由于Unix強(qiáng)調(diào)進(jìn)程快速執(zhí)行的能力,所以這個(gè)優(yōu)化是很重要的。這里補(bǔ)充一點(diǎn):Linux COW與exec沒有必然聯(lián)系
?
注:最新的Linux的fork狀態(tài)還是看LKD吧
提高<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
3)Vfork
vfork():這個(gè)做法更加火爆,內(nèi)核連子進(jìn)程的虛擬地址空間結(jié)構(gòu)也不創(chuàng)建了,直接共享了父進(jìn)程的虛擬空間,當(dāng)然了,這種做法就順?biāo)浦鄣墓蚕砹烁高M(jìn)程的物理空間。(其行為在vfork的初期P2已經(jīng)非常類似于被創(chuàng)建成了和P1在同一個(gè)進(jìn)程中的線程了,因?yàn)樗麄兺耆蚕砹颂摂M空間)
注,因?yàn)?/span>Linux上并不推薦使用vfork,所以課上不再展開講。原因:vfork的好處有限,僅限于不拷貝頁表項(xiàng),而且vfork的語義比較微妙,使用時(shí)對父進(jìn)程的進(jìn)一步動(dòng)作有依賴(共享物理空間所致),所以沒有特別需要,并不建議多使用vfork。
?
以下參考有點(diǎn)偏理論化,可以參考但和最新的LKD對Linux的描述稍有出入:
1)?Linux多進(jìn)程開發(fā)(二)進(jìn)程創(chuàng)建之vfork的學(xué)習(xí):
http://liam2199.blog.51cto.com/2879872/1231680
2)?fork與vfork的區(qū)別
http://blog.csdn.net/jianchi88/article/details/6985326
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
3.3.3 實(shí)驗(yàn)
<<< 實(shí)驗(yàn)手冊4.3, 練習(xí)fork >>>
3.4 exec函數(shù)族
3.4.1基本介紹(P20~P21)
}?fork函數(shù)用于創(chuàng)建一個(gè)子進(jìn)程,該子進(jìn)程幾乎拷貝了父進(jìn)程的全部內(nèi)容。
}?exec函數(shù)族提供了一種在進(jìn)程中啟動(dòng)另一個(gè)程序執(zhí)行的方法。它可以根據(jù)指定的文件名或目錄名找到可執(zhí)行文件,并用它來取代原調(diào)用進(jìn)程的數(shù)據(jù)段、代碼段和堆棧段。在執(zhí)行完之后,原調(diào)用進(jìn)程的內(nèi)容除了進(jìn)程號外,其他全部都被替換了。
}?可執(zhí)行文件既可以是二進(jìn)制文件,也可以是任何Linux下可執(zhí)行的腳本文件。
每當(dāng)進(jìn)程調(diào)用一種exec函數(shù)時(shí),該進(jìn)程完全由新程序代換,而新程序從main函數(shù)開始執(zhí)行。Exec并不創(chuàng)建新進(jìn)程,所以前后進(jìn)程ID也不會(huì)變。Exec只是用另一個(gè)新程序替換了當(dāng)前進(jìn)程的正文、數(shù)據(jù)、堆和棧段。
3.4.2 exec函數(shù)族語法(P22-P23)
參考APUE2nd的8.10章節(jié). 演示<<< misc/exec.pptx >>>
| ? | 可執(zhí)行文件查找方式 | 向新程序傳遞參數(shù)表的方式 | 向新程序傳遞環(huán)境表的方式 | |||
| 函數(shù) | 路徑名pathname | 文件名filename | 列表方式list | 矢量數(shù)組方式vector | 繼承調(diào)用進(jìn)程的environ變量 | 指定新的環(huán)境字符串指針數(shù)組envp[] |
| execl | * | ? | * | ? | * | ? |
| execlp | ? | * | * | ? | * | ? |
| execle | * | ? | * | ? | ? | * |
| execv | * | ? | ? | * | * | ? |
| execvp | ? | * | ? | * | * | ? |
| execve | * | ? | ? | * | ? | * |
| 字母表示 | ? | p | l | v | ? | e |
l?返回值:注意如果exec執(zhí)行成功是不會(huì)返回的,除非失敗返回-1。
l?形參:
2?對文件查找方式為p的,
ü?如果filename中包含/,則將filename等同為pathname進(jìn)行處理
ü?否則就查看PATH環(huán)境變量,在PATH中指定的目錄下依次查找可執(zhí)行文件
2?對傳遞參數(shù)為列表方式l的,注意列表包含argv0并且不要遺漏最后一個(gè)為NULL/0:arg0, arg1, …?argn, NILL
?舉例,<<<<< 見P24-P25 samples\1.ProcessI\3.4-exec?>>>>>>
3.4.3 實(shí)驗(yàn)
<<< 實(shí)驗(yàn)手冊4.4, 基于手冊4.3 做4.4的第一部分exec ?>>>
3.5 exit/_exit
直接看ppt的圖和例子就好
exit()函數(shù)與_exit()函數(shù)最大的區(qū)別就在于exit()函數(shù)在調(diào)用exit系統(tǒng)調(diào)用之前要檢查文件的打開情況,把文件緩沖區(qū)中的內(nèi)容寫回文件,就是圖中的“清理I/O緩沖”一項(xiàng)。
參考:exit()與_exit()在linux進(jìn)程控制中的區(qū)別
http://blog.csdn.net/xiaozhi_su/article/details/4165174
舉例<<<<<<<< samples\1.ProcessI\3.5-exit
Exit的例子,P29頁例子,說明exit后面得代碼不會(huì)被執(zhí)行
P30頁的例子:?samples\1.ProcessI\3.5-exit\diff_exit
對于_exit后為何不會(huì)打印第二句話,請學(xué)生自己回答一下。
注意printf("This is the end");?這句話最后沒有\n, 所以該語句被調(diào)用后會(huì)緩存而不顯示,_exit被調(diào)用后進(jìn)程結(jié)束就沒有機(jī)會(huì)顯示了,而exit還會(huì)做一次類似fflush的操作。>>>>>>>>>>>>>>>>>>>>>>>>>
3.6 wait和waitpid
3.6.1引言:
回顧一下前面講過的僵尸進(jìn)程以及回收的問題
3.6.2 介紹wait, waitpid(P31-P33)
l?先說Wait:
1)他的主要作用是用來接收子進(jìn)程的狀態(tài),退出或終止?fàn)顟B(tài)
2)對于我們一個(gè)程序來說,可能有多個(gè)子進(jìn)程,那么接收哪個(gè)子進(jìn)程的狀態(tài),是不確定的
3)wait函數(shù)會(huì)暫停我們的調(diào)用進(jìn)程,直到子進(jìn)程的終止。(收到子進(jìn)程的退出信號)
l?再說waitpid
1)?pid不深入講涉及進(jìn)程組的概念,因?yàn)檫@里還不回講到進(jìn)程組。所以重點(diǎn)講pid>和pid=-1的情況。當(dāng)?shù)谝粋€(gè)參數(shù)pid為-1時(shí),就是wait的功能,等待任一子進(jìn)程退出,還可以指定特定的pid
2)?Option選項(xiàng)比較特殊,我們可以用兩個(gè)常規(guī)選項(xiàng),0與WNOHANG,0的作用與wait一樣,會(huì)阻塞,而WNOHANG不會(huì)暫停我們的調(diào)用進(jìn)程,根據(jù)子進(jìn)程是否退出或暫停,如果退出就返回0,如果正確返回就返回子進(jìn)程號。還有一個(gè)WUNTRACED,這個(gè)是用來查看子進(jìn)程自暫停或退出以來還未報(bào)告,就返回其狀態(tài)。對任務(wù)支持不是很好,這個(gè)不常用。
l?Wait和waitpid的返回狀態(tài)碼:
Man 2 wait 使用系統(tǒng)定義的宏對wait的status進(jìn)行判斷和讀取,不要直接打印。帶領(lǐng)學(xué)生看man手冊。注意WIFEXITED和WEXITSTATUS。
舉例:<<<<< 利用wait回收子進(jìn)程的僵尸
回顧消除僵尸的正確方式:1)父進(jìn)程提前退出;2)真父委托繼父;3)真父wait。1已經(jīng)前面演示過,2涉及signal等到后面演示,這里主要演示3。比較wait后和寄養(yǎng)后僵尸不出現(xiàn)
samples\1.ProcessI\3.6-wait\wait_z.c: 制造僵尸
samples\1.ProcessI\3.6-wait\wait.c: 使用wait回收
samples\1.ProcessI\3.6-wait\waitpid.c,使用waitpid回收
簡單說一下對于由真父調(diào)用wait, waitpid來接收子進(jìn)程退出狀態(tài)。達(dá)到收尸的作用。但簡單使用該方法需要父進(jìn)程等待子進(jìn)程結(jié)束,會(huì)導(dǎo)致父進(jìn)程掛起。特別是在某些服務(wù)器實(shí)現(xiàn)上,一般由父進(jìn)程fork很多子進(jìn)程,如果需要父進(jìn)程去長時(shí)間等待子進(jìn)程,會(huì)很影響服務(wù)器進(jìn)程的并發(fā)性能。為了避免父進(jìn)程長時(shí)間等待還有一種方法,但要涉及signal函數(shù)的處理,留到后面signal的時(shí)候再講。>>>>>>>>>
3.6.3 實(shí)驗(yàn)
<<<< 課件P34大家看圖寫程序,我們先來分析一下,我們這張圖呢是有兩上進(jìn)程,子進(jìn)程中暫停5秒,父進(jìn)程輪循環(huán)檢測子進(jìn)程的退出狀態(tài),直到進(jìn)程退出才返回。
也可以讓學(xué)生用阻塞的方式,即wait來實(shí)現(xiàn)。阻塞與非阻塞的輪循的概念:
l?阻塞:當(dāng)進(jìn)程條件得不到滿足時(shí),系統(tǒng)會(huì)調(diào)用schedule()將進(jìn)程掛起,當(dāng)我們條件滿足調(diào)用wake喚醒阻塞進(jìn)程
l?非阻塞:當(dāng)條件不滿足,會(huì)立即返回執(zhí)行下面的語句。
l?輪詢:對于咱們的程序來說,阻塞與非阻塞都不能滿足咱們的要求,咱們可以得用循環(huán)的機(jī)制與非阻塞的機(jī)制配合,這種方法叫做輪循。>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<< 有時(shí)間做一下實(shí)驗(yàn)手冊4.4的第二部分 >>>
4.?Linux守護(hù)進(jìn)程
重點(diǎn):
l?了解守護(hù)進(jìn)程的特點(diǎn)
l?了解ps中查看守護(hù)進(jìn)程的方法
l?了解會(huì)話相關(guān)概念
l?掌握編寫守護(hù)進(jìn)程的方法
4.1 基本概念:
先來復(fù)習(xí)一下,進(jìn)程的類型,進(jìn)程大體分為三類,交互進(jìn)程,批處理進(jìn)程,守護(hù)進(jìn)程,
引入守護(hù)進(jìn)程的基本概念:(可以參考的PPT)守護(hù)線程的特點(diǎn):
}?運(yùn)行方式: 守護(hù)進(jìn)程,也就是通常所說的Daemon進(jìn)程,是Linux中的后臺(tái)服務(wù)進(jìn)程。周期性的執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。Linux系統(tǒng)有很多守護(hù)進(jìn)程,大多數(shù)服務(wù)都是用守護(hù)進(jìn)程實(shí)現(xiàn)的。比如:像我們的tftp,samba,nfs等相關(guān)服務(wù)。UNIX的守護(hù)進(jìn)程一般都命名為*d的形式,如httpd,telnetd等等
}?生命周期: 守護(hù)進(jìn)程會(huì)長時(shí)間運(yùn)行,常常在系統(tǒng)啟動(dòng)時(shí)就開始運(yùn)行,直到系統(tǒng)關(guān)閉時(shí)才終止
}?守護(hù)進(jìn)程不依賴于終端:顯而異見,從終端開始運(yùn)行的進(jìn)程都會(huì)依附于這個(gè)終端,這個(gè)終端稱為這些進(jìn)程的控制終端。當(dāng)控制終端被關(guān)閉時(shí),相應(yīng)的進(jìn)程都會(huì)被自動(dòng)關(guān)閉。咱們平常寫進(jìn)程時(shí),一個(gè)死循環(huán)程序,咱們不知道有ctrl+c的時(shí)候,怎么關(guān)閉它呀,是不是關(guān)閉終端呀。也就是說關(guān)閉終端的同時(shí)也關(guān)閉了我們的程序,但是對于守護(hù)進(jìn)程來說,其生命周期守護(hù)需要突破這種限制,它從開始運(yùn)行,直到整個(gè)系統(tǒng)關(guān)閉才會(huì)退出,所以守護(hù)進(jìn)程不能依賴于終端。?
4.2 查看系統(tǒng)中的守護(hù)進(jìn)程
Ps –axj
-a: 顯示所有
-x:顯示沒有控制終端的進(jìn)程
-j:顯示與作業(yè)有關(guān)的信息(顯示的列):會(huì)話期ID(SID),進(jìn)程組ID(PGID),控制終端(TT),終端進(jìn)程組ID(TRGID)
如何識別一個(gè)守護(hù)進(jìn)程:
l?所有的守護(hù)進(jìn)程都是以超級用戶啟動(dòng)的(UID為0);
l?沒有控制終端(TTY為?);
l?終端進(jìn)程組ID為-1(TPGID表示終端進(jìn)程組ID,該值表示與控制終端相關(guān)的前臺(tái)進(jìn)程組,如果未和任何終端相關(guān),其值為-1;
l?所有的守護(hù)進(jìn)程的父進(jìn)程都為init進(jìn)程(PID為1的進(jìn)程)。
舉例:<<<<<< ?看看守護(hù)進(jìn)程的例子。找一個(gè)Xd的進(jìn)程看看
2109 ?2110 ?1124 ?1124 ? ?????-1?S ????1000 ??0:00 gnome-pty-helper?//和終端無關(guān)的進(jìn)程,TTY為?TPGID為?
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
對照ppt先介紹一下ps中title的含義
4.3會(huì)話相關(guān)概念:進(jìn)程組、前臺(tái)進(jìn)程組、后臺(tái)進(jìn)程組、會(huì)話、控制終端。
參考:http://lesca.me/archives/process-relationship.html
http://www.cnblogs.com/hazir/p/linux_kernel_pid.html
www.cs.fsu.edu/~xyuan/cop5570/lect7_session.ppt
http://www.win.tue.nl/~aeb/linux/lk/lk-10.html
提前到這里先講一下,結(jié)合參考課件P41的圖。分
第一部分:進(jìn)程組&會(huì)話
l?進(jìn)程組:
2?shell里的每個(gè)進(jìn)程都屬于一個(gè)進(jìn)程組,創(chuàng)建進(jìn)程組的目的是用于簡化向組內(nèi)所有進(jìn)程發(fā)送信號的操作,即如果一個(gè)信號是發(fā)給一個(gè)進(jìn)程組,則這個(gè)組內(nèi)的所有進(jìn)程都會(huì)受到該信號。
2?進(jìn)程組ID叫做PGID,進(jìn)程組內(nèi)的所有進(jìn)程都有相同的PGID,等于該組組長的PID。(進(jìn)程組組長:進(jìn)程組中有一個(gè)進(jìn)程擔(dān)當(dāng)組長。進(jìn)程組ID(PGID)等于進(jìn)程組組長的進(jìn)程ID。)已知一個(gè)進(jìn)程,要得到該進(jìn)程所屬的進(jìn)程組ID可以調(diào)用getpgrp。一個(gè)進(jìn)程可以通過另一個(gè)系統(tǒng)調(diào)用setpgrp來加入一個(gè)已經(jīng)存在的進(jìn)程組或者創(chuàng)建一個(gè)新的進(jìn)程組。
2?如果內(nèi)核支持_POSIX_JOB_CONTROL(該宏被定義)則內(nèi)核會(huì)為Shell 上的每一條命令行(可能由多個(gè)命令通過管道等連接)創(chuàng)建一個(gè)進(jìn)程組。從這點(diǎn)上看,進(jìn)程組不是進(jìn)程的概念,而是shell上才有,所以在task_struct里并沒有存儲(chǔ)進(jìn)程組id之類的變量。
2?進(jìn)程組的生命周期到組中最后一個(gè)進(jìn)程終止或其加入其他進(jìn)程組(離開本進(jìn)程組)為止。
l?會(huì)話:一般一個(gè)用戶登錄后新建一個(gè)會(huì)話,每個(gè)會(huì)話也有一個(gè)ID來標(biāo)識(SID)。登錄后的第一個(gè)進(jìn)程叫做會(huì)話領(lǐng)頭進(jìn)程(session leader),通常是一個(gè)shell/bash。對于會(huì)話領(lǐng)頭進(jìn)程,其PID=SID。
舉例:<<<<< 看看一個(gè)session的大致情況
proc1 | proc2 &
proc3 | proc4 | proc5
results in a session with three groups, see ‘ps –j’?我們可以打開兩個(gè)終端ps aj| grep <終端號>看一下另外一個(gè)終端里的情況。>>>>>
總結(jié)會(huì)話和進(jìn)程組的關(guān)系:
2?A session contains a number of process groups,
2?A?process group contains a number of processes
2?A?process contains a number of threads.?
2?All these objects have numbers, and we have thread IDs, process IDs, process group IDs and session IDs.
第二部分:會(huì)話中的終端,前臺(tái)&后臺(tái)的概念
此處引入一個(gè)問題,Only one I/O device for all processes (and process groups) in a session. Which process should get the input from the keyboard?
l?控制終端:一個(gè)會(huì)話一般會(huì)擁有一個(gè)控制終端用于執(zhí)行IO操作。會(huì)話的領(lǐng)頭進(jìn)程打開一個(gè)終端之后, 該終端就成為該會(huì)話的控制終端。與控制終端建立連接的會(huì)話領(lǐng)頭進(jìn)程也稱為控制進(jìn)程?(controlling process)?。一個(gè)會(huì)話只能有一個(gè)控制終端。
l?前臺(tái)進(jìn)程組: 該進(jìn)程組中的進(jìn)程能夠向終端設(shè)備進(jìn)行讀、寫操作的進(jìn)程組。例如登陸shell(例如bash)通過調(diào)用int tcsetpgrp(int fd, pid_t pgrp);?函數(shù)設(shè)置為某個(gè)進(jìn)程組pgrp關(guān)聯(lián)終端設(shè)備fd,該函數(shù)執(zhí)行成功后,該進(jìn)程組pgrp成為前臺(tái)進(jìn)程組。
l?后臺(tái)進(jìn)程組:該進(jìn)程組中的進(jìn)程只能夠向終端設(shè)備寫。
l?終端進(jìn)程組ID:每個(gè)進(jìn)程還有一個(gè)屬性,終端進(jìn)程組ID(TPGID),用來標(biāo)識一個(gè)進(jìn)程是否處于一個(gè)和終端相關(guān)的進(jìn)程組中。前臺(tái)進(jìn)程組中的進(jìn)程的TPGID=PGID,后臺(tái)進(jìn)程組的PGID≠TPGID。若該進(jìn)程和任何終端無關(guān),其值為-1。通過比較他們來判斷一個(gè)進(jìn)程是屬于前臺(tái)進(jìn)程組,還是后臺(tái)進(jìn)程組。
總結(jié):
2?每個(gè)會(huì)話有且只有一個(gè)前臺(tái)進(jìn)程組,但會(huì)有0個(gè)或者多個(gè)后臺(tái)進(jìn)程組。
2?產(chǎn)生在控制終端上的輸入(Input)和信號(Signal)將發(fā)送給會(huì)話的前臺(tái)進(jìn)程組中的所有進(jìn)程。對于輸出(Output)來說,則是在前臺(tái)和后臺(tái)共享的,即前臺(tái)和后臺(tái)的打印輸出都會(huì)顯示在屏幕上。
2?終端上的連接斷開時(shí) (比如網(wǎng)絡(luò)斷開或 Modem 斷開), 掛起信號將發(fā)送到控制進(jìn)程(controlling process)?。
實(shí)例總結(jié):課件P41頁
?
一個(gè)用戶登錄后創(chuàng)建一個(gè)會(huì)話。一個(gè)會(huì)話中只存在一個(gè)前臺(tái)進(jìn)程組,但可以存在多個(gè)后臺(tái)進(jìn)程組。第一次登陸后第一個(gè)創(chuàng)建的進(jìn)程是shell,也就是會(huì)話的領(lǐng)頭進(jìn)程,該領(lǐng)頭進(jìn)程缺省處于一個(gè)前臺(tái)進(jìn)程組中并打開一個(gè)控制終端可以進(jìn)行數(shù)據(jù)的讀寫。當(dāng)在shell里運(yùn)行一行命令后(不帶&)創(chuàng)建一個(gè)新的進(jìn)程組,命令行中如果有多個(gè)命令會(huì)創(chuàng)建多個(gè)進(jìn)程,這些進(jìn)程都處于該新建進(jìn)程組中,shell將該新建的進(jìn)程組設(shè)置為前臺(tái)進(jìn)程組并將自己暫時(shí)設(shè)置為后臺(tái)進(jìn)程組。
舉例 <<<<<<<<<< 演示實(shí)例總結(jié)的圖:
ping 127.0.0.1 -aq | grep icmp?// 通過管道將兩個(gè)命令串接起來ping –q不顯示timeout信息
將其設(shè)置到后臺(tái)并runing
采用類似方法,在前臺(tái)再新建一個(gè)進(jìn)程組。
開啟另外一個(gè)終端并運(yùn)行
Ps axj或者運(yùn)行 ps axj | grep pts/0即過濾只看pts/0里的會(huì)話
PPID ??PID ?PGID ??SID TTY ?????TPGID STAT ??UID ??TIME COMMAND
2109 ?2111 ?2111 ?2111 pts/0 ????2538 Ss ???1000 ??0:01 bash
?2111 ?2503 ?2503 ?2111 pts/0 ????2538 S ????1000 ??0:00 ping 127.0.0.1 -aq
?2111 ?2504 ?2503 ?2111 pts/0 ????2538 S ????1000 ??0:00 grep --color=auto icmp
?2111 ?2538 ?2538 ?2111 pts/0 ????2538 S+ ???1000 ??0:00 ping 127.0.0.2 -aq
?2111 ?2539 ?2538 ?2111 pts/0 ????2538 S+ ???1000 ??0:00 grep --color=auto timeo
l?SID都相同,說明大家都在一個(gè)Session里
l?有三個(gè)進(jìn)程組 2111,2503 和2538。 我們可以看到用|連起來的ping和grep是在一個(gè)進(jìn)程組里的。
l?2538這個(gè)進(jìn)程組是一個(gè)前臺(tái)的進(jìn)程組,因?yàn)槠銹GID==TGPID, 2503這個(gè)進(jìn)程組是一個(gè)后臺(tái)進(jìn)程組
在終端中執(zhí)行Ctrl+C后
在第二個(gè)終端里繼續(xù)ps axj | grep pts/0
PPID ??PID ?PGID ??SID TTY ?????TPGID STAT ??UID ??TIME COMMAND
2109 ?2111 ?2111 ?2111 pts/0 ????2111 Ss+ ??1000 ??0:01 bash
?2111 ?2503 ?2503 ?2111 pts/0 ????2111 S ????1000 ??0:00 ping 127.0.0.1 -aq
?2111 ?2504 ?2503 ?2111 pts/0 ????2111 S ????1000 ??0:00 grep --color=auto icmp
l?2538那個(gè)前臺(tái)進(jìn)程組的所有進(jìn)程都消失了,說明信號會(huì)發(fā)給前臺(tái)進(jìn)程組的所有進(jìn)程
l?2111,即bash所在的那個(gè)進(jìn)程組成為了前臺(tái)進(jìn)程組。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
4.4 Linux守護(hù)進(jìn)程編寫步驟
參考
1)華清遠(yuǎn)見blog:http://fasight001.spaces.eepw.com.cn/articles/article/item/99552
2)linux學(xué)習(xí)筆記-讀《Linux編程技術(shù)詳解》-守護(hù)進(jìn)程:http://blog.csdn.net/iron_lzn/article/details/5517776
先帶領(lǐng)學(xué)生再回顧一下前述守護(hù)進(jìn)程的特點(diǎn):后臺(tái)運(yùn)行,不依賴于終端。
4.4.1 Step1創(chuàng)建子進(jìn)程,父進(jìn)程退出。(P39)
實(shí)質(zhì)是?讓init進(jìn)程成為新產(chǎn)生進(jìn)程的父進(jìn)程。調(diào)用fork函數(shù)創(chuàng)建子進(jìn)程后,使父進(jìn)程立即退出。從而使產(chǎn)生的子進(jìn)程將變成孤兒進(jìn)程,并被init進(jìn)程接管,同時(shí),所產(chǎn)生的新進(jìn)程將變?yōu)樵诤笈_(tái)運(yùn)行;利用前面介紹的父進(jìn)程先于子進(jìn)程退出后內(nèi)核會(huì)自動(dòng)托付子進(jìn)程給init的原理。
4.4.2 Step2在子進(jìn)程中創(chuàng)建新會(huì)話。(P40)
這個(gè)步驟是創(chuàng)建守護(hù)進(jìn)程最重要的一步,雖然實(shí)現(xiàn)非常簡單,但意義卻非常重大。
?setsid()函數(shù)的作用。一個(gè)進(jìn)程調(diào)用setsid()函數(shù)后,會(huì)發(fā)生如下事件:
2?首先內(nèi)核會(huì)創(chuàng)建一個(gè)新的會(huì)話,并讓該進(jìn)程成為該會(huì)話的leader進(jìn)程,
2?同時(shí)伴隨該session的建立,一個(gè)新的進(jìn)程組也會(huì)被創(chuàng)建,同時(shí)該進(jìn)程成為該進(jìn)程組的組長。
2?該進(jìn)程此時(shí)還沒有和任何控制終端關(guān)聯(lián)。若需要?jiǎng)t要另外調(diào)用tcsetpgrp,前面講前臺(tái)進(jìn)程組時(shí)介紹過。
調(diào)用setsid()有以下3個(gè)作用:
l?讓進(jìn)程擺脫原會(huì)話的控制。
l?讓進(jìn)程擺脫原進(jìn)程組的控制。
l?讓進(jìn)程擺脫原控制終端的控制。
那么,在創(chuàng)建守護(hù)進(jìn)程時(shí)為什么要調(diào)用setsid()函數(shù)呢?讀者可以回憶一下創(chuàng)建守護(hù)進(jìn)程的第一步,在那里調(diào)用了fork()函數(shù)來創(chuàng)建子進(jìn)程再令父進(jìn)程退出。由于在調(diào)用fork()函數(shù)時(shí),子進(jìn)程全盤復(fù)制了父進(jìn)程的會(huì)話期、進(jìn)程組和控制終端等,雖然父進(jìn)程退出了,但原先的會(huì)話期、進(jìn)程組和控制終端等并沒有改變,因此,還不是真正意義上的獨(dú)立。而setsid()函數(shù)能夠使進(jìn)程完全獨(dú)立出來,從而脫離所有其他進(jìn)程和終端的控制。
詳細(xì)見man 2 setsid。
4.4.3 Step3 改變當(dāng)前目錄為根目錄。
這一步也是必要的步驟。使用fork()創(chuàng)建的子進(jìn)程繼承了父進(jìn)程的當(dāng)前工作目錄。由于在進(jìn)程運(yùn)行過程中,當(dāng)前目錄所在的文件系統(tǒng)(如“/mnt/usb”等)是不能卸載的,這對以后的使用會(huì)造成諸多的麻煩(如系統(tǒng)由于某種原因要進(jìn)入單用戶模式)。因此,通常的做法是讓“/”作為守護(hù)進(jìn)程的當(dāng)前工作目錄,這樣就可以避免上述問題。當(dāng)然,如有特殊需要,也可以把當(dāng)前工作目錄換成其他的路徑,如/tmp。改變工作目錄的常見函數(shù)是chdir()。
4.4.4 Step4 重設(shè)文件權(quán)限掩碼。
文件權(quán)限掩碼是指屏蔽掉文件權(quán)限中的對應(yīng)位。例如,有一個(gè)文件權(quán)限掩碼是050,它就屏蔽了文件組擁有者的可讀與可執(zhí)行權(quán)限。由于使用fork()函數(shù)新建的子進(jìn)程繼承了父進(jìn)程的文件權(quán)限掩碼,這就給該子進(jìn)程使用文件帶來了諸多的麻煩。因此,把文件權(quán)限掩碼設(shè)置為0,可以大大增強(qiáng)該守護(hù)進(jìn)程的靈活性。設(shè)置文件權(quán)限掩碼的函數(shù)是umask()。在這里,通常的使用方法為umask(0)。即賦予最大的能力。
回顧/介紹umask的用法
l?首先在一個(gè)普通文件創(chuàng)建時(shí)內(nèi)核的文件系統(tǒng)有個(gè)限制:不允許你在創(chuàng)建一個(gè)普通文件時(shí)就賦予它執(zhí)行權(quán)限,必須在創(chuàng)建后用chmod命令增加這一權(quán)限。目錄則允許設(shè)置執(zhí)行權(quán)限。所以一個(gè)文件被創(chuàng)建時(shí)的最大權(quán)限是666(110-110-110),目錄被創(chuàng)建的最大權(quán)限是777(111-111-111)
l?掩碼就是指出在此基礎(chǔ)上用戶可以設(shè)置哪些位在創(chuàng)建時(shí)也不允許使能。
假設(shè)umask(002)002即 000-000-010, 即不允許在創(chuàng)建時(shí)賦予other寫權(quán)限,那么創(chuàng)建時(shí)
文件的權(quán)限就是110-110-100, 即664
目錄的權(quán)限就是111-111-101, 即775
4.4.5 Step5 關(guān)閉文件描述符。
同文件權(quán)限掩碼一樣,用fork()函數(shù)新建的子進(jìn)程會(huì)從父進(jìn)程那里繼承一些已經(jīng)打開的文件。這些被打開的文件可能永遠(yuǎn)不會(huì)被守護(hù)進(jìn)程讀或?qū)?#xff0c;但它們一樣消耗系統(tǒng)資源,而且可能導(dǎo)致所在的文件系統(tǒng)無法被卸載。
在上面的第(2)步之后,守護(hù)進(jìn)程已經(jīng)與所屬的控制終端失去了聯(lián)系,因此,從終端輸入的字符不可能達(dá)到守護(hù)進(jìn)程,守護(hù)進(jìn)程中用常規(guī)方法(如printf())輸出的字符也不可能在終端上顯示出來。所以,文件描述符為0、1和2的3個(gè)文件(常說的輸入、輸出和報(bào)錯(cuò)這3個(gè)文件)已經(jīng)失去了存在的價(jià)值,也應(yīng)被關(guān)閉。通常按如下方式關(guān)閉文件描述符:參考ppt
4.4.6 流程總結(jié)?
這樣,一個(gè)簡單的守護(hù)進(jìn)程就建立起來了。創(chuàng)建守護(hù)進(jìn)程的流程圖如圖2所示。課件上的圖上的描述太簡單,建議加上更多的解釋才好,例如下圖。
圖2 創(chuàng)建守護(hù)進(jìn)程流程圖
4.4.7?守護(hù)進(jìn)程的出錯(cuò)處理(提高)
4.4.8 實(shí)驗(yàn):
<<<< 不建議參考實(shí)驗(yàn)手冊,先對照我的例子講講D:\work\courses\notes\2.2-Process\labs \4.5?
參考 Daemon程序的原理和實(shí)現(xiàn):http://blog.csdn.net/acs713/article/details/8696799?>>>>>>>>>
總結(jié)
- 上一篇: 存储类型_malloc_typedef小
- 下一篇: ARM汇编指令