这篇文章可以看作是我在GitHub上的一个项目的设计大纲。简要说明了该项目的设计思路和技术要点。本文纯原创,无参考。不过在设计过程中记录了相关文章:使用管道捕获并处理程序文本中的信号本文地址:https://segmentfault.com/a/1190000010098194项目的基本原理众所周知,异步I/O,适用的系统API是epoll。这里主要包括epoll_ctl()和epoll_wait()两个函数。前者配置epoll中每个文件描述符的机制,后者是关键阻塞调用。本项目基于epoll,实现了一个类似于libevent的异步I/O库。函数的具体用法可以参考manpage,或者项目的AMCEpoll.c源文件的_dispatch_main_loop()函数。设计原因早期打算写一个通用的进程间通信库。通信库想基于异步I/O来做,不想只用bareepoll。想着自己对异步I/O的原理有了大概的了解,就自己研究写一个。目前为止大致完成的AMCEpoll,还没有经过严格的测试和验证。它更像是一个学习项目,或课后作业。这就像AndrewS.Tanenbaum教授的Minix。但是,这个库是为实际使用而设计的。如果要靠谱,建议使用开源的libevent来搭建;如果觉得libevent太大,推荐libev;如果你觉得libev的开发者太少,不可靠,你可以使用libuv。如果你需要一个有自己知识产权的产品,那么你可以自己设计一个——这就是我开发这个库的初衷。API通用API位于AMCEpoll.h文件中。各函数说明如下:接口说明structAMCEpoll相当于libevent的“事件库”。这是整个AMCEpoll对象,每个对象执行一个事件循环。structAMCEpollEvent相当于libevent的“事件”。与libevent不同的是,每个事件都可以从一个基中分离出来并添加到另一个基中。但其实应该没有这个要求,只是说我的程序架构允许这样。AMCEpoll支持三种事件:文件事件:或fd事件,文件描述符事件。基于Linux的文件描述符的事件。同时支持事件超时信号事件:即信号事件。可以捕获信号,然后在事件循环中进行处理,而无需担心信号函数的上下文。signaltime也支持超时机制timeoutevent:纯超时事件,支持一次性计时,也支持循环计时。AMCEpoll_New()创建、初始化并返回一个AMCEpoll对象。参数pollSize是指epoll_wait()函数中的maxevents参数。AMCEpoll_Free()销毁AMCEpoll对象。如果对象中有事件,并且事件关注EP_EVENT_FREE事件,就会收到相应的回调。不能在事件循环中调用。AMCEpoll_NewEvent()创建并返回一个事件(AMCEpollEvent)对象。参数说明如下:fd:创建文件事件时,fd参数为对应的文件描述符;创建信号事件时,fd是信号代码。对于纯超时,fd必须设置为-1。需要注意的是,所有的文件事件在事件循环开始前必须保证fd是非阻塞的。事件:一组由EP_EVENT_XXX掩码组成的时间列表。每个掩码的具体作用将在后面讨论。纯超时事件还没有整合(有什么难的,毕竟fd事件和signal事件的timeout已经完成了,只是最近没时间。。。)。timeout:超时时间,单位毫秒。当然,实际上超时调用是大于这个数的,毕竟功能切换是需要时间的。压力大的时候,建议超时不要小于1秒,最好大于10秒。callback:接收事件的回调函数。userData:事件回调函数的自定义参数。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()在当前线程上启动事件循环。当没有事件注册时,事件循环会退出,也就是这个函数会返回。AMCEpoll_LoopExit()退出事件循环。如果在事件循环外调用这个函数,AMCEpoll会在完成下一个事件链后退出;如果在事件循环中间调用,AMCEpoll将在完成当前事件链后退出。Eventchain的原理后面会讲到。事件类型事件类型为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),/*仅在添加事件时使用*/EP_MODE_EDGE=(1<<9),/*仅在添加事件时使用*/};创建事件时,可以使用以上所有类型;并且在回调的时候,回调函数只能接收EV_EVENT_XXX类型的值。三种AMCEpollEvents支持的具体类型如下:————_READ_WRITE_ERROR_FREE_TIMEOUT_SIGNAL_PERSIST_EDGE文件事件OOOOO.OO信号事件...OOOOOO超时事件...OO...具体参数说明如下:EP_EVENT_READ:读取事件。如果发生此事件类型,则将其视为文件事件。EP_EVENT_WRITE:读取事件。如果发生此事件类型,则将其视为文件事件。EP_EVENT_ERROR:一般是系统调用错误。预订。EP_EVENT_FREE:“销毁”事件。当调用AMCEpoll_FreeEvent()时触发。EP_EVENT_TIMEOUT:超时事件EP_EVENT_SIGNAL:信号事件。如果发生此事件类型,则将其视为信号事件。不得与文件事件共存。EP_MODE_PERSIST:持久事件。当一个回调(free除外)完成时,程序会自动将事件及其超时配置添加到事件循环中。当然,如果程序手动添加,也是可以的,但不是必须的。EP_MODE_EDGE:边沿触发模式。如果使用这种模式,只有在事件发生时才会触发回调。一个典型调用过程的基本使用过程与libevent和libev非常相似。可以参考我的这篇文章:《使用 libev 构建 TCP 响应服务器的简单流程》例如一个UDP事件的基本流程:创建一个AMCEpoll对象创建一个DGRAMfd,如果需要,根据fd绑定创建一个AMCEpollEvent对象并将AMCEpollEvent添加对象到AMCEpoll对象并编写回调。当遇到读取事件时,调用recvfrom()来调度其他事件。可以参考工程中的test_server.c文件。此文件创建三个事件:一个DNS请求和解析,以及一个UDP读取事件。得到DNS响应后,清除自身事件。一个HTTP服务器(其实HTTP响应是不规范的,请不要在意),回显数据。是边沿触发的TCP读取事件。两个信号事件,分别监听SIGINT和SIGQUIT。当检测到前者时,退出AMCEpoll事件循环。当检测到后者时,将打印调试信息。下一篇文章将讲解该项目的实现。文章尚未完结,敬请期待。
