我在使用带有USB串行控制台的mac上使用kevent时遇到问题。我把它缩小到:
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/event.h>
#include <sys/ioctl.h>
#include <termios.h>
#define DEVICE "/dev/cu.usbserial-0011111D"
int main() {
int kqueue_fd = kqueue();
if (kqueue_fd < 0) {
printf("Failed to open kqueue\n");
return -1;
}
int device_fd = open(DEVICE, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (device_fd < 0) {
printf("Failed to open device: %s\n", DEVICE);
return -1;
}
printf("Opened %d\n", device_fd);
enum { MAX_EVENTS = 1 };
struct kevent events[MAX_EVENTS];
EV_SET(&events[0], device_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
int r = kevent(kqueue_fd, events, 1, NULL, 0, NULL);
if (r < 0) {
printf("kevent failed: %s\n", strerror(errno));
return -1;
}
struct timespec sleep_time;
sleep_time.tv_sec = 5;
sleep_time.tv_nsec = 0;
int ready = kevent(kqueue_fd, NULL, 0, (struct kevent*) &events,
MAX_EVENTS, &sleep_time);
if (ready == 0) {
printf("No event\n");
return 0;
}
for (int i = 0; i < ready; i++) {
printf(".ident %ld, .filter %d, .flags 0x%x, .fflags 0x%x, "
".data: %ld, .udata %p\n",
events[i].ident,
events[i].filter,
events[i].flags,
events[i].fflags,
events[i].data,
events[i].udata);
int unread = 0;
r = ioctl(events[i].ident, FIONREAD, &unread);
if (r < 0) {
printf("ioctl failed: %d: %s\n", errno, strerror(errno));
}
}
}
当我在调用kevent()的过程中运行并拔下USB设备时,我得到:
Opened 4
.ident 4, .filter -1, .flags 0x1, .fflags 0x0, .data: 6, .udata 0x0
ioctl failed: 6: Device not configured
我的理解是该事件的内容转换为:
FD 4,EVFILT_READ,EV_ADD,fd上剩余6个字节。但是ioctl()失败了(因为设备被删除了),并且errno也是6,所以看起来好像event.data正在返回errno,而不是剩下的字节。
如何区分正常读取案例和已删除设备的情况?过滤器,旗帜和fflags在两种情况下都是相同的。
其他信息
如果我从打开串行控制台切换到管道,然后写一个字节然后关闭写入结束,我得到:
pipe() fd: 5 -> 4
.ident 4, .filter -1, .flags 0x1, .fflags 0x0, .data: 1, .udata 0x0
.ident 4, .filter -1, .flags 0x8001, .fflags 0x0, .data: 0, .udata 0x0
这就是我的期望,因为0x8000是EV_EOF。