程序間關系
程序組
- 程序組是一個或多個程序的集合
- 每個程序除了有一個程序ID之外,還屬于一個程序組
- 這些程序通常與同一作業相關聯,可以接受來自同一終端的各種信号
- 每個程序組有唯一程序組ID,每個程序組可以有一個組長程序,其程序ID和程序組ID一緻
- 組長程序可以建立一個程序組,建立該組中程序,然後終止
- 隻要某個程序組中有一個程序存在,該程序組就存在,與組長程序是否終止無關
- 從程序組的建立到最後一個程序離開的時間區成為程序組的生命周期 他們的ppid是一樣的,屬于同一控制終端,pid和pgid一樣的是程序組組長
作業
- 程序組在某種意義上可稱之為作業,在我們所用的shell中,shell對于前背景的控制的是作業或程序組
- shell對作業的控制,可以 運作一個前台作業和多個背景作業
- 在前台新起作業,shell是無法運作,因為它被提到了背景,但是如果前台程序退出,shell就又被提到了前台,是以可以繼續接受使用者輸入
- 程序組和作業的唯一差別:程序組的程序建立出來的子程序還是屬于程序組,但是作業中的程序建立出來的子程序不屬于這個作業
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 pid_t pid=fork();
6 if(pid<0)
7 {
8 return 1;
9 }
10 else if(pid==0)
11 {
12 while(1)
13 {
14 printf("child%d###\n",getpid());
15 sleep(1);
16 }
17 }
18 else
19 {
20 int i=3;
21 while(i)
22 {
23 printf("parent%d#####\n",getpid());
24 --i;
25 sleep(1);
26 }
27 }
28 return 0;
29 }
fork建立出子程序,預設在前台運作,此時輸入指令是不會起任何作用的,3秒過後,父程序退出,此時指令又可以讀取運作了,這是因為當父程序退出後,子程序被1号程序托管,自動成為背景作業,此時shell又被提到前台,就可以正常運作了
會話
- 會話是一個或多個程序組的集合
- 一個會話有一個控制終端,這通常是登入到其上的終端裝置(在終端登入情況下)或僞終端裝置(在網絡登入情況下)
- 建立與控制終端連接配接的會話首程序被稱為控制程序
- 一個會話中的幾個程序組可被分為一個前台程序組以及一個或多個背景程序組
- 是以一個會話中,應該包括控制程序,一個前台程序組和任意背景程序組
//建立背景作業
程序 & sleep 100 &
//檢視背景作業
jobs
//将背景作業提到前台
fg num
num 是jobs裡第幾個背景程序
//使用ctrl+z 将其放在背景,并且狀态為stopped暫停狀态
//将程序放在背景運作
bg
守護程序
守護程序也叫精靈程序,是在背景運作的一種特殊程序,它獨立于控制終端并且周期性的執行某種人物或等待處理某些發生的時間
- 是系統服務程序,這些系統服務程序沒有控制終端,不能直接和使用者互動
- 除守護程序外其他程序都是在使用者登入或運作程式時建立,在系統登出時終止,但系統服務程式不受使用者登入登出的影響(以為和終端沒有關系)
- 自成會話,自成程序組
- 可以用ps axj指令檢視系統中的程序,參數a表示不僅目前使用者的程序,也列出所有其他使用者的程序,參數x表示不僅有控制終端的程序,也列出所有無控制終端的程序,參數j表示列出與作業控制相關的資訊
- 凡是TPGID寫着-1的都是沒有控制終端的程序,也就是守護程序
- 在COMMAND一列用[ ]括起來的名字表示核心線程,這些線程在核心裡建立,沒有使用者空間代碼,是以沒有程式檔案名和指令行,通常采用以K開頭的名字,表示Kernel
- udevd負責維護/dev目錄下的裝置檔案,acpid負責電源管理,syslogd負責維護/var/log下的日志檔案可以看出,守護程序通常采用以d結尾的名字,表示Daemon
守護程序存在的原因:控制終端因為某些原因會發送一些信号,接受到信号的程序去執行這些信号的預設處理動作會導緻程序退出,這就使得程序不能正常的處理某些任務,是以就需要像征收戶程序這樣接受不到信号的程序。讓程序獨立于控制終端,執行某些任務或處理某些事件
建立守護程序的幾個重要部分
fork()兩次
第一次fork是為了調用setsid,因為調用setsid函數的程序一定不是程序組的組長,讓父程序建立出子程序,然後退出,此時子程序一定不是程序組的組長
調用setsid
這是建立守護程序最重要的一步,調用setsid建立一個會話,讓調用此函數的程序成為會話首程序
#include<unsitd.h>
id_t setsid(void);
該函數調用成功時傳回新建立的Session的id,也就是目前程序的id,出錯傳回-1
如果調用這個函數的程序是程序組的組長,那麼setsid()就會出錯,傳回-1
要保證目前程序不是程序組的組長也很容易,隻要先fork()在調用setsid就行了
fork建立的子程序和父程序在同一個程序組中,程序組的組長必然是該組的第一個程序
是以子程序不可能是該組的第一個程序,也就不可能會使組長,在子程序中調用setsid就不會有問題了
成功調用該函數的結果是:
目前程序稱為會話首程序 控制程序,目前程序的id就是會話的id
建立一個新的程序組,目前程序成為程序組的組長,目前程序的id就是程序組的id
如果目前程序原本有一個控制終端,則它失去這個控制終端,成為一個沒有控制終端的程序
所謂失去控制終端是指,原來的控制終端仍然是打開的,仍然可以讀寫,但隻是一個普通的打開檔案而不是控制終端了
将目前工作目錄改成根目錄
守護程序會保護目前目錄下的檔案或目錄下不被删除或解除安裝
那麼如果某一時刻需要對該目錄下的檔案進行删除操作,就不執行
為什麼不能删除或解除安裝守護程序目前目錄下的檔案或目錄
因為避免出現因為删除或解除安裝目前目錄下的檔案或目錄而導緻守護程序運作失敗
系統提供的daemon函數
#include<unsitd.h>
int daemon(int nochdir, int noclose);
當nochdir為0時,目前目錄變為根目錄,否則不變
當noclose為0時,标準輸入,标準輸出和錯誤輸出中定向為/del/null,也就是不輸出任何資訊,否則照樣輸出
傳回值
deamon()調用了fork()
如果fork成功,那麼父程序就調用_exit(2)退出
是以看到的錯誤資訊 全部是子程序産生的
如果成功函數傳回0,否則傳回-1
1 #include<stdio.h>
2 #include<signal.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 #include<fcntl.h>
6 #include<stdlib.h>
7 #include<sys/stat.h>
8 void mydaemon(void)
9 {
10 int i;
11 int fd0;
12 pid_t pid;
13 struct sigaction sa;
14 umask(0);
15 if(pid==fork()<0)
16 perror("fork");
17 else if(pid>0)
18 exit(0);
19 setsid();
20 sa.sa_handler=SIG_IGN;
21 sidemptyset(&sa.sa_mask);
22 sa.sa_flags=0;
23 if(sigaction(SIGCHLD,&sa,NULL)<0)
24 return ;
25 if(pid=fork()<0)
26 {
27 perror("fork");
28 return ;
29 }
30 else if(pid!=0)
31 exit(0);
32 if(chdir("/")<0)
33 {
34 printf("chdir dir error\n");
35 return ;
36 }
37 close(0);
38 fd0=open("/dev/null",O_RDWR);
39 dup2(fd0,1);
40 dup2(fd0,2);
41 }
42 int main()
43 {
44 // daemon(0,0);
45 mydaemon();
46 while(1)
47 {
48 sleep(1);
49 }
50 return 0;
51 }