【问题标题】:Executable for the writer file with mkfifo halts可用于具有 mkfifo 停止的写入器文件
【发布时间】:2017-06-20 02:35:49
【问题描述】:

据我了解,根据https://linux.die.net/man/3/mkfifo

我得到一个暗示,我必须有读写器文件,以便

利用管道文件。下面的源码是writer文件,

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>

int main(){

    int fd;
    char *myfifo = "./myfifo";

    mkfifo(myfifo, 0777);
    fd = open(myfifo, O_WRONLY);    


    int PID = fork();   

    if(PID == 0){

        execl("./reader.o", "reader", (char*)NULL);

    }

    write(fd, "Rock and roll baby\0", sizeof("Rock and roll baby"));
    close(fd);
    unlink(myfifo);

    return 0;

}

下面提供的来源是阅读器文件。

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX_BUF 1024

int main(){

    int fd;

    char* myfifo = "./myfifo";
    char buf[MAX_BUF];

    fd = open(myfifo, O_RDONLY);
    read(fd, buf, MAX_BUF);
    write(STDOUT_FILENO, buf, MAX_BUF);

    close(fd);
    exit(EXIT_SUCCESS);

    return 0;

}

当运行编写器文件的可执行文件时,命令提示符进入

在打印换行符后停止。我对这个问题的假设是因为

编写器文件中的open() 无法检测到管道文件,

是这样吗?

谢谢。

【问题讨论】:

  • 除非您对open() 的参数进行欺骗,否则读取器和写入器都不会从open() 调用返回,直到有另一个进程打开了FIFO 的“另一端”。也就是说,读者等到有写者,写者等到有读者。因此,您需要在尝试打开 FIFO 之前 fork 并执行子进程。这也解决了另一个问题——你应该在执行子进程之前关闭 FIFO 的写端。子进程会挂起,因为它有一个用于 FIFO 的写入文件描述符和一个读取文件描述符;没有 EOF。

标签: c unix mkfifo


【解决方案1】:

我建议你应该在fork之前创建FIFO,但只在fork之后打开FIFO。这避免了各种各样的问题。在大多数情况下,我使用write() 向标准错误报告错误;虽然它不如使用fprintf(stderr, …) 方便。

请注意,作者在消息末尾写入一个空字节。读取器获取空字节,但在将结果字符数组(它不再是字符串;字符串末尾有一个终端空字节)写入标准输出之前用换行符覆盖它。如果代码使用&lt;stdio.h&gt; 写入数据(例如printf("%s\n", buf)),则不需要用换行符替换空字节。

writer.c

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

#ifndef READER
#define READER "./reader"
#endif

int main(void)
{
    char *myfifo = "./myfifo";

    if (mkfifo(myfifo, 0777) != 0)
    {
        write(STDERR_FILENO, "Failed to create FIFO\n",
                      sizeof("Failed to create FIFO\n") - 1);
    }

    int PID = fork();

    if (PID == 0)
    {
        execl(READER, "reader", (char *)NULL);
        write(STDERR_FILENO, "Failed to execute reader\n",
                      sizeof("Failed to execute reader\n") - 1);
        exit(EXIT_FAILURE);
    }
    if (PID < 0)
    {
        write(STDERR_FILENO, "Failed to fork\n",
                      sizeof("Failed to fork\n") - 1);
        exit(EXIT_FAILURE);
    }

    int fd = open(myfifo, O_WRONLY);
    if (fd < 0)
    {
        write(STDERR_FILENO, "Failed to open FIFO for writing\n",
                      sizeof("Failed to open FIFO for writing\n") - 1);
        unlink(myfifo);
        exit(EXIT_FAILURE);
    }

    write(fd, "Rock and roll baby", sizeof("Rock and roll baby"));
    close(fd);
    unlink(myfifo);
    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        printf("Child %d exited with status 0x%.4X\n", corpse, status);

    return 0;
}

reader.c

#include <fcntl.h>
#include <unistd.h>

#define MAX_BUF 1024

int main(void)
{
    char* myfifo = "./myfifo";
    int fd = open(myfifo, O_RDONLY);
    if (fd < 0)
        write(STDERR_FILENO, "Failed to open FIFO for reading\n",
                      sizeof("Failed to open FIFO for reading\n")-1);
    else
    {
        char buf[MAX_BUF];
        int nbytes = read(fd, buf, MAX_BUF);
        if (nbytes > 0)
        {
            buf[nbytes-1] = '\n';
            write(STDOUT_FILENO, buf, nbytes);
        }
        close(fd);
    }
    return 0;
}

示例输出

Rock and roll baby
Child 43734 exited with status 0x0000

【讨论】:

    【解决方案2】:
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <unistd.h>
    
    int main(){
    
        int fd;
        char *myfifo = "./myfifo";
    
        int PID = fork();   
    
        if(PID == 0){
    
            execl("./reader.o", "reader", (char*)NULL);
    
        }
    
        mkfifo(myfifo, 0777);
        fd = open(myfifo, O_WRONLY);    
    
        write(fd, "Rock and roll baby\0", sizeof("Rock and roll baby"));
        close(fd);
        unlink(myfifo);
    
        return 0;
    
    }
    

    在将代码主体(execl所在的位置)移到

    之上

    mkfifo(),

    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #define MAX_BUF 1024
    
    int main(){
    
        sleep(3);
    
        int fd;
    
        char* myfifo = "./myfifo";
        char buf[MAX_BUF];
    
        fd = open(myfifo, O_RDONLY);
        read(fd, buf, MAX_BUF);
        write(STDOUT_FILENO, buf, MAX_BUF);
    
        close(fd);
    
        exit(EXIT_SUCCESS);
    
        return 0;
    
    }
    

    并且让读者有 sleep() 3 秒,程序开始

    工作;但是,有谁知道这两个程序是否可以打开()管道文件

    恰好同时?

    谢谢。

    【讨论】:

    • 我想也许你应该解释一下为什么你在作者写的字符串中有额外的空字节——这对我来说似乎没有必要(你可以使用相同的字符串两次,而不是使用两个略有不同的字符串)字符串)。
    • 为了节省时间,我决定不在 buf 中标准输出字符串的确切大小。
    • 好吧,你可以使用write(fd, "Rock and roll baby", sizeof("Rock and roll baby"));,而不是write(fd, "Rock and roll baby\0", sizeof("Rock and roll baby"));。这包括在 FIFO 上发送的数据中的空字节。这有它的优点;您需要预先确定长度或使用结束标记。
    • 请原谅我对第一条评论的误解。感谢您的洞察力。
    • NP——无论如何,我看到你大多是从问题中复制代码。我认为您应该在分叉之前创建 FIFO;否则,读者可能会尝试打开仍然不存在的 FIFO 并失败,从而严重破坏鼠标和编码器的最佳计划。不过,公开调用都应该在分叉之后;对于读者来说,这是不可避免的,但对于作者来说却不是。
    猜你喜欢
    • 1970-01-01
    • 2021-08-18
    • 2021-12-19
    • 2014-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-09
    • 2015-11-14
    相关资源
    最近更新 更多