【问题标题】:C - Unix pipe, close() affects the output?C - Unix管道,close()影响输出?
【发布时间】:2015-11-08 06:02:20
【问题描述】:

我在使用 unix 管道时遇到了一个奇怪的错误。我写了一个简短的程序来展示这个问题。

代码如下:

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>


int main(int argc, char*args[]){
    int fd[2];
    pipe(fd);
    int pid = fork();
    if(pid<0){
        perror("fork()");
        exit(1);
    }else if(pid == 0){
        close(fd[0]);
        const char* msg = "I'm here\n";
        size_t len = strlen(msg) + 1; // +1 for null char
        write(fd[1], msg, len);
        while(1){/*does other work*/}
    }else{
        close(fd[1]);
    }
    pid = fork();
    if(pid<0){
        perror("fork()");
        exit(1);
    }else if(pid == 0){
        close(fd[0]);
        const char* msg = "I'm here\n";
        size_t len = strlen(msg) + 1; // +1 for null char
        write(fd[1], msg, len);
        while(1){/*does other work*/}
    }else{
        close(fd[1]);
    }
    //close(fd[1]);
    sleep(5);

    const char* msg = "I'm here\n";
    size_t len = strlen(msg) + 1; // +1 for null char
    char str[len];
    fcntl(fd[0],F_SETFL, O_NONBLOCK);
    if(read(fd[0], str, len)<=0){
        printf("Nothing from child\n"); 
    }
    /*does other work*/
    fcntl(fd[0],F_SETFL, O_NONBLOCK);
    if(read(fd[0], str, len)<=0){
        printf("Nothing from child\n"); 
    }
    printf("finished read\n");
    /*does other work*/
    wait(NULL);
    return 0;
}

父进程派生出两个子进程并创建一个管道。两个子进程写入管道,父进程从管道读取。

父进程应该恢复子进程写入的所有信息。

但是,当我运行上述程序时,父级打印出来

Nothing from child
finished read

为什么?

有趣的是,如果我这样写一个 close(fd[1]):

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>


int main(int argc, char*args[]){
    int fd[2];
    pipe(fd);
    int pid = fork();
    if(pid<0){
        perror("fork()");
        exit(1);
    }else if(pid == 0){
        close(fd[0]);
        const char* msg = "I'm here\n";
        size_t len = strlen(msg) + 1; // +1 for null char
        write(fd[1], msg, len);
        while(1){/*does other work*/}
    }else{

    }
    pid = fork();
    if(pid<0){
        perror("fork()");
        exit(1);
    }else if(pid == 0){
        close(fd[0]);
        const char* msg = "I'm here\n";
        size_t len = strlen(msg) + 1; // +1 for null char
        write(fd[1], msg, len);
        while(1){/*does other work*/}
    }else{

    }
    close(fd[1]);//HERE IS THE DIFFERENCE
    sleep(5);

    const char* msg = "I'm here\n";
    size_t len = strlen(msg) + 1; // +1 for null char
    char str[len];
    fcntl(fd[0],F_SETFL, O_NONBLOCK);
    if(read(fd[0], str, len)<=0){
        printf("Nothing from child\n"); 
    }
    /*does other work*/
    fcntl(fd[0],F_SETFL, O_NONBLOCK);
    if(read(fd[0], str, len)<=0){
        printf("Nothing from child\n"); 
    }
    printf("finished read\n");
    /*does other work*/
    wait(NULL);
    return 0;
}

它有效,如果我根本没有 close(fd[1]) 也有效。我很迷茫,为什么 close() 的位置会影响管道的读取?

【问题讨论】:

  • 为什么你认为关闭fd[1]两次就可以了?
  • 在第一个示例中,您在 fork 第二个孩子之前关闭了管道的写入端,这样孩子就无法写入管道。你通过移动关闭来修复它。有什么问题?
  • 啊,我明白了。谢谢,我忘了老二会写完结的!
  • 您应该始终测试write 的返回计数。特别是对于管道和套接字,partial write 确实会发生

标签: c unix pipe


【解决方案1】:

我不确定这是否是你的问题,但这种说法显然是错误的:

write(fd[1], "I'm here\n", 256);

您正在从源缓冲区写入 256 个字节,这是一个非常短的静态字符串。我想你想说的是:

const char* msg = "I'm here\n";
size_t len = strlen(msg); // could be strlen(msg)+1 - see discussion in comments.
write(fd[1], "I'm here\n", len);

【讨论】:

  • 呃...您最后一次通过write() 或任何其他I/O 函数输出空终止字节 是什么时候?在终端上,这没问题,但在使用管道时,这可能会导致严重的问题。
  • 好点。一个真正的协议将包括读写器之间的一些正式方式,以就如何检测字符串的结尾达成一致。但是由于他只是调用read 并使用比接收消息所需的缓冲区更大的缓冲区,他怎么知道消息中有多少字节?这就是我最初包含 null 的原因 - 这样他的 read 就不会留下没有 null 字符的缓冲区。
  • 你是对的。但是,如果他不写两个或更多单独的字符串,那么他可以简单地read 直到 EOF 就可以了。
  • @KemyLand 可以将任何字节写入管道,包括空字节。 Unix 文件中没有特殊字节。
  • 您好,感谢您的帮助,我已经实施了您的建议(我认为),但它仍然会产生相同的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
  • 2011-12-11
  • 1970-01-01
相关资源
最近更新 更多