【问题标题】:Unix : What happens to a pipe(half-duplex) if the process diesUnix:如果进程死亡,管道(半双工)会发生什么
【发布时间】:2016-05-19 06:20:17
【问题描述】:

我试图证明我的一个疑问,两个不相关的进程可以共享半双工管道的fd并进行通信。

我为此创建了两个程序。但后来我又问了一个问题,如果进程死亡,管道会发生什么?因为当我打印出消息时,我的读者收到了一些垃圾消息。

作家

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int fd[2];
    char str[] = "hello\n";
    if(pipe(fd) < 0)
        perror("Pipe creation failed\n");

    //Since i am a writer, i should close the reading end as a best practice
    close(fd[0]);

    /*
    The processes need not to be related processes, in order to use the half duplex pipes. fd is just a number/identifier
    which can be shared across different processes 
    */
    printf("Hey there !!! use this file descriptor for reading : %d\n", fd[0]);

    //writing message   
    write(fd[1],str,strlen(str)+1);
    return 0;
}

阅读器

#include <stdio.h>
#include <unistd.h>

int main()
{
    int fd,bytesRead;
    char buffer[1024];

    printf("please enter the fd :");
    scanf("%d",&fd);

    bytesRead = read(fd,buffer,1024);

    printf("Bytes Read : %d\nMessage : %s\n", bytesRead, buffer);
    return 0;
}

【问题讨论】:

  • 行不通。 fd 编号是进程本地的,因此不相关的进程无法共享它们。 (我的标准输入是我的!打开 fd 0 无法读取它。)使用命名管道或 fifo。
  • 阅读器没有“读取垃圾值”,阅读器无法读取任何内容,而您没有注意到是因为您没有检查错误。

标签: c unix pipe


【解决方案1】:

你不能这样做。

文件描述符表是每个进程的;每个进程都有自己单独的一组打开文件描述符(请注意 打开文件描述符,每个进程和 打开文件描述,这是系统范围的区别,在open(2) 中讨论)。如果您想在进程之间共享文件描述符,您需要通过fork(2) 继承它,或者通过sendmesg(2) 通过unix(7) 域套接字将其传递给SCM_RIGHTS,并在cmesg(3) 标头中使用SCM_RIGHTS

(在 Linux 上,您还可以传递到 /proc/[PID]/fd/... 的路径,其他系统可能有自己的非便携式等效项)。

在您的情况下发生的事情是读取失败(您给它一个未打开的文件描述符),使您的缓冲区带有未初始化的垃圾。由于不检查返回值,所以你永远不知道它失败了。

【讨论】:

  • 哦,谢谢...pass it through a unix(7) domain socket via sendmesg(2) with SCM_RIGHTS in the cmesg(3) header 你能指出一些好的教程来说明如何做到这一点或发布一些代码sn-p吗?
  • @InsaneCoder:点击cmesg(3) 并按Ctrl+F "SCM_RIGHTS"。
【解决方案2】:

在管道手册页中,

pipe() 创建一个管道,一个可用于进程间通信的单向数据通道。数组 pipefd 用于返回两个引用管道末端的文件描述符。 pipefd[0] 指的是管道的读取端。 pipefd[1] 指的是管道的写端。写入管道写入端的数据由内核缓冲,直到从管道的读取端读取。

管道主要用于相关进程(父子进程)。您不能将管道用于非相关进程。

在相关过程中,一端是封闭的。在另一端进程获得 SIGPIPE 信号。

使用管道的示例程序:-

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

   int
   main(int argc, char *argv[])
   {
       int pipefd[2];
       pid_t cpid;
       char buf;

       if (argc != 2) {
        fprintf(stderr, "Usage: %s <string>\n", argv[0]);
        exit(EXIT_FAILURE);
       }

       if (pipe(pipefd) == -1) {
           perror("pipe");
           exit(EXIT_FAILURE);
       }

       cpid = fork();
       if (cpid == -1) {
           perror("fork");
           exit(EXIT_FAILURE);
       }

       if (cpid == 0) {    /* Child reads from pipe */
           close(pipefd[1]);          /* Close unused write end */

           while (read(pipefd[0], &buf, 1) > 0)
               write(STDOUT_FILENO, &buf, 1);

           write(STDOUT_FILENO, "\n", 1);
           close(pipefd[0]);
           _exit(EXIT_SUCCESS);

       } else {            /* Parent writes argv[1] to pipe */
           close(pipefd[0]);          /* Close unused read end */
           write(pipefd[1], argv[1], strlen(argv[1]));
           close(pipefd[1]);          /* Reader will see EOF */
           wait(NULL);                /* Wait for child */
           exit(EXIT_SUCCESS);
       }
   }

上面的程序创建了一个管道,然后fork(2)s创建了一个子进程;孩子继承了一组重复的文件描述符,这些文件描述符引用了同一管道。在 fork(2) 之后,每个进程都会关闭管道不需要的描述符(请参阅 pipe(7))。然后父进程将程序命令行参数中包含的字符串写入管道,子进程从管道中一次读取一个字节,并将其回显到标准输出中。

【讨论】:

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