首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【linux学习指南】SIGCHLD信号

【linux学习指南】SIGCHLD信号

作者头像
学习起来吧
发布2024-12-31 09:38:00
发布2024-12-31 09:38:00
3270
举报
文章被收录于专栏:学习C/++学习C/++

📝SIGCHLD信号

进程⼀章讲过⽤waitwaitpid函数清理僵⼫进程,⽗进程可以阻塞等待⼦进程结束,也可以⾮阻塞地查询是否有⼦进程结束等待清理(也就是轮询的⽅式)。采⽤第⼀种⽅式,⽗进程阻塞了就不能处理⾃⼰的⼯作了;采⽤第⼆种⽅式,⽗进程在处理⾃⼰的⼯作的同时还要记得时不时地轮询⼀下,程序实现复杂。

其实,⼦进程在终⽌时会给⽗进程发SIGCHLD信号,该信号的默认处理动作是忽略,⽗进程可以⾃定义SIGCHLD信号的处理函数,这样⽗进程只需专⼼处理⾃⼰的⼯作,不必关⼼⼦进程了,⼦进程终⽌时会通知⽗进程,⽗进程在信号处理函数中调⽤wait清理⼦进程即可。

请编写⼀个程序完成以下功能:⽗进程fork出⼦进程,⼦进程调⽤exit(2)终⽌,⽗进程⾃定义SIGCHLD信号的处理函数,在其中调⽤wait获得⼦进程的退出状态并打印。

事实上,由于UNIX的历史原因,要想不产⽣僵⼫进程还有另外⼀种办法:⽗进程调⽤sigactionSIGCHLD的处理动作置为SIG_IGN,这样fork出来的⼦进程在终⽌时会⾃动清理掉,不会产⽣僵⼫进程,

也不会通知⽗进程。系统默认的忽略动作和⽤⼾⽤sigaction函数⾃定义的忽略通常是没有区别的,但这是⼀个特例。此⽅法对于Linux可⽤,但不保证在其它UNIX系统上都可⽤。请编写程序验证这样做不会产⽣僵⼫进程。

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

void handler(int sig)
{
    pid_t id;
    while ((id = waitpid(-1, NULL, WNOHANG)) > 0)
    {
        printf("wait child success: %d\n", id);
    }
    printf("child is quit! %d\n", getpid());
}

int main()
{
    signal(SIGCHLD, handler);
    pid_t cid;
    if ((cid = fork()) == 0)
    { // child
        printf("child : %d\n", getpid());
        sleep(3);
        exit(1);
    }
    while (1)
    {
        printf("father proc is doing some thing!\n");
        sleep(1);
    }
    return 0;
}

查看进程代码

代码语言:javascript
复制
while true; do   
    ps -ajx | grep sig  
    ps -ajx | head -1  
    sleep 1  
done
  1. 信号处理函数 handler
代码语言:javascript
复制
void handler(int sig)
{
    pid_t id;
    while ((id = waitpid(-1, NULL, WNOHANG)) > 0)
    {
        printf("wait child success: %d\n", id);
    }
    printf("child is quit! %d\n", getpid());
}
  • 这是一个自定义的信号处理函数,用于处理特定的信号(在程序中注册它来处理 SIGCHLD 信号)。当接收到相应信号时,这个函数就会被调用执行。
  • 函数接收一个整型参数 sig,代表接收到的信号编号,不过在这个函数内部其实并没有使用这个参数来做不同的分支处理,它主要关注的是处理子进程退出的情况。
  • 在函数内部有一个 while 循环,循环条件是 (id = waitpid(-1, NULL, WNOHANG)) > 0
    • waitpid 函数用于等待子进程状态改变并获取相关信息,这里的参数 -1 表示等待任意子进程(如果指定具体的子进程ID,就只会等待那个特定的子进程);NULL 作为第二个参数表示不关心子进程的终止状态信息(如果想获取具体状态,可以传入相应的指针来接收状态值);WNOHANG 是一个宏,它使得 waitpid 函数在没有已终止的子进程可等待时立即返回,而不是阻塞等待。
    • 只要 waitpid 调用返回的值大于 0,就意味着成功等到了一个已终止的子进程,此时进入循环体,通过 printf 打印出等待到的子进程的进程ID(printf("wait child success: %d\n", id);)。这个循环可以连续处理多个同时终止的子进程(如果存在这种情况)。
  • 循环结束后(也就是没有已终止的子进程可等待了),通过 printf 打印出当前进程(也就是父进程)的进程ID,表示子进程已经退出完毕,当前提示信息所在的进程(父进程)还在运行(printf("child is quit! %d\n", getpid());)。
  1. main 函数部分
代码语言:javascript
复制
int main()
{
    signal(SIGCHLD, handler);
    pid_t cid;
    if ((cid = fork()) == 0)
    { // child
        printf("child : %d\n", getpid());
        sleep(3);
        exit(1);
    }
    while (1)
    {
        printf("father proc is doing some thing!\n");
        sleep(1);
    }
    return 0;
}
  • signal(SIGCHLD, handler);:这行代码使用 signal 函数来注册信号处理函数。SIGCHLD 是一个信号,它在子进程状态发生改变(比如子进程终止、暂停、继续等情况)时会发送给父进程。这里将 SIGCHLD 信号和自定义的 handler 函数关联起来,意味着当父进程收到 SIGCHLD 信号时,就会调用 handler 函数来处理相应情况。
  • pid_t cid; 定义了一个 pid_t 类型的变量 cid,用于存储后续 fork 函数返回的进程ID值。
  • if ((cid = fork()) == 0) 这是一个关键的条件判断语句,通过 fork 函数创建子进程:
    • fork 函数在父进程中返回新创建的子进程的进程ID(大于 0),在子进程中返回 0,如果创建子进程失败则返回 -1。这里判断 cid 是否等于 0,如果等于 0,说明当前代码执行的是子进程的逻辑分支。
    • 在子进程的逻辑中(也就是 if 条件成立的代码块):
      • 首先通过 printf 打印出自己的进程ID(printf("child : %d\n", getpid());),方便后续观察确认。
      • 然后调用 sleep 函数让子进程休眠 3 秒,模拟子进程执行一些任务的时间消耗。
      • 最后通过 exit 函数终止子进程,并返回状态值 1
  • 对于父进程(也就是 if 条件不成立,cid 大于 0 的情况),进入到后面的 while (1) 无限循环中:
    • 在这个循环里,每次循环都会通过 printf 打印出 father proc is doing some thing!,表示父进程正在进行一些操作,然后调用 sleep 函数暂停 1 秒再进入下一次循环,以此模拟父进程持续进行一些任务的过程。
  • 不过要注意的是,代码最后的 return 0; 其实在当前逻辑下是无法执行到的,因为父进程进入了一个无限循环,正常情况下不会跳出循环去执行这行返回语句。

makefile文件:

代码语言:javascript
复制
sig:sig.c 
	gcc -o sig sig.c  
.PHONY:clean 
clean: 
	rm -f sig
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📝SIGCHLD信号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档