【问题标题】:C - Multiple children - signals overwriting each otherC - 多个孩子 - 信号相互覆盖
【发布时间】:2017-05-23 17:59:36
【问题描述】:

我有一个父进程在循环中使用 fork 创建了 16 个子进程。最终,每个子进程都会发送一个 SIGUSR1 信号,该信号由父进程中的处理函数处理。

我的问题是——一些孩子在处理来自另一个孩子的信号时发送信号。我读到处理函数然后停止并处理新信号,忽略它正在处理的当前信号。

我试图通过在处理函数的开头发送:kill(0,SIGSTOP) 来解决这个问题,但看起来这也会停止父进程。有没有办法只向孩子发送这个信号?

如果不可能,使用wait, waitpid and kill 可以实现我的目标吗?

添加了下面的代码,我省略了检查读取、打开等的返回值等内容。

处理函数:

void my_signal_handler( int signum, siginfo_t* info, void* ptr)
{
    kill(0, SIGSTOP);
    int sonPid = info->si_pid;
    char* pipeName = malloc(14 + sizeof(int));//TODO ok?
    sprintf(pipeName, "//tmp//counter_%d" , (int) sonPid); //TODO double //?
    size_t fdPipe = open(pipeName, O_RDONLY);
    int cRead;
    int countRead = read(fdPipe,&cRead,sizeof(int));
    COUNT+= cRead;
    kill(0, SIGCONT);
    return;
}

创建子进程:

struct sigaction new_action;
memset(&new_action, 0, sizeof(new_action));
new_action.sa_handler = my_signal_handler;
new_action.sa_flags = SA_SIGINFO;
if( 0 != sigaction(SIGUSR1, &new_action, NULL) )
{
    printf("Signal handle registration failed. %s\n", strerror(errno));
    return -1;
}
for(int i=0; i<16; i++){
    pid_t cpid = fork();
    if(cpid == 0) // child
    {
        execv("./counter",argvv); // some arguments to the function
        printf("execv failed: %s\n", strerror(errno));
        return -1;
    }
    else{
        continue;
    }

孩子们的计数器程序,简而言之,它计算文件某些部分中字符counc的出现,然后将其打印到管道中。:

int main(int argc, char** argv){
    int counter = 0;
    char counc = argv[1][0];
    char* filename = argv[2];
    off_t offset = atoll(argv[3]);
    ssize_t length = atoll(argv[4]); 
    int fd = open(filename, O_RDWR | O_CREAT);
    char* arr = (char*)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); 
    for(int i=0; i<length; i++){
        if(arr[i] == counc){
            counter++;
        }
    }
    pid_t proid = getpid();
    pid_t ppid = getppid();
    char* pipeName = malloc(14 + sizeof(pid_t));
    sprintf(pipeName, "//tmp//counter_%d" , (int) proid);
    size_t fdPipe = mkfifo(pipeName, 0777);
    int didopen = open(pipeName,O_WRONLY);
    size_t wrote = write(fdPipe,&counter , 1 );
    if(wrote < 0){
        printf(OP_ERR, strerror( errno ));
        return errno;
    }
    kill(ppid, SIGUSR1);
//close the pipe and unmap the array
return 1;
}

【问题讨论】:

  • 请将您的代码发布为正确的Minimal, Complete, and Verifiable example(请仔细阅读页面)。
  • 我认为在这种情况下可以进行解释,但我很快会添加一个代码示例。
  • 这不是真的,正在处理的信号被阻塞直到处理程序完成,所以如果它们都发送相同的信号,你应该没问题。一些代码将有助于识别您的真正问题。顺便说一句,保持处理程序简短,只设置标志并在信号处理程序之外进行复杂处理。
  • 了解信号安全功能。您的处理程序调用 other 函数。理想情况下,只需将 volatile sig_atomic_t 变量设置为主代码的标志,并在处理程序之外执行任何其他操作。
  • @FelixPalmen 我试过了,我得出的结论是我必须在处理程序中使用 sprintf 来获取管道的名称。除非管道在处理程序中打开,否则我认为孩子不能写入它。

标签: c multiprocessing signals handler


【解决方案1】:

如果我正确理解了您的问题,那么您在父亲和孩子的所需信号功能方面遇到了麻烦。如果这不是你的情况,请纠正我。

如果您的问题是这样,您可以简单地创建一些if 语句,以测试fork() 调用返回的pid,然后仅在您是父亲时执行。所以……

if (pid == 0) 
          //ChildProcess();
          // do nothing!
     else 
          //ParentProcess();
          // do something!

请注意,您必须将 size_t pid 定义为全局变量,以便在您正在实现的主函数和信号处理函数中都可见!

【讨论】:

  • 不是——问题是父进程有16个子进程,当他处理来自子1的信号时,子2发送一个新的信号,导致子1的信号没有被处理正确。
  • 然后你只需要预先定义哪个 PID 指的是什么孩子,然后像这样创建if 语句。
【解决方案2】:

除了其他人已经指出的关于在信号处理程序中使用非异步安全函数的内容之外,我会冒昧地猜测问题出在以下两件事中:

  1. 您错误地假设通过kill 发送的信号已排队等待它们发送到的进程。他们没有排队(除非您在支持它的操作系统上使用sigqueue);而是更新目标进程的未决(尚未处理)信号集。见signal queuing in C。或者,
  2. 您在不支持“可靠信号语义”的操作系统上运行此代码。请参阅man 3 sysv_signal 了解以下见解(恐怖):

然而 sysv_signal() 提供了 System V 不可靠的信号语义,即: a) 当调用处理程序时,信号的处置被重置为默认值; b) 在信号处理程序执行时,不会阻止信号的进一步实例的传递;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-01
    相关资源
    最近更新 更多