进程信号的未决状态(pending status)

本文介绍了进程信号中未决状态的概念,即信号产生但尚未传递给进程的状态。当进程对某个信号进行阻塞时,该信号会变为未决状态。通过举例说明,解释了如何通过sigpending()检查未决信号,并讨论了信号处理函数在解除阻塞后的执行情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这两天看了apue有关进程信号的部分,觉得未决状态这个词很是不一般,呵呵。一开始当我看到这个词,我不理解,什么意思呢,读了好几遍。不知道是书里面讲的晦涩难懂,还是脑子越来越不行了,就是没有搞明白。后来看到后面的例子,然后再回想一下,终于清楚了!下面记录一下吧。

定义:信号产生和传递之间的时间间隔内,称此信号是未决的;

简单的说就是:一个已经产生的信号,但是还没有传递给任何进程,此时该信号的状态就称为未决状态。

未决状态信号的产生主要是因为进程对此信号的阻塞。例如为进程产生一个选择为阻塞的信号,而且对该信号的动作是系统默认动作或捕捉该信号,则为该进程将此信号保持为未决状态,直到该进程对此信号解除了阻塞或者对此信号的动作改为忽略。

我们知道,进程的信号屏蔽字规定了要阻塞传送到该进程的信号集。当产生了一个该进程已阻塞的信号,系统会为该进程生成一个信号集(其实该信号集是一直存在的,不是产生阻塞信号时才生成的),并将该阻塞的信号保存在此信号集中即将此信号集对应的信号置为未决状态。可以通过调用sigpending()来返回该信号集。

下面看一个apue上的例子:

#include <stdio.h>

#include <signal.h>
#include <unistd.h>

//信号处理函数
static void sig_quit(int sigo)
{
        printf("caught SIGQUIT...\n");
        //将SIGQUIT信号的处理方式恢复成系统默认的处理动作
        if(signal(SIGQUIT, SIG_DFL) == SIG_ERR)
                printf("can't reset SIGQUIT");
}
int main()
{
        sigset_t newmask, oldmask, pendmask;

        //定义对信号SIGQUIT的捕捉
  
<think>我们正在处理关于Linux中SIGCHLD信号使用示例的查询。用户提供了多个引用,这些引用展示了SIGCHLD信号的不同用法,包括忽略信号以避免僵尸进程、使用信号处理函数回收子进程资源等。根据引用,我们可以总结如下:-引用[1]和[3]展示了将SIGCHLD信号的处理动作设置为忽略(SIG_IGN),这样当子进程退出时,内核会自动回收子进程资源,不会产生僵尸进程。-引用[4]和[5]展示了使用自定义的信号处理函数来捕获SIGCHLD信号,并在处理函数中调用wait或waitpid来回收子进程资源,同时可以获取子进程的退出状态。用户的需求是“LinuxSIGCHLD信号使用示例代码”,因此我们将提供两种常见的使用场景的代码示例:1.忽略SIGCHLD信号,让内核自动回收子进程(简单,无需父进程主动等待)。2.捕获SIGCHLD信号,在信号处理函数中回收子进程资源(可以获取子进程退出状态)。另外,我们需要注意:-在信号处理函数中,应使用异步信号安全的函数。例如,避免使用printf(但为了示例清晰,我们可能会在示例中使用,但会注明)。-使用waitpid时,应使用WNOHANG选项来非阻塞地等待所有已终止的子进程,以防止在信号处理函数中阻塞过久。下面我们将提供两个代码示例,并解释关键部分。###示例1:忽略SIGCHLD信号(自动回收子进程)这种方法适用于父进程不需要知道子进程退出状态的场景。设置SIG_IGN后,子进程退出时,内核会立即回收资源,不会产生僵尸进程。```c#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<signal.h>intmain(){//将SIGCHLD信号的处理动作设置为忽略signal(SIGCHLD,SIG_IGN);//也可以使用sigactionpid_tpid=fork();if(pid<0){perror("fork");exit(1);}if(pid==0){//子进程printf("Childprocess(PID=%d)isrunning.\n",getpid());sleep(3);printf("Childprocessexiting.\n");exit(0);}else{//父进程printf("Parentprocess(PID=%d)iswaiting...\n",getpid());//父进程不需要显式等待子进程,因为SIGCHLD被忽略,内核会自动回收sleep(5);//等待足够时间让子进程退出printf("Parentprocessexiting.\n");}return0;}```**解释**:-使用`signal(SIGCHLD,SIG_IGN)`设置忽略SIGCHLD信号。-子进程运行3秒后退出,父进程睡眠5秒以确保子进程退出。-由于设置了忽略,子进程退出后不会成为僵尸进程。###示例2:捕获SIGCHLD信号并回收子进程这种方法允许父进程信号处理函数中回收子进程并获取其退出状态。```c#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<signal.h>#include<sys/wait.h>#include<sys/types.h>//SIGCHLD信号处理函数voidsigchld_handler(intsigno){intstatus;pid_tpid;//使用非阻塞的waitpid循环回收所有已终止的子进程while((pid=waitpid(-1,&status,WNOHANG))>0){if(WIFEXITED(status)){printf("Childprocess(PID=%d)exitedwithstatus%d.\n",pid,WEXITSTATUS(status));}elseif(WIFSIGNALED(status)){printf("Childprocess(PID=%d)wasterminatedbysignal%d.\n",pid,WTERMSIG(status));}}}intmain(){//设置信号处理函数structsigactionsa;sa.sa_handler=sigchld_handler;sigemptyset(&sa.sa_mask);sa.sa_flags=SA_RESTART|SA_NOCLDSTOP;//SA_RESTART:重启被中断的系统调用;SA_NOCLDSTOP:不接收子进程停止的信号if(sigaction(SIGCHLD,&sa,NULL)==-1){perror("sigaction");exit(1);}//创建多个子进程for(inti=0;i<5;i++){pid_tpid=fork();if(pid<0){perror("fork");exit(1);}elseif(pid==0){//子进程printf("Childprocess(PID=%d)started.\n",getpid());sleep(i+1);//子进程睡眠不同时间printf("Childprocess(PID=%d)exiting.\n",getpid());exit(0);}}//父进程持续运行while(1){printf("Parentprocess(PID=%d)isrunning...\n",getpid());sleep(10);}return0;}```**解释**:-使用`sigaction`设置信号处理函数(比`signal`更安全可控)。-在信号处理函数`sigchld_handler`中,使用`waitpid`循环回收所有已终止的子进程。`waitpid`使用`WNOHANG`选项,表示非阻塞等待,这样一次信号到来时可以回收多个子进程。-`sa_flags`设置为`SA_RESTART`,使得在信号处理函数返回后,被中断的系统调用(如sleep)能够自动重启;`SA_NOCLDSTOP`表示只接收子进程终止的信号,而不接收子进程停止(如暂停)的信号。-父进程创建5个子进程,每个子进程睡眠不同时间后退出。父进程在一个循环中运行,当子进程退出时,信号处理函数会回收它们并打印退出信息。###注意事项1.**信号处理函数中的安全函数**:在信号处理函数中,我们使用了`printf`,这在实际应用中是不安全的(因为`printf`不是异步信号安全函数)。但在简单示例中,为了演示,我们使用了它。在实际应用中,应避免在信号处理函数中使用非异步信号安全函数。可以考虑使用写日志文件(使用异步信号安全的写入方式)或设置标志位等方式。2.**多个子进程同时退出**:如果多个子进程同时退出,可能会只产生一个SIGCHLD信号(这是Unix系统的特性,多个相同的信号可能被合并)。因此,在信号处理函数中必须循环调用`waitpid`直到没有更多终止的子进程。3.**避免僵尸进程**:无论使用忽略信号还是捕获信号回收,目的都是避免僵尸进程。忽略信号的方式更简单,但捕获信号的方式可以获取子进程的退出状态。###相关问题根据用户的问题和我们的回答,生成相关问题:</think>### Linux SIGCHLD 信号使用示例代码 SIGCHLD 信号在 Linux 多进程编程中至关重要,主要用于通知父进程进程状态变化(如终止、暂停或恢复)。以下是几种常见使用场景的代码示例: #### 1. 忽略 SIGCHLD 信号(自动回收子进程) ```c #include <stdio.h> #include <unistd.h> #include <signal.h> #include <stdlib.h> int main() { // 忽略 SIGCHLD 信号,子进程退出后由内核自动回收 signal(SIGCHLD, SIG_IGN); // 关键设置[^1][^3] if (fork() == 0) { // 子进程 printf("Child PID: %d running\n", getpid()); sleep(2); printf("Child exiting\n"); exit(0); } else { // 父进程 printf("Parent PID: %d waiting\n", getpid()); sleep(5); // 给子进程退出留出时间 system("ps -o pid,state,command"); // 检查僵尸进程状态 } return 0; } ``` **特点**: - 使用 `signal(SIGCHLD, SIG_IGN)` 让内核自动回收子进程 - 不会产生僵尸进程[^3] - 无法获取子进程退出状态 #### 2. 捕获 SIGCHLD 信号(手动回收子进程) ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> // SIGCHLD 信号处理函数 void sigchld_handler(int signo) { int status; pid_t pid; // 非阻塞回收所有终止的子进程 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { printf("Child %d exited with status: %d\n", pid, WEXITSTATUS(status)); } } } int main() { struct sigaction sa; sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; // 关键标志[^4] // 注册信号处理 if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(1); } // 创建多个子进程 for (int i = 0; i < 3; i++) { if (fork() == 0) { printf("Child %d started\n", getpid()); sleep(i + 1); // 不同退出时间 exit(i); } } // 父进程持续运行 while (1) { printf("Parent working...\n"); sleep(5); } return 0; } ``` **特点**: - 使用 `waitpid(-1, &status, WNOHANG)` 非阻塞回收子进程 - `SA_NOCLDSTOP` 标志避免接收子进程暂停信号[^4] - 可获取子进程退出状态 - 循环处理确保回收所有终止子进程 #### 3. 处理多个子进程退出 ```c #include <stdio.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <sys/types.h> void wait_children(int signo) { int status; // 回收任意终止的子进程 while (waitpid(-1, &status, WNOHANG) > 0) { printf("Recycled child process\n"); } } int main() { signal(SIGCHLD, wait_children); // 注册处理函数[^5] // 创建 5 个子进程 for (int i = 0; i < 5; i++) { if (fork() == 0) { sleep(i + 1); // 子进程存活时间递增 exit(0); } } // 父进程等待 printf("Press Enter to exit parent...\n"); getchar(); // 等待用户输入 return 0; } ``` **特点**: - 使用 `waitpid` 配合 `WNOHANG` 避免阻塞 - 循环处理确保回收多个同时退出的子进程[^5] - 防止信号丢失导致的僵尸进程累积 ### 关键机制说明 1. **信号处理原理**: - 子进程退出时内核向父进程发送 SIGCHLD - 信号产生后记录在未决信号集(pending set)[^2] - 父进程捕获信号后调用注册的处理函数 2. **最佳实践**: - 使用 `sigaction` 替代 `signal`(更可靠) - 处理函数中必须使用非阻塞 `waitpid` - 设置 `SA_NOCLDSTOP` 避免不必要信号[^4] - 循环回收防止多个子进程同时退出时的信号合并 3. **僵尸进程避免**: ```mermaid graph TD A[子进程退出] --> B{父进程处理方式} B -->|忽略SIGCHLD| C[内核立即回收] B -->|注册处理函数| D[waitpid回收资源] B -->|未处理信号| E[成为僵尸进程] ``` ### 注意事项 - **异步信号安全**:处理函数中只能调用异步安全函数(如 `write` 代替 `printf`) - **信号丢失**:多个子进程同时退出可能只触发一次 SIGCHLD,需循环回收 - **阻塞调用**:处理函数可能中断某些系统调用,使用 `SA_RESTART` 标志自动重启[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值