进程 、进程组、会话、控制终端之间的关系
一個進程組可以包含多個進程
????? 進程組中的這些進程之間不是孤立的,他們彼此之間或者存在者父子、兄弟關(guān)系,或者在功能有相近的聯(lián)系。
????? 那linux為什么要有進程組呢?其實提供進程組就是方便管理這些進程。假設(shè)要完成一個任務(wù),需要同時并發(fā)100個進程,當(dāng)用戶由于
???? 某種原因要終止這個任務(wù)時,要是沒有進程組,就需要一個個去殺死這些進程,設(shè)置了進程組之后,就可以對進程組中的每個進程進行殺死。??
???? 每個進程必定屬于一個進程組,也只能屬于一個進程組。??
??? 一個進程除了有進程ID外,還有一個進程組ID,每個進程組也有唯一的進程組ID。
?? 每個進程組有一個進程組組長,進程組組長的進程ID和組ID相同
函數(shù)getpgrp和getpgid可以返回調(diào)用進程的進程組ID
??? #include <unistd.h>
?? pid_t getpgrp(void);
? pid_t? getpgid(pid_t pid);
??????????? //返回值:成功則返回進程組ID,失敗返回-1.
函數(shù)setpgid可以使進程加入現(xiàn)有的組或者創(chuàng)建一個新進程組。
?? #include <unistd.h>
? int setpgid(pid_t pid, pid_t pgid );
setpgid將pid進程的進程組ID設(shè)置為pgid.
(1)pid=pgid ,則表示將pid指定的進程設(shè)為進程組組長
(2)pid=0 .則使用調(diào)用進程的進程ID
(3)pgid=0,則將pid指定進程用作進程組ID。 ????????
一個會話又可以包含多個進程組。一個會話對應(yīng)一個控制終端
??
??? linux是一個多用戶多任務(wù)的分時操作系統(tǒng),必須要支持多個用戶同時登陸同一個操作系統(tǒng),當(dāng)一個用戶登陸一次終端時就會產(chǎn)生一個會話,
? 每個會話有一個會話首進程,即創(chuàng)建會話的進程,建立與終端連接的就是這個會話首進程,也被稱為控制進程。一個會話可以包括多個進程組,
?? 這些進程組可被分為一個前臺進程組和一個或多個后臺進程組。為什么要這么分呢?前臺進程組是指需要與終端進行交互的進程組(只能有一個)
? 比如有些進程是需要完成IO操作的,那么這個進程就會被設(shè)置為前臺進程組.當(dāng)我們鍵入終端的中斷鍵和退出鍵時,就會將信號發(fā)送到前臺進程
? 組中的所有進程。而 后臺進程組是指不需要與終端進程交互的進程組,比如:一些進程不需要完成IO 操作,或者一些守護進程就會 被設(shè)置為后臺進程組(可以有多個),
? (這是我的理解,不知道對錯)。? 如果終端接口檢測到網(wǎng)絡(luò)已經(jīng)斷開連接,則會將掛斷信號發(fā)送給會話首進程。
????????????
進程調(diào)用setsid函數(shù)建立一個新會話.
#include <unistd.h
#include <unistd.h>
pid_t? setsid(pid_t pid);
? ? ? ? ? //返回:成功則返回進程組ID,失敗則返回-1.
如果調(diào)用次函數(shù)的進程不是進程組的組長,則會創(chuàng)建一個新會話,結(jié)果將發(fā)生下面3件事情:
(1)該進程會變?yōu)樾聲挼氖走M程。
(2)該進程會成為一個新進程組的組長進程
(3)該進程沒有控制終端。
如果該調(diào)用進程已經(jīng)是一個進程組的組長,則調(diào)用會出錯。
為了保證不會出錯,通常先fork一個子進程,在關(guān)閉父進程,因為子進程繼承了父進程的進程組ID,
而進程iD則是新分配的,兩者不可能相等,從而保證了子進程不會是進程組組長。(后面編寫守護進程時會用到。)
怎樣編寫守護進程:
1. 在后臺運行。?
為避免掛起控制終端將Daemon放入后臺執(zhí)行。方法是在進程中調(diào)用fork使父進程終止,讓Daemon在子進程中后臺執(zhí)行。?
if(pid=fork())?
exit(0);//是父進程,結(jié)束父進程,子進程繼續(xù)?
2. 脫離控制終端,登錄會話和進程組?
有必要先介紹一下Linux中的進程與控制終端,登錄會話和進程組之間的關(guān)系:進程屬于一個進程組,進程組號(GID)就是進程組長的進程號(PID)。登錄會話可以包含多個進程組。這些進程組共享一個控制終端。這個控制終端通常是創(chuàng)建進程的登錄終端。?
控制終端,登錄會話和進程組通常是從父進程繼承下來的。我們的目的就是要擺脫它們,使之不受它們的影響。方法是在第1點的基礎(chǔ)上,調(diào)用setsid()使進程成為會話組長:?
setsid();?
說明:當(dāng)進程是會話組長時setsid()調(diào)用失敗。但第一點已經(jīng)保證進程不是會話組長。setsid()調(diào)用成功后,進程成為新的會話組長和新的進程組長,并與原來的登錄會話和進程組脫離。由于會話過程對控制終端的獨占性,進程同時與控制終端脫離。?
3. 禁止進程重新打開控制終端?
現(xiàn)在,進程已經(jīng)成為無終端的會話組長。但它可以重新申請打開一個控制終端。可以通過使進程不再成為會話組長來禁止進程重新打開控制終端:?
if(pid=fork())?
exit(0);//結(jié)束第一子進程,第二子進程繼續(xù)(第二子進程不再是會話組長)?
4. 關(guān)閉打開的文件描述符?
進程從創(chuàng)建它的父進程那里繼承了打開的文件描述符。如不關(guān)閉,將會浪費系統(tǒng)資源,造成進程所在的文件系統(tǒng)無法卸下以及引起無法預(yù)料的錯誤。按如下方法關(guān)閉它們:?
for(i=0;i 關(guān)閉打開的文件描述符close(i);>?
5. 改變當(dāng)前工作目錄?
進程活動時,其工作目錄所在的文件系統(tǒng)不能卸下。一般需要將工作目錄改變到根目錄。對于需要轉(zhuǎn)儲核心,寫運行日志的進程將工作目錄改變到特定目錄如/tmpchdir("/")?
6. 重設(shè)文件創(chuàng)建掩模?
進程從創(chuàng)建它的父進程那里繼承了文件創(chuàng)建掩模。它可能修改守護進程所創(chuàng)建的文件的存取位。為防止這一點,將文件創(chuàng)建掩模清除:umask(0);?
7. 處理SIGCHLD信號?
處理SIGCHLD信號并不是必須的。但對于某些進程,特別是服務(wù)器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結(jié)束,子進程將成為僵尸進程(zombie)從而占用系統(tǒng)資源。如果父進程等待子進程結(jié)束,將增加父進程的負(fù)擔(dān),影響服務(wù)器進程的并發(fā)性能。在Linux下可以簡單地將SIGCHLD信號的操作設(shè)為SIG_IGN。?
signal(SIGCHLD,SIG_IGN);?
這樣,內(nèi)核在子進程結(jié)束時不會產(chǎn)生僵尸進程。這一點與BSD4不同,BSD4下必須顯式等待子進程結(jié)束才能釋放僵尸進程。?
daemontest.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
void init_daemon()
{
?? pid_t pid ;
?? int i ;
?? pid =fork();
?? if(pid<0)
?? {
?????? printf("fork error secondly!\n");
?????? exit(1);
?? }
?? else if(pid>0)//結(jié)束父進程
?? {
?????? printf("this is first parent process!\n");
?????? exit(0);
?? }//子進程繼續(xù)運行
?? setsid() ;//前面為setsid正確調(diào)用提供了前提,使子進程成為新的會話組長和
???????? //新的進程組長
?? pid=fork();
?? if(pid<0)//子進程成為無終端的會話組長,但是還是可以打開終端,為了
????????? //使進程脫離終端,使之成為不是會話組長
?? {
?????? printf(" fork error secondly!\n");
?????? exit(1);
?? }
?? else if(pid>0)//關(guān)閉第一個子進程
?? {
??????? printf("this is first child process!\n");
??????? exit(0);
?? }//第二個子進程繼續(xù)運行
?? for(i=0;i<NOFILE;i++)
?? {
????? close(i);
?? }
?? chdir("/tmp");
?? umask(0);
?? return;
}
main.c
#include <stdio.h>
#include <stdlib.h>
void init_daemon(void);
int main(void)
{
???? FILE *fp ;
???? init_daemon() ;
???? while(1)
???? {
????????? if((fp=fopen("daemon.log","a"))>=0)
?? ?? {
?? ?????? fprintf(fp,"%s","good");
?? ?????? fclose(fp);
?? ?????? sleep(10);
?? ?? }
??? ?
???? }
???? exit(0);
}
運行:
yuan@YUAN:~$ ./daemontest
this is first parent process!
this is first child process!
yuan@YUAN:~$ ps -axj
結(jié)果:
? PPID?? PID? PGID?? SID TTY????? TPGID STAT?? UID?? TIME COMMAND
??? 1? 2970? 2969? 2969 ??????????? -1 S???? 1000?? 0:00 ./daemontest
總結(jié)
以上是生活随笔為你收集整理的进程 、进程组、会话、控制终端之间的关系的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 集合的结构示意图
- 下一篇: uniapp使用colorUI 组件