这篇文章能够算是我在 GitHub 上一个工程的设计概要了。简要说明了该工程的设计思路以及技术要点。git
本文章纯原创,没有参考资料。不过有设计过程当中记录下的相关文章:程序员
使用 pipe 在程序正文中捕获和处理信号github
本文地址:http://www.javashuo.com/article/p-zyoylygv-cx.htmlsegmentfault
总所周知,在 Linux 中实现异步 I/O,适用的系统 API 就是 epoll
。这里主要包括 epoll_ctl()
和 epoll_wait()
两个函数。前者配置各个文件描述符在 epoll 里的机制,然后者则是关键的阻塞调用。服务器
这个工程,就是基于 epoll
,实现相似于 libevent
的异步 I/O 库。架构
函数的具体用法能够参照 man 页,或者工程的 AMCEpoll.c 源文件的 _dispatch_main_loop()
函数。异步
早期打算通用的写一个进程间通讯库。通讯库想要使基于异步 I/O 来作,又不想只是使用裸的 epoll。想起本身对异步 I/O 的原理也算是大体了解了,就本身研究着写一个。目前已经大体完成的 AMCEpoll
并无通过严格的测试验证,更多地像是一个学习工程,或者说是课后做业同样。就像 Andrew S. Tanenbaum 教授的 Minix
相似哈。函数
不过这个库是瞄准了实用来设计的。若是要面向可靠的话,建议用开源的 libevent
来构建;若是以为 libevent 太大了,则推荐 libev
;若是以为 libev
的开发者太少,不靠谱的话,那么能够用 libuv
。若是须要有本身知识产权的产品的话,那么,能够本身设计一个——这也就是我开发这个库的初衷。oop
公共的 API 都在 AMCEpoll.h 文件中。各函数的说明以下:学习
struct AMCEpoll
至关于 libevent 的 “event base”。这是整个 AMCEpoll 对象,每个对象可执行一个事件循环(event loop)。
struct AMCEpollEvent
至关于 libevent 的 “event”。与 libevent 不一样的是,每个 event 能够从某个 base 中分离,再加入到另外一个 base 里面去。不过实际上应该没有这样的需求,只是说个人程序架构容许这么作。
AMCEpoll 支持三种事件:
AMCEpoll_New()
建立、初始化并返回一个 AMCEpoll 对象。其中参数 pollSize 指的是 epoll_wait()
函数中的 maxevents
参数。
AMCEpoll_Free()
销毁一个 AMCEpoll 对象。若是对象中还有事件存在,而且事件关注了 EP_EVENT_FREE
事件,则会收到对应的回调。
不能在 event loop 中调用。
AMCEpoll_NewEvent()
建立并返回一个事件(AMCEpollEvent)对象。各参数说明以下:
AMCEpoll_FreeEvent()
销毁一个事件。若是事件关注了 EP_EVENT_FREE
,那么会在此时收到回调。程序员能够在这个地方执行一些库之外的清理工做,好比 close(fd)
之类的。
若是该事件已经加入到某个 AMCEpoll 对象中,那么这个函数会出错。若是要避免这个问题,建议使用 AMCEpoll_DelAndFreeEvent()
函数。
AMCEpoll_AddEvent()
将某个 AMCEpollEvent 对象加入到 AMCEpoll 中,这样才能在事件循环中关注这个对象上的事件。
AMCEpoll_DelEvent()
将已经加入到 AMCEpoll 的时间分离开来,成为 “孤魂野鬼” 状态。
AMCEpoll_DelAndFreeEvent()
Del 和 Free 两个函数的结合。
AMCEpoll_SetEventTimeout()
,AMCEpoll_GetEventTimeout()
从新设置或者获取一个事件的超时时间。这只有在建立时,添加了 EP_EVENT_TIMEOUT
事件,才会有效。
AMCEpoll_Dispatch()
在当前线程启动事件循环。当没有事件注册在案的时候,event loop 会退出,也就是这个函数会返回。
AMCEpoll_LoopExit()
退出事件循环。若是这个函数在 event loop 外调用,那么 AMCEpoll 会在走完下一次 event chain 以后退出;若是在 event loop 中间调用,则 AMCEpoll 会走完当前 event chain 以后退出。Event chain 的原理,在后文说明。
事件类型就是 event_t
数据类型,声明以下:
enum { EP_EVENT_READ = (1 << 0), EP_EVENT_WRITE = (1 << 1), EP_EVENT_ERROR = (1 << 2), EP_EVENT_FREE = (1 << 3), EP_EVENT_TIMEOUT = (1 << 4), EP_EVENT_SIGNAL = (1 << 5), EP_MODE_PERSIST = (1 << 8), /* only used when adding events */ EP_MODE_EDGE = (1 << 9), /* only used when adding events */ };
当建立事件的时候,上述全部的类型均可以使用;而当回调的时候,回调函数只可能接收到 EV_EVENT_XXX
类型的值。三种 AMCEpollEvent 所支持的具体类型以下:
———— | _READ | _WRITE | _ERROR | _FREE | _TIMEOUT | _SIGNAL | _PERSIST | _EDGE |
---|---|---|---|---|---|---|---|---|
文件事件 | O | O | O | O | O | . | O | O |
信号事件 | . | . | O | O | O | O | O | O |
超时事件 | . | . | . | O | O | . | . | . |
具体的参数说明以下:
EP_EVENT_READ
:读事件。若是出现这个事件类型,则视为文件事件。EP_EVENT_WRITE
:读事件。若是出现这个事件类型,则视为文件事件。EP_EVENT_ERROR
:通常是系统调用出错。保留。EP_EVENT_FREE
:“销毁” 事件。当调用 AMCEpoll_FreeEvent()
的时候触发。EP_EVENT_TIMEOUT
:超时事件EP_EVENT_SIGNAL
:信号事件。若是出现这个事件类型,则视为信号事件。不得与文件事件同时存在。EP_MODE_PERSIST
:持续事件。当一次回调(除了 free)完成后,程序会自动将事件及其超时配置加入到 event loop 中。固然,若是程序手动 add 的话,也是能够的,只是不必。EP_MODE_EDGE
:边缘触发模式。若是使用这个模式的话,只在出现事件才会触发回调。基本的使用流程和 libevent 和 libev 很是相似,能够参照个人这篇文章:《使用 libev 构建 TCP 响应服务器的简单流程》
好比一个 UDP 事件基本流程:
recvfrom()
其余事件的用法,则能够参照工程内的 test_server.c 文件。这个文件建立了三种事件:
SIGINT
和 SIGQUIT
。监听到前者时,退出 AMCEpoll 事件循环。监听到后者时,打印调试信息。下一篇会说明一下工程的实现篇。文章还没有完成,敬请期待。