https://www.cnblogs.com/wunaozai/p/3950249.html Libev库学习(1)
https://www.cnblogs.com/wunaozai/p/3954131.html Libev库学习(2)
https://www.cnblogs.com/wunaozai/p/3955156.html Libev库学习(3)
https://www.cnblogs.com/wunaozai/p/3960494.html Zlib库的安装与使用html
https://segmentfault.com/a/1190000006173864 01:概述和 ev_loop
https://segmentfault.com/a/1190000006200077 02:watcher 基础
https://segmentfault.com/a/1190000006679929 03:经常使用 watcher 接口java
https://www.cnblogs.com/gqtcgq/p/7247102.html Libev中的相对时间定时器 ev_timer
https://www.cnblogs.com/gqtcgq/p/7247100.html Libev中的绝对时间定时器 ev_periodic
https://www.cnblogs.com/gqtcgq/p/7247095.html Libev源码分析08:Libev中的信号监视器nginx
http://blog.chinaunix.net/uid-25203957-id-1753940.html Libev 初步
http://blog.chinaunix.net/uid-25203957-id-1760908.html 互斥锁和条件变量程序员
ev_io
:支持 Linux 的select
、poll
、epoll
;BSD 的kqueue
;Solaris 的event port mechanisms
ev_signal
:支持各类信号处理、同步信号处理ev_timer
:相对事件处理ev_periodic
:排程时间表ev_child
:进程状态变化事件ev_start
:监视文件状态ev_fork
:有限的fork事件支持Libev 使用一个ev_tstamp
数据类型来表示1970年以来的秒数,实际类型是 C 里面的double
类型。segmentfault
Libev 使用三种层级的错误:后端
ev_set_syserr_cb
所设置的回调。默认行为是调用abort()
assert
assert
如下函数能够在任意时间调用,用于配置 libev 库:安全
ev_tstamp ev_time ();
返回当前的时间。并发
void ev_sleep (ev_tstamp interval);
休眠一段指定的时间。若是interval
小于等于0,则马上返回。最大支持一天,也就是86400秒app
int ev_version_major ();
int ev_version_minor ();
能够调用这两个函数,而且与系统与定义的EV_VERSION_MAJOR
和EV_VERSION_MINOR
做对比,判断是否应该支持该库异步
unsigned int ev_supported_backends ();
unsigned int ev_recommand_backends ();
unsigned int ev_embeddable_backends ();
返回该 libev 库支持的和建议的后端列表
void ev_set_allocator ( void *(*cb)(void *ptr, long size)throw() );
从新设置realloc
函数。对于一些系统(至少包括 BSD 和 Darwin)的 realloc 函数可能不正确,libev 已经给了替代方案。
void ev_set_syserr_cb ( void (*cb)(const char *msg)throw() );
设置系统错误的 callback。默认调用perror()
并abort()
void ev_feed_signal (int signum)
模拟一个signal
事件出来
Event loop 用一个结构体struct ev_loop *
描述。Libev 支持两类 loop,一是 default loop,支持 child process event;动态建立的 event loops 就不支持这个功能
struct ev_loop *ev_default_loop (unsigned int flags);
初始化 default loops。若是已经初始化了,那么直接返回而且忽略 flags。注意这个函数并非线程安全的。只有这个 loop 能够处理ev_child
事件。
struct ev_loop *ev_loop_new (unsigned int flags);
这个函数是线程安全的。通常而言,每一个 thread 使用一个 loop。如下说明 flag 项的各个值:
EVFLAG_AUTO
:默认值,经常使用EVFLAG_NOENV
:指定 libev 不使用LIBEV_FLAGS
环境变量。经常使用于调试和测试EVFLAG_FORKCHECK
:与ev_loop_fork()
相关,本文暂略EVFLAG_NOINOTIFY
:在ev_stat
监听中不使用inotify
APIEVFLAG_SIGNALFD
:在ev_signal
监听中使用signalfd
APIEVFLAG_NOSIGMASK
:使 libev 避免修改 signal mask。这样的话,你要使 signal 是非阻塞的。在将来的 libev 中,这个 mask 将会是默认值。EVBACKEND_SELECT
:通用后端EVBACKEND_POLL
:除了 Windows 以外的全部后端均可以用EVBACKEND_EPOLL
:Linux 后端EVBACKEND_KQUEUE
:大多数 BSD 的后端EVBACKEND_DEVPOLL
:Solaris 8 后端EVBACKEND_PORT
:Solaris 10 后端void ev_loop_destroy (struct ev_loop *loop);
销毁ev_loop
。注意这里要将全部的 IO 清除光以后再调用,由于这个函数并不停止全部活跃(active)的 IO。部分 IO 不会被清除,好比 signal。这些须要手动清除。这个函数通常和ev_loop_new
一块儿出如今同一个线程中。
void ev_loop_fork (struct ev_loop *loop);
这个函数致使ev_run
的子过程重设已有的 backend 的 kernel state。重用父进程建立的 loop。能够和pthread_atfork()
配合使用。
须要在每个须要在 fork 以后重用的 loop 中调用这个函数。必须在恢复以前或者调用ev_run()
以前调用。若是是在fork
以后建立的 loop,不须要调用。
使用 pthread 的代码例以下:
static void post_fork_chuild (void) {
ev_loop_fork (EV_DEFAULT);
}
...
pthread_atfork (NULL, NULL, post_fork_child);
int ev_is_default_loop (struct ev_loop *loop);
判断当前 loop 是否是 default loop。
unsigned int ev_iteration (struct ev_loop *loop);
返回当前的 loop 的迭代数。等于 libev pool 新事件的数量(?)。这个值对应ev_prepare
和ev_check
调用,并在 prepare 和 check 之间增一。
unsigned int ev_depth (struct ev_loop *loop);
返回ev_run()
进入减去退出次数的差值。
注意,致使ev_run
异常退出的调用(setjmp / longjmp, pthread_cancel, 抛出异常等)均不会致使该值减一。
unsigned int ev_backend (struct ev_loop *loop);
返回EVBACKEND_*
值
ev_tstamp ev_now (loop)
获得当前的“event loop time”。在 callback 调用期间,这个值是不变的。
void ev_new_update (loop)
更新从ev_now()
中返回的时间。没必要要的话,不要使用,由于这个函数的开销相对是比较大的。
void ev_suspend (struct ev_loop *loop);
void ev_resume (struct ev_loop *loop);
暂停当前的 loop,使其刮起当前的全部工做。同时其 timeout 也会暂停。若是恢复后,timer 会从上一次暂停状态继续及时——这一点对于实现一些要连同时间也一块儿冻结的功能时,很是有用。
注意已经 resume 的loop不能再 resume,反之已经 suspend 的 loop 不能再 suspend。
bool ev_run (struct ev_loop *loop, int flags);
初始化 loop 结束后,调用这个函数开始 loop。若是 flags == 0,直至 loop 没有活跃的时间或者是调用了 ev_bread 以后中止。
Loop 能够是异常使能的,你能够在 callback 中调用longjmp
来终端回调而且跳出 ev_run,或者经过抛出 C++ 异常。这些不会致使 ev_depth 值减小。
EVRUN_NOWAIT
会检查而且执行全部未解决的 events,但若是没有就绪的时间,ev_run 会马上返回。EVRUN_ONCE
会检查全部的 events,在至少每个 event 都执行了一次事件迭代以后才返回。但有时候,使用ev_prepare
/ev_check
更好。
如下是ev_run
的大体工做流程:
ev_break
状态LOOP:
EVFLAG_FORKCHECK
,则检查 fork,若是检测到 fork,则排队并调用全部的 fork watchersev_break
被调用了,则直接跳转至 FINISHev_now
的值ev_now
的值,执行 time jump 调整ev_break
被调用了,或者使用了EVRUN_ONCE
或者EVRUN_NOWAIT
,则若是没有活跃的 watchers,则 FINISH,不然 continueFINISH:
EVBREAK_ONE
,则重设 ev_break 状态void ev_break (struct ev_loop *loop, how);
中断 loop。参数能够是 EVBREAK_ONE
(执行完一个内部调用后返回)或EVBREAK_ALL
(执行完全部)。
下一次调用 ev_run 的时候,相应的标志会清除
void ev_ref (struct ev_loop *loop);
void ev_unref (struct ev_loop *loop);
相似于 Objective-C 中的引用计数,只要 reference count 不为0,ev_run 函数就不会返回。
在作 start 以后要 unref;stop 以前要 ref。
void ev_set_io_collect_interval (struct ev_loop *loop, ev_tstamp interval);
void ev_set_timeout_collect_interval (struct ev_loop *loop, ev_tstamp interval);
两个值均默认为0,表示尽可能以最小的延迟调用 callback。但这是理想的状况,实际上,好比 select 这样低效的系统调用,因为能够一次性读取不少,因此能够适当地进行延时。经过使用比较高的延迟,可是增长每次处理的数据量,以提升 CPU 效率。
void ev_invoke_pending (struct ev_loop *loop);
调用全部的 pending 的 watchers。这个除了能够在 callback 中调用(少见)以外,更多的是在重载的函数中使用。参见下一个函数
void ev_set_invoke_pending_cb (struct ev_loop *loop, void (*invoke_pending_cb(EV_P)));
重载 ev_loop 调用 watchers 的函数。新的回调应调用 ev_invoke_pending
。若是要恢复默认值,则置喙 ev_invoke_pending 便可。
int ev_pending_count (struct ev_loop *loop);
返回当前有多少个 pending 的 watchers。
void ev_set_loop_release_cb (struct ev_loop *loop, void (*release)(EV_P)throw(), void (*acquire)(EV_P)throw());
这是一个 lock 操做,你能够自定义 lock。其中 release 是 unlock,acquire 是 lock。release 是在 loop 挂起以等待events 以前调用,而且在开始回调以前调用 acquire。
void ev_set_userdata (struct ev_loop *loop, void *data);
void *ev_userdata (struct ev_loop *loop);
设置 / 读取 loop 中的用户 data。这一点和 libevent 很不一样,libevent 的参数 / 用户数据是以 event 为单位的,而 libev 的原生用户数据是以 loop 为单位的。
void ev_verify (struct ev_loop *loop);
验证当前 loop 的设置。若是发现问题,则打印 error msg 并 abort()
。
如下是一段示意性的代码,使用的是ev_io
:
static void my_cb (struct ev_loop *loop, ev_io *w, int revents)
{
ev_io_stop (w);
ev_break (loop, EVBREAK_ALL);
}
some_main()
{
...
struct ev_loop *loop = ev_default_loop (0);
ev_io stdin_watcher;
ev_init (&stdin_watcher, my_cb);
ev_io_set (&stdin_watcher, STDIN_FILENO, EV_READ);
ev_io_start (loop, &stdin_watcher);
ev_run (loop, 0);
...
}
每个 watcher 类型有一个附属的 watcher 结构体。(通常是struct ev_XXX
或ev_XXX
)
每个 watcher 结构都须要用ev_init
初始化,每个 watcher 都有对应的ev_XXX_set
函数、ev_XXX_start
函数、ev_XXX_stop
函数。在 ev_run 以前进行各个 watcher 的 ev_start。
只要 watcher 是 active,就不能再调用 init。
每一个 callback 都有三个参数:loop, watcher, 事件的掩码值。可能的掩码值有:
EV_READ
EV_WRITE
EV_TIMER
:ev_timer 超时EV_PERIODIC
:ev_periodic 超时EV_SIGNAL
:某线程接收了 ev_signal 中指定的 signalEV_CHILD
:ev_child 中指定的 pid 得到了一个状态变化EV_STAT
:ev_stat 中指定的 path 的属性修改了EV_IDLE
:ev_idle watcher 发现无事可作EV_PREPARE
, EV_CHECK
:全部 ev_prepare watchers 在 loop 开始收集事件前调用;全部ev_check watchers 则在之后调用。回调可在这两个 watchers 中开始/中止相应的 watchers。EV_EMBED
:ev_embed watcherEV_CLEANUP
:event loop 即将被销毁EV_ASYNC
:asuny watcher 已经被异步通知EV_CUSTOM
:不是 libev 发送的信号。参见ev_feed_event
EV_ERROR
:在 libev 内存不够用时可能产生;fd 被外部关闭时也可能产生void ev_init (ev_TYPE *watcher, callback)
使用这个宏初始化 watcher。此外还须要调用相应的 ev_XXX_set
函数。参见下文:
void ev_TYPE_set (ev_TYPE *watcher, [args])
设置指定类型的 wetaher。init 函数必须在此以前被调用一次,此后能够设置任意次的 set 函数。
不能对一个 active 的 watcher 调用此函数,但 pending 能够。好比:ev_io w;
ev_init (&w, my_cb);
ev_io_set (&w, STDIN_FILENO, EV_READ);
void ev_TYPE_set (ev_TYPE *watcher, callback, [args])
这个宏将 init 和 set 糅合在一块儿使用
void ev_TYPE_start (loop, ev_TYPE *watcher)
开始(激活)指定的 watcher。若是 watcher 已是 active,则调用无效。
void ev_TYPE_stop (loop, ev_TYPE *watcher)
中止 watcher,并清空 pending 状态。若是要释放一个 Watcher,最好都显式地调用 stop。
bool ev_is_active (ev_TYPE *watcher)
若是 watcher 被执行了一次 start,而且未被 stop,则返回 true。
bool ev_is_pending (ev_TYPE *watcher)
当且仅当 watcher pending 时返回 true。(如:有未决的事件,可是 callback 未被调用)
callback ev_cb (ev_TYPE *watcher)
void ev_set_cb (ev_TYPE *watcher, callback)
读 / 写 callback
void ev_set_priority (ev_TYPE *watcher, int priority)
int ev_priority (ev_TYPE *watcher)
Priority 是一个介于EV_MAXPRI
(默认2)和EV_MIN_PRI
(默认-2)之间的值。数值越高越优先被调用。但除了 ev_idle,每个 watcher 都会被调用。
当 watcher 是 active 或 pending 时并不能修改。
实际上 priority 大于-2到2的范围也是没问题的。
void ev_invoke (loop, ev_TYPE *watcher, int revents);
使用指定的参数调用 callback
int ev_clear_pending (loop, ev_TYPE *watcher);
清除指定 watcher 的 pending 状态,而且返回 revents 位。若是 watcher 不是 pending 则返回0
void ev_feed_event (loop, ev_TYPE *watcher, int revents)
模拟一个事件。参见ev_feed_fd_event
和ev_feed_signal_event
除了前文说起的 active 和 pending 状态以外,本小节描述了更加详细的 watcher 状态。
initialized
:经过调用ev_TYPE_init
对 watcher 进行初始化,这是注册到 loop 以前的必要步骤。能够再次调用 ev_TYPE_init 进行操做。
started
/running
/active
:调用ev_TYPE_start
以后的状态,而且开始等待事件。在这个状态下,除了特别说起的少数状况以外,它不能存取、移动、释放,只能维持着对它的指针。
pending
:当 watcher 是 active 而且一个让 watcher 感兴趣的事件到来,那么 watcher 进入 pending。这个状态的 watcher 能够 access,但不能存取、移动、释放。
stopped
:调用ev_TYPE_stop
,此时状态与 initialized 相同。
这个 watcher 负责检测文件描述符(如下简称fd)是否可写入数据或者是读出数据。最好是将fd设置为非阻塞的。
注意有时候在调用read
时是没有数据的(返回0),此时一个一个非阻塞的read
会获得EAGAIN
错误。
(如下两个特殊问题,是 libev 文档中特别提到的,可是我看不太懂……)
部分系统须要显式地调用close
(如kqueue
、epoll
),不然当一个 fd 消失、而新的 fd 进入,占用同一个 fd 号时,libev
不知道这是一个新的fd。
libev 一侧解决的办法是每次调用ev_io_set
时,都假定这是一个新的 fd。
dup
操做 fd 的特殊问题一些后端(backend)不能注册普通的 fd 事件,只能注册underlying file descriptions
,这意味着使用dup()
或其余奇怪操做的fd,只能由其中一个被接收到。
这没有有效的解决办法,除非将后端设置为BACKEND_SELECT
或EVBACKEND_POLL
ev_io
对于文件泪说没有什么用,只要文件存在,就当即会有时间。对于stdin
和stdout
,请谨慎使用,确保这二者没有被重定向至文件。
记得使用ev_loop_fork
,而且使用EVFLAG_FORKCHECK
。不过对于epoll
和kqueue
以外的无需担忧。
只是提醒一下:记得处理SIGPIPE
事件。
accept
一个没法接受的链接大多数 POSIX accpet 实现中在删除由于错误而致使的链接时(如 fd 到达上限)都回产生一个错误的操做,好比使 accept 失败但不拒绝链接,只产生ENFILE
错误。但这个会致使 libev 仍是将其标记为 ready 状态。
推荐方法是列出全部的错误并记录下来,或者是暂时关闭 watchers。
void ev_io_init (ev_io *, callback, int fd, int events)
void ev_io_set (ev)io *, int fd, int events)
其中 events 能够是EV_WRITE
和EV_READ
的组合。
static void stdin_readable_db (struct ev_loop *loop,
ev_io *w,
int revents)
{
ev_io_stop (loop, w)
...... // 从 w->fd 中进行read
}
......
some_init_func ()
{
......
struct ev_loop *loop = ev_default_init (0);
ev_io stdin_readable;
ev_io_init (&stdin_readable, stdin_readable_db , STDIN_FILENO, EV_READ);
ev_io_start (loop, &stdin_readable);
ev_run (loop, 0);
...
}
Libev 提供了一个相对超时机制的定时器。所谓的“相对”,就是说这个定时器的参数是:指定以当前时间为基准,延迟多久出发事件。这个定时器与基于万年历的日期/时间是无关的,只基于系统单调时间。
下面列出一个以60秒为单位的循环定时器做为例子,来讲明使用ev_timer的不一样策略
ev_timer_init (timer, callback, 60.0, 6.0);
ev_timer_start (loop, timer)
标准设置。或——
ev_timer_stop (loop, timer);
ev_timer_set (timer, 60.0, 0.0);
ev_timer_start (loop, timer)
这样的设置,当每次有活跃时间时,中止timer,而且重启它。第一个参数是首次超时,第二个参数是第二次开始的固定超时时间。
可是这样的方法虽然比较简易,可是时间不稳定,并且开销较大
ev_timer_again
重设使用ev_timer_again
,能够忽略ev_timer_start
ev_init (timer, callback);
timer->repeat = 60.0;
ev_timer_again (loop, start);
上面的初始化完成后,在 callback 里调用:
timer->repeat = 60.0;
ev_timer_again (loop, timer);
能够改变 timeout 值,无论 timer 是否 active
这个方式的基本思路是由于许多 timeout 时间都比 interval 大不少,此时要记住上一次活跃的时间,而后再 callback 中检查真正的 timeout
ev_tstamp g_timeout = 60.0;
ev_tstamp g_last_activity;
ev_timer g_timer;
static void callback (EV_P_ev_timer *w, int revents) {
ev_tstamp after = g_last_activity - ev_now(EV_A) + g_timeout;
// 若是小于零,表示时间已经发生了,已超时
if (after < 0.0) {
...... // 执行 timeout 操做
}
else {
// callback 被调用了,可是却有一些最近的活跃操做,说明未超时
// 此时就按照须要设置的新超时事件来处理
ev_timer_set (w, after, 0.0);
ev_timer_start (loop, g_timer);
}
}
启用这种模式,记得初始化时将g_last_activity
设置为ev_now
,而且调用一次callback (loop, &g_timer, 0)
;当活跃时间到来时,只需修改全局的 timeout 变量便可,而后再调用一次 callback
g_timeout = new_value
ev_timer_stop (loop, &timer)
callback (loop, &g_timer, 0)
使用场景:有成千上万个请求,而且都须要 timeouts
当 timeout 开始前,计算 timeout 的值,而且将 timeout 放在链表末尾。而后当链表前面的项须要 fire 时。使用ev_timer
来将其 fire 掉。
当有 activity 时,将 timer 从 list 中一处,重算 timeout,而且再附到 list 末尾,确保若是ev_timer
已经被 list 的第一项取出时,更新它
假设在500.9秒的时候请求延时1秒,那么当501秒到来时,可能致使 timeout,这就是“太早”问题。Libev的策略是对于这种状况,在502秒时才执行 timeout。可是这又有“太晚”的问题,请程序员注意.
Suspenged animation,也称为休眠,指的是将机子置于休眠状态。注意不一样的机子不一样的系统这个行为可能不同。
其中一种休眠是使得全部程序感受只是通过了很小的一段时间通常(时间跳跃)
推荐在SIGTSTP
处理中调用ev_suspend
和ev_resume
ev_now_update()
的开销很大,请谨慎使用
Libev使用的时一个内部的单调时钟而不是系统时钟,而ev_timer
则是基于系统时钟的,因此在作比较的时候二者不一样步。
void ev_timer_init (ev_timer *, callback, ev_tstamp after, ev_tstamp repeat);
void ev_timer_set (ev_timer *, ev_tstamp after, ev_tstamp repeat);
若是repeat为正,这个timer会重复触发,不然只触发一次。
void ev_timer_again (loop, ev_timer *)
ev_tstamp ev_timer_remaining (loop, ev_timer *)
void ev_periodic_init (ev_periodic *, callback, ev_tstamp offset,
ev_tstamp interval, reschedule_cb)
void ev_periodic_set (ev_periodic *, ev_tstamp offset,
ev_tstamp interval, reschedule_cb)
如下是几种不一样应用场景的设置方法:
ev_tstamp (*reschedule_cb)(ev_periodic *w, ev_tstamp now);
例程是:
static ev_tstamp my_scheduler (...) {
return now + 60.0;
}
相似于 Linux 内核的jiffies
,返回下一个时间点。
这个timer很是便于用来提供诸如“下一个正午12点”之类的定时器。
void ev_periodic_again (loop, ev_periodic *)
关闭并重启 watcher,参见前文。
ev_tstamp ev_periodic_at (ev_periodic *)
返回下一次触发的绝对时间。
在哦你跟一个 loop 能够屡次观测同一个 signal,可是没法在多个 loop 中观测同一个 signal。此外,SIGCHILD
只能在 default loop 中监听。
在子进程调用 exec
以前,应当将 signal mask 重设为你所需的默认值。最简单的方法就是子进程作一个pthread_atfork()
来重设。
POSIX 的很多功能(如sigwait)只有在进程中的全部线程屏蔽了 signal 时才真正生效
为了解决这个问题,若是真的要使用这些功能的话,建议在建立线程以前屏蔽全部的 signal,而且在建立 loops 的时候指定EVFLAG_NOSIGMASK
,而后制定一个 thread 用来接收 signals。
void _ev_signal_init (ev_signal *, callback, int signum)
void ev_signal_set (ev_signal *, int signum)
当接收到SIGCHILD
事件时,child watcher 触发。大部分状况下,子进程退出或被杀掉。只要这个 watcher 的 loop 未开始,你甚至能够在 shild 被 fork 以后才加入 child watcher。
Ev_child 的优先级固定是EV_MAXPRI
。
void ev_chile_init (ev_child *, callback, int pid, int trace)
void ev_child_set (ev_child *, int pid, int trace)
Pid 若是指定0的话,表示任意子进程。能够在 ev_child 中观察rstatus
成员来了解子进程状态。
int pid;
表示监控中的 pid,只读。
int rpid;
可读写,表示检测到状态变化的 pid
int tstatus;
可读写,表示由 rpid 致使的进程的 exit/trace 状态值。
使用 ev_stat 时,监控目标位置上无需存在文件,由于文件从“不存在”变为存在也是一种状态变化。
文件路径必须是绝对路径,不能存在“./
”或“../
”。
Ev_stat 的实现其实只是按期调用stat()
来判断文件属性的变化,因此能够指定检查周期。指定0的话会使用默认事件周期。
正由于这是轮询操做,因此这个功能不适合作大数据量或者是大并发检测;同时,ev_stat 是异步的。
默认关闭大文件支持(使用32位的stat
)。若是要使用大文件支持(ABI),libev 的做者在这里吐槽,说你要游说操做系统的发布方去支持……囧rz
有些系统的文件时间仅精确到秒,这就意味着 ev_stat 没法区分秒如下的变更。
void ev_stat_init (ev_stat *, callback, const char *path, ev_tstamp interval);
void ev_stat_set (ev_stat *, const char *path, ev_tstamp interval);
void ev_stat_stat (loop, ev_stat *);
第三个函数使用新的文件 stat 值去更新 stat buffer,使用此函数来使得你作的一些配置更改不会被触发。
ev_statdata attr
只读,表明文件最近一次的状态。ev_statdata
和struct stat
基本是相通的。
ev_statdata prev
文件上一次的状态
ev_tstamp interval
const char *path
都是只读,字面意义上的意思。
void ev_idle_init (ev_idle *, callback)
这个功能没有研究过,暂记着把。
void ev_once (loop, int fd, int events, ev_tstamp timeout, callback)
从指定的f fd 中指定一个超时事件,这个函数的方便之处在于无需作 alloc
/conf
/start
/stop
/free
。
Fd 能够小于0,这样就没有 I/O 监控,而且“events”会被忽略。
void ev_feed_event (loop, int fd, int revents);
向一个 fd 发送事件。须要注意的是,这个功能貌似是只能在 loop 内调用才有效,异步地在 loop 的另外一个线程直接调用是无效的。
void ev_feed_signal_event (loop, signum)
向一个 loop 模拟 signal。参见 ev_feed_signal
。
Create tcp echo server using libev
Listen
socketaccept
事件accept
调用read
注意:原文例子中未显示的是,应当将 fd 设置为非阻塞的。带非阻塞设置的代码以下:
some_init_func()
{
...
sd = socket (PF_INET, SOCK_STREAM, 0);
flags = fcntl (sd, F_GETEL, 0);
fcntl (sd, F_SETEL, flags | O_NONBLOCK);
bzero (&addr, sizeof(addr));
... // 设置 Address 和 port
bind (sd, (struct sockaddr *)(&addr), sizeof(addr));
...
}
some_init_func()
{
...
listen (sd, 2);
...
}
accept()
的 watchersome_init_func()
{
...
ev_io_init (&w_accept, accept_cb, sd, EV_READ);
ev_io_start (loop, &w_accept);
...
}
回调函数以下:
static void accept_cb (struct ev_loop *loop,
struct ev_io *watcher,
int revents)
{
...
client_sd = accept (watcher->fd, // accept() 调用,接受传入链接
(struct sockaddr *)(&client_addr),
&client_len);
...
w_client = (struct ev_io *)malloc(sizeof(struct ev_io)); // 为 read watcher 准备内存
...
ev_io_init (w_client, read_cb, client_sd, EV_READ); // 这里就只示例 read 事件了。write 事件同理
ev_io_start (loop, w_client);
}
read()
的 callbackstatic void read_cb (struct ev_loop *loop,
struct ev_io *watcher,
int revents)
{
...
readCount = recv (watcher->fd, buffer, BUFFER_SIZE, 0); // 读取的方法就视乎程序员的实现啦
send (watcher->fd, buffer, readCount, 0); // 把数据 echo 回去
...
}
原文例子使用的就是recv
/send
,实际上我我的偏心的是read
/write
ev_loop (loop, 0); // 这里能够直接使用 default loop
============== End