最近接触到一些比较底层的东西,感受跟操做系统原理结合起来看,理解起来会更容易。html
在使用 kevent 的时候遇到了一个很奇怪的问题,在 stackoverflow 上面提了个问题fileno errors when calling kevent function,麻烦有知道的能够回答一下。python
Stackoverflow 上的大神给我了点提示,若是直接使用 fd = open('test').fileno(),确实是能够取获得打开文件的文件描述符,可是文件自己 file object 会由于没有变量引用而被垃圾回收器回收。我试了一下手动关闭垃圾回收器暂时没有成功,可是用变量引用 fileobject 后确实能解决问题。此处仍是有疑惑。linux
在调试 kevent 的时候遇到了不少问题,直接看 Python 的 C 语言源代码或者 PyPy 的源码会一会儿清晰不少。app
例如在 Python 2.7.10 的 selectmodule.c 中,kqueue_event_init 函数在处理 ident 的时候会首先判断是否是长整形,若是不是长整型,再尝试用 PyObject_AsFileDescriptor 来做为文件描述符赋值。这个函数位于 fileobject.c 中,它会判断咱们传入的 ident 中有没有调用 fileno 方法,若是有,就先执行这个方法,而后将返回的文件描述符返回给 kqueue_event_init。socket
这里在 CPython 中,kqueue 和 kevent 相关的操做经过直接调用系统 API 而成,在 PyPy 中,它经过 RPython 的 Foreign Function Interface 来调用系统 API。ide
kqueue 是 BSD 系统下的一套消息接口,其基本原理与 epoll 类似,它避免了遍历全部被监控的文件描述符的操做。函数
咱们在 Python 中可使用 select 库调用系统接口来实现 kqueue,它有一个 kevent 对象和一个 kqueue 对象,参见参考资料。操作系统
import select fd = open('test') kq = select.kqueue() # 规定咱们所要作的操做,分别为 添加事件,使能该事件,事件被取出后恢复标记 flags = select.KQ_EV_ADD | select.KQ_EV_ENABLE | select.KQ_EV_CLEAR # 要监控的事件类型,分别为 删除,写入,追加写入,重命名 fflags = select.KQ_NOTE_DELETE | select.KQ_NOTE_WRITE | select.KQ_NOTE_EXTEND \ | select.KQ_NOTE_RENAME # 初始化该事件 ev = select.kevent(fd, filter=select.KQ_FILTER_VNODE, flags=flags, fflags=fflags) while 1: # 将上述事件加入到 kqueue 中,此时程序被阻塞,直到事件发生,或者咱们能够设置 timeout 字段 revents = kq.control([ev], 1, None) # 取出返回的事件 for e in revents: # 若是是 EXTEND 事件,下同 if e.fflags & select.KQ_NOTE_EXTEND: # 打印 EXTEND,下同 print 'extend' elif e.fflags & select.KQ_NOTE_WRITE: print 'write' elif e.fflags & select.KQ_NOTE_RENAME: print 'rename' elif e.fflags & select.KQ_NOTE_DELETE: print 'delete' else: print e
监控文件系统有个很著名的 watchdog 库,它在 linux 上采用了 inotify,在 MAC 上除了 kqueue 之外还可用 FSEvents 来监控。调试
inotify 和 FSEvent 都是专门用于监控文件系统的,能监控的文件操做类型更多,而 epoll 和 kqueue 除了文件系统以外,还能够监控 sockets 或者其余的 I/O。详见 Difference between inotify and epoll。code