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