gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-01 17:53:38 37 4
gpt4 key购买 nike

我正在开发一个应用程序,该应用程序使用 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();
}
}

此重现器 fork 一个子进程,该子进程创建 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 的写入。

这一系列事件运行良好。

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

最有趣的部分:这段代码在 FreeBSD 上的工作方式符合我的预期。

版本信息:

Mac OS X:10.11.6

FreeBSD 10.4-RELEASE-p3

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

最佳答案

这不是您问题的最佳答案,但我希望可以帮助您找到使用 kqueue 时可能影响代码行为的其他差异。之间macOSFreeBSD

就我而言,我使用 kqueue EVFILT_VNODE 来检查更改,但根据操作系统,我需要定义不同的标志 openModeDir使用syscall.Open

对于 macOS ( openmode_darwin.go ),我使用这个:

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

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

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

来自macOS docs open(2) ,这是标志描述:

O_EVTONLY       descriptor requested for event notifications only

来自 FreeBSD open(2) ,没有 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 ,但正如之前提到的,希望能给您在处理 Kqueue 时提供一个想法,就我而言,这个简单的标志更改会产生影响。

关于c - Mac OS 和 FreeBSD 之间 kqueue 处理 fifo 的差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49481723/

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com