【问题标题】:Check if unix pipe closed without writing anything?检查unix管道是否关闭而不写任何东西?
【发布时间】:2016-08-08 09:53:37
【问题描述】:

基本上,我有一个父进程,它分叉一个子进程并通过管道将它的输入为标准输入。子进程可以在以下两种情况之一中终止:

  • 管道的写入端被父级关闭,这意味着它到达了标准输入的末尾,因此收到了一个EOF
  • 或者它通过管道接收某个输入(在这种情况下为-1)并退出

我的父代码大致如下:

close(pi[0]); // close input end
signal(SIGPIPE, SIG_IGN); // do not handle SIGPIPE
char buffer;
int ok = 1;
while(ok && read(STDIN_FILENO, &buffer, 1) > 0)  {
    int b_written = write(pi[1], &buffer, 1);
    if(b_written == -1) {
        if(errno == EPIPE) ok = 0;
        else perror("pipe write"); // some other error
    }
}

如您所见,我通过检查errno == EPIPE 来检查管道的读取端是否已关闭。然而,这意味着读取循环在关闭之前会进行一次额外的迭代。我怎么可能轮询以查看管道是否已关闭而不必向其写入内容?

【问题讨论】:

  • 你目前的处理方式有什么问题?你可以做些什么来检查孩子是否还活着,这可能比多做一个write() 的成本更高。
  • 阅读SIGPIPE
  • @jdarthenay 它可以工作,但是在孩子死后它会在退出之前等待标准输入中的另一个输入
  • 您对子代码有控制权吗?你可以让它终止时使用kill()吗?
  • 即使你找到了方法,你也有竞争条件。哦,好吧,大多数时候它会做正确的事。

标签: c unix process pipe


【解决方案1】:

当孩子检测到它已经完成时,它可以发送一个信号,例如SIGUSR1。父母可以在收到SIGUSR1 信号时设置一个标志,并在尝试读取输入之前检查这个标志。但我不确定SIGUSR1 在从stdin 读取输入之前检查标志ans 后无法接收)。所以我更喜欢使用控制管道,每次孩子知道它将能够再读取一个数据,它会在这个控制管道中写入一个 1。结果可能是这样的:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

#define STOP_VALUE 100
#define SIZE_STDIN_BUFFER 1024

static char can_read_more = 1;

static int handle_child(int *p_child_input_stream, int *p_control_stream)
{
    int pipefd[2][2];
    pid_t fk;

    if (pipe(pipefd[0]) < 0) // Pipe to read input from 
    {
        perror("pipe");
        return -1;
    }

    if (pipe(pipefd[1]) < 0) // Pipe to notifiate parent input can be processed
    {
        perror("pipe");
        close(pipefd[0][0]);
        close(pipefd[0][1]);
        return -1;
    }

    if ((fk = fork()) < 0)
    {
        perror("fork");
        close(pipefd[0][0]);
        close(pipefd[0][1]);
        close(pipefd[1][0]);
        close(pipefd[1][1]);
        return -1;
    }

    if (fk == 0)
    {
        close(pipefd[0][1]);
        close(pipefd[1][0]);
        write(pipefd[1][1], &can_read_more, sizeof(char)); // sizeof(char) == 1

        ssize_t nb_read = 0;
        char buffer;
        while (nb_read >= 0)
        {
            nb_read = read(pipefd[0][0], &buffer, sizeof(char));
            if (nb_read > 0)
            {
                printf("0x%02x\n", (unsigned int) buffer);
                if (buffer == STOP_VALUE)
                {
                    nb_read = -1;
                }
                else
                {
                    write(pipefd[1][1], &can_read_more, sizeof(char));
                }
            }
        }
        close(pipefd[0][0]);
        close(pipefd[1][1]);
        exit(0);
    }

    close(pipefd[0][0]);
    close(pipefd[1][1]);

    *p_child_input_stream = pipefd[0][1];
    *p_control_stream = pipefd[1][0];

    return 0;
}

int main()
{
    int child_input_stream;
    int control_stream;

    if (handle_child(&child_input_stream, &control_stream) < 0)
    {
        return 1;
    }

    char stdin_buffer[SIZE_STDIN_BUFFER];
    char buffer;
    int ok = 1;
    int child_available_input = 0;

    while(ok)
    {
        while (child_available_input <= 0 && ok)
        {
            ssize_t nb_control = read(control_stream, &buffer, sizeof(char));
            if (nb_control > 0)
            {
                child_available_input += buffer;
            }
            else
            {
                fprintf(stderr, "End of child reading its input detected.\n");
                ok = 0;
            }
        }

        if (ok)
        {
            if (fgets(stdin_buffer, SIZE_STDIN_BUFFER, stdin) == NULL)
            {
                ok = 0;
            }
            else
            {
                if (stdin_buffer[strlen(stdin_buffer) - 1] == '\n')
                {
                    stdin_buffer[strlen(stdin_buffer) - 1] = '\0';
                }

                char dummy;
                int input;
                if (sscanf(stdin_buffer, "%d%c", &input, &dummy) == 1)
                {
                    buffer = (char) input;
                    write(child_input_stream, &buffer, sizeof(char));
                    child_available_input--;
                }
            }
        }
    }

    return 0;
}

【讨论】:

    【解决方案2】:

    这个 sn-p 将检查可写管道的另一端是否使用poll(2) 关闭。这适用于 Linux - 我不确定其他操作系统或 POSIX 所说的内容。

    #include <poll.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <unistd.h>
    
    bool is_pipe_closed(int fd) {
        struct pollfd pfd = {
            .fd = fd,
            .events = POLLOUT,
        };
    
        if (poll(&pfd, 1, 1) < 0) {
            return false;
        }
    
        return pfd.revents & POLLERR;
    }
    

    【讨论】:

      猜你喜欢
      • 2016-03-26
      • 1970-01-01
      • 2020-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多