进程组
- 一个或多个进程的集合;
- 可以接受同一终端的各种信号,同一信号发送进程组就等于发送给组中的所有进程;
- 每个进程组有一个唯一的进程组ID;
- 进程组的消亡要等到组中所有的进程结束;
- kill发送给进程组:kill -9 -进程组号(不要忘记负号)
#include <unistd.h>
pid_t getpgrp(void);--老版
返回:调用进程的进程组ID;
pid_t getpgid(pid_t pid);--新版
返回:进程pid所在进程组的ID,出错返回-1;
组长进程
- 每个进程组可以有个组长进程,组长进程的ID就是进程组的ID;
- 组长进程可以创建进程组以及该组中的进程;
- 进程组的创建从第一个进程(组长进程)加入开始;
- 进程组的组号取第一个加入组的进程(组长进程)编号;
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
返回:成功返回0,出错返回-1;
功能:将进程加入到指定的进程组中,pid为进程号,pgid为组号;
前台进程组
- 自动接收终端信号的组成为前台进程组;
- 在终端通过Ctrl+C等动作产生的信号首先被前台进程组接受;
- 在shell启动的若干个进程组默认是父进程所在的组为前台进程组;
- 除非是默认,否则都要通过调度才能成为前台进程组;
#include <unistd.h>
pid_t tcgetpgrp(int fd);
返回:若成功返回前台进程组的ID,出错返回-1;
功能:获得前台进程组ID;
int tcsetpgrp(int fd, pid_t pgrpid);
返回:成功返回0,出错返回-1;
功能:使用pgrpid设置前台进程组ID;
fd必须引用该会话的控制终端,0代表当前正在使用的终端;
示例1:创建一个进程扇,并实现如下图所示的进程组。
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.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 process
if(i == 0)
setpgid(pid, group1);
if(i == 1)
{
setpgid(pid, pid);
group2 = getpgid(pid);
}
if(i == 2)
setpgid(pid, group2);
}
else
{
// child process
if(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;
}
注意:1.由于创建的是进程扇,所以每次子进程执行完毕后都需要退出循环;
2.我们需要在父进程与子进程中执行相同的操作,因为我们不知道是父进程先运行还是子进程先运行;
示例2:创建一个进程链,实现如下图所示的进程组。
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
// 创建一个进程链
int main(void)
{
setpgid(getpid(), getpid());//将父进程设置为第一个进程组的组长进程
pid_t group1 = getpgid(getpid());
int i = 0;
for(; i < 2; ++i)
{
pid_t pid = fork();
if(pid < 0)
{
perror("fork error");
exit(1);
}
else if(pid > 0)
{
// parent process
if(i == 0)
{
setpgid(pid, pid);
}
if(i == 1)
{
setpgid(pid, group1);
}
break;
}
else
{
// child process
if(i == 0)
setpgid(getpid(), getpid());
if(i == 1)
setpgid(getpid(), group1);
}
}
printf("pid:%d, ppid:%d, pgid:%d\n", getpid(), getppid(), getpgid(getpid()));
sleep(1);
return 0;
}
程序执行结果如下所示:
示例3:创建两个进程组,并将其中一个设置为前台进程组,如下图所示。
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
setpgid(getpid(), getpid());
pid_t group1 = getpgid(0); // 将当前进程设置为组长进程
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)
{
if(i == 0)
{
setpgid(pid, pid);
group2 = getpgid(pid);
tcsetpgrp(0, group2); // 将该进程组设置为前台进程组
}
if(i == 1)
setpgid(pid, group2);
if(i == 2)
setpgid(pid, group1);
}
else
{
if(i == 0)
{
setpgid(getpid(), getpid());
group2 = getpgid(getpid());
tcsetpgrp(0, group2);
}
if(i == 1)
setpgid(getpid(), group2);
if(i == 2)
setpgid(getpid(), group1);
break;
}
}
printf("pid:%d, ppid:%d, pgid:%d, fore_pgid:%d\n", getpid(), getppid(), getpgid(getpid()), tcgetpgrp(0));
for(int i = 0; i < 3; ++i)
wait(0);
return 0;
}