二十七、Linux 进程与信号---进程组和组长进程
27.1 进程组
27.1.1 进程组介绍
- 进程组为一个或多个进程的集合
- 进程组可以接受同一终端的各种信号,同一个信号发送进程组等于发送给组中的所有进程
- 每个进程组有唯一的进程组 ID
- 进程组的消亡要等到组中所有的进程结束
- 进程组的消亡:kill 发送信号给进程组
- kill -9 -进程组号
27.1.2 进程组 ID 获取--- getpgrp 和 getpgid
(1)getpgrp --- 获取调用进程的进程组ID
#include <unistd.h>
pid_t getpgrp(void);
- 函数说明
- getpgrp()用来取得目前进程所属的组识别码。此函数相当于调用 getpgid(0);
- 返回值
- 返回目前进程所属的组识别码。
(2)getpgid --- 获取 pid 所在进程组的 ID
#include <unistd.h>
pid_t getpgid( pid_t pid);
- 函数说明
- getpgid()用来取得参数 pid 指定进程所属的组识别码。如果参数 pid 为0,则会取得目前进程的组识别码。
- 返回值
- 执行成功则返回组识别码,如果有错误则返回 -1 ,错误原因存于 errno 中。
- 错误代码
- ESRCH 找不到符合参数 pid 指定的进程。
- getpgid(getpid()) 获取指定进程的进程组
- getpgid(0) 获取当前进程的进程组
27.2 组长进程
27.2.1 概念
- 每个进程组可以有个组长进程,组长进程的ID就是进程组的ID
- 组长进程可以创建进程组以及该组中的进程
- 进程组的创建从第一个进程(组长进程)加入开始
- 进程组的组号取第一个加入组的进程(组长进程)编号
27.2.2 设置进程组 ID--- setpgid(创建进程组或将指定进程加入到指定的进程组中)
#include <unistd.h>
int setpgid(pid_t pid,pid_t pgid);
- 函数说明
- setpgid() 将参数 pid 指定进程所属的组 ID 设为参数 pgid 指定的组 ID。如果参数 pid 为 0 ,则会用来设置目前进程的组识别码,如果参数 pgid 为0,则会以目前进程的进程 ID 来取代。
- 函数功能:将进程加入到指定的进程组中, pid 为进程号, pgid 为组号
- 返回值
- 执行成功则返回组 ID,如果有错误则返回-1,错误原因存于 errno 中。
- 错误代码
- EINVAL 参数 pgid 小于0。
- EPERM 进程权限不足,无法完成调用。
- ESRCH 找不到符合参数 pid 指定的进程。
27.3 例子
27.3.1 构建进程扇组
构建一个进程扇,要求每两个进程为一个进程组,如下所示

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h> int main(void)
{ //创建进程组,组长进程为父进程
setpgid(getpid(), getpid()); /* 组1 */
// 或 pid_t group1 = getpgid(0);
pid_t group1 = getpgid(getpid());
pid_t group2; int i = ;
pid_t pid; for(; i < ; i++) {
pid = fork();
if(pid < ) {
perror("fork error`");
exit();
} else if(pid > ) {//父进程
//父进程中执行和子进程相同的操作
if(i == ) {
//要注意fork 在父进程中返回的是子进程的 pid
setpgid(pid, group1);
} //第二个子进程作为组长进程,要创建进程组
if(i == ) {
setpgid(pid, pid);
group2 = getpgid(pid);
} if(i == ) {
//第三个子进程加入到 group2
setpgid(pid, group2);
}
} else {//子进程
//将第一个子进程加入到 group1
if(i == ) {
setpgid(getpid(), group1);
} //第二个子进程作为组长进程,要创建进程组
if(i == ){
setpgid(getpid(), getpid());
group2 = getpgid(getpid());
} if(i == ) {
setpgid(getpid(), group2);
} //因为是进程扇,每一个子进程要退出循环
//父进程继续循环 fork
break;
}
} printf("pid: %d, ppid: %d, pgid: %d\n", getpid(), getppid(), getpgid()); //防止僵尸进程产生
for(i = ; i < ; i++) {
wait();
} exit();
}
编译运行:

27.3.2 构建进程链组

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h> int main()
{
//创建进程组1,父进程为组长进程
setpgid(getpid(), getpid());
pid_t group1 = getpgid(getpid()); pid_t pid;
int i = ;
for(;i < ; i++) {
pid = fork();
if(pid < ) {
perror("fork error");
exit();
} else if(pid > ) {
if(i == ) {
//创建进程组2,第一个子进程作为组长进程
setpgid(pid, pid);
} if(i == ) {
//将第二个子进程加入到 group1中
setpgid(pid, group1);
} // 在进程链中,父进程操作完退出循环
break;
} else if(pid == ) { if(i == ) {
//创建进程组2,第一个子进程作为组长进程
setpgid(getpid(), getpid());
} if(i == ) {
//将第二个子进程加入到 group1中
setpgid(getpid(), group1);
}
}
} printf("pid: %d, ppid: %d, pgid: %d\n", getpid(), getppid(), getpgid()); //防止僵尸进程产生
for(i = ; i < ; i++) {
wait();
} return ;
}
编译运行:

27.3.3 进程组删除
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h> int main(void)
{
//创建进程组,父进程作为组长进程
setpgid(getpid(), getpid()); pid_t pid = fork();
if(pid < ){
perror("fork error");
exit();
} else if(pid > ) {
//将子进程加入到父进程所在的组
setpgid(pid, getpgid(getpid()));
} else {
//将子进程加入到父进程所在的组
setpgid(getpid(), getpgid(getppid()));
} printf("pid: %d, ppid: %d, pgid: %d\n", getpid(), getppid(), getpgid()); pause();//进程暂停,等待信号 return ;
}
编译运行:
(1)杀掉进程组


(2)杀掉父进程


杀掉父进程后,子进程依然存在,只不过变成了孤儿进程,并被2323进程所领养
(3)杀掉子进程


子进程变为了僵尸进程,子进程被杀死,但是父进程并没有回收子进程。
杀掉父进程:

僵尸进程被回收
27.3.4 子进程不加入父进程组中
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h> int main(void)
{
//创建进程组,父进程作为组长进程
setpgid(getpid(), getpid()); pid_t pid = fork();
if(pid < ){
perror("fork error");
exit();
} else if(pid > ) {
//将子进程加入到父进程所在的组
setpgid(pid, getpgid(getpid()));
} else {
//将子进程加入到父进程所在的组
//setpgid(getpid(), getpgid(getppid()));
} printf("pid: %d, ppid: %d, pgid: %d\n", getpid(), getppid(), getpgid()); pause();//进程暂停,等待信号 return ;
}
编译运行:


没有将子进程加入到组中,依然将子进程加入到父进程组了。
注意:从 shell 上启动一个父进程,然后从父进程上创建若干个子进程,默认情况下都加入到父进程所在的组中,组号就是父进程的 PID,组长就为父进程。
二十七、Linux 进程与信号---进程组和组长进程的更多相关文章
- 二十八、Linux 进程与信号---前台进程组
28.1 介绍 28.1.1 概念 自动接受终端信号的组称为前台进程组 在终端通过 ctrl + c 等动作产生的信号首先被前台进程组接受 在 shell 启动的若干个进程组默认是父进程所在的组为前台 ...
- (二十七)Linux的inode的理解
一.inode是什么? 理解inode,要从文件储存说起. 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存512字节(相当于0.5KB). 操作系统 ...
- linux 下信号处理命令trap && linux下各种信号的意义
1.用途说明 trap是一个shell内建命令,它用来在脚本中指定信号如何处理.比如,按Ctrl+C会使脚本终止执行,实际上系统发送了SIGINT信号给脚本进程,SIGINT信号的默认处理方式就是退出 ...
- 第9章 Linux进程和信号超详细分析
9.1 进程简单说明 进程是一个非常复杂的概念,涉及的内容也非常非常多.在这一小节所列出内容,已经是我极度简化后的内容了,应该尽可能都理解下来,我觉得这些理论比如何使用命令来查看状态更重要,而且不明白 ...
- Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存
Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> ...
- 三十一、Linux 进程与信号——SIGCHLD 信号、kill和raise函数以及alarm函数
31.1 SIGCHLD 信号 子进程状态发生变化(子进程结束)产生该信号,父进程需要使用 wait 调用来等待子进程结束并回收它. 避免僵尸进程 #include <stdio.h> # ...
- Linux/UNIX之信号(1)
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/walkerkalr/article/details/24462723 信号(1) 信号是软件中断.每 ...
- 25 Linux中的信号
Linux中的信号 信号是进程在运行过程中,由自身产生或由进程外部发过来的消息(事件).每个信号用一个整型常量宏表示,以SIG开头,比如SIGCHLD.SIGINT等,它们在系统头文件中定义,也可以通 ...
- Linux 两组信号对比(关闭和停止进程信号)
之前看信号的时候,没有太注意不同信号的对比.今天再次看到的时候,突然感觉对一些信号,非常相似,乃至非常容易混淆.今天周末就抽空总结一下. 一.关闭进程信号 常见的4中关闭进程信号是SIGKILL,SI ...
随机推荐
- cf1000E We Need More Bosses (tarjan缩点+树的直径)
题意:无向联通图,求一条最长的路径,路径长度定义为u到v必须经过的边的个数 如果把强联通分量都缩成一个点以后,每个点内部的边都是可替代的:而又因为这是个无向图,缩完点以后就是棵树,跑两遍dfs求直径即 ...
- Java collection 容器
http://www.codeceo.com/article/java-container-brief-introduction.html Java实用类库提供了一套相当完整的容器来帮助我们解决很多具 ...
- A1142. Maximal Clique
A clique is a subset of vertices of an undirected graph such that every two distinct vertices in the ...
- python中的单例模式、元类
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- 配置ssl
1.配置 <Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000&q ...
- Linux之LVS 20180708
LVS负载均衡项目,是Linux开源项目中专门用于负载均衡的,主要应用在client访问server时,通过LVS来分配对应的server来响应client的请求.client访问时,都是访问的LVS ...
- java中不定参数的使用
https://www.cnblogs.com/xy-hong/p/7192796.html
- mysql写shell小技巧
set global general_log=on;set @file=0x653A2F2F7777772F2F782E706870;set global general_log_file=@file ...
- 流明(lux)和坎德拉;
流明是光照度: 坎德拉是光强: 流明是光通量的单位, cd是光强单位 光强是单位立体角的光通量: 照度是单位面积的光通量: 尼特是亮度单位 1尼特 = 1CD/m^2: 1 lx = 1 流明每 ...
- Advertising.csv
TV,radio,newspaper,sales1,230.1,37.8,69.2,22.12,44.5,39.3,45.1,10.43,17.2,45.9,69.3,9.34,151.5,41.3, ...