无法在 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) 读取 的相关文章

  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • 如何在没有 Control.Invoke() 的情况下从后台线程修改控件属性

    最近 我们遇到了一些旧版 WinForms 应用程序 我们需要更新一些新功能 在专家测试该应用程序时 发现一些旧功能被破坏 无效的跨线程操作 现在 在您认为我是新手之前 我确实有一些 Windows 窗体应用程序的经验 我不是专家 但我认为
  • 如何在我的应用程序中使用 Windows Key

    Like Windows Key E Opens a new Explorer Window And Windows Key R Displays the Run command 如何在应用程序的 KeyDown 事件中使用 Windows
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • 为什么禁止在 constexpr 函数中使用 goto?

    C 14 对你能做什么和不能做什么有规则constexpr功能 其中一些 没有asm 没有静态变量 看起来相当合理 但标准也不允许goto in constexpr功能 即使它允许其他控制流机制 这种区别背后的原因是什么 我以为我们已经过去
  • C# 中值类型和引用类型有什么区别? [复制]

    这个问题在这里已经有答案了 我知道一些差异 值类型存储在堆栈上 而引用类型存储在托管堆上 值类型变量直接包含它们的值 而引用变量仅包含对托管堆上创建的对象位置的引用 我错过了任何其他区别吗 如果是的话 它们是什么 请阅读 堆栈是一个实现细节
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • c# Asp.NET MVC 使用FileStreamResult下载excel文件

    我需要构建一个方法 它将接收模型 从中构建excel 构建和接收部分完成没有问题 然后使用内存流导出 让用户下载它 不将其保存在服务器上 我是 ASP NET 和 MVC 的新手 所以我找到了指南并将其构建为教程项目 public File
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • 使用向量的 merge_sort 在少于 9 个输入的情况下效果很好

    不知何故 我使用向量实现了合并排序 问题是 它可以在少于 9 个输入的情况下正常工作 但在有 9 个或更多输入的情况下 它会执行一些我不明白的操作 如下所示 Input 5 4 3 2 1 6 5 4 3 2 1 9 8 7 6 5 4 3
  • 如何在 Team Foundation 上强制发表有意义的签入评论?

    我有一个开发团队有一个坏习惯 他们写道poor签入评论 当我们必须在团队基础上查看文件的历史记录时 这使得它成为一场噩梦 我已经启用了变更集评论政策 这样他们甚至可以在签到时留下评论 否则他们不会 我们就团队的工作质量进行了一些讨论 他们很
  • 使用 LINQ 查找列表中特定类型的第一个元素

    使用 LINQ 和 C 在元素列表中查找特定类型的第一个项目的最短表示法是什么 var first yourCollection OfType
  • 在 MacO 和 Linux 上安装 win32com [重复]

    这个问题在这里已经有答案了 我的问题很简单 我可以安装吗win32com蟒蛇API pywin32特别是 在非 Windows 操作系统上 我一直在Mac上尝试多个版本pip install pywin32 都失败了 下面是一个例子 如果你
  • 在Linux中创建可执行文件

    我计划做的一件事是编写 非常简单的 Perl 脚本 并且我希望能够在不从终端显式调用 Perl 的情况下运行它们 我明白 要做到这一点 我需要授予他们执行权限 使用 chmod 执行此操作非常简单 但它似乎也是一个稍微费力的额外步骤 我想要
  • 用 C 实现 Unix shell:检查文件是否可执行

    我正在努力用 C 语言实现 Unix shell 目前正在处理相对路径的问题 特别是在输入命令时 现在 我每次都必须输入可执行文件的完整路径 而我宁愿简单地输入 ls 或 cat 我已经设法获取 PATH 环境变量 我的想法是在 字符处拆分
  • Bing 地图运行时错误 Windows 8.1

    当我运行带有 Bing Map 集成的 Windows 8 1 应用程序时 出现以下错误 Windows UI Xaml Markup XamlParseException 类型的异常 发生在 DistanceApp exe 中 但未在用户
  • C++ 成员函数中的“if (!this)”有多糟糕?

    如果我遇到旧代码if this return 在应用程序中 这种风险有多严重 它是一个危险的定时炸弹 需要立即在应用程序范围内进行搜索和销毁工作 还是更像是一种可以悄悄留在原处的代码气味 我不打算writing当然 执行此操作的代码 相反
  • 如何连接字符串和常量字符?

    我需要将 hello world 放入c中 我怎样才能做到这一点 string a hello const char b world const char C string a hello const char b world a b co
  • 不同类型的指针可以互相分配吗?

    考虑到 T1 p1 T2 p2 我们可以将 p1 分配给 p2 或反之亦然吗 如果是这样 是否可以不使用强制转换来完成 或者我们必须使用强制转换 首先 让我们考虑不进行强制转换的分配 C 2018 6 5 16 1 1 列出了简单赋值的约束

随机推荐

  • 首选的跨平台 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