13-Openwrt libubox uloop
上一章节将libubox的一些简单组件介绍了一下,其实里面还有很多东西,只能等用到的时候再去学习,这边再介绍一个libubox里面比较经常用到的组件,就是uloop,uloop下面有太多东西了。
uloop是libubox下的一个模块,有三个功能:文件描述符触发事件的监控,timeout定时器处理, 当前进程的子进程的维护。
主框架接口
- 初始化事件循环 int uloop_init(void)
创建一个epoll的句柄,最多监控32个文件描述符。
设置文件描述符属性,如FD_CLOEXEC。
- 事件循环主处理入口 void uloop_run(void)
uloop_run轮询处理定时器、进程、描述符事件。
遍历定时器timeouts链表判断是否有定时器超时,如果有则进行相应的回调处理,没有跳过。
判断是否有子进程退出SIGCHLD信号,有就会遍历processes进程处理的链表,调勇相应的回调函数,没有跳过。计算出距离下一个最近的定时器的时间,作为文件描述符事件epoll的超时时间。然后epoll进行事件监听,如果有文件描述符准备就绪(可读写时间)则调用相应的回调函数,或者有信号进行中断epoll返回停止监听,否则epoll阻塞直到超时时间完成。
- 销毁事件循环 void uloop_done(void)
关闭epoll句柄。
清空定时器链表中的所有的定时器。
清空进程处理事件链表中删除所有的进程事件节点。
1 uloop_fd
epoll 的本质是什么:
https://mp.weixin.qq.com/s/MzrhaWMwrFxKT7YZvd68jw
挂载一个cb回掉函数即可,fd有改变时即触发
void read_std_callback(struct uloop_fd *u, unsigned int events)
{
char buf[1024] = {0};
if (ULOOP_READ) {
if ( read(u->fd, buf, 1024) > 0) {
printf("read_std: %s\n", buf);
}
}
}
void uloop_fd_test(void)
{
struct uloop_fd fd_test = {
.cb = read_std_callback,
.fd = STDIN_FILENO,
};
uloop_init();
/*添加uloop_fd*/
uloop_fd_add(&fd_test, ULOOP_READ);
uloop_run();
uloop_fd_delete(&fd_test);
uloop_done();
}
2 定时器time
如下,一个定时器的使用就是这么简单。
void timeout_callback(struct uloop_timeout *timeout)
{
printf("timeout_callback\r\n");
uloop_timeout_set(timeout, 5000);
}
void uloop_timeout_test(void)
{
struct uloop_timeout fd_timeout = {
.cb = timeout_callback,
};
uloop_init();
uloop_timeout_set(&fd_timeout, 5000); //5 second
uloop_run();
uloop_timeout_cancel(&fd_timeout);
uloop_done();
}
3 uloop_fd fd串口使用
平常一直在纠结linux的串口编程要怎么弄,又是read,又是select的太过麻烦了,赶快转成用libubox的epool方式,太方便了。
测试流程,开启一个定时器,5秒发一次数据,收到的数据会直接跑到回调函数里面,666
void uloop_fd_uart_test(void)
{
int fd_uart = -1;
fd_uart = UartInit();
if(-1 == fd_uart)
{
printf("uart init error\r\n");
return;
}
struct uloop_fd fd_uart_test = {
.cb = read_uart_callback,
.fd = fd_uart,
};
struct uloop_timeout fd_uart_timeout = {
.cb = write_uart_timeout_callback,
};
uloop_init();
/*添加uloop_fd*/
uloop_fd_add(&fd_uart_test, ULOOP_READ);
uloop_timeout_set(&fd_uart_timeout, 5000); //5 second
uloop_run();
uloop_fd_delete(&fd_uart_test);
uloop_timeout_cancel(&fd_uart_timeout);
uloop_done();
}
回调函数:
int m_uartFd = -1;
void read_uart_callback(struct uloop_fd *u, unsigned int events)
{
unsigned char buffer[128] = {0};
int len = 0;
if (ULOOP_READ) {
len = read(u->fd, buffer, 1024);
if (len > 0)
{
#if 1
{
char PrintBuff[1024];
int uiPrintLen = 0;
uiPrintLen = sprintf((PrintBuff + uiPrintLen), "######## Uart Read(%02d)", len);
for(int ii = 0; ii < len; ii++)
{
uiPrintLen += sprintf((PrintBuff + uiPrintLen),"%02X ", buffer[ii]);
}
printf("%s\r\n", PrintBuff);
}
#endif
}
}
}
void write_uart_timeout_callback(struct uloop_timeout *timeout)
{
//char buffer[] = {0x55,0xAA,0x00,0x01,0x00,0x00,0x00};
unsigned char buffer[] = {0xFF,0xFF,0xAA,0x02,0x11,0x22,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x04,0x02,0x11,0x22,0x40,0x99,0x16};
int len = 0;
printf("write_uart_timeout_callback\r\n");
len = write(m_uartFd, buffer, 21);
#if 1
{
char PrintBuff[1024];
int uiPrintLen = 0;
uiPrintLen = sprintf((PrintBuff + uiPrintLen), "######## Uart Write(%02d)", len);
for(int ii = 0; ii < len; ii++)
{
uiPrintLen += sprintf((PrintBuff + uiPrintLen),"%02X ", buffer[ii]);
}
printf("%s\r\n", PrintBuff);
}
#endif
uloop_timeout_set(timeout, 5000);
}
串口初始化:
int UartInit(void)
{
int baudrate = 115200;
int databits = 8;
int stopbits = 1;
char parity = 'N';
m_uartFd = open("/dev/ttyS2",O_RDWR|O_NOCTTY|O_NDELAY);
if(m_uartFd <= 0)
{
perror("file open error");
return -1;
}
//恢复串口为阻塞状态
if(fcntl(m_uartFd, F_SETFL, 0) < 0)
{
perror("fcntl failed!");
return -1;
}
struct termios options;
int speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};
int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300};
int i;
/*tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将它们保存于options,该函数还可以测试配置是否正确,该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.
*/
if( tcgetattr( m_uartFd,&options) != 0)
{
perror("SetupSerial 1");
return -1;
}
//设置串口输入波特率和输出波特率
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++)
{
if(baudrate == name_arr[i])
{
cfsetispeed(&options, speed_arr[i]);
cfsetospeed(&options, speed_arr[i]);
}
}
//修改控制模式,保证程序不会占用串口
options.c_cflag |= CLOCAL;
//修改控制模式,使得能够从串口中读取输入数据
options.c_cflag |= CREAD;
//不使用流控制
options.c_cflag &= ~CRTSCTS;
//特殊字符不做转换
options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
//设置数据位
//屏蔽其他标志位
options.c_cflag &= ~CSIZE;
switch (databits)
{
case 5:
options.c_cflag |= CS5;
break;
case 6:
options.c_cflag |= CS6;
break;
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
perror("Unsupported data size");
return -1;;
}
//设置校验位
switch (parity)
{
case 'n':
case 'N': //无奇偶校验位。
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
break;
case 'o':
case 'O'://设置为奇校验
options.c_cflag |= (PARODD | PARENB);
options.c_iflag |= INPCK;
break;
case 'e':
case 'E'://设置为偶校验
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_iflag |= INPCK;
break;
case 's':
case 'S': //设置为空格
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
perror("Unsupported parity");
return -1;
}
// 设置停止位
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB; break;
case 2:
options.c_cflag |= CSTOPB; break;
default:
perror("Unsupported stop bits");
return -1;
}
//修改输出模式,原始数据输出
options.c_oflag &= ~OPOST;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
//options.c_lflag &= ~(ISIG | ICANON);
//设置等待时间和最小接收字符
options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */
options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */
//如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读
tcflush(m_uartFd,TCIFLUSH);
//激活配置 (将修改后的termios数据设置到串口中)
if (tcsetattr(m_uartFd,TCSANOW,&options) != 0)
{
perror("com set error!\n");
return -1;
}
printf("Led Uart Init OK,fd(%d).\r\n",m_uartFd);
return m_uartFd;
}
4 uloop usock
uloop_fd的时候不止这些,sock也可以监听,只要时流设备都可以监听,如下例子:
服务器:
void recv_sock_callback(struct uloop_fd *u, unsigned int events)
{
char buf[1024] = {0};
int connect_fd;
struct sockaddr_in cli_addr;
socklen_t len = sizeof(struct sockaddr);
connect_fd = accept(u->fd, (struct sockaddr *)(&cli_addr), &len);
if (connect_fd < 0) {
perror("accept");
return;
}
if (recv(connect_fd, buf, 1024, 0) > 0) {
printf("recv_buf: %s\n", buf);
}
close(connect_fd);
}
int usock_and_uloop_fd_test(void)
{
int type = USOCK_TCP | USOCK_SERVER | USOCK_NOCLOEXEC | USOCK_IPV4ONLY;
const char *host = "127.0.0.1";
const char *service = "1212";
int u_fd = usock(type, host, service);
if (u_fd < 0) {
perror("usock");
return -1;
}
struct uloop_fd fd_sock = {
.cb = recv_sock_callback,
.fd = u_fd,
};
uloop_init();
/*添加uloop_fd*/
uloop_fd_add(&fd_sock, ULOOP_READ);
uloop_run();
uloop_fd_delete(&fd_sock);
uloop_done();
return 0;
}
客户端:
#include "ztest.h"
void log_init(void)
{
ulog_open(ULOG_SYSLOG, LOG_USER, NULL);
ulog_threshold(LOG_INFO);
}
int main(int argc, char **argv){
int type = USOCK_TCP | USOCK_NOCLOEXEC | USOCK_IPV4ONLY;
const char *host = "127.0.0.1";
const char *service = "1212";
log_init();
ULOG_INFO("--------zclient--------\n");
int c_fd = usock(type, host, service);
if(c_fd < 0) {
perror("usock");
return -1;
}
send(c_fd, "helloworld", 10, 0);
close(c_fd);
return 1;
}
uloop出来fd,timeout还有一个process,不过我自己没有用过,别人与用过的可以留下链接改成用的时候参考。
typedef void (*uloop_fd_handler)(struct uloop_fd *u, unsigned int events) // 描述符
typedef void (*uloop_timeout_handler)(struct uloop_timeout *t) // 定时器
typedef void (*uloop_process_handler)(struct uloop_process *c, int ret) // 进程
代码位于github:https://github.com/creatorly/TestCode/tree/master/openwrt/ztest