在阻塞模式下打开命名管道时信号丢失

时间:2019-04-05 00:37:17

标签: c++ signals blocking signal-handling

背景: 我编写了一个程序,其中“父级”创建了2个子进程(发送方和接收方)。我以这样的方式实现了子代,即当一个子代失败时,它会向父代发送信号(SIGUSR2)。当父级接收到该信号时,找到另一个进程(例如,如果“接收方”发送了该信号,则父级找到了相应的“发送方”进程)并向其发送SIGINT信号。此后,父级将创建2个新子级。在针对SIGINT的孩子的信号处理程序中,我进行了一些清理(关闭命名管道,释放堆内存并正常退出)。我让“接收者”子代始终将SIGUSR2发送给父代,然后退出,因此父代将不得不找到“发送者”子代,向其发送信号并一次又一次创建新子代。

问题: 问题在于,只有第一个“发送方”子级收到信号,而其他(新的“发送方”子级)停留在以非阻塞模式打开命名管道的过程中,永远不会退出。

因此,我在阻止模式下打开了命名管道,并且“发件人”卡在了那里。根据{{​​3}}的手册,在等待完成打开过程中收到信号时,函数返回-1,并且errno设置为EINTR。 但是看起来开放系统调用永远不会失败,信号也永远不会收到

因此,在SIGUSR2的 parent 信号处理程序中,我将以下信号发送至与发送信号的“接收方”相对应的“发送方”进程:

int stat;
cout << "Going to kill " << pidINFO2->myPID << endl;
int ret = kill(pidINFO2->myPID,SIGINT);
if (ret==0) cout << "SIGNAL WAS SENT SUCCESSFULLY" <<endl;

waitpid(pidINFO2->myPID,&stat,0);
cout << "AFTER WAIT" << endl;

这是整个发件人孩子的代码:

#include <iostream>
#include <cstring>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;

#include "interface.h"

char* input_dirName, *common_dirName, *myID, *syncID, *buflen;

void SIGINT_handler(int signo)
{
    cout << "INTO SENDER " << getpid() << " SIGNAL HANDLER" << endl;
    /* Remove the pipe we created */
        /* Make the name+path of the named pipe */
        char fifoNamePath[4096];
        strcpy(fifoNamePath,common_dirName);
        strcat(fifoNamePath,"/");
        strcat(fifoNamePath,myID);
        strcat(fifoNamePath,"_to_");
        strcat(fifoNamePath,syncID);
        strcat(fifoNamePath,".fifo");
        /* ------------------------------------ */
        unlink(fifoNamePath);
    /* -------------------------- */
    delete[] input_dirName;
    delete[] myID;
    delete[] syncID;
    delete[] common_dirName;
    delete[] buflen;

    exit(0);
}

int main(int argc, char* argv[])
{
     cout << "INTO SENDER CHILD -> " << getpid() << endl;

     struct sigaction term;
     term.sa_handler = SIGINT_handler;
     term.sa_flags = SA_RESTART;
     sigfillset(&term.sa_mask);
     if (sigaction(SIGINT, &term, NULL) == -1) {
             perror("sigaction");
             return EXIT_FAILURE;
     }

    common_dirName = new char[strlen(argv[1])+1];
    strcpy(common_dirName,argv[1]);
    input_dirName = new char[strlen(argv[2])+1];
    strcpy(input_dirName,argv[2]);
    myID = new char[strlen(argv[3])+1];
    strcpy(myID,argv[3]);
    syncID = new char[strlen(argv[4])+1];
    strcpy(syncID,argv[4]);
    buflen = new char[strlen(argv[5])+1];
    strcpy(buflen,argv[5]);


    /* Create and/or open the named pipes (FIFO) */
        /* Make the name+path of the named pipe */
        char fifoNamePath[4096];
        strcpy(fifoNamePath,common_dirName);
        strcat(fifoNamePath,"/");
        strcat(fifoNamePath,myID);
        strcat(fifoNamePath,"_to_");
        strcat(fifoNamePath,syncID);
        strcat(fifoNamePath,".fifo");
        /* ------------------------------------ */
        if (mkfifo(fifoNamePath,0777)!=0) //try to make the FIFO
        {
            if (errno != EEXIST) //if we get an error and that error is different than EEXIST, then we send a signal to the parent to try again with a new child
            {
                perror("Error: ");
                delete[] input_dirName;
                delete[] myID;
                delete[] syncID;
                delete[] common_dirName;
                delete[] buflen;
                kill(getppid(),SIGUSR2);
                exit(0);
            }
        }
        //if we don't get an error or if the error is EEXIST (which means that the fifo is already made by the other process) we open it

        cout << "before open in " << getpid() << endl;
        int fd;
        if ( (fd = open(fifoNamePath, O_WRONLY)) == -1) //we try to open the fifo "read-only" in BLOCKING mode
        {
            perror("Error: ");
            delete[] input_dirName;
            delete[] myID;
            delete[] syncID;
            delete[] common_dirName;
            kill(getppid(),SIGUSR2);
            unlink(fifoNamePath);
            exit(0);
        }
        cout << "Sender opened -> " << fifoNamePath << " on " << fd << endl;
        char buffer[50] = "SOMETHING";
        int rsize;
        if ( ( rsize = write(fd,&buffer,strlen(buffer)+1) )==-1)
        {
            perror("Error: ");
            exit(5);
        }
        close(fd);
    /* --------------------------- */

    cout << "send completed for " << syncID << endl;
    delete[] input_dirName;
    delete[] myID;
    delete[] syncID;
    delete[] common_dirName;
    delete[] buflen;
    kill(getppid(),SIGUSR1);
    exit(0);
}

因此,我期望所有“发送方”子级都从父级接收SIGINT信号,但似乎只有第一个“发送方”收到了信号,而其他“发送方”却没有,尽管父级表明该信号已成功发送

我得到的实际输出是:(它们共享相同的终端。无论显示INTO *** CHILD的位置,都是指父级创建了2个新进程)

[child]INTO SENDER CHILD -> 23410
[child]INTO RECEIVER CHILD -> 23409
[child]before open in 23410
[parent]The process -> 23409 terminated with an error!
[parent]Going to kill 23410
[parent]SIGNAL WAS SENT SUCCESSFULLY
[child]INTO SENDER 23410 SIGNAL HANDLER
[parent]AFTER WAIT
[child]INTO RECEIVER CHILD -> 23413
[child]INTO SENDER CHILD -> 23414
[child]before open in 23414
[parent]The process -> 23413 terminated with an error!
[parent]Going to kill 23414
[parent]SIGNAL WAS SENT SUCCESSFULLY

并且终端卡在那里。 parent在waitpid上被阻止,并且 child在open上被阻止(我猜是因为它打印的是“ before open in 23414”,而不是在open system call之后的消息) 。 那么,为什么创建的第二个“发送方”没有收到父级的SIGINT信号,因为父级说它已发送(信号已成功发送)。

我在做什么错了?

0 个答案:

没有答案