ev_io - 这个文件描述符可读/可写吗?
I/O 监视器在每个事件循环中检查一个文件描述符是可读还是可写,或者说,什么时候读取数据或者写入一些数据不会阻塞进程。这一机制叫作边元触发,因为你是一直在接收事件,一直到条件发生。 记住当你不对该事件感兴趣或者说不想接收以后的事件,你可以停止监视器。
一般来说你可以为每个fd注册任意数量的读/写监视器(如果你自己不被迷惑的话)。将所有的文件描述符设置为非阻塞(non-block)模式会也是个好主意(但是如果你知道你在做什么的话,这也不是必须的)。
如果你不能使用非阻塞模式,那么一定要用众所周知的后端(在写这个的时候,只有
EVBACKEND_SELECT 和 EVBACKEND_POLL ).
Another thing you have to watch out for is that it is quite easy to
receive "spurious" readiness notifications, that is your callback might
be called with EV_READ but a subsequent read (2) will actually block
because there is no data. Not only are some backends known to create a
lot of those (for example Solaris ports), it is very easy to get into
this situation even with a relatively standard program structure. 这样最好还是用非阻塞 I/O: 对一个程序来说,一个额外的返回EAGAIN 的read 远比阻塞直至有数据来更合适。Thus
如果你不能以非阻塞模式跑fd (for example you should
not play around with an Xlib connection), 那么你必须使用别的众所周知的后端,如poll (fortunately in our Xlib example, Xlib already
does this on its own, so its quite safe to use),测试一个文件描述符是否真的可读。有些人还用 SIGALRM 和一个计时器,只是为了保证你不会被永久阻塞在那里。
说实在的,最好是用非阻塞模式。
即将消失的文件描述符的特殊问题
有些后端 (如:kqueue, epoll) 需要明确的被告知一个文件即将被关闭 (或者通过明确的调用 close ,或者其他方式,如 dup2 )。其原因是,你为某些文件描述符注册了你感兴趣的监视器,但是当它不存在了的时候,系统会默默的丢弃掉对它的处理。如果这时另一个有相同数字的文件描述符在libev中注册,事实上它无法把它识别为一个不同的文件描述符。
To avoid having to explicitly tell libev about such cases, libev follows
the following policy: Each time ev_io_set is being called, libev
will assume that this is potentially a new file descriptor, otherwise
it is assumed that the file descriptor stays the same. That means that
you have to call ev_io_set (or ev_io_init ) when you change the
descriptor even if the file descriptor number itself did not change.
This is how one would do it normally anyway, the important point is that
the libev application should not optimise around libev but should leave
optimisations to libev.
The special problem of dup'ed file descriptors
Some backends (e.g. epoll), cannot register events for file descriptors,
but only events for the underlying file descriptions. That means when you
have dup () 'ed file descriptors or weirder constellations, and register
events for them, only one file descriptor might actually receive events.
There is no workaround possible except not registering events
for potentially dup () 'ed file descriptors, or to resort to
EVBACKEND_SELECT or EVBACKEND_POLL .
The special problem of fork
Some backends (epoll, kqueue) do not support fork () at all or exhibit
useless behaviour. Libev fully supports fork, but needs to be told about
it in the child.
To support fork in your programs, you either have to call
ev_default_fork () or ev_loop_fork () after a fork in the child,
enable EVFLAG_FORKCHECK , or resort to EVBACKEND_SELECT or
EVBACKEND_POLL .
The special problem of SIGPIPE
While not really specific to libev, it is easy to forget about SIGPIPE :
when writing to a pipe whose other end has been closed, your program gets
sent a SIGPIPE, which, by default, aborts your program. For most programs
this is sensible behaviour, for daemons, this is usually undesirable.
So when you encounter spurious, unexplained daemon exits, make sure you
ignore SIGPIPE (and maybe make sure you log the exit status of your daemon
somewhere, as that would have given you a big clue).
Watcher-Specific Functions
- ev_io_init (ev_io *, callback, int fd, int events)
- ev_io_set (ev_io *, int fd, int events)
Configures an ev_io watcher. The fd is the file descriptor to
receive events for and events is either EV_READ , EV_WRITE or
EV_READ | EV_WRITE , to express the desire to receive the given events.
- int fd [read-only]
The file descriptor being watched.
- int events [read-only]
The events being watched.
Examples
Example: Call stdin_readable_cb when STDIN_FILENO has become, well
readable, but only once. Since it is likely line-buffered, you could
attempt to read a whole line in the callback.
static void
stdin_readable_cb (struct ev_loop *loop, ev_io *w, int revents)
{
ev_io_stop (loop, w);
.. read from stdin here (or from w->fd) and handle any I/O errors
}
...
struct ev_loop *loop = ev_default_init (0);
ev_io stdin_readable;
ev_io_init (&stdin_readable, stdin_readable_cb, STDIN_FILENO, EV_READ);
ev_io_start (loop, &stdin_readable);
ev_loop (loop, 0);
|