【问题标题】:Missing message in FIFOFIFO 中缺少消息
【发布时间】:2021-05-17 13:19:17
【问题描述】:

我已经构建了一个带有 FIFO(命名管道)的 IPC 程序。

一个非常有趣的问题是我的书面信息可能会丢失。 以下是代码sn-p。

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main()
{
    char buffer[2000] = {0};
    strcpy(buffer, "abc");
    char *write_path = "test-123";
    mkfifo(write_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    int dummy = open(write_path, O_RDONLY | O_NONBLOCK);
    int fd = open(write_path, O_WRONLY | O_NONBLOCK);

    int bytes = write(fd, buffer, 2000);
    printf("write_path:%d %s %d %d\n", bytes, write_path, dummy, fd);
    close(fd);
    close(dummy);
}

如何复制?

  1. ubuntu 1804

  2. gcc main.c -o main

  3. ./main

它将处于未决状态。我认为,它应该输出 abc。

【问题讨论】:

  • 我不知道为什么会这样。
  • 你能分享一个minimal reproducible example吗?您当前的代码不足以确定问题。
  • 许多人确信所有其他功能都运行良好,但考虑到您的调试存在缺陷或 Linux 处理管道的方式存在根本错误,我会怀疑前者。
  • 对不起,我的代码包含数千个代码,是我公司的私人项目。
  • fd。我无法编辑问题。我正在尝试用最少的代码重现问题。

标签: c linux system


【解决方案1】:

您在非阻塞模式下打开FIFO,这意味着 I/O 函数可能会因(-1 和 errno 设置为)EAGAIN 或 EWOULDBLOCK 而失败,而不是阻塞(等待)函数完成。在 Linux 中,FIFO 的开放描述符与pipes 具有相同的语义。

如果您费心检查错误(每当 open() 或 write() 返回 -1 指示错误时打印 strerror(errno)),您已经知道失败的原因。照原样,显示的代码甚至没有重现问题。以下代码,

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    const char   *msg = "The Example Message.\n";
    const size_t  msg_len = strlen(msg);
    int           rfd, wfd;

    if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        const char *mypath = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", mypath);
        fprintf(stderr, "       %s FIFO\n", mypath);
        fprintf(stderr, "\n");
        fprintf(stderr, "This program tests opening and writing a short message to the FIFO.\n");
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    rfd = open(argv[1], O_RDONLY | O_NONBLOCK);
    if (rfd == -1) {
        fprintf(stderr, "%s: Cannot open FIFO for reading: %s.\n", argv[1], strerror(errno));
        return EXIT_FAILURE;
    }

    wfd = open(argv[1], O_WRONLY | O_NONBLOCK);
    if (wfd == -1) {
        fprintf(stderr, "%s: Cannot open FIFO for writing: %s.\n", argv[1], strerror(errno));
        return EXIT_FAILURE;
    }

    ssize_t  n = write(wfd, msg, msg_len);
    if (n == -1) {
        fprintf(stderr, "%s: Cannot write to FIFO: %s.\n", argv[1], strerror(errno));
        return EXIT_FAILURE;
    } else
    if (n != (ssize_t)msg_len) {
        fprintf(stderr, "%s: Wrote only %zd of %zu bytes to the FIFO.\n", argv[1], n, msg_len);
        return EXIT_FAILURE;
    } else {
        printf("Success!\n");
    }

    close(wfd);
    close(rfd);

    return EXIT_SUCCESS;
}

干净地编译 (gcc -Wall -Wextra -O2 source.c -o binary),并实现所示代码(但带有错误检查,并使用命令行参数作为要打开的 FIFO 的名称)。但是,它不会验证命名文件是否为 FIFO。如果在不存在的 FIFO 上运行,它会报错 “FIFO:无法打开 FIFO 进行读取:没有这样的文件或目录。”

如果你创建了 FIFO (mkfifo test-fifo) 并运行测试程序,没有错误;唯一的输出是“成功!”

如果你创建了 FIFO,并且反复向它写入数据但从未从中读取数据,则在某些时候 FIFO 的内核缓冲区已满,运行测试程序将报告 "test-fifo: Cannot write到 FIFO:资源暂时不可用。”(这意味着 write() 返回 -1 且 errno==EWOULDBLOCK 或 errno==EAGAIN)。

你可以通过运行来模拟这个,例如bash -c 'exec 4&lt;&gt;test-fifo ; dd if=/dev/zero of=test-fifo bs=1 oflag=nonblock status=progress',它打开test-fifo FIFO 读写(在Linux 中总是成功)到描述符4,然后运行dd 用零填充它,使用Bash(子)shell。在我的系统上,一个 FIFO 可以容纳 65536 字节 (64k)。像这样填充 FIFO 后,运行测试程序 (./binary test-fifo) 将失败。

希望您能看到错误检查的重要性和实用性。这不是您应该考虑的“我稍后会在/如果我有时间的时候添加它们”;它们也是重要的开发工具。

【讨论】:

  • 我认为FIFO是持久的,但事实并非如此。我说'cat
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-24
  • 2016-10-08
  • 1970-01-01
  • 1970-01-01
  • 2016-08-07
  • 1970-01-01
  • 2018-03-06
相关资源
最近更新 更多