
uloop是提供事件驱动机制接口,类似libevent事件框架,基于epoll接口来实现的。
uloop三大功能:事件管理(uloop_fd)、超时管理(uloop_timeout)和进程管理(uloop_process),定义在uloop.h中。
1. 整体框架
: /** 2: * 初始化事件循环 3: *主要工作是poll_fd = epoll_create(32);/* 创建一个epoll的文件描述符监控句柄。最多监控32个文件描述符 4: **/ : int uloop_init(void) : { : if (poll_fd >= ) : return ; : : poll_fd = epoll_create();/* 创建一个epoll的句柄。最多监控32个文件描述符 */ : if (poll_fd < ) : return -; : : fcntl(poll_fd, F_SETFD, fcntl(poll_fd, F_GETFD) | FD_CLOEXEC); /* fd_cloexecs */ : return ; : } : : : /** 20: * 事件循环主处理入口 21: *1.当某一个进程第一次调用uloop_run时,注册sigchld和sigint信号 22: *2.循环获取当前时间,把超时的timeout处理掉,有一条timeout链表在维护 23: *3.循环检测是否收到一个sigchld信号,如果收到,删除对应的子进程,有一条process子进程链表在维护 24: *4.循环调用epoll_wait 监相应的触发事件文件描述符fd 25: **/ : void uloop_run(void) : { : static int recursive_calls = ; /* static value */ : struct timeval tv; : : /* 32: * Handlers are only updated for the first call to uloop_run() (and restored 33: * when this call is done). 34: */ : if (!recursive_calls++) /* 第一次运行uloop_run时调用, 注册信号处理函数 */ : uloop_setup_signals(true); : : uloop_cancelled = false; : while(!uloop_cancelled) : { : uloop_gettime(&tv); /* 获取当前时间 */ : uloop_process_timeouts(&tv); /* 把超时的timeout清理掉 */ : if (uloop_cancelled) : break; : : if (do_sigchld) /* 收到一个sigchld的信号 */ : uloop_handle_processes(); /* 销毁该进程的uloop_process */ : uloop_gettime(&tv); : uloop_run_events(uloop_get_next_timeout(&tv));/* 处理相应的触发事件fd */ : } : : if (!--recursive_calls) : uloop_setup_signals(false); : } : : : /** 58: * 销毁事件循环 59: * 关闭epoll描述符 60: * 销毁子进程链表 61: * 销毁timeout链表 62: **/ : void uloop_done(void) : { : if (poll_fd < ) : return; : : close(poll_fd); : poll_fd = -; : : uloop_clear_timeouts(); : uloop_clear_processes(); : }
// 设置uloop内部结束循环标志
static inline void uloop_end(void)
{
uloop_cancelled = true;
}
int uloop_init(void);
void uloop_run(void);
void uloop_done(void);
2. uloop_fd
uloop是一个I/O循环调度,将不同文件描述符添加到轮询中。
文件描述符fd的管理由uloop_fd结构来设置。仅需设置fd和事件发生时的回调函数,数据结构的其他部分供内部使用。
默认采用非阻塞和水平触发。
#define ULOOP_READ (1 << 0)
#define ULOOP_WRITE (1 << 1)
#define ULOOP_EDGE_TRIGGER (1 << 2)
#define ULOOP_BLOCKING (1 << 3) #define ULOOP_EVENT_MASK (ULOOP_READ | ULOOP_WRITE) /* internal flags */
#define ULOOP_EVENT_BUFFERED (1 << 4)
#ifdef USE_KQUEUE
#define ULOOP_EDGE_DEFER (1 << 5)
#endif #define ULOOP_ERROR_CB (1 << 6) struct uloop_fd
{
uloop_fd_handler cb;
int fd;
bool eof;
bool error;
bool registered;
uint8_t flags;
}; int uloop_fd_add(struct uloop_fd *sock, unsigned int flags);
int uloop_fd_delete(struct uloop_fd *sock);
typedef void (*uloop_fd_handler)(struct uloop_fd *u, unsigned int events);
3. uloop_timeout
超时管理部分由uloop_timeout结构来管理,在定时时间到了之后调用回调函数,定时时间单位为毫秒。
uloop定时器是一次性定时器,超时后会自动删除。
libubox使用一个全局排序链表(按照超时时间升序排列)存储定时器节点。
注:uloop将定时器节点按照绝对时间升序排队,每次uloop循环先处理已超时的定时器,然后取定时器队列首节点(即最近一个将要超时的定时器节点),减去当前时间得到下次将要超时的相对时间;
然后用这个相对时间作为超时时间调用epoll_wait。
uloop将定时器节点按照绝对时间升序排队,每次uloop循环先处理已超时的定时器,然后取定时器队列首节点(即最近一个将要超时的定时器节点),减去当前时间得到下次将要超时的相对时间;然后用这个相对时间作为超时时间调用epoll_wait。
-->
struct uloop_timeout
{
struct list_head list;
bool pending; //是否已经加入超时链表等待调度
uloop_timeout_handler cb;
struct timeval time;
};
int uloop_timeout_add(struct uloop_timeout *timeout);
int uloop_timeout_set(struct uloop_timeout *timeout, int msecs);
int uloop_timeout_cancel(struct uloop_timeout *timeout);
int uloop_timeout_remaining(struct uloop_timeout *timeout); typedef void (*uloop_timeout_handler)(struct uloop_timeout *t)
uloop_timeout_add()添加定时器,要求已初始化timeout结构,应避免直接使用uloop_timeout_add()。
uloop_timeout_set()设定定时器超时事件为当前时间+指定超时时间(msecs)。内部封装了uloop_timeout_add(),应调用本函数添加定时器。
4. uloop_process
当前进程的子进程管理。建立一个链表,按进程号升序方式管理所有进程id。
uloop进程管理是一次性任务,触发后会自动删除。
struct uloop_process
{
struct list_head list;
bool pending; //是否已经加入任务链表等待调度 uloop_process_handler cb;
pid_t pid;
}; int uloop_process_add(struct uloop_process *p);
int uloop_process_delete(struct uloop_process *p); typedef void (*uloop_process_handler)(struct uloop_process *c, int ret)