一、整體大綱
二、進程組與守護進程
1. 進程組
概念和特性
進程組,也稱之為作業。BSD於1980年前后向Unix中增加的一個新特性。代表一個或多個進程的集合。每個進程都屬於一個進程組。在waitpid函數和kill函數的參數中都曾使用到。操作系統設計的進程組的概念,是為了簡化對多個進程的管理。
當父進程,創建子進程的時候,默認子進程與父進程屬於同一進程組。進程組ID==第一個進程ID(組長進程)。所以,組長進程標識:其進程組ID==其進程ID
可以使用kill -SIGKILL -進程組ID(負的)來將整個進程組內的進程全部殺死。
組長進程可以創建一個進程組,創建該進程組中的進程,然后終止。只要進程組中有一個進程存在,進程組就存在,與組長進程是否終止無關。
進程組生存期:進程組創建到最后一個進程離開(終止或轉移到另一個進程組)。
一個進程可以為自己或子進程設置進程組ID
2. 進程組操作函數
1)getpgrp函數
獲取當前進程的進程組ID
pid_t getpgrp(void); 總是返回調用者的進程組ID
2)getpgid函數
獲取指定進程的進程組ID
pid_t getpgid(pid_t pid); 成功:0;失敗:-1,設置errno
如果pid = 0,那么該函數作用和getpgrp一樣。
練習:查看進程對應的進程組ID
3)setpgid函數
改變進程默認所屬的進程組。通常可用來加入一個現有的進程組或創建一個新進程組。
int setpgid(pid_t pid, pid_t pgid); 成功:0;失敗:-1,設置errno
將參1對應的進程,加入參2對應的進程組中。
注意:
1. 如改變子進程為新的組,應fork后,exec前。
2. 權級問題。非root進程只能改變自己創建的子進程,或有權限操作的進程
練習:修改子進程的進程組ID
3. 會話
1)創建會話
創建一個會話需要注意以下6點注意事項:
a. 調用進程不能是進程組組長,該進程變成新會話首進程(session header)
b. 該進程成為一個新進程組的組長進程。
c. 需有root權限(ubuntu不需要)
d. 新會話丟棄原有的控制終端,該會話沒有控制終端
e. 該調用進程是組長進程,則出錯返回
f. 建立新會話時,先調用fork, 父進程終止,子進程調用setsid
2)getsid函數
獲取進程所屬的會話ID
pid_t getsid(pid_t pid); 成功:返回調用進程的會話ID;失敗:-1,設置errno
pid為0表示察看當前進程session ID
ps ajx命令查看系統中的進程。參數a表示不僅列當前用戶的進程,也列出所有其他用戶的進程,參數x表示不僅列有控制終端的進程,也列出所有無控制終端的進程,參數j表示列出與作業控制相關的信息。
組長進程不能成為新會話首進程,新會話首進程必定會成為組長進程。
3)setsid函數
創建一個會話,並以自己的ID設置進程組ID,同時也是新會話的ID。
pid_t setsid(void); 成功:返回調用進程的會話ID;失敗:-1,設置errno
調用了setsid函數的進程,既是新的會長,也是新的組長。
練習:fork一個子進程,並使其創建一個新會話。查看進程組ID、會話ID前后變化 。
總結:
會話:進程組的更高一級,多個進程對應一個會話。
進程組:多個進程在同一個組,第一個進程默認是進程組的組長。
創建會話的時候,組長不可以創建,必須是組員創建。
創建會話的步驟:創建子進程,父進程去死,子進程自當會長。
4. 守護進程
Daemon(精靈)進程,是Linux中的后台服務進程,通常獨立於控制終端並且周期性地執行某種任務或等待處理某些發生的事件。一般采用以d結尾的名字。
Linux后台的一些系統服務進程,沒有控制終端,不能直接和用戶交互。不受用戶登錄、注銷的影響,一直在運行着,他們都是守護進程。如:預讀入緩輸出機制的實現;ftp服務器;nfs服務器等。
創建守護進程,最關鍵的一步是調用setsid函數創建一個新的Session,並成為Session Leader。
創建守護進程模型:
1)創建子進程,父進程退出
所有工作在子進程中進行形式上脫離了控制終端
2)在子進程中創建新會話
setsid()函數
使子進程完全獨立出來,脫離控制
3)改變當前目錄為根目錄
chdir()函數
防止占用可卸載的文件系統
也可以換成其它路徑
4)重設文件權限掩碼
umask()函數
防止繼承的文件創建屏蔽字拒絕某些權限
增加守護進程靈活性
5)關閉文件描述符
繼承的打開文件不會用到,浪費系統資源,無法卸載
開始執行守護進程核心工作
守護進程退出處理程序模型
總結:
守護進程的步驟:
a. 創建子進程fork
b. 父進程退出
c. 子進程當會長 setid
d. 切換工作目錄 $HOME
e. 設置掩碼 umask
f. 關閉文件描述符 0, 1, 2,為了避免浪費資源
g. 執行核心邏輯
h. 退出
練習:創建一個守護進程:每分鍾在$HOME/log/創建一個文件 程序名.時間戳?

1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <string.h> 7 #include <stdlib.h> 8 #include <signal.h> 9 #include <sys/time.h> 10 #include <time.h> 11 12 #define _FILE_NAME_FORMAT_ "%s/log/mydaemon.%ld" //定義文件格式化 13 14 void touchfile(int num) 15 { 16 char *HomeDir = getenv("HOME"); 17 char strFileName[256] = {0}; 18 19 sprintf(strFileName, _FILE_NAME_FORMAT_, HomeDir, time(NULL)); 20 int fd = open(strFileName, O_RDWR|O_CREAT, 0666); 21 if (fd < 0) 22 { 23 perror("open err"); 24 exit(1); 25 } 26 close(fd); 27 } 28 29 int main() 30 { 31 //1. 創建子進程,父進程退出 32 pid_t pid = fork(); 33 if (pid > 0) 34 { 35 exit(1); 36 } 37 //2. 當會長 38 setsid(); 39 //3. 設置掩碼 40 umask(0); 41 //4. 切換目錄 42 chdir(getenv("HOME")); 43 //5. 關閉文件描述符 44 //close(0); 45 //close(1); 46 //close(2); 47 //6. 執行核心邏輯 48 struct itimerval myit = {{60, 0}, {1, 0}}; 49 setitimer(ITIMER_REAL, &myit, NULL); 50 51 struct sigaction act; 52 act.sa_flags = 0; 53 sigemptyset(&act.sa_mask); 54 act.sa_handler = touchfile; 55 56 sigaction(SIGALRM, &act, NULL); 57 58 while(1) 59 { 60 //每隔一分鍾在家目錄下創建文件 61 sleep(1); 62 } 63 //7. 退出 64 return 0; 65 }
擴展了解:
通過nohup指令也可以達到守護進程創建的效果
nohup cmd [>> 1.log] &
nohup指令會讓cmd收不到SIGHUP信號
& 代表后台運行