Mac OS 和 FreeBSD 之间 kqueue 处理 fifo 的差异?

2024-02-08

我正在开发一个应用程序,该应用程序使用 fifos 进行 IPC,并使用事件通知 API(例如 epoll 或 kqueue)来监视 fifos 以获取要读取的数据。

应用程序期望,如果 fifo 的写入器终止,则读取器将通过事件通知 API 接收事件,从而允许读取器注意到写入器已终止。

我目前正在将此应用程序移植到 macos,并且我在使用 kqueue 时遇到了一些奇怪的行为。我已经能够创建此行为的重现器:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <sys/errno.h>

static int child() {
    char child_fifo_path[64];
    char parent_fifo_path[64];

    printf("Child %d\n", getpid());

    sprintf(child_fifo_path, "/tmp/child-%d", getpid());
    sprintf(parent_fifo_path, "/tmp/parent-%d", getpid());

    mkfifo(child_fifo_path, 0644);
    mkfifo(parent_fifo_path, 0644);

    int parent_fd = open(parent_fifo_path, O_RDONLY);
    if (parent_fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    unsigned char parent_val;
    read(parent_fd, &parent_val, 1);

    printf("Received %hhx from parent\n", parent_val);

    int child_fd = open(child_fifo_path, O_WRONLY);
    if (child_fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    write(child_fd, &parent_val, 1);

    printf("Wrote %hhx to parent\n", parent_val);

    close(parent_fd);
    close(child_fd);
    return EXIT_SUCCESS;
}

static int parent(pid_t child_pid) {
    char child_fifo_path[64];
    char parent_fifo_path[64];

    printf("Parent %d\n", getpid());

    sprintf(child_fifo_path, "/tmp/child-%d", child_pid);
    sprintf(parent_fifo_path, "/tmp/parent-%d", child_pid);

    int result = -1;
    while (result == -1) {
        struct stat buf;
        result = stat(child_fifo_path, &buf);
        if (result == -1) {
            if (errno != ENOENT) {
                perror("open");
                return EXIT_FAILURE;
            }
        }
    }

    unsigned char val = 20;

    int parent_fd = open(parent_fifo_path, O_WRONLY);
    if (parent_fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    write(parent_fd, &val, 1);

    printf("Wrote %hhx to child\n", val);

    int child_fd = open(child_fifo_path, O_RDONLY);
    if (child_fd == -1) {
        perror("open");
        close(parent_fd);
        return EXIT_FAILURE;
    }

    int kq = kqueue();

    struct kevent event;
    EV_SET(&event, child_fd, EVFILT_READ, EV_ADD, 0, 0, 0);

    result = kevent(kq, &event, 1, NULL, 0, NULL);
    if (result == -1) {
        perror("kevent");
        close(child_fd);
        close(parent_fd);
        return EXIT_FAILURE;
    }

    int done = 0;
    while (!done) {
        memset(&event, 0, sizeof(event));

        printf("Waiting for events\n");

        result = kevent(kq, NULL, 0, &event, 1, NULL);
        if (result == -1) {
            perror("kevent");
            close(child_fd);
            close(parent_fd);
            return EXIT_FAILURE;
        }

        if (event.ident == child_fd) {
            if (event.flags & EV_EOF) {
                printf("Child exited\n");
                done = 1;
            }else if ( event.data > 0 ) {
                unsigned char child_val;

                result = read(child_fd, &child_val, 1);
                if (result == -1) {
                    perror("read");
                    return EXIT_FAILURE;
                }

                printf("Received %hhx from child\n", child_val);
            }
        }
    }

    return EXIT_SUCCESS;
}

int main(int argc, char *argv[]) {

    pid_t child_pid = fork();
    if (child_pid == -1) {
        perror("fork");
        return EXIT_FAILURE;
    }

    if (child_pid) {
        return parent(child_pid);
    } else {
        return child();
    }
}

该重现器派生了一个子进程,它创建了 2 个 fifo:/tmp/parent-$CHILD_PID and /tmp/child-$CHILD_PID。家长要等到/tmp/parent-$CHILD_PID创建,然后向其中写入一个字节。孩子打开了/tmp/parent-$CHILD_PID并阻止读取父级写入的字节。完成后,子进程将通过以下方式将相同的字节写入父进程:/tmp/child-$CHILD_PID。父级使用 kqueue 来观察写入/tmp/child-$CHILD_PID.

这一系列事件运行良好。

当子进程关闭其文件时会出现此问题/tmp/child-$CHILD_PID。我发现这个事件没有通过 kqueue 报告给父级。

最有趣的部分:这段代码在 FreeBSD 上的工作方式正如我所期望的那样。

版本信息:

Mac OS X: 10.11.6

FreeBSD 10.4-RELEASE-p3

在这种情况下,macos 上的 kqueue 和 FreeBSD 上的 kqueue 有区别吗?如果是这样,是否有一些文档记录了这种差异?


这不是您问题的最佳答案,但我希望可以帮助您找到使用时可能影响代码行为的其他差异kqueue https://en.wikipedia.org/wiki/Kqueue之间macOS https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/kqueue.2.html and FreeBSD https://www.freebsd.org/cgi/man.cgi?query=kqueue

就我而言,我使用 kqueueEVFILT_VNODE检查更改,但根据操作系统我需要定义不同的标志打开模式目录 https://github.com/immortal/immortal/blob/9dc21bf711db026874cb52b76e81a59ce234d8a3/kqueue.go#L10当使用syscall.Open

对于 macOS(openmode_darwin.go https://github.com/immortal/immortal/blob/master/openmode_darwin.go)我用这个:

openModeDir  = syscall.O_EVTONLY | syscall.O_DIRECTORY
openModeFile = syscall.O_EVTONLY

对于 FreeBSD (开放模式.go https://github.com/immortal/immortal/blob/master/openmode.go) I use:

openModeDir  = syscall.O_NONBLOCK | syscall.O_RDONLY | syscall.O_DIRECTORY
openModeFile = syscall.O_NONBLOCK | syscall.O_RDONLY

From macOS 文档打开(2) https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/open.2.html,这是标志描述:

O_EVTONLY       descriptor requested for event notifications only

并从,没有O_EVTONLY.

总而言之,这就是我所说的 kqueue:

...
watchfd, err := syscall.Open(dir, openModeDir, 0700)
if err != nil {
    return err
}

kq, err := syscall.Kqueue()
if err != nil {
    syscall.Close(watchfd)
    return err
}

ev1 := syscall.Kevent_t{
    Ident:  uint64(watchfd),
    Filter: syscall.EVFILT_VNODE,
    Flags:  syscall.EV_ADD | syscall.EV_ENABLE | syscall.EV_CLEAR,
    Fflags: syscall.NOTE_WRITE | syscall.NOTE_ATTRIB,
    Data:   0,
}
...

我在用go https://golang.org/,但正如之前提到的,希望可以在处理问题时给你一个想法Kqueue,就我而言,这个简单的标志更改就产生了影响。

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

Mac OS 和 FreeBSD 之间 kqueue 处理 fifo 的差异? 的相关文章

随机推荐

  • 检查 Swift 中的网络状态,找不到接受提供的参数的“~=”重载

    我在用着可达性 h库来检查我的 swift 应用程序中的网络状态 我在 案例 行中遇到了这个问题 找不到接受提供的 重载 论点 我该如何解决它 func checkNetworkStatus var reachability Reachab
  • 为什么这个查询不返回 10 行?

    我在一个唯一的列上连接两个表 这两个表都包含 10 行 我知道桌子cte可以有重复项 但仍然应该有10结果中的记录 请参阅DBFIDLE1 https dbfiddle uk C3fkuh3s with ints as select 1 a
  • 在 Visual Studio for Mac 17.5 预览版中找不到 SDK Microsoft.NET.Sdk

    当我想从 VS 2022 for Mac 17 5 预览版中的模板创建新的 MonoGame iOS 项目时 收到此错误消息 What is missing in Preferences What should I install None
  • ASP.NET 预编译期间出现类型解析错误

    在 NET 3 5 Web 应用程序的 ASP NET 预编译过程中 各种初始化在类型初始值设定项中执行 当环境配置不正确时 类型初始值设定项之一会引发自定义异常 但是 当抛出我们的自定义异常时 aspnet compiler exe 会告
  • 自动调用 Silverlight 行为的 OnDetaching()

    我在 silverlight 控件上使用了多种混合行为和触发器 我想知道是否有任何机制可以自动分离或确保当不再使用控件 即从可视化树中删除 时为行为或触发器调用 OnDetaching 我的问题是 由于其中一种行为 控件存在托管内存泄漏 该
  • 如何设置 ArrayList 中对象的变量值

    我正在执行一项任务 我必须 创建一个具有以下属性 变量的 Employee 类 姓名 年龄 部门 创建一个名为 Department 的类 其中将包含员工列表 A Department 类将有一个方法返回按年龄排序的员工 b 部门的值只能是
  • 如何在Python中给定时间后结束程序运行

    我希望我的 Python 程序运行一个算法给定的秒数 然后打印迄今为止最好的结果并结束 最好的方法是什么 我尝试了以下方法 但没有成功 程序在打印后继续运行 def printBestResult self print self bestR
  • 将 HTML 字符串添加到 OpenXML (*.docx) 文档

    我正在尝试使用 Microsoft 的 OpenXML 2 5 库来创建 OpenXML 文档 一切都运行良好 直到我尝试将 HTML 字符串插入到我的文档中 我已经在网上搜索过 这是我到目前为止所想到的 只截取我遇到问题的部分 Parag
  • wkHTMLtoPDF 支持@page 规则吗?

    我一直在尝试使用这个简单的命令来探索 wkHTML2PDF 的功能 wkhtml2pdf test htm test pdf 它似乎不支持可能包含在附加 CSS 文件中的 page 规则 或者我错过了什么 一些参数的例子 简而言之 答案是否
  • MediaPlayer,仅视频 m3u8 HTML 流有效

    我正在将 MediaPlayer 与 m3u8 音频流一起使用 这会导致日志错误消息 Error 1 1010 当我查看错误代码时 第一个参数似乎没问题 https github com android platform external
  • C# 功能请求:在匿名类型上实现接口

    我想知道需要什么才能使这样的事情发挥作用 using System class Program static void Main var f new IFoo Foo foo Print gt Console WriteLine Foo i
  • python“set.intersection”对于n个集合的时间复杂度

    我想知道该问题的复杂性set intersection蟒蛇 我查看了 python 的文档和在线 wiki 但没有找到该方法对于多组的时间复杂度 The python wiki 关于时间复杂度列表 https wiki python org
  • 为 ComboBox 项目分配值

    我目前正在尝试为 winform 的货币制作一个下拉框 组合框 这是我到目前为止所拥有的 但我注意到下拉框的数据绑定版本有一个特殊选项 所以我想知道是否可以创建与此类似的东西 而无需对整个字符串进行比较或在数据库中创建表 列表和组合框可以包
  • 遍历并修改C json字符串

    在 C 中遍历和修改 JSON 字符串的正确方法是什么 具体来说 我有一个字符串 body buf 打印出来时 print length d n s n body len body len body buf 它看起来像这样 length 1
  • 如何在 Android 中共享文本和图像

    我想分享我的应用程序的内容 文字与图像 对于这个问题 我写了下面的代码 Intent sharingIntent new Intent Intent ACTION SEND sharingIntent setType sharingInte
  • 如何在 Symfony2 中自定义/本地化断言消息?

    当你使用 Assert NotBlank约束并且给定字段为空 那么您会收到错误 该值不应为空 我想在应用程序范围内更改此消息 而不更改 Symfony2 源代码 如何做到这一点 自定义验证错误消息非常简单 但乍一看似乎并不棘手 默认区域设置
  • 从 sql 文件恢复单个表转储

    我有 sql 文件的完整转储 例如dump full sql尺寸的1 3GB 它有一些像这样的表 dancing core spice dancing sea beast forde clear one forde super now 现在
  • 枚举mysql查询中使用的表?

    有没有办法枚举mysql查询中使用的表 可以说我有疑问 SELECT FROM db people people facts pf INNER JOIN db system connections sm ON sm source id pf
  • 如何查看 Chrome 扩展程序的总安装数?

    我正在通过 Chrome 网上商店分发 Chrome 扩展程序 但我无法找到网上商店告诉我我的扩展程序有多少 总安装量 的地方 我想知道自从我第一次上传扩展程序以来总共有多少人安装了它 在谷歌上进行一些快速搜索 在谷歌帮助部分中找到了以下文
  • Mac OS 和 FreeBSD 之间 kqueue 处理 fifo 的差异?

    我正在开发一个应用程序 该应用程序使用 fifos 进行 IPC 并使用事件通知 API 例如 epoll 或 kqueue 来监视 fifos 以获取要读取的数据 应用程序期望 如果 fifo 的写入器终止 则读取器将通过事件通知 API