一、進程組
1. 進程組
(1)進程組,也稱之為作業,BSD與1980年前后向UNIX中增加的一個新特性,代表一個或多個進程的集合。每個進程都屬于一個進程組,在waitpid函數和kill函數的參數中都曾經使用到,操作系統設計的進程組的概念,是為了簡化對多個進程的管理。
當父進程創建子進程的時候,默認子進程與父進程屬于同一個進程組,進程組ID等于進程組第一個進程ID(組長進程)。所以,組長進程標識:其進程組ID等于其進程ID.
組長進程可以創建一個進程組,創建該進程組的進程,然后終止,只要進程組中有一個進程存在,進程組就存在,與組長進程是否終止無關。
(2)kill發送給進程組
使用?kill -n -pgid?可以將信號 n 發送到進程組 pgid 中的所有進程。例如命令?kill -9 -4115?表示殺死進程組 4115 中的所有進程。
2. getpgid、getpgrp函數原型:
pid_t getpgrp(void);
pid_t getpgid(pid_t pid);
分析:
- 函數1:獲取當前進程的進程組ID
- 函數2:如果pid = 0,那么該函數作用和getpgrp一樣。
?
3. setpgid函數函數原型:改變進程默認所屬的進程組,通常可用來加入一個現有的進程組或新進程組。
int setpgid(pid_t pid, pid_t pgid);
分析:將參數1對應的進程,加入參數2對應的進程組中。
注意:
- 如改變子進程為新進程組,用fork后,exec前。
- 權級問題:非root進程只能改變自己創建的子進程,或有權限操作的進程。
4. 測試代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{pid_t pid;if ((pid = fork()) < 0) {perror("fork");exit(1);}else if (pid == 0) //子進程{printf("child PID = %d\n", getpid());printf("child Group ID = %d\n", getpgid(0)); //返回組idsleep(7);printf("-------Group ID of child id change to %d\n", getpgid(0));exit(0);}else if (pid > 0) //父進程{sleep(1);setpgid(pid, pid); //讓子進程自立門戶,成為進程組組長,以它的pid為進程組 id sleep(13);printf("\n");printf("parent PID = %d\n", getpid());printf("parent's parent PID = %d\n", getppid());printf(" parent Group ID = %d\n", getpgid(0));sleep(5);setpgid(getpid(), getppid()); //改變父進程組id為父進程的父進程printf("\n-------Group ID of parent is change to %d\n", getpgid(0));while (1);}return 0;
}
輸出結果:
?
二、進程組的應用
1. 實驗一:
題目:利用進程扇完成一個小實驗。該進程扇有 1 個父進程和 3 個子進程,我們希望達到圖 1 中的效果,即將進程 0 (父進程)和進程 1 設置成一組,假設為組 1,將進程 2 和 進程 3 設置成另一個組,假設為組 2. 另外,我們希望進程 0 和進程 2 分別是這兩個組的組長。? ?
?
1. 測試代碼:
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>int main()
{int pid, i;int group1, group2;// 設置父進程(進程 0)為組長 setpgid(getpid(), getpid());group1 = getpgid(getpid());for (i = 1; i <= 3; ++i) {pid = fork();if (pid == 0) child{if (i == 1) {// 如果 group1 根本不存在,就會出問題。// 比如進程 0 已經運行結束。setpgid(getpid(), group1);}else if (i == 2) {setpgid(getpid(), getpid());group2 = getpgid(getpid());}else if (i == 3) {// 試想如果進程 2 還沒運行,進程 3 先運行了,// 這時候 group2 還未進行設置,這里就會有問題。// 或者進程 2 已經結束,那進程 3 的設置也會失敗setpgid(getpid(), group2);}break;}else if (pid < 0) {perror("fork");return -1;}}printf("進程 %d, pid: %d -> ppid: %d, pgid: [%d], (%s)\n", i % 4, getpid(), getppid(), getpgid(getpid()), strerror(errno));while (1) sleep(1);return 0;
}
輸出結果:?
測試代碼:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>int main(void)
{setpgid(getpid(), getpid());pid_t group1 = getpgid(getpid());pid_t group2;int i = 0;for(; i < 3; ++i){pid_t pid = fork();if(pid < 0){perror("fork error");exit(1);}else if(pid > 0){// parent processif(i == 0)setpgid(pid, group1);if(i == 1){setpgid(pid, pid);group2 = getpgid(pid);}if(i == 2)setpgid(pid, group2);}else{// child processif(i == 0)setpgid(getpid(), group1);if(i == 1){setpgid(getpid(), getpid());group2 = getpgid(getpid());}if(i == 2)setpgid(getpid(), group2);break;}}printf("pid:%d, ppid:%d, pgid:%d\n", getpid(), getppid(), getpgid(getpid()));for(int i = 0; i < 3; ++i)wait(0);return 0;
}
輸出結果:
2. 實驗二:
題目:利用進程扇完成一個小實驗。該進程扇有 1 個父進程和 3 個子進程,我們希望達到圖 1 中的效果,即將進程 0 (父進程)和進程 1 設置成一組,假設為組 1,將進程 2 和 進程 3 設置成另一個組,假設為組 2. 另外,我們希望進程 0 和進程 2 分別是這兩個組的組長。
測試代碼:
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>int main()
{int pid, i;int group1, group2;setpgid(getpid(), getpid());group1 = getpgid(getpid());for (i = 0; i < 3; ++i) {pid = fork();if (pid > 0) //父進程{if (i == 0) {setpgid(pid, pid);group2 = getpgid(pid);} else if (i == 1) {setpgid(pid, group1);} else if (i == 2){setpgid(pid, group2);} break;} else if (pid == 0) //子進程{if (i == 0) {setpgid(getpid(), getpid());group2 = getpgid(getpid());} else if (i == 1) {setpgid(getpid(), group1);} else if (i == 2) {setpgid(getpid(), group2);} } else if (pid < 0) {perror("fork");return -1; } }printf("進程 %d, pid: %d -> ppid: %d, pgid: [%d]\n", i, getpid(), getppid(), getpgid(getpid()));while(1) sleep(1);return 0;
}
?