【Linux】程序間關係與守護程序
程序間關係
程序組/作業/會話
程序組
- 程序組是一個或多個程序的集合,通常它們與一組作業相關聯,可以接受來自同一終端的各種訊號。
- 每個程序除了有一個程序ID之外,還屬於一個程序組。
- 每個程序組都有唯一的程序組ID(整數,也可以存放在pid_t型別中)。
- 程序組,每個程序組有一個領頭程序。
- 程序組由程序組ID來唯一標識。
- 每個程序組都有一個組長程序,組長程序的程序號等於程序組ID。組長程序可以建立一個程序組、建立該組中的程序。只要某個程序組中有一個程序存在,則該程序組就存在,與組長程序是否終止無關。從程序組建立開始到其中最後一個程序離開為止的時間區間成為程序組的生存期。程序組中最後一個程序可以終止或者轉移到另一個程序組中。
例如:
程序:17157 ,17158,17159
程序組長:17157 程序組當中的第一個程序
&:表示將程序組放到後臺執行
ps選項:
- a:不僅可以列出當前使用者的程序,也可以列出所有其他使用者的程序。
- x:表示不僅列有控制終端的程序,也列出所有無控制終端的程序。
- j:表示列出與作業控制相關的資訊。
作業
- Shell分前後來控制的不是程序而是作業或者程序組。
- 一個前臺作業可以由多個程序組成,一個後臺也可以由多個程序組成。
- Shell可以執行一個前臺作業和任意多個後臺作業。—作業控制
- 在前臺新起作業,shell是無法執行,因為他被提到了後臺。但是如果前臺程序退出,shell就又被提到了前臺,所以可以繼續接受使用者輸入。
作業與程序組的區別:如果作業中的某個程序又建立了子程序,則子程序不屬於作業
例:
int main()
{
pid_t id=fork();
if(id<0)
{
perror("fork");
return -1;
}
else if(id==0)
{
while(1)
{
printf ("child (%d) # i am running!\n",getpid());
sleep(1);
}
}
else
{
int i=5;
while(i)
{
printf("parent(%d)# i am going to dead...%d\n",getpid(),i--);
sleep(1);
}
}
return 0;
我們可以看到,程式跑起來後,在前臺新起了1個作業,包含父子兩個程序。5s之後,shell無法接受任何命令,說明此時的前臺作業不是shell。
但當父程序退出後,子程序還在執行,但此時輸入的命令,shell可以處理,說明此時shell變成了前臺作業。
但子程序所屬的程序組還在,組長是父程序(已退出),殺掉即可。
作業控制
程序前後臺操作用到以下命令或按鍵:
Ctrl+C:終止並退出前臺命令的執行,回到SHELL
Ctrl+Z:暫停前臺命令的執行,將該程序放入後臺,回到SHELL
jobs:檢視當前在後臺執行的命令,可檢視命令程序號碼
&:執行命令時,在命令末尾加上&可讓命令在後臺執行
fg N:將命令程序號碼為N的命令程序放到前臺執行,同%N
bg N:將命令程序號碼為N的命令程序放到後臺執行
會話
- 會話是一個或多個程序組的集合。
- 一個會話可以有一個控制終端。
- 建立與控制終端連線的會話首程序被稱為控制程序。
- 一個會話中的幾個程序組可被分為一個前臺程序組以及一個或多個後臺程序組。
所以一個會話中,應該包括控制程序(會話首程序),一個前臺程序組和任意後臺程序組。
SID:會話id
守護程序
守護程序也叫精靈程序,是在後臺執行的一種特殊程序,它獨立於控制終端並且週期性的執行某種任務或等待處理某些發生的事件;
使用者使守護程序獨立於所有終端是因為,在守護程序從一個終端啟動的情況下,這同一個終端可能被其他的使用者使用。例如,使用者從一個終端啟動守護程序後退出,然後另外一個人也登入到這個終端。使用者不希望後者在使用該終端的過程中,接收到守護程序的任何錯誤資訊。同樣,由終端鍵人的任何訊號(例如中斷訊號)也不應該影響先前在該終端啟動的任何守護程序的執行。雖然讓伺服器後臺執行很容易(只要shell命令列以&結尾即可),但使用者還應該做些工作,讓程式本身能夠自動進入後臺,且不依賴於任何終端。
守護程序沒有控制終端,因此當某些情況發生時,不管是一般的報告性資訊,還是需由管理員處理的緊急資訊,都需要以某種方式輸出。Syslog 函式就是輸出這些資訊的標準方法,它把資訊傳送給 syslogd 守護程序。
//用ps axj 檢視系統中的程序。
ps axj | more
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 1 1 1 ? -1 Ss 0 0:04 /sbin/init
0 2 0 0 ? -1 S 0 0:00 [kthreadd]
2 3 0 0 ? -1 S 0 0:00 [migration/0]
2 4 0 0 ? -1 S 0 0:03 [ksoftirqd/0]
2 5 0 0 ? -1 S 0 0:00 [stopper/0]
2 6 0 0 ? -1 S 0 0:09 [watchdog/0]
2 7 0 0 ? -1 S 0 0:09 [events/0]
2 8 0 0 ? -1 S 0 0:00 [cgroup]
2 9 0 0 ? -1 S 0 0:00 [khelper]
2 10 0 0 ? -1 S 0 0:00 [netns]
2 11 0 0 ? -1 S 0 0:00 [async/mgr]
2 12 0 0 ? -1 S 0 0:00 [pm]
2 13 0 0 ? -1 S 0 0:00 [sync_supers]
2 14 0 0 ? -1 S 0 0:00 [bdi-default]
2 15 0 0 ? -1 S 0 0:00 [kintegrityd/0]
2 16 0 0 ? -1 S 0 0:05 [kblockd/0]
2 17 0 0 ? -1 S 0 0:00 [kacpid]
2 18 0 0 ? -1 S 0 0:00 [kacpi_notify]
2 19 0 0 ? -1 S 0 0:00 [kacpi_hotplug]
2 20 0 0 ? -1 S 0 0:00 [ata_aux]
2 21 0 0 ? -1 S 0 0:33 [ata_sff/0]
2 22 0 0 ? -1 S 0 0:00 [ksuspend_usbd]
2 23 0 0 ? -1 S 0 0:00 [khubd]
2 24 0 0 ? -1 S 0 0:00 [kseriod]
2 25 0 0 ? -1 S 0 0:00 [md/0]
建立守護程序
setsid函式
#include <unistd.h>
id_t setsid(void);
注意:呼叫該函式之前,當前程序不允許是程序組的組長,否則返回-1。
先fork再呼叫。fork建立的子程序和父程序在同一個程序組中,程序組的組長必然是該組的第一個程序,故子程序不可能是該組的第一個程序。
成功呼叫該函式的結果是:
1.當前程序成為會話首程序(控制程序),當前程序的 id 就是會話的id。
2.建立一個新的程序組,當前程序成為程序組的組長,當前程序的 id 就是程序組的 id。
3.如果當前程序原本有一個控制終端,則它失去這個控制終端,成為一個沒有控制終端的程序。
4.所謂失去控制終端是指,原來的控制終端仍然是開啟的,仍然可以讀寫,但只是一個普通的開啟檔案而不是控制終端了。
程式碼:
//實現守護程序
//
//
void mydaemon()
{
int pid=-1;
umask(0);
pid=fork();
//讓子程序到後臺執行
if(pid>0)
{
exit(0);
}
else if(pid<0)
{
perror("fork");
}
//為子程序建立會話
if(setsid()<0)
{
exit(-1);
}
chdir("/");//改變當前工作目錄
//防止子程序與其他程序關係起來
if(pid>0)
{
exit(0);
}
else if(pid<0)
{
perror("fork");
}
close(0);
close(1);
close(2);
}
int main()
{
mydaemon();
while(1)
{
sleep(1);
}
return 0;
}
系統提供的daemon函式:
#include <unistd.h>
int daemon(int nochdir, int noclose);
// 引數
// 當 nochdir為 0 時,當前目錄變為根目錄,否則不變;
// 當 noclose為 0 時,標準輸入、標準輸出和錯誤輸出重定向為/dev/null,也就是不輸出任何資訊,否則照樣輸出。
// 返回值:
// deamon()呼叫了fork()
// 如果fork成功,那麼父程序就呼叫_exit(2)退出,
// 所以看到的錯誤資訊 全部是子程序產生的。
// 如果成功函式返回0,否則返回-1並設定errno。
#include <stdio.h>
#include <unistd.h>
int main()
{
daemon(0, 0);
while(1);
return 0;
}