Mac OS和FreeBSD在fifos的kqueue处理方面有区别吗? [英] Difference in kqueue handling of fifos between Mac OS and FreeBSD?

查看:115
本文介绍了Mac OS和FreeBSD在fifos的kqueue处理方面有区别吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一个将fifos用于IPC并使用事件通知API(例如epoll或kqueue)来监视fifos以便读取数据的应用程序.

I'm working on an application that uses fifos for IPC and uses an event-notification API (such as epoll or kqueue) to monitor the fifos for data to be read.

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

The application expects that if the writer for a fifo terminates that the reader will receive an event via the event notification API, allowing the reader to notice that the writer terminated.

我目前正在将该应用程序移植到macOS,并且kqueue遇到了一些奇怪的行为.我已经能够创建这种行为的复制者:

I'm currently porting this application to macos and I'm running into some odd behavior with kqueue. I've been able to create a reproducer of this behavior:

#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/tmp/child-$CHILD_PID.父级等待直到创建/tmp/parent-$CHILD_PID,然后向其写入一个字节.子级打开/tmp/parent-$CHILD_PID并阻塞以读取父级写入的字节.完成后,子代将通过/tmp/child-$CHILD_PID将相同的字节写入父代.父级使用kqueue观察对/tmp/child-$CHILD_PID的写入.

This reproducer forks a child process, which creates 2 fifos: /tmp/parent-$CHILD_PID and /tmp/child-$CHILD_PID. The parent waits until /tmp/parent-$CHILD_PID is created and then writes a byte to it. The child opens /tmp/parent-$CHILD_PID and blocks to read the byte written by the parent. Once complete, the child goes to write that same byte to the parent via /tmp/child-$CHILD_PID. The parent uses kqueue to observe the write to /tmp/child-$CHILD_PID.

此事件序列工作正常.

当孩子关闭其引用/tmp/child-$CHILD_PID的文件时,就会发生此问题.我看到此事件未通过kqueue报告给父级.

The issue occurs when the child closes its file referring to /tmp/child-$CHILD_PID. I'm seeing that this event is not reported to the parent via kqueue.

最有趣的部分:这段代码可以像我期望的那样在FreeBSD上正常工作.

The most interesting part: this code works as I would expect on FreeBSD.

版本信息:

Mac OS X: 10.11.6

FreeBSD 10.4-RELEASE-p3

在这种情况下,macOS上的kqueue和FreeBSD之间是否有区别?如果是这样,是否有一些文档可以证明这种差异?

Is there a difference between kqueue on macos and FreeBSD in this context? If so, is there some documentation that documents this difference?

推荐答案

这不是您问题的最佳答案,但我希望可以帮助您找到其他可能影响使用 FreeBSD

This is not the best answer to your question but I hope can help you find other differences that may influence your code behavior when using kqueue between macOS and FreeBSD

在我的情况下,我使用kqueue EVFILT_VNODE检查更改,但是基于操作系统,我需要定义不同的标志

In my case I use kqueue EVFILT_VNODE to check for changes, but based on the operating system I need to define different flags openModeDir when using the syscall.Open

对于macOS( openmode_darwin.go ),我使用以下方法:

For macOS (openmode_darwin.go) I use this:

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

对于FreeBSD( openmode.go ),我使用:

And for FreeBSD (openmode.go) I use:

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

macOS文档打开( 2),这是标志说明:

O_EVTONLY       descriptor requested for event notifications only

然后从 FreeBSD open(2) ,没有O_EVTONLY.

将所有这些放在一起,我称之为kqueue:

Putting all together this is how I call 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 ,但是如前所述,希望在处理Kqueue时可以给您一个想法,在我的情况下,标志的这种简单变化就有所不同.

I am using go, but as mentioned before hope can give you an idea while dealing with Kqueue, In my case this simple change of flags made a difference.

这篇关于Mac OS和FreeBSD在fifos的kqueue处理方面有区别吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆