【问题标题】:problems in select() and sending a signal SIGUSR1 in the end (C language)select() 中的问题并最终发送信号 SIGUSR1(C 语言)
【发布时间】:2011-04-26 05:54:04
【问题描述】:

我正在实现一个经典的 map-reduce 程序,其中我有一个父级,它生成 N 个子级(maps)+ 1 个(reduce)。父级通过未命名的管道向 N 个子级中的每一个发送信息。映射处理请求并发送结果,一个 int,以减少。 reduce 进行选择并总结从 map 到 reduce 的管道上写入的每个计数器。

最后,reduce 必须发送一个带有结果的信号 SIGUSR1,但我的代码执行了很多次并且错误,因为它总是在信号处理程序中打印 o。是代码的一部分:

void reduce() {

    int answer;
    int i = 0;
    fd_set set;
    FD_ZERO(&set); //clean set of pipes

    while (1) {
        for (i = 0; i < maps_nr; i++) {
            FD_SET(fd_maps_to_reduce[i][READ], &set); 
        }
        if (select(FD_SETSIZE, &set, NULL, NULL, NULL) > 0) {
            printf("Entrou no select\n");
            for (i = 0; i < maps_nr; i++) { 
                if (FD_ISSET(fd_maps_to_reduce[i][READ], &set)) {
                    close(fd_maps_to_reduce[i][WRITE]);
                    if (read(fd_maps_to_reduce[i][READ], &answer, sizeof (int))) {
                        result += answer;
                        printf("Result in reduce =%d\n", result);
                    } else {
                        printf("Reduce failed to read from pipe from son :%d!\n", i);
                    }
                }
            }
        }//end of select
        printf("Reduce is going to send a signal with result= %d!\n", result);
        kill(getppid(), SIGUSR1);
        printf("Already send!\n");
    }
}

在父级中,创建管道和子级之后,我有这样的东西:

(...)
signal(SIGUSR1, handle_signal);
while(exit) {
    (...)//this is a menu
    for i->N 
        send a struct to each child (through write in respective pipe)
    after the for do:
    pause();//waiting for a signal to be caught
    if (errno==EINTR)
       printf("caught sigusr1");
}

void handle_signal(int signum) {
    signal(SIGUSR1, handle_signal);
    //print results
    printf("Result: %d\n",result);
}

问题是reduce进程正确求和并正确打印,但是信号被发送了很多次,我只想要一个,即在wend向父级发送信号sigusr1,它在pause()中被阻止,并打印全局 var 结果。

我该怎么做? reduce是不是有什么问题?

【问题讨论】:

  • 我认为在信号处理程序中使用 printf 并不安全。
  • 另一个切线点:您应该使用sigaction 而不是signal,除非您希望您的程序在Linux 上的行为与在BSD 上的行为不同。

标签: c signals parent-child select-function


【解决方案1】:

首先,您可以像这样创建一个更好看的 select() 循环:

while (newfds = readfds, select(n, &newfds, NULL, NULL, NULL))

现在,谈谈你的问题。正如我从上面的代码中看到的那样,每次 select() 解除阻塞时,您都在向父级发送信号,这可能在每个映射进程中发生多次。 select() 可能会在每次 任何 map 进程向 reduce 进程发送数据时解除阻塞并运行循环中的所有其余代码。哪怕是半个答案。

如果您想在减少所有内容后发送信号,则必须实现一些逻辑来检测所有进程都已完成,结束循环,然后(在循环外)向父级发送信号。

编辑:尝试这样的事情(我删除了您代码的一些细节,以使示例更清晰)。

void reduce() {

    int i, answer, waiting, ret;
    fd_set read_set, selected_set;

    FD_ZERO(&read_set);

    for (i = 0; i < maps_nr; i++)
        FD_SET(fd_maps_to_reduce[i][READ], &read_set); 

    waiting = maps_nr; /* how many answers are we expecting? */

    while(waiting > 0 &&
          selected_set = read_set,
          select(FD_SETSIZE, &selected_set, NULL, NULL, NULL)) {

        for (i = 0; i < maps_nr; i++) {

            if (FD_ISSET(fd_maps_to_reduce[i][READ], &set)) {
                close(fd_maps_to_reduce[i][WRITE]);

                /* read your result. Once you have it: */
                FD_CLR(fd_maps_to_reduce[i][READ], &read_set);
                /* Now you won't wait for that pipe to produce data. */
                waiting--;
            }

        }
    }

    /* Now you are out of the select loop. Signal, or whatever. */

}

编辑 2: 顺便说一下,您的结果可能打印为 0,因为您在这里处理不同的进程。 reduce 进程有自己的结果变量副本,它不会更改主进程上的那个。您必须对其进行 IPC,如果已经为此编写了代码,则可能是另一个管道。

【讨论】:

  • 错误:二进制 && 的操作数无效(具有“int”和“fd_set”)错误:在需要标量的地方使用了结构类型值。我该如何比较它们?逗号不起作用,我用 && 代替。
  • 我只是忘了在 if 语句的第二部分加上括号,在 && 之后
  • 是的,但问题不在于。在这里:while(waiting > 0 && selected_set = read_set, select(FD_SETSIZE, &selected_set, NULL, NULL, NULL)) { 我们可以分配或测试 fd_set 与其他类似:selected_set=read_set 或 ==,它不是不可能,因为它是一个结构。但是我删除了那个条件并且它有效!我认为没有它就足够了
  • 不,相信我,问题不在于'=',你可以在那里使用它就好了。这是因为第二个条件应该放在括号之间,或者圆括号')'
猜你喜欢
  • 2021-03-14
  • 2017-11-29
  • 1970-01-01
  • 1970-01-01
  • 2020-02-26
  • 1970-01-01
  • 2021-12-01
  • 2016-01-06
  • 2016-01-21
相关资源
最近更新 更多