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

你可能感兴趣的:(Openwrt)