作業,進程組,終端

1:進程組:
每一個進程除了有一個進程ID,還屬於進程組.進程組是一個或者多個進程的集合.通常,它們與同一作業相關聯,可以接收來自同一終端的各種信號.每個進程組有一個唯一的進程組ID,每個進程組都可以有一個組長進程.組長進程的標誌是,其進程組ID等於其進程ID.
組長進程可以創建一個進程組,創建該組中的進程,然後終止.只要在某個進程組中一個進程存在,則該進程組就存在,這與組長進程是否終止無關.

函數getpgrp返回調用者的進程組ID

#include<unistd.h>
pid_t getpgrp(void);

2:作業
shell分前後臺來看控制的不是進程而是作業(Job)或者進程組(Process Group).一個前臺作業可以由多個進程組成,一個後臺作業也可以由多個進程組成,shell可以運行一個前臺作業和任意多個後臺作業,這稱爲作業控制
作業與進程組的區別:如果作業中的某個進程又創建了子進程,則子進程不屬於作業.一旦作業運行結束,shell就把自己提到前臺,如果原來的前臺進程還存在,它自動變爲後臺進程組

3:會話
會話(Session)是一個或多個進程組的集合
一個會話可以有一個控制終端.這通常是登錄到其上的終端設備或僞終端設備.建立與控制連接的會話首進程被稱爲控制進程.一個會話中的幾個進程組課被分成一個前臺進程組以及一個或多個後臺進程組.所以一個會話中,應該包括控制進程,一個前臺進程進程組和任意進程組.

我們來編寫一個前臺進程:
int mian()
{
while(1);
return 0;
}
a)前臺進程:
這裏寫圖片描述
b)後臺進程
當給前臺進程加上&,表明該前臺進程轉成後臺進程.
&表示後臺進程
這裏寫圖片描述

4:作業控制
1:Session與進程組
前面我們知道”shell可以同時運行一個前臺進程和任意多個後臺進程”是不全面的,事實上,shell前臺作業可以由多個進程組成,一個後臺作業也可以,由多個進程組成,shell可以同時運行一個前臺作業和任意多個後臺作業,這稱爲作業控制(Job Control)
1 $proc1 | proc2

1 proc3|proc4 proc5
2:進程調用setsid函數建立一個新會話
其中proc1和proc2屬於同一個後臺進程組,proc3,proc4和 proc5屬於同一個前臺進程組,shell本身屬於一個單獨的進程組,=.這些進程組的控制終端相同它們同屬於一個會話,當用戶在控制終端輸入特殊的控制鍵(Ctrl+C產生SIGINT,Ctrl+C\產生SIGQUIT,Ctrl+Z,產生SIGTSTP),內核發送相應的信號給前臺進程組中的所有進程
這裏寫圖片描述

我們從session和進程組的角度重新來看登錄和執行命令的過程
這裏寫圖片描述
表明這個作業由ps和cat兩個進程組成,在前臺運行,從PPID可以知道這兩個進程的父進程是bash,從PGRP列可以看出,bash在id爲4152的進程組中,而bash的id也是4152,所以bash是進程組的Leader,而兩個子進程在id爲4152的進程組中,ps是這個進程組的Leader.從Session空可以看出三個進程都在同一個Session中,bash是Session Leader.從TPGID可以看出,前臺進程組的id是4166,,就是兩個子進程所在的進程組.

這裏寫圖片描述
ps和cat屬於同一個作業,bash不等作業結束打印提示信息[1] 4174,然後給出新的提示符接受新的命令,[1]是作業的編號,如果同時運行多個作業可以用這個編號區分.4174是cat進程的id.

3:與作業控制有關的信號
這裏寫圖片描述
將cat放到後臺運行,由於cat需要讀標準輸入,而該後臺進程是不能讀終端輸入的,因此內核法SIGTTIN信號給進程,該信號的默認處理動作是使進程停止.
這裏寫圖片描述
jobs命令可以查看當前有哪些作業,fg命令可以將某個作業提至前臺運行,如果該作業的進程正在後臺運行則提至前臺運行,如果該作業是停止狀態,則給進程組的每個進程發SIGCONT信號使它繼續運行.參數%1表示將第一個作業提至前臺運行,cat提到前臺後掛起等待輸入,Ctrl+Z則向所有前臺進程發SIGTSTP信號,該信號的默認動作是使進程停止,cat繼續以後臺的形式存在.
這裏寫圖片描述

bg命令可以讓某個停止的作業在後臺繼續運行,也需要給該作業的進程組的每個進程發SIGCONT信號,cat進程繼續運行,cat進程繼續運行,又要讀終端輸入,然而它在後臺不能讀終端輸入,所以又收到SIGTTIN而停止
這裏寫圖片描述
用kill命令給一個停止的進程發SIGTERM信號,這個信號並不會立即處理,而要等待進程準備繼續運行之前處理,默認動作是終止進程

5: 終端:
在UNIX系統中,用戶通過終端登錄系統後得到一個shelll進程,這個終端成爲shell進程的控制終端,控制終端保存在pcb中的信息,而我們知道fork()會複製pcb中的信息,因此由shell進程啓動的其他進程的控制終端也是這個終端.

默認情況下,每個進程的標準輸入,標準輸出和標準錯誤輸出都指向控制終端,進程從標準輸入讀也就是讀用戶的鍵盤輸入,進程往標準輸出或保準錯誤輸出寫也就是輸出到顯示器上.

每個進程都可以通過有個特殊的設備文件/dev/tty訪問它的控制終端.事實上每個終端設備讀對應一個不同的設備文件,./dev/tty提供了一個通用的接口,一個進程要訪問它的控制終端既可以通過/dev/tty也可以通過該終端設備所對應的設備文件來訪問.ttyname函數可以由文件描述符查出對應的文件名,該文件描述符必須指向一個終端設備而不是任意文件.
查看終端對應的設備:

#include<stdio.h>
#include<unistd.h>

int main()
{
printf("fd:  %d->%s\n",0,ttyname(0));
printf("fd:  %d->%s\n",1,ttyname(1));
printf("fd:  %d->%s\n",2,ttyname(2));

return 0;
}

這裏寫圖片描述

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