【问题标题】:Why SIGPIPE doesn't occur when the read end of a pipe is closed?为什么当管道的读取端关闭时不会发生 SIGPIPE?
【发布时间】:2018-01-31 14:47:57
【问题描述】:

我正在尝试编写一个包含两个进程的程序:

一个进程产生一些 a + b 问题并将其打印到其标准输出(如printf("%d %d\n", a, b)),并通过标准输入从另一个进程获取答案,并将答案记录到 log.txt。当所有的问题都被问到时,进程会打印出“-1 -1”来表示问题的结束。

另一个进程通过标准输入接收 a + b 问题(如scanf("%d%d", &a, &b)),并将答案打印到其标准输出。当a = b = -1 进程退出时。

我使用两对管道来连接两个进程。我使用dup2 将管道与标准输入和标准输出绑定。我的源代码如下所示:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>

// Get a + b question through stdin, and print answer to stdout
void calc_a_plus_b() {
    int a, b;
    while (scanf("%d%d", &a, &b) > 0)
    {
        if (a == -1 && b == -1)
            break;
        printf("%d\n", a + b);
        fflush(stdout);
    }
}

// Ask a + b through stdout, and get answer through stdin
// Log answer to log.txt
void ask_a_plus_b() {
    FILE* log = fopen("log.txt", "w");
    int i, a, b, c;
    for (i = 0; i < 3; i++) {
        a = i; b = i + 1;
        printf("%d %d\n", a, b);
        fflush(stdout);
        scanf("%d", &c);
        fprintf(log, "%d\n", c);
    }
    printf("-1 -1\n");
    fclose(log);
}

int main()
{
    pid_t ask_pid, calc_pid, term_pid;
    int ask_to_calc[2], calc_to_ask[2];    // Two pairs of pipe
    int status;

    // Create pipe
    pipe(ask_to_calc); pipe(calc_to_ask);

    // Create calculate process
    calc_pid = fork();
    if (calc_pid == 0) {
        // Close useless pipe
        close(ask_to_calc[1]);
        close(calc_to_ask[0]);

        // Bind pipe to stdin and stdout
        dup2(ask_to_calc[0], 0);
        dup2(calc_to_ask[1], 1);
        close(ask_to_calc[0]);
        close(calc_to_ask[1]);

        calc_a_plus_b();
        return 0;
    }

    // Create ask process
    ask_pid = fork();
    if (ask_pid == 0) {
        // Close useless pipe
        close(ask_to_calc[0]);
        close(calc_to_ask[1]);

        // Bind pipe to stdin and stdout
        dup2(calc_to_ask[0], 0);
        dup2(ask_to_calc[1], 1);

        ask_a_plus_b();
        return 0;
    }

    // Wait for children to exit
    while ((term_pid = wait(&status)) > 0) {
        if (WIFSTOPPED(status))
        {
            // If child stopped but hasn't exited, ignore
            continue;
        } else if (WIFSIGNALED(status)) {
            // Child exited due to signal
            printf("%s terminated due to signal %d\n", ask_pid == term_pid ? "ask" : "calc", WTERMSIG(status));
        } else {
            // Child exited normally
            printf("%s terminated normally\n", ask_pid == term_pid ? "ask" : "calc");
        }
    }
    return 0;
}

这段代码运行正常,并打印出来

calc terminated normally
ask terminated normally

来自man 7 pipe (http://man7.org/linux/man-pages/man7/pipe.7.html) 我知道,当管道读取端的所有文件描述符都已关闭,而另一个进程仍在尝试写入该管道时,将发生 SIGPIPE。所以我决定把calc_a_plus_b中的所有代码都去掉,看看会发生什么。

令人惊讶的是,SIGPIPE 没有发生。程序只打印出来

calc terminated normally

之后程序就卡住了。我猜这是因为ask_a_plus_b 中的printf 被屏蔽了。

我想我已经关闭了管道ask_to_calc的读取端的所有文件描述符(我已经将管道绑定到计算进程的标准输入,但是进程已经退出,所以它的标准输入也被关闭了?),那么为什么当请求进程尝试写入管道时没有发生 SIGPIPE?

【问题讨论】:

  • 请不要标记 c++ c.您正在使用其中一个,因此请相应地标记。
  • 与你的问题无关,但是while (scanf("%d%d", &amp;a, &amp;b) &gt; 0) -> while (scanf("%d%d", &amp;a, &amp;b) == 2) 或者你应该在进入循环之前初始化ab
  • 两件事。 1. SIGPIPE 直到第二次写入封闭管道后才发送(第一次返回 0,第二次生成 SIGPIPE)。 2.你的父进程仍然打开管道。
  • 你是对的......关闭父进程中的管道后,由于SIGPIPE,请求进程现在终止。抱歉弄错了,太尴尬了……-_-”

标签: c linux process signals


【解决方案1】:

感谢@Art 的评论。我忘了关闭父进程中的管道-_-"

关闭父进程中的管道后,请求进程现在由于 SIGPIPE 而终止。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-20
    • 1970-01-01
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-07
    相关资源
    最近更新 更多