libev源代码分析--基本的接口函数

本文详细分析了libev库中的关键接口,包括ev_io、ev_timer、ev_periodic等,阐述了它们在不同场景下的触发事件及其工作原理。同时,介绍了libev如何处理系统错误和内存分配,以及在不同平台下的兼容性考虑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ibev的代码很简练 但对于看他代码的人 简直就是噩梦 到处都是宏 宏还嵌套 于是我在看libev代码时 对其进行了还原 去掉了宏
1struct ev_io {
2int fd;
3int events;
4struct ev_watcher_list *list;
5int active;
6int pending;
7int priority;
8void* data;
9void (*cb)(structev_loop *loop, structev_io *w,int revents);
10};

ev_io在触发EV_READ或者是EV_WRITE被调用

1struct ev_timer {
2ev_tstamp at;
3ev_tstamp repeat;
4int active;
5int pending;
6int priority;
7void* data;
8void (*cb)(structev_loop *loop, structev_timer *w,int revents);
9};

ev_timer在特定的时间调用,并周期性进行,其基于单调时钟
(PS:单调时钟:此时间来源会严格的线性递增,一般linux会使用系统正常运行时间来表示,也就是从开机开始算起) 触发事件EV_TIMEOUT

1struct ev_periodic {
2ev_tstamp offset;
3ev_tstamp interval;
4ev_tstamp (*reschedule_cb)(structev_periodic *w, ev_tstamp now);
5ev_tstamp at;
6int active;
7int pending;
8int priority;
9void* data;
10void (*cb)(structev_loop *loop, structev_periodic *w,int revents);
11};

ev_periodic在特定的时间调用,可能会在定期间隔反复调用,其基于UTC时间
(PS:UTC:协调时间 也就是从1970年1月1日00:00:00开始记时) 触发事件EV_PERIODIC

1struct ev_signal {
2int signum;
3struct ev_watcher_list *next;
4int active;
5int pending;
6int priority;
7void* data;
8void (*cb)(structev_loop *loop, structev_signal *w,int revents);
9};

ev_signal当接收到指定的信号时调用 触发事件EV_SIGNAL

1struct ev_child {
2int flag;
3int pid;
4int rpid;
5int rstatus;
6struct ev_watcher_list *next;
7int active;
8int pending;
9int priority;
10void* data;
11void (*cb)(structev_loop *loop, structev_child *w,int revents);
12};

ev_child当接收到SIGCHLD信号并且waitpid表示了给出的pid时调用 触发EV_CHILD事件
其不支持优先级

1struct ev_stat {
2ev_timer timer;
3ev_tstamp interval;
4const char *path;
5ev_statdata prev;
6ev_statdata attr;
7int wd;
8struct ev_watcher_list *next;
9int active;
10int pending;
11int priority;
12void* data;
13void (*cb)(structev_loop *loop, structev_stat *w,int revents);
14};

ev_stat当每次指定的路径状态数据发生改变时调用 触发EV_STAT

1struct ev_idle {
2int active;
3int pending;
4int priority;
5void* data;
6void (*cb)(structev_loop *loop, structev_idle *w,int revents);
7};

ev_idle当啥事情都不需要做的时候调用,用来保持进程远离阻塞 触发EV_IDLE

1struct ev_prepare {
2int active;
3int pending;
4int priority;
5void* data;
6void (*cb)(structev_loop *loop, structev_prepare *w,int revents);
7};

ev_prepare每次执行mainloop主循环,在主循环之前调用 触发EV_PREPARE;

1struct ev_check {
2int active;
3int pending;
4int priority;
5void* data;
6void (*cb)(structev_loop *loop, structev_check *w,int revents);
7};

ev_check每次执行mainloop主循环,在主循环之后调用 触发EV_CHECK

1struct ev_fork {
2int active;
3int pending;
4int priority;
5void* data;
6void (*cb)(structev_loop *loop,structev_fork *w,int revents);
7};

ev_fok在fork行为被检测到,并且在检测子进程之前调用 触发EV_FORK;

1struct ev_cleanup {
2int active;
3int pending;
4int priority;
5void* data;
6void (*cb)(structev_loop *loop,structev_cheanup *w,int revents);
7};

ev_cleanup在主循被销毁之后调用 触发EV_CLEANUP

1struct ev_embed {
2struct ev_loop* other;
3ev_io io;
4ev_prepare prepare;
5ev_check check;
6ev_timer timer;
7ev_periodic periodic;
8ev_idle idle;
9ev_fork fork;
10ev_cleanup cleanup;
11};

ev_embed用于将一个事件循环嵌套到另一个中,当事件循环处理事件的时候被调用

1struct ev_async {
2sig_atomic_t volatile sent;
3int active;
4int pending;
5int priority;
6void* data;
7void (*cb)(structev_loop *loop, structev_async *w, int revents);
8};

ev_async当ev_async_send通过watcher调用时调用,触发EV_ASYNC

1union ev_any_watcher {
2struct ev_watcher w;
3struct ev_watcher_list wl;
4struct ev_io io;
5struct ev_timer timer;
6struct ev_periodic periodic;
7struct ev_signal signal;
8struct ev_child child;
9#if EV_STAT_ENABLE
10struct ev_stat stat;
11#endif
12#if EV_IDLE_ENABLE
13struct ev_idle idle;
14#endif
15struct ev_prepare prepare;
16struct ev_check check;
17#if EV_FORK_ENABLE
18struct ev_fork fork;
19#endif
20#if EV_CLEANUP_ENABLE
21struct ev_cleanup cleanup;
22#endif
23#if EV_EMBED_ENABLE
24struct ev_embed embed;
25#endif
26#if EV_ASYNC_ENABLE
27struct ev_async async;
28#endif
29};

该结构的存在用以强制类似结构的布局

1struct ev_watcher {
2int active;
3int pending;
4int priority;
5void* data;
6void (*cb)(structev_loop* loop, structev_watcher *w, int revent);
7}
8 
9struct ev_watcher_list {
10struct ev_wather_list *next;
11int active;
12int pending;
13int prioirty;
14void* data;
15void (*cb)(structev_loop* loop, structev_watcher_list *w,intrevent);
16};
1static unsigned int ev_linux_version(void)

通过使用utsname结构以及uname函数获取linux版本号

1static void ev_printerr(constchar *msg)

输出错误新方法,实现很简单用write直接往STDERR写数据

1void ev_set_syserr_cb(void(*cb)(const char *msg))

设置系统错误控制方法回调

1static void ev_syserr(constchar *msg)

系统错误处理函数,首先判断msg是否为空,msg==NULL填充为”(libev) system error”,然后如果系统错误控制回调不为空 也就是通过上面方法设置的,就调用该控制回调 否则的话就格式化输出 并中断程序运行

1static void* ev_realloc_emul(void*ptr, long size)

作者分装的一个realloc,主要为了屏蔽不同平台的差异,比如 有些系统(如openBSD)不支持realloc(x,0)这种行为,于是作者进行了判定如果是含有__GLIBC__宏 那么就可以之间诶用realloc 否则的话当size为0则进行free操作

1static void *(*alloc)(void*ptr, long size) = ev_realloc_emul;

定义了一个变量,并赋值,啥变量 一个指向返回值是void* 参数是void*,long的函数指针 不过看着总觉得不爽 习惯于typedef的写法,另一方面也就是说alloc等价与ev_realloc_emul哦

1void ev_set_allocator(void*(*cb)(void* ptr,longsize))

设置alloc函数的实现,我们在上面的语句看到alloc的默认实现是啥,通过该函数可以改变其实现

1inline_speed void* ev_realloc(void* ptr,long size)

一个realloc的实现调用的alloc,比之增加错误日志输出 有意思的是这里有一个叫做inline_speed的宏,libev的作者在libev中大量使用了,使人看代码相当不爽的说

1#if EV_FEATURE_CODE
2#define inline_speed static inline
3#else
4#define inline_speed static noinline
5#endif

这里又一把宏定义,不过都是见名知义,EV_FEATURE_CODE默认情况是非0,也就是inline_speed默认为static inline

1#define ev_malloc(size) ev_realloc(0,(size))
2#define ev_free(ptr)m ev_realloc((ptr),0)

我们看到ev_malloc以及ev_free其实也是通过ev_realloc实现的

1typedef struct{
2ev_watcher_list* head; //监听者链表
3unsigned charevents; //监听的事件
4unsigned charreify;//状态位 用来表示具体是EV_ANFD_REIFY还是EV_IOFDSET
5unsigned charemask;//epoll用来保存内核mask的值
6unsigned charunused;//同名字
7#if EV_USE_EPOLL
8unsigned integen;
9#endif
10#if EV_SELECT_ISWINSOCKET || EV_USE_IOCP
11SOCKET handle
12#endif
13#if EV_USE_IOCP
14OVERLAPPED or,ow;
15#endif
16} ANFD;

文件描述符信息结构

1typedef struct {
2ev_watcher* w;
3int events;
4} ANPENDING;

指定等待事件的监听者结构

1typedef struct {
2ev_watcher_list* head;
3} ANFS;

每个inotify-id对应的哈希表的每个节点的结构

1typedef struct {
2ev_tstamp at;
3ev_watcher_time* w;
4} ANHE;

堆结构的节点

1struct ev_loop {
2ev_tstamp ev_rt_now;
3}
1ev_tstamp ev_time(void)

获取当前时间如果设置了EV_USE_REALTIME会考虑是否使用clock_gettime纳秒(have_realtiem如果为非负就使用 clock_gettime了) 否则使用gettimeofday 注意这里的时间都是和当前设置的时间是有关系的 也就是说如果我把系统时间修改了 可能得到过去的时间
另外这里用了一个宏expect_true 其原型是__builtin_expect((a) != 0, 1)也就是告诉编译器绝大多数情况下a != 0是满足的 方便编译器进行优化 同理相反的还有expect_false

1inline_size ev_tstamp get_clock(void)

这个函数优先是使用CLOCK_MONOTONIC时间 也就是单调时间不会被修改减小 然后达不到要求 则调用ev_time来获取时间 inline_size就是 static inline

1ev_tstamp ev_now(structev_loop *loop)

获取当前时间时间 也就是loop->ev_rt_now

1void ev_sleep(ev_tstamp delay)

如果可以使用nanosleep则使用nanosleep 如果在window下使用Sleep,否则使用select实现

1inline_size intarray_next(int elem, int cur, int cnt)

用来生成内存对齐大小的实际需要开辟的内存大

1static ninline void* array_realloc(intelem, void *base, int *cur, int cnt)

开辟或重新非配一定大小的内存块,该方法会调用上面的函数进行内存对齐大小 所以返回后的大小不一定会和想开辟的相等

1array_init_zero(base,count)

顾名思义初始化为0,通过memset实现

1array_needsize(type,base,cur,cnt,init)

分配需要大小的内存 就是分配出内存 并初始化为0

1array_free(stem,idx)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值