什么是uloop
uloop是libubox的一个模块,而libubox是openwrt的一个基础库。本文对libubox不重点敞开讨论。重点记录下自己项目中使用uloop的应用。
uloop重点有三个功能:文件描述符触发事件的监控,timeout定时器处理, 当前进程的子进程的维护。其实是一种最简化的伪线程机制,可以将事务、定时器通过触发文件描述符fd的监控。
对于linux应用开发,涉及一些小业务场景(数据转发量不大的业务控制类),又需要监听不同fd的场景,如需要创建多个socket通信,大家一般就thread开起来。
多线程的好处就不多描述,大家baid一堆。但也带来了缺陷,如线程之间做需要互斥。然后在简单的业务控制场景,对并行要求并不高的,用一个单线程即能解决的事情,就不要用多线程去搞,除了编码带来的复杂性,也带来了不安全性和系统资源浪费。
伪线程
不同业务的事务触发条件、时机不一样,处理方法也不一样,一个单线程如何解决。
伪线程的概念即通过单线程来模拟多任务,做过RTOS的都知道,由MCU触发硬件中断,注册中断处理函数,来实现不同中断(业务)的处理。uloop采用类似的结构,只是通过文件描述符来解决。
ufd.fd = socket(...);
ufd.cb = socket_recv_cb;
uloop_fd_add(&ufd, ULOOP_READ);
如上,一个uloop简单的事务处理注册方法,监听某个socket,当有数据时候,回调到ufd.cb函数进行处理。
uloop架构
前面说到的uloop的三大功能,在uloop模块内维护着三个全局变量poll_fd,timeouts-链表,processes-链表
主要框架:
- 初始化事件循环
int uloop_init(void)
创建一个epoll的句柄,最多监控32个文件描述符。
设置文件描述符属性,如FD_CLOEXEC。
- 事件循环主处理入口
void uloop_run(void)
- 销毁事件循环
void uloop_done(void)
关闭epoll句柄。
清空定时器链表中的所有的定时器。
清空进程处理事件链表中删除所有的进程事件节点。
重点看uloop_run():
while(!uloop_cancelled)
{
uloop_gettime(&tv);
uloop_process_timeouts(&tv);
if (uloop_cancelled)
break;if (do_sigchld)
uloop_handle_processes();
uloop_run_events(uloop_get_next_timeout(&tv));
}
- 查看定时器链表第一个节点是否到期,到期则执行该节点的处理函数(定时器节点按时间顺序排序加入到链表)
- 处理子进程事务。(本节暂不讨论)
- 判断所有fd是否已触发了事务-由epoll机制方式,如已产生事件则触发该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)
应用
我们常用的控制台输入方法,scanf或getchar,这两个方法是阻塞的,会一直等待控制台输入返回后才会继续往下执行,如果一个场景需要等待同时,又能处理其他事情,即可用到uloop方式:
本例,通过uloop创建了一个定时器,每2s打印一下,同时又监听控制台输入,如果有输入,则重复打印一次输入内容:
/*recv message */
static void input_recv_cb(struct uloop_fd *fd, unsigned int events)
{
uint16_t data_len = 0;memset(g_read_buf, 0, FRAME_MAX_LEN);
data_len = input_read(g_read_buf, fd->fd);
nlog("input:%s\n", (char *)g_read_buf);
return;
}static void print_cb(struct uloop_timeout *t)
{
printf("print here\n");
uloop_timeout_set(&printf_timeout, 2*1000);
}static void nt_init()
{
ufd.fd = 0;//标准输入
ufd.cb = input_recv_cb;
uloop_fd_add(&ufd, ULOOP_READ);printf_timeout.cb = print_cb;
uloop_timeout_set(&printf_timeout, 2*1000);//2s打印一次
}int main(void)
{
uloop_init();nt_init();
uloop_run();
uloop_done();return 0;
}
详细的代码:(内含uloop模块)