无法在 Linux 中阻止从命名管道 (FIFO) 读取

2024-04-30

很奇怪,我似乎无法完成这项工作。这是我的架构:我有一个命名管道,它将在永远运行 root读取器进程和多个应用程序编写器进程。读者进程必须是blocking当作家们在nonblocking。因此,这就是我在阅读器进程中所做的,该进程将运行root特权。

reader.c

#define PIPE_ID "/dev/shm/mypipe"
// This function configures named pipe
void configure_pipe() {
  // So that others can write
  umask(0000);
  if(mkfifo(PIPE_ID, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH | S_IWGRP
        | S_IWOTH) != 0) {
    perror("mkfifo error\n");
  }
}

在主函数中:

int main (int argc, char **argv)
{
  int Process_Pipe_ID;
  configure_pipe();

  // main loop which never ends
  while(1) {
    if((Process_Pipe_ID = open(PIPE_ID, O_RDONLY | O_NONBLOCK)) < 0) {
      perror("pipe file open error\n");
      exit(-1);
    }
    int bytes_read = 0;
    Message_Struct_t msg;

    // loop to keep reading from the pipe if there are messages to be read
    while((bytes_read = read(Process_Pipe_ID, &msg, sizeof(Message_Struct_t))) != 0) {
      if(bytes_read < 0) {
        perror("Pipe reading error in Scheduling agent\n");
        exit(-1);
      }
      printf("Read: %d, first: %d, second: %d\n", bytes_read, msg.first, msg.second);
      fflush(stdout);
    }
    close(Process_Pipe_ID);
  }
}

我希望这位读者不会被屏蔽open但如果管道上有东西,它应该继续从命名管道中读取。那么,如果它收到0意思是EOF(管道中没有可用的内容)那么它应该关闭文件描述符并再次打开它以继续尝试从管道中读取。有点像忙等待 https://en.wikipedia.org/wiki/Busy_waiting.

我期望bytes_read正是sizeof(Message_Struct_t)(24 字节)因为我将 writer 设置为原子。 24字节小于PIPE_BUF, 所以Linux保证 http://man7.org/linux/man-pages/man7/pipe.7.html只要我不超过管道的大小限制,它就是原子的。我绝对不会超出尺寸限制。我的作家programs就像客户一样;他们到来、执行并终止。所以管道的书写面并不总是open。这是我非常简单的作家:

writer.c

void writeInts(int first, int second) {
  Process_Pipe_ID = open(PIPE_ID, O_WRONLY | O_NONBLOCK);
  Message_Struct_t msg;
  msg.first = first;
  msg.second = second;
  int num;
  if((num = write(Process_Pipe_ID, &msg, sizeof(msg))) < 0) {
    perror("Error in writing\n");
    exit(-1);
  }  else
    printf("%d bytes wrote to pipe.\n", num);
  close(Process_Pipe_ID);
}

然而,我得到了非常奇怪的输出。我在屏幕上没有看到任何内容(对于reader.c)直到我按下enter。当我按下回车键时,我得到以下信息:

Read: 1, first: 0, second: 0
Read: 1, first: 0, second: 0
Read: 1, first: 0, second: 0

当我按其他键然后enter,我明白了:

Read: 1, first: 0, second: 0
aa
Read: 3, first: 0, second: 0
aaa
Read: 4, first: 0, second: 0

我不知道到底发生了什么以及如何解决这个问题。我想要阻塞读取,而写入者是非阻塞和原子的。我进行了很多搜索,然后编写了代码,但很奇怪我无法让它工作。


很奇怪,我似乎无法完成这项工作。

嗯,不是真的。你有奇怪的要求和脆弱的断言,你试图遵循。

读取器进程必须是阻塞的。

不……你为什么要做出这样的限制?考虑

ssize_t blocking_read(fd, void *buf, size_t len)
{
    struct timeval  timeout;
    fd_set          fds;
    int             r;
    ssize_t         n;

    /* First, do a speculative read, just in case
       there is data already available. */
    n = read(fd, buf, len);
    if (n >= 0)
        return n;
    else
    if (n != -1) {
        /* Paranoid check, will never happen .*/
        errno = EIO;
        return -1;
    } else
    if (errno != EAGAIN && errno != EWOULDBLOCK)
        return -1;

    /* Wait for data to become available. */
    FD_ZERO(&fds);
    while (1) {
        FD_SET(fd, &fds);
        timeout.tv_sec = 60; /* One minute */
        timeout.tv_usec = 0; /* and no millionths of seconds */

        r = select(fd + 1, &fds, NULL, NULL, NULL, &timeout);
        if (r < 0)
            return -1; /* errno set by select() */
        else
        if (!r)
            continue;  /* Timeout */

        n = read(fd, buf, len);
        if (n >= 0)
            return n;
        else
        if (n != -1) {
            /* Paranoid check, will never happen .*/
            errno = EIO;
            return -1;
        } else
        if (errno != EAGAIN && errno != EWOULDBLOCK)
            return -1;
    }
}

它的作用类似于对阻塞和非阻塞描述符的阻塞读取。如果没有发生任何事情,它会每分钟唤醒一次,但您可以对其进行调整,直到它足够长而无关紧要。 (我会考虑一秒到 86400 秒之间的值,大约一天。任何更长的时间都是愚蠢的。记住这是一个超时,而不是普通的睡眠:任何信号传递或传入数据都会立即唤醒它。)

这样,您就可以最初以 0400 模式创建 FIFO(r--------), 打开它O_RDONLY | O_NONBLOCK在阅读器中,然后使用例如fchmod(fifofd, 0222)更改其模式(0222 =-w--w--w-)以允许作家。这些都不会阻碍。在读取器准备好之前,写入器打开 FIFO 的任何尝试都不会成功。

读写器不打开和关闭FIFO;它只是不停地打电话blocking_read().

如果写入器打开 FIFO 非阻塞只写(O_WRONLY | O_NONBLOCKING),他们会失败errno = ENXIO如果没有读者,或者有errno = EACCES如果阅读器正在运行但尚未准备好。当有读者时,写入就会成功,除非读者跟不上。 (当读取器的缓冲区已满时,写入器将收到错误errno = EAGAIN or errno = EWOULDBLOCK.)

写入者可以轻松地进行非阻塞写入,并使用可自定义的超时来等待写入成为可能;这是一个非常相似的功能blocking_read() above.

我希望 bytes_read 恰好等于 sizeof(Message_Struct_t) (24 字节),因为我将 writer 设置为原子。 24 字节小于 PIPE_BUF,因此只要不超过管道的大小限制,Linux 就保证它是原子的。

也许在最佳条件下。

例如,如果恶意用户执行以下操作:echo 1 > your_pipe当作者在写消息时,你就失去了消息的边界。读取器获得两个字节(1和换行符)和消息的初始部分,下一次读取将获取该消息的最后两个字节和下一条消息的初始部分,只要写入者写入套接字的速度与读取器读取的速度一样快或更快。

由于管道和 FIFO 永远不会保留消息边界,因此您的方法极其脆弱。更好的方法是使用保留消息边界的数据报套接字。

我绝对不会超出尺寸限制。我的作家程序就像客户;他们到来、执行并终止。因此管道的写入侧并不总是打开的。

您很容易超出大小限制。

只有一个管道缓冲区,如果写入者数量多于读取者可以跟上的数量,则它可能会变满(因此非阻塞写入会失败)。很容易导致这种情况发生:例如,如果读者这样做anything处理数据,使用两个并发编写器(如 Bashfor ((;;)) ; do printf 'message' > fifo ; done) will填充缓冲区,并导致任何非阻塞编写器失败errno = EAGAIN or errno = EWOULDBLOCK.

这不仅仅是理论上的;而且是现实的。在实践中使用 Bash 和 mknod 很容易证明。


我有一种感觉,OP 正在构建一场灾难,等待他们当前的需求组合发生,特别是使用管道(或 FIFO)进行数据报传输。

就我个人而言,我会使用Unix域数据报套接字 http://man7.org/linux/man-pages/man7/unix.7.html绑定到路径名,可能/var/run/yourservice。这将保证消息边界(两个不同的消息不会混合,就像管道或 FIFO 那样)。读者和作者都可以使用辅助数据 http://man7.org/linux/man-pages/man3/cmsg.3.html通过SCM_CREDENTIALS http://man7.org/linux/man-pages/man7/unix.7.html,它允许读取器验证写入器使用的用户 ID 和组 ID。

(编写者可以选择真实身份或有效身份。内核始终验证SCM_CREDENTIALS辅助消息字段,并且不会允许发送错误的数据。换句话说,SCM_CREDENTIALS辅助消息字段将始终是正确的在消息发送的那一刻.)

(请注意,使用数据报协议,读取器无法验证发送消息的进程的详细信息,因为当读取器收到 SCM_CREDENTIALS 辅助消息时,原始发送者可能已执行另一个进程,或者退出,操作系统重用该消息其他新进程的进程 ID。要验证使用哪个可执行文件发送消息,需要一种面向连接的协议,例如 Unix 域流套接字,写入器发送两到三条消息,所有消息都具有相同的 SCM_CREDENTIALS 辅助消息. 正确地做到这一点相当棘手,因此大多数程序员认为这种验证很糟糕。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

无法在 Linux 中阻止从命名管道 (FIFO) 读取 的相关文章

  • IEnumerable 的 String.Join(string, string[]) 的类似物

    class String包含非常有用的方法 String Join string string 它从数组创建一个字符串 用给定的符号分隔数组的每个元素 但一般来说 它不会在最后一个元素之后添加分隔符 我将它用于 ASP NET 编码 以用
  • 并行化斐波那契序列生成器

    我正在学习并行化 在一项练习中 我得到了一些我应该提高性能的算法 其中之一是斐波那契数列生成器 array 0 0 array 1 1 for q 2 q lt MAX q array q array q 1 array q 2 我怀疑 这
  • 将 OpenCV Mat 转换为数组(可能是 NSArray)

    我的 C C 技能很生疏 OpenCV 的文档也相当晦涩难懂 有没有办法获得cv Mat data属性转换为数组 NSArray 我想将其序列化为 JSON 我知道我可以使用 FileStorage 实用程序转换为 YAML XML 但这不
  • 在 ASP.NET MVC 中将模型从视图传递到控制器

    我正在 ASP NET MVC 中开发我的第一个应用程序 但遇到了一个我无法解决的问题 即使在阅读了整个互联网之后也是如此 因此 我有几个使用视图模型创建的视图 它们是报告 这些视图模型是根据用户选择标准填充的 我正在尝试构建一种接受模型并
  • C# 处理标准输入

    我目前正在尝试通过命令行断开与网络文件夹的连接 并使用以下代码 System Diagnostics Process process2 new System Diagnostics Process System Diagnostics Pr
  • 全局使用和 .NET Standard 2.0

    我最近意识到我可以使用 C 10 功能文件范围的命名空间在 NET Standard 2 0 项目中也可以通过设置
  • 带有运算符语法的错误消息,但不带有函数语法的错误消息

    为什么我在调用 unary 时收到错误消息 使用运算符语法 如果我用函数语法调用它就可以了 现场演示 https godbolt org z j7AbeQ template
  • 将日期时间转换为指定格式

    我有这个日期格式yy MM dd HH mm ss ex 12 02 21 10 56 09 问题是 当我尝试使用以下代码将其转换为不同格式时 CDate 12 02 21 10 56 09 ToString MMM dd yyyy HH
  • 在 C++11 中移出 stdpriority_queue 的元素

    最小的工作示例 include
  • 如何在 C# 中获取 Json 数组?

    我有一个像这样的 Json 字符串 我想将它加载到 C 数组中 当我尝试这样做时 我收到异常 我的字符串 customerInformation customerId 123 CustomerName Age 39 Gender Male
  • 将错误代码映射到 C++ 中的字符串

    将错误代码从枚举映射到字符串的更有效方法是什么 在 C 中 例如 现在我正在做这样的事情 std string ErrorCodeToString enum errorCode switch errorCode case ERROR ONE
  • C++ 到 C# 事件处理

    所以我有我的C WinForm 应用程序 我从中调用我的C CLI MFC dll图书馆 但也有一些events在我的 C 库上 甚至此事件也发生在该库的本机 非 CLI 部分 我需要从我的 C 应用程序调用一些代码 并获取一些有关此事件的
  • 快速像素绘图库

    我的应用程序以每像素的方式生成 动画 因此我需要有效地绘制它们 我尝试过不同的策略 库 但结果并不令人满意 尤其是在更高分辨率的情况下 这是我尝试过的 SDL 好的 但是慢 OpenGL 像素操作效率低下 xlib 更好 但仍然太慢 svg
  • C 中使用 getrandom 实现随机浮点数

    我试图生成一个介于 0 和 1 之间的随机浮点数 无论是在 0 1 还是 0 1 对我来说都不重要 网上关于此的每个问题似乎都涉及rand 呼叫 播种time NULL 但我希望能够每秒多次调用我的程序 并每次都获得不同的随机数 这引导我找
  • Autoconf 问题:“错误:C 编译器无法创建可执行文件”

    我正在尝试使用 GNU 自动工具构建一个用 C 编写的程序 但显然我设置错误 因为当configure运行 它吐出 configure error C compiler cannot create executables 如果我看进去con
  • Linq.Select() 中的嵌套表达式方法调用

    I use Select i gt new T 每次手动点击数据库后将我的实体对象转换为 DTO 对象 以下是一些示例实体和 DTOS 用户实体 public partial class User public int Id get set
  • 无法识别解决方案文件夹中的 Visual Studio 2017 Nuget.config

    我在使用 Visual Studio 2017 时遇到问题 新的解决方案不断引用 C Users yopa AppData Roaming NuGet Nuget config 中意外位置的 Nuget config 文件 我已将 nuge
  • 这种尺寸对齐是如何工作的

    对于所提供的评论 我无法理解以下代码 这段代码的作用是什么 以及等效的代码是什么8 aligned segment size must be 4 aligned attr gt options ssize 3 Here ssize is o
  • SQL Server“未找到网络路径”在不同环境中随机且不频繁地发生

    类似 如果不是同一个问题 随机遇到网络路径未找到异常 https stackoverflow com questions 38696448 network path not found exception encountered rando
  • C++ Boost ASIO 简单的周期性定时器?

    我想要一个非常简单的周期性计时器每 50 毫秒调用我的代码 我可以创建一个始终休眠 50 毫秒的线程 但这很痛苦 我可以开始研究用于制作计时器的 Linux API 但它不可移植 I d like使用升压 我只是不确定这是否可能 boost

随机推荐

  • 首选的跨平台 IPC Perl 模块是什么?

    我想创建一个简单的 IO 对象 它代表一个向另一个程序打开的管道 我可以在应用程序运行时定期写入另一个程序的 STDIN 我希望它是防弹的 因为它可以捕获所有错误 并且是跨平台的 我能找到的最佳选择是 open sub io read lo
  • 如何避免 Selenium 中的“StaleElementReferenceException”?

    我正在使用 Java 实现大量 Selenium 测试 有时 我的测试由于以下原因失败StaleElementReferenceException https developer mozilla org en US docs Web Web
  • App Store 上是否允许嵌入 dylib 的 iOS 8 应用程序?

    iOS 8 现在支持动态框架 是否意味着 App Store 提交允许这样做 似乎以前的开发人员能够在内部应用程序中使用 dylib 但在提交到 App Store 的内容中使用它们会导致您被拒绝 情况仍然如此 还是 iOS 8 中的这一更
  • 如何在快速人工智能中获得给定测试集的预测并计算准确性?

    我正在尝试加载由导出的学习者learn export 我想针对测试集运行它 我希望我的测试集有标签 以便我可以测量其准确性 这是我的代码 test src TextList from df df path cols texts split
  • 重新审视混合字符串值的字母数字排序

    请注意 我之前提出了一个非常相似的问题 但要求已发生变化 对混合字符串值进行字母数字排序 https stackoverflow com questions 3842719 alphanumeric sort on mixed string
  • TypeScript 中多个互斥参数

    给定以下 JavaScript 函数 function x foo fooId bar barId 我想将其转换为 TypeScript 以便调用者必须传入foo or fooId 但不能两者兼而有之 同样对于bar and barId 例
  • 在 python 中将 blob 保存到文件中

    我正在尝试将通过 ajax 发送的 blob 保存为 python 中的文件 以前经历过这个Python 如何在二进制和 Base 64 之间相互转换 https stackoverflow com questions 5305456 py
  • 如何配置 Ransack Rails Gem 以添加 NULLS LAST 进行排序

    我希望兰萨克总是添加NULLS LAST这会将空值放在排序列的最后 有办法做到这一点吗 我在 github 上开了一个问题 https github com activerecord hackery ransack issues 443 h
  • 从 Windows 命令行连接到 websocket

    是否可以从 Windows 命令行连接到 websocket 我已经从 Mac 终端使用了 WSCAT 但我似乎找不到替代方案 任何帮助 将不胜感激 Windows 中没有内置可与 WebSocket 配合使用的工具 虽然你可以使用teln
  • 在 Java 中从复杂的 HTML 表格中提取数据到二维数组

    如何转换 HTML 表格带有 colspan 和 rowspanJava中的二维数组 矩阵 我在 Python 和 jQuery 中找到了很好的解决方案 但在 Java 中却没有 只有通过 jsoup 的非常简单的表 XSLT 有一种很好的
  • R——对缺失值的二维数据集进行插值的方法

    我目前正在使用 Akima 插值例程来进行二维线性插值 我目前正在尝试通过排除不良数据点和依赖于它们的插值来尽可能地进行线性插值 我不想做任何样条拟合 只是线性插值 我可以想出两种使用现有的 akima 包来做到这一点的方法 通过将二维数据
  • 身份验证错误:无法响应以下任何质询:{} Android - 401 Unauthorized

    身份验证错误 无法响应以下任何质询 Android 401 Unauthorized 我已从此链接参考在 Android 上使用 HttpPost 和 DefaultHttpClient 时出现身份验证错误 https stackoverf
  • 如何使用TortoiseSVN更改密码?

    我需要更改我的 SVN 密码 我正在使用 TortoiseSVN 客户端 我找不到密码更改或添加用户选项 是否可以 是否有任何解决方法或命令行语法来创建 SVN 用户或编辑用户 更改访问 Subversion 的密码 通常这将由您的 Sub
  • html 表单在表单提交时发送 GET 而不是 POST

    请原谅我的网络技能 但我有一个非常基本的问题 我有这个 html 表单 理想情况下应该用 post 调用我的 login url 但由于某种原因 它总是向该 url 发送 get 请求并失败 我不明白这是怎么发生的 这是我的 html 表单
  • 编程 Jersey 资源中的路径参数

    我正在使用 Jersey 的编程 API 描述here https jersey github io documentation latest resource builder html在运行时从配置文件动态创建配置资源 我创建这些资源的代
  • 如何使用 Neon SIMD 将无符号字符转换为有符号整数

    如何转换变量的数据类型uint8 t to int32 t使用霓虹灯 我找不到执行此操作的任何内在因素 假设您想要将 16 x 8 位整数的向量转换为 4 个 4 x 32 位整数的向量 您可以通过首先解压缩为 16 位 然后再次解压缩为
  • 如何使用plotly包绘制用一种缩放颜色着色的饼图

    我有这个示例数据框 gt Data Produits Pourcentages 1 Cr me de jour 27 10 2 s rum 14 50 3 Cr me de nuit 13 80 4 masque 8 82 5 d maqu
  • 使用 openopenPanel() 方法打开 matAutocomplete

    我正在使用 Angular Material 的垫自动完成 https material angular io components autocomplete overview组件 并根据docs https material angula
  • 使用 ThreeJS 获取球体纹理上的点击位置

    目前 我有一个带有纹理的球体 它绕 y 轴旋转 我还有在 3D 空间中单击的位置 以及球体上的旋转位置 我认为 目标 获取纹理上的位置 例如 我想获取我点击的图像的哪个方块 参见示例球体和下图 在实践中 我不会使用此图像 但我觉得这将是一个
  • 无法在 Linux 中阻止从命名管道 (FIFO) 读取

    很奇怪 我似乎无法完成这项工作 这是我的架构 我有一个命名管道 它将在永远运行 root读取器进程和多个应用程序编写器进程 读者进程必须是blocking当作家们在nonblocking 因此 这就是我在阅读器进程中所做的 该进程将运行ro