system阻塞SIGCHLD信号原因

system阻塞SIGCHLD信号原因

标签: c
198人阅读 评论(0) 收藏 举报
分类:

代码1:APUE10.18节的system函数源代码 
int system(const char *cmdstring) /* with appropriate signal handling */
{
    pid_t               pid;
    int                 status;
    struct sigaction    ignore, saveintr, savequit;
    sigset_t            chldmask, savemask;
    ……
    sigemptyset(&chldmask);         /* now block SIGCHLD */
    sigaddset(&chldmask, SIGCHLD);
    if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) 
        return(-1);

    if ((pid = fork()) 
        status = -1;    /* probably out of processes */
    } else if (pid == 0) {          /* child */
        ……
        sigprocmask(SIG_SETMASK, &savemask, NULL);

        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        _exit(127);     /* exec error */
    } else {                        /* parent */
       while (waitpid(pid, &status, 0) 
           if (errno != EINTR) {
               status = -1; /* error other than EINTR from waitpid() */
               break;
           }
    }
    ……
    if (sigprocmask(SIG_SETMASK, &savemask, NULL) 
        return(-1);

    return(status);}
如果该函数的调用者在其SIGCHLD信号处理函数中调用了waitpid函数来获得任意子进程的终止状态,且在fork之前没有阻塞SIGCHLD信号,则子进程结束,父进程执行SIGCHLD信号处理函数且得到了子进程的终止状态。而该函数中的waitpid函数由于没有得到其创建的子进程的终止状态而出错。

定义了一个函数,函数中fork了一个子进程,并且定义的函数要得到关于子进程的一些信息,例如子进程ID、子进程终止状态等,而该函数的调用者所注册的SIGCHLD信号处理函数会影响定义的这个函数获取这些信息,因此为了避免该函数在获取这些信息之前,由于子进程终止触发SIGCHLD信号处理函数先执行,在fork之前都将SIGCHLD信号阻塞了,在函数获取了相关信息后,才对SIGCHLD信号接触阻塞。 


接下来我们以代码1为例完整的解释一下阻塞SIGCHLD信号的原因:

在一个进程终止或停止时,SIGCHLD信号被送给其父进程。按系统默认,将忽略此信号。如果父进程希望了解其子进程的这种状态改变,则应捕捉此信号。信号捕捉函数中通常要调用wait函数以取得子进程ID和其终止状态。

一般的,父进程在生成子进程之后会有两种情况:一是父进程继续去做别的事情;另一是父进程什么都不做,一直在wait子进程退出。

SIGCHLD信号就是为第一种情况准备的,它让父进程去做别的事情,而只要父进程注册了处理该信号的函数,在子进程退出时就会调用该函数,在函数中wait子进程得到终止状态之后再继续做父进程的事情。


我们先来写一个function_model函数模型,如下所示:
int function_model(…) {    pid_t               pid;    int                 status;    ……    if ((pid = fork())         status = -1;        } else if (pid == 0) {          /* child */       ……    } else {                        /* parent */       while (waitpid(pid, &status, 0)            if (errno != EINTR) {               status = -1;                break;           }    }        ……    return(status);}


当我们定义了一个具有function_model函数模型结构的函数时,应该在fork之前将SIGCHLD信号阻塞,fork之后,父子进程的SIGCHLD信号都是被阻塞的。子进程在进行任何的操作之前,应该将SIGCHLD恢复至原来的设置。父进程则应该在waitpid函数之后,将SIGCHLD恢复至原来的设置。


即这种类型的函数的正确的格式为:

int function_model(…) 
{
    pid_t               pid;
    int                 status;
    sigset_t            chldmask, savemask;
    ……
    sigemptyset(&chldmask);         /* now block SIGCHLD */
    sigaddset(&chldmask, SIGCHLD);
    if(sigprocmask(SIG_BLOCK, &chldmask, &savemask) 
        return(-1);

    if ((pid = fork()) 
        status = -1;    
} else if (pid == 0) {          /* child */

sigprocmask(SIG_SETMASK, &savemask, NULL);
       ……
    } else {                        /* parent */
       while (waitpid(pid, &status, 0) 
           if (errno != EINTR) {
               status = -1; 
               break;
           }
    }
    ……
if(sigprocmask(SIG_SETMASK, &savemask, NULL) 
    return(-1);
    return(status);
}

父进程阻塞SIGCHLD信号的原因在于:如果function_model函数的调用者caller在调用function_model之前,注册了SIGCHLD信号的处理函数sigchld_handler,且sigchld_handler函数中调用了waitpid函数等待任意子进程结束,如下所示:

void sigchld_handler(int signo)
{
    pid_t pid;
    int status;
    while((pid=waitpid(-1,&status,WNOHANG))>0)
    {
        ……
}
return ;
}

    若我们没有阻塞SIGCHLD信号,则当function_model函数中fork的子进程结束时,会向父进程发送SIGCHLD信号,则其信号处理函数sigchld_handler被调用,并在sigchld_handler函数中调用了waitpid得到了这个子进程的退出状态,然后释放掉其占用的进程表等其它资源。返回到function_model函数中继续执行,由于其创建的子进程的退出状态已经被获取,内核不再保存其退出状态,所以其中调用的waitpid函数由于无法得到其退出状态而出错,并将status设置为-1。
    若我们阻塞了SIGCHLD信号,则当function_model函数中fork的子进程结束时,内核会向父进程发送SIGCHLD信号,但SIGCHLD信号被阻塞而不递送给父进程,即处于未决状态,因此function_model函数中的waitpid就能正确得到这个子进程的退出状态。当解除对SIGCHLD信号的阻塞时,SIGCHLD信号会被递交给父进程,引发父进程调用sigchld_handler函数,由于在function_model函数中以调用waitpid获取了function_model函数创建的子进程的退出状态,内核不再保存其终止信息,所以sigchld_handler中的waitpid也不会的到其退出状态。

    下面是Advanced Programming in the UNIX® Environment: Second Edition(APUE)中的解释:
POSIX.1 states that if wait or waitpid returns the status of a child process while SIGCHLD is pending, then SIGCHLD should not be delivered to the process unless the status of another child process is also available. None of the four implementations discussed in this book implements this semantic. Instead, SIGCHLD remains pending after the system function calls waitpid; when the signal is unblocked, it is delivered to the caller. If we called wait in the sig_chld function in 
Figure 10.26
, it would return 1 with errno set to ECHILD, since the system function already retrieved the termination status of the child.

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

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

相关文章

设计模式6---(单例模式的概念及其实现(懒汉式和饿汉式),线程安全)

单例模式 单例模式的概念 单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。 GoF 对单例模式的定义是:保证一个类、只有一个实…

套接字编程---2(TCP套接字编程的流程,TCP套接字编程中的接口函数,TCP套接字的实现,TCP套接字出现的问题,TCP套接字多进程版本,TCP套接字多线程版本)

TCP模型创建流程图 TCP套接字编程中的接口 socket 函数 #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int domain, int type, int protocol); domain: AF_INET 这是大多数用来产生socket的协议&#xff0c;使用TCP或UDP来传输&…

Linux中netstat工具详解

简介 Netstat 命令用于显示各种网络相关信息&#xff0c;如网络连接&#xff0c;路由表&#xff0c;接口状态 (Interface Statistics)&#xff0c;masquerade 连接&#xff0c;多播成员 (Multicast Memberships) 等等。 常见参数 -a (all)显示所有选项&#xff0c;默认不显示…

网络基础 2-1(应用层,HTTP三点注意,HTTP协议格式, 最简单的HTTP服务器)

应用层 应用层 负责应用程序之间的数据沟通-----协议都是用户自己定的 自定制协议&#xff1a; 结构化数据传输 序列化&#xff1a; 将数据对象以指定的协议&#xff08;数据格式&#xff09;进行可用于持久化存储或者数据传输时的数据组织 例如在分布式的系统中&#xf…

网络基础2-2(传输层,端口,详谈UDP)

传输层 负责数据能够从发送端传输接收端. 端口号 端口号(Port)标识了一个主机上进行通信的不同的应用程序;在TCP/IP协议中, 用 “源IP”, “源端口号”, “目的IP”, “目的端口号”, “协议号” 这样一个五元组来标识一个通信(可以通过 netstat -n查看);一个端口只能被一个…

网络基础2-3(TCP协议,三次握手,四次挥手,TIME_WAIT状态的作用,TCP如何保证可靠传输,TCP连接中状态转化,滑动窗口,流量控制,快速重传,拥塞窗口,延迟应答,捎带应答,粘包问题)

TCP协议 TCP协议概念 TCP全称为 “传输控制协议(Transmission Control Protocol”). 人如其名, 要对数据的传输进行一个详细的控制 TCP协议格式 1. 源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去; 2. 32位序号/32位确认号: 后面详细讲; 3. 4位TCP报头长度: 表示该…

字符串题目 1 --------判断两个字符串是否为旋转词

题目描述 如果一个字符串为str&#xff0c;把字符串的前面任意部分挪到后面形成的字符串交str的旋转词。比如str“12345”&#xff0c;str的旋转串有“12345”、“45123”等等。给定两个字符串&#xff0c;判断是否为旋转词。 输入描述: 输出包含三行&#xff0c;第一个两个…

字符串题目---2判断两个字符串是否为变形词

题目描述 给定两个字符串str1和str2&#xff0c;如果str1和str2中出现的字符种类出现的一样且每种字符出现的次数也一样&#xff0c;那么str1和str2互为变形词。请判断str1和str2是否为变形词 输入描述: 输入包括3行&#xff0c;第一行包含两个整数n&#xff0c;m(1 \leq n,…

设计模式7----代理模式

代理模式 概念 Proxy 模式又叫做代理模式&#xff0c;是结构型的设计模式之一&#xff0c;它可以为其他对象提供一 种代理&#xff08;Proxy&#xff09;以控制对这个对象的访问。 所谓代理&#xff0c;是指具有与代理元&#xff08;被代理的对象&#xff09;具有相同的接口的…

网络基础3-1(细谈IP协议头, 网络层,子网划分,路由选择,数据链路层,以太网帧格式,MAC地址,再谈ARP协议)

IP协议 IP协议头格式 4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是44位头部长度(header length): IP头部的长度是多少个。32bit, 也就是 length * 4 的字节数. 4bit表示大 的数字是15, 因此IP头部大长度是60字节8位服务类型(Type Of Service): 3位优先权字段(已…

网络中典型协议--(DNS,输入url后, 发生的事情. ,ICMP,NAT)

DNS&#xff08;Domain Name System&#xff09; DNS是一整套从域名映射到IP的系统 域名服务器发展背景 TCP/IP中使用IP地址和端口号来确定网络上的一台主机的一个程序. 但是IP地址不方便记忆. 于是人们发明了一种叫主机名的东西, 是一个字符串, 并且使用hosts文件来描述主机…

高级IO--1 ---(五种典型IO,阻塞IO,非阻塞IO,信号驱动IO,异步IO, IO多路转接)

高级IO&#xff1a; 五种典型IO&#xff1a; 阻塞IO/非阻塞IO/信号驱动IO/异步IO/IO多路转接 IO多路转接模型&#xff1a;select/poll/epoll 五种典型IO 阻塞IO IO操作的流程&#xff1a;等待IO操作条件具备&#xff0c;然后进行数据拷贝 为了完成IO操作发起调用&#xff…

IO多路转接模型----(select的模型,select的优缺点,poll的模型,poll的优缺点)

IO多路转接模型&#xff1a;select/poll/epoll 对大量描述符进行事件监控(可读/可写/异常) select模型 用户定义描述符的事件监控集合 fd_set&#xff08;这是一个位图&#xff0c;用于存储要监控的描述符&#xff09;; 用户将需要监控的描述符添加到集合中&#xff0c;这个描…

IO多路转接模型-----epoll

epoll&#xff1a; Linux下性能最高的多路转接模型 epoll 有3个相关的系统调用. epoll_create 功能&#xff1a;创建epoll&#xff0c;在内核中创建eventpoll结构体&#xff0c;size决定了epoll最多监控多少个描述符&#xff0c;在Linux2.6.8之后被忽略&#xff0c;但是必须…

再写单链表(不带头单链表)

单链表 实际中链表的结构非常多样&#xff0c;以下情况组合起来就有8种链表结构&#xff1a; 单向、双向带头、不带头循环、非循环 虽然有这么多的链表的结构&#xff0c;但是我们实际中最常用还是两种结构&#xff1a; 无头单向非循环链表&#xff1a;结构简单&#xff0…

再写双向循环链表

#pragma once #include<assert.h> #include<malloc.h> #include<stdio.h> typedef int DLDataType;//定义链表结点结构 typedef struct DListNode{DLDataType value;struct DListNode *prev; //指向前一个结点struct DListNode *next; //指向后一个结点 } DL…

链表题目--1 删除链表中所有等于val的值

注意事项 要删除的结点相邻第一个结点就是要删除的结点 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* removeElements(struct ListNode* head, int val){if(headNULL){return NULL;}struct …

链表题目--2 求链表的中间结点 和 求链表中倒数第k个结点

求链表的中间结点 思路 一个走两步&#xff0c;一个走一步。一个走到尾&#xff0c;另外一个就走到了中间 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head…

链表题目---3 合并两个有序单链表 和 分割链表

合并两个有序单链表 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){if(l1 NULL){return l2;}else if(l2 NULL){return l1;}struc…

链表题目---4 删除链表中重复的结点 和 判断链表是否为回文链表

删除链表中重复的结点 /* struct ListNode {int val;struct ListNode *next;ListNode(int x) :val(x), next(NULL) {} }; */ class Solution { public:ListNode* deleteDuplication(ListNode* pHead){if(pHead NULL){return NULL;}ListNode *prev NULL; //用于删除的结点, 是…