【Linux系统编程学习】父进程捕获SIGCHLD信号以处理僵尸进程

在这里插入图片描述
配合之前说过的sigaction函数和waitpid函数,我们可以解决子进程变成僵尸进程的问题。

先看如下示例程序:

#include <sys/time.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>int main() {pid_t pid;int i;// 循环创建20个子进程for(i = 0; i < 20; ++i) {pid = fork();if(pid == 0) {break;}}if(pid > 0) {while(1) {printf("parent pid : %d \n", getpid());sleep(2);}} else if(pid == 0) {printf("child pid : %d \n", getpid());}return 0;
}

创建20个子进程,同时父进程不结束,使用ps -aux查看进程状态:
在这里插入图片描述
可以看到20个子进程均成为僵尸进程,如下示例程序利用子进程结束后给父进程发送SIGCHLD信号这一机制,解决此问题:

#include <sys/time.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <wait.h>void catchFunc(int signo) {// 处理僵尸子进程printf("捕捉到了信号:%d \n", signo);while(1) {// 用waitpid回收任意子进程, 并设置为非阻塞int ret = waitpid(-1, NULL, WNOHANG);if(ret > 0) {// 说明回收了一个子进程printf("子进程%d已被回收\n", ret);} else if(ret == 0) {// 表明当前有子进程在运行break;} else if(ret == 1){// 表明以及没有子进程了break;}}
}int main() {// 提前设置好阻塞信号集,阻塞SIGCHLD,因为有可能子进程很快结束,父进程还没有注册完信号捕捉sigset_t set;sigemptyset(&set);sigaddset(&set, SIGCHLD);sigprocmask(SIG_BLOCK, &set, NULL);pid_t pid;int i;// 循环创建20个子进程for(i = 0; i < 20; ++i) {pid = fork();if(pid == 0) {break;}}if(pid > 0) {// 父进程来捕获SIGCHLD信号,在自定义捕获函数中释放子进程的内核资源。struct sigaction act;act.sa_flags = 0;act.sa_handler = catchFunc;sigemptyset(&act.sa_mask);int res = sigaction(SIGCHLD, &act, NULL);if(res == -1) {perror("sigaction error");exit(1);}// 注册完信号捕捉以后,解除阻塞sigprocmask(SIG_UNBLOCK, &set, NULL);while(1) {//printf("parent pid : %d \n", getpid());sleep(2);}} else if(pid == 0) {//printf("child pid : %d \n", getpid());}return 0;
}

运行结果如下:
在这里插入图片描述
这段程序有几个需要注意的细节:

  1. 有人可能会产生误解:觉得应该每个子进程死亡以后,发送SIGCHLD信号给父进程,然后父进程中捕获到SIGCHLD信号,内核调用一次catchFunc来回收该子进程,循环往复… 其实这是有问题的,因为一个子进程死亡后,在执行catchFunc的过程中又有多个子进程死亡而发送SIGCHLD信号,我们之前说过,某个信号捕捉函数执行期间,该信号自动被屏蔽,所以紧接着死亡的这些子进程就无法被回收。而解决这一问题的方法是:在catchFunc捕捉函数中int ret = waitpid(-1, NULL, WNOHANG);设置为非阻塞,并加上死循环,让其一次调用多次回收,知道当前没有死亡的子进程了,就break出来继续执行父进程的代码。
  2. 应当提前设置好阻塞信号集,阻塞SIGCHLD,因为有可能子进程很快结束,父进程还没有注册完信号捕捉。等到父进程执行int res = sigaction(SIGCHLD, &act, NULL);完成SIGCHLD信号捕捉的注册后,再使用sigprocmask(SIG_UNBLOCK, &set, NULL);解除阻塞。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux系统编程学习】Linux线程控制原语

此为牛客Linux C课程笔记。 0. 关于线程 注意&#xff1a;LWP号和线程id不同&#xff0c; LWP号是CPU分配时间片的依据&#xff0c;线程id是用于在进程内部区分线程的。 1. 线程与进程的区别 对于进程来说&#xff0c;相同的地址(同一个虚拟地址)在不同的进程中&#xff0c;反…

【Linux网络编程学习】预备知识(网络字节序、IP地址转换函数、sockaddr数据结构)

此为牛客Linux C课程和黑马Linux系统编程笔记。 1. 网络字节序 我们已经知道&#xff0c;内存中的多字节数据相对于内存地址有大端和小端之分。 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。网络数据流同样有大端小端之分&#xff0c;那么如何定义网络数…

【Linux网络编程学习】socket API(socket、bind、listen、accept、connect)及简单应用

此为牛客Linux C课程和黑马Linux系统编程笔记。 1. 什么是socket 所谓 socket&#xff08;套接字&#xff09;&#xff0c;就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。 一个套接字就是网络上进程通信的一端&#xff0c;提供了应用层进程利用网络协议交换…

【Linux网络编程学习】使用socket实现简单服务器——多进程多线程版本

此为牛客Linux C课程和黑马Linux系统编程笔记。 1. 多进程版 1.1 思路 大体思路与上一篇的单进程版服务器–客户端类似&#xff0c;都是遵循下图&#xff1a; 多进程版本有以下几点需要注意&#xff1a; 由于TCP是点对点连接&#xff0c;服务器主进程连接了一个客户端以后…

【Linux网络编程学习】I/O多路复用——select和poll

此为牛客Linux C课程和黑马Linux系统编程笔记。 0. I/O多路复用 所谓I/O就是对socket提供的内存缓冲区的写入和读出。 多路复用就是指程序能同时监听多个文件描述符。 之前的学习中写了多进程和多线程版的简单服务器模型&#xff0c;但是有个问题&#xff1a;每次新来一个客…

【Linux网络编程学习】I/O多路复用——epoll

此为牛客Linux C课程和黑马Linux系统编程笔记。 1. 关于epoll epoll是Linux下多路复用IO接口select/poll的增强版本&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率&#xff0c;因为它会复用文件描述符集合来传递结果而不用迫使开发者每次…

【Linux网络编程学习】阻塞、非阻塞、同步、异步以及五种I/O模型

文章目录1. 基本概念1.1 阻塞与非阻塞1.2 同步与异步1.3 为什么没有“异步阻塞”2. 五种IO模型2.1 阻塞 blocking2.2 非阻塞 non-blocking2.3. IO复用&#xff08;IO multiplexing&#xff09;2.4 信号驱动&#xff08;signal-driven&#xff09;2.5 异步&#xff08;asynchron…

LRU缓存 数据结构设计(C++)

做LeetCode第146题LRU缓存&#xff0c;觉得收获不小&#xff0c;特此记录。 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存。int get(int key) 如果关键…

STM32时钟树解析

本人之前其实也用STM32做过一些小东西&#xff0c;但因为时钟的初始化一般是直接在SystemInit时钟系统初始化函数里直接配置为72MHz&#xff0c;所以对于STM32的时钟框图并没有怎么理会&#xff0c;今天刚好有空就重新看了一下并写一篇博客记录一下吧&#xff0c;以免以后又忘了…

S3C2440时钟体系

S3C2440在默认情况下&#xff0c;整个系统全靠一个12MHz的外部晶振提供频率来工作运行的&#xff0c;也就是说CPU、内存、UART、ADC等所有需要用到时钟频率的硬件都工作在12MHz下&#xff0c;但是通过查阅芯片手册我们知道CPU时钟最高可为400MHZ&#xff0c;那么怎么设置时钟让…

关于MCU、CPU扩展SDRAM的一个小知识

像上图这种硬件电路图上的16个数据位和我们在初始化SDRAM的时候设置的16位数据位宽是指我们读写SDRAM的时候可以同时读写16个数据位&#xff0c;数据线越多肯定越快&#xff0c;但是数据线也不可能无限增加&#xff0c;我们在程序里是可以读写8位&#xff0c;16位&#xff0c;3…

S3C2440扩展SDRAM

本文主要目的是记录一下S3C2440扩展SDRAM的一些知识&#xff0c;方便以后查阅。 通过查阅手册我们知道&#xff0c;2440有8个可以用来扩展内存的BANK&#xff0c;其中第6和第7还可用来扩展SDRAM 下面我们来看一下2440扩展SDRAM需要设置哪些寄存器。 一、BWSCON寄存器 该寄存器…

汇编语言的相对跳转和绝对跳转以及反汇编代码解析

上图第一行的b1 main为相对跳转&#xff0c;即跳转到pcoffset,其中pc为当前pc值&#xff0c;offset可以理解为偏移地址&#xff0c;也就是根据当前所在地址加上偏移地址实现跳转&#xff0c;为相对跳转。 我们来看看它的反汇编代码 上图清除完bss区后使用b1指令跳转到30000668…

韦东山嵌入式第一期14课第004节_und异常模示程序示例_P笔记

本节课的第一个程序韦老师是想让大家见识一下未定义异常&#xff0c;而第二个程序是对第一个程序进行改进&#xff0c;防止在某些条件下执行不了&#xff0c;下面就来讲一下第2个程序改进了哪些地方并且有什么用。 程序在此路径中&#xff1a;源码文档图片\源码\源码_20180321…

关于NOR FLASH地址左右移的问题

问题引入&#xff1a;不知道你会不会有这样的疑问&#xff1a;为什么在发送解锁命令时&#xff0c;我们不用右移一位&#xff0c;而发送扇区地址时却要右移一位&#xff08;nor_cmd函数内部已经左移一位&#xff09;&#xff0c;这里先补充说明一下说明是cpu角度和nor角度&…

在linux下利用ls命令进行模糊查找

如上图&#xff0c;我们当前路径下有三个文件&#xff0c;分别为helloworld.c以及helloworld和1.c&#xff0c;直接输入命令ls则显示所有文件&#xff0c;我们可以利用ls 加*的方向进行模糊查找。 输入ls 目录名 形式的命令行&#xff0c;则是对该目录名下的文件全部进行显示&a…

Makefile常见符号意思

Makefile里有许许多多的符号&#xff0c;对于新手而言如果没有经常使用&#xff0c;就很容易忘记&#xff0c;所以我把常见符号的意义写下&#xff0c;方便日后忘记查询。本文章会持续更新... 1.$&#xff1a;代表目标&#xff1b;$^代表所有依赖&#xff0c;$^代表第一个依赖。…

Linux下串口通信详解

https://blog.csdn.net/u010783226/article/details/73369097

fstat、stat和lstat 区别

nt fstat(int filedes, struct stat *buf); int stat(const char *path, struct stat *buf); int lstat(const char *path, struct stat *buf); 一眼就能看出来fstat的第一个参数是和另外两个不一样的&#xff0c;fstat区别于另外两个系统调用的地方在于&#xff0c;fstat系…