進程組linux
概念:一個或多個進程的集合。shell
每個進程除了有一個進程ID外,還屬於一個進程組,同時也只能屬於一個進程組。每一個進程組都有一個惟一的進程組ID,且均可以有一個組長進程。通常在進程組中,第一個進程是組長進程。服務器
爲啥要建立進程組呢?爲了方便對進程進行管理。假設要完成一個任務,須要同時併發10個進程,當用戶處於某種緣由要終止這個任務時,如若沒有進程組,就須要手動的一個一個的去殺死這10個進程,而且嚴格按照進行間的關係順序,不然會打亂進程間的關係,有了進程組,就能夠將這10個進程設置一個進程組,他們共有一個組號(pgrp),而且選取一個進程做爲組長,(一般選取「輩分」最高的那個,一般該進程的ID就是該進程組的ID)。如今就能夠經過殺死整個進程組來關閉這10個進程。組長進程能夠建立進程組,建立該組中的進程,而後終止。只要在某個進程組中一個進程存在,則該組進程就存在,這與組長進程是否終止無關。併發
做業ide
shell分先後臺來控制的是做業或進程組,不是進程。一個前臺做業能夠由多個進程組成,一個後臺能夠由多個進程組成。函數
做業控制:shell能夠運行一個前臺做業和任意多個後臺做業。 測試
一、 與做業控制有關的信號:spa
咱們cat爲例(把他放在後臺,從終端讀)線程
(1)因爲cat須要讀標準輸入(也就是終端輸入),然後臺進程是不能讀終端輸入的,所以內核發SIGTTIN信號給進程, 該信號的默認處理動做是使進程中止。blog
[liu153@liu153 7-31_class16]$ cat &
[1] 895
[liu153@liu153 7-31_class16]$ //再嗯回車
[1]+ Stopped cat
[liu153@liu153 7-31_class16]$
(2)jobs命令:查看當前先後先後臺有哪些做業
(3)fg命令:能夠將某個做業提至前臺運行:
a、若是該做業的進程組正在後臺運行則提至前臺運行;
b、若是該做業處於中止狀態,則給進程組的每一個進程發SIGCONT信號使它繼續行。
參數%1表示將第1個做業提至前臺運行。
cat提到前臺運行後,掛起等待終端輸入,當 輸入hello並回車後,cat打印出一樣的一行,而後繼續掛起等待輸入。緊接着, 若是輸入Ctrl-Z則向全部前 臺進程發SIGTSTP 信號,該信號的默認動做是使進程中止,cat繼續之後臺做業的形式存在。
(4)bg命令:可讓某個中止的做業在後臺繼續運行。也須要給該做業的進程組的每一個進程發SIGCONT信號。cat進程繼續運行,又要讀終端輸入,然而它在後臺不能讀終端輸入,因此又收到SIGTTIN信號而中止。
二、給一箇中止的進程發SIGTERM與SIGKILL信號的區別:
(1)kill 命令給一箇中止的進程發送SIGTERM信號時並不會當即被處理,而是等到合適的時候處理,默認處理動做是終止進程。
(2)SIGKILL信號既不能被阻塞也不能被忽略 ,也不能用自定義函數捕捉 ,只能按系統的默認動做馬上處理(SIGSTOP 信號也與此相似)。(這樣保證了無論什麼樣的進程都能用 SIGKILL終止或者用SIGSTOP中止, 當系統出現異 常時管理員老是有辦法殺掉有問題的進程或者暫時停掉懷疑有問題的進程。)
做業與進程組的區別:
若是一個做業中的某個進程又建立了一個子進程,該子進程不屬於做業。一旦做業運行結束,shell就把它提到前臺,若是原來前臺進程還存在,它自動變爲後臺進程組。
會話
概念:一個或多個進程組的集合,但只能有一個前臺進程組。
控制進程:創建與控制終端鏈接的會話首進程。每一個會話都有一個會話首領(leader)即建立會話的進程。
sys_setsid()調用能建立一個會話。
注意:只有當前進程不是進程組的組長時,才能建立一個新的會話。
一次會話中應該包括:一個控制進程,一個前臺進程和任意多個後臺進程。
終端
控制終端:會話的領頭進程打開一個終端,以後,該終端就成爲該會話的控制終端。一個會話只能有一個控制終端。
進程屬於一個進程組,進程組屬於一個會話,會話可能有也可能沒有控制終端。通常而言,當用戶在某個終端上登陸時,一個新的會話就開始了。進程組由組中的領頭進程標識,領頭進程的進程標識符就是進程組的組標識符。相似地,每一個會話也對應有一個領頭進程。
同一會話中的進程經過該會話的領頭進程和一個終端相連,該終端做爲這個會話的控制終端。一個會話只能有一個控制終端,而一個控制終端只能控制一個會話。用戶經過控制終端,能夠向該控制終端所控制的會話中的進程發送鍵盤信號。
當咱們打開多個終端窗口時,實際上就建立了多個終端會話。每一個會話都會有本身的前臺工做和後臺工做。
查看終端設備
測試代碼:
#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;
}
終端1運行結果:
[liu153@liu153 7-31_class16]$ ./a.out
fd :0->/dev/pts/2
fd :1->/dev/pts/2
fd :2->/dev/pts/2
終端2運行結果:
[liu153@liu153 7-31_class16]$ ./a.out
fd :0->/dev/pts/0
fd :1->/dev/pts/0
fd :2->/dev/pts/0
[liu153@liu153 7-31_class16]$
終端3運行結果:
[liu153@liu153 7-31_class16]$ ./a.out
fd :0->/dev/pts/3
fd :1->/dev/pts/3
fd :2->/dev/pts/3
[liu153@liu153 7-31_class16]$
守護進程(精靈進程)
是運行在後臺的一種特殊進程,獨立於控制終端而且週期性的執行某種任務或等待處理某些發生的事件。守護進程是一種頗有用的進程,Linux的大多數服務器就是用守護進程實現的。 守護進程完成許多系統任務。大多數守護進程以d結尾,凡是中括號括起來的都是內核線程。
root 2 0.0 0.0 0 0 ? S Jul27 0:00 [kthreadd]
root 17 0.0 0.0 0 0 ? S Jul27 0:00 [kacpid]
root 22 0.0 0.0 0 0 ? S Jul27 0:00 [ksuspend_usbd]
root 23 0.0 0.0 0 0 ? S Jul27 0:04 [khubd]
root 24 0.0 0.0 0 0 ? S Jul27 0:00 [kseriod]
root 28 0.0 0.0 0 0 ? S Jul27 0:00 [khungtaskd]
root 30 0.0 0.0 0 0 ? SN Jul27 0:00 [ksmd]
root 38 0.0 0.0 0 0 ? S Jul27 0:00 [pciehpd]
root 40 0.0 0.0 0 0 ? S Jul27 0:00 [kpsmoused]
root 72 0.0 0.0 0 0 ? S Jul27 0:00 [kstriped]
root 1103 0.0 0.0 0 0 ? S Jul27 0:00 [kauditd]
root 2001 0.0 0.0 0 0 ? S< Jul27 0:00 [krfcommd]
守護進程的特性:
一、後臺運行(最重要的)
二、守護進程必須與其運行前的環境隔離開來。
三、啓動方式有其特殊之處:能夠在linux系統啓動時從啓動腳本/etc/rc.d中啓動,能夠在做業規劃進程cround啓動。還能夠由用戶終端(一般是shell)執行。
後臺進程與守護進程的區別
一、後臺進程與特定終端關聯,與會話緊密相聯。
二、守護進程是後臺進程的一種,與終端無關
建立守護進程
一、調用umask將文件模式建立屏蔽字段設置爲0
二、調用fork函數,父進程退出。
緣由:1)若是該守護進程是做爲一條簡單的shell命令啓動的,那麼父進程終止使得shell認爲該命令已經執行完畢。
2)保證子進程不是一個 進程組的組長進程。
三、調用setsid建立一個新會話。
setsid會致使:
1)調用進程成爲新會話的首進程。
2)調用進程成爲一個進程組的組長進程 。
3)調用進程沒有控制終端。(再次fork一次,保證 daemon進程,以後不會打開tty設備)
四、將當前工做目錄更改成根目錄。
五、關閉不在須要的文件描述符。
六、 其餘:忽略SIGCHLD信號。
建立守護進程代碼:
測試1:
#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include<signal.h>
#include <fcntl.h>
void my_daemon()
{
umask(0);// 設置文件掩碼爲0
pid_t id = fork();
if (id == 0)
{
// child
setsid();// 設置 新會話
chdir("/");// 更換 目錄
close(0);
close(1);
close(2);
signal(SIGCHLD,SIG_IGN);// 註冊子進程退出忽略信號
}
else
{
sleep(14);
exit(0);// 終止父進程
}
close(0);// 關閉標準輸入
int fd0 = open("dev/null", O_RDWR);// 重定向全部標準輸出、 錯誤到/dev/null
dup2(fd0, 1);
dup2(fd0, 2);
}
int main()
{
my_daemon();
while(1);
}
測試2;
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
void create_daemon(void)
{
int i;
int fd0;
pid_t pid;
struct sigaction sa;
umask(0);//設置文件掩碼爲0
if(pid = fork() < 0){
}else if(pid != 0){
exit(0);//第一次fork()終止父進程
}
setsid();//設置新會話
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGCHLD,&sa,NULL) < 0){//註冊子進程退出忽略信號
return ;
}
if(pid = fork() < 0){//爲什麼要fork兩次?->再次fork,終止子進程,保證孫子進程不是話首進程,從而保證後續不會再和其餘終端關聯
printf("fork error !\n");
return ;
}else if(pid != 0){
exit(0);
}
if(chdir("/")<0){//更改工做目錄到根
printf("child dir error\n");
return ;
}
close(0);
fd0 = open("/dev/null",O_RDWR);//關閉標準輸入,重定向全部標準(輸入輸出錯誤)到/dev/null
dup2(fd0,1);
dup2(fd0,2);
}
int main()
{
create_daemon();
while(1)
{
sleep(1);
}
return 0;
}
運行監視:
關閉標準輸入,重定向全部標準(輸入輸出錯誤)到/dev/null