33-Openwrt libubox uloop

109 篇文章 10 订阅
87 篇文章 28 订阅
本文介绍了Libubox库中的uloop模块,包括文件描述符事件监控、定时器处理和进程管理。通过实例展示了如何使用uloop_fd监听文件描述符变化,uloop_timeout设置和管理定时器,以及uloop_process监控子进程并执行回调。
摘要由CSDN通过智能技术生成

目录

1 uloop_fd

2 定时器time

3 uloop_fd fd串口使用

4 uloop_fd usock

5 uloop_process


上一章节将libubox的一些简单组件介绍了一下,其实里面还有很多东西,只能等用到的时候再去学习,这边再介绍一个libubox里面比较经常用到的组件,就是uloop,uloop下面有太多东西了。

uloop是libubox下的一个模块,有三个功能:文件描述符触发事件的监控,timeout定时器处理, 当前进程的子进程的维护。

uloop只适合于单线程的任务,多线程会有bug???


主框架接口

  • 初始化事件循环 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 的本质是什么:

挂载一个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_fd 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)  // 进程


5 uloop_process

uloop_process一般越来监控的进程ID,比如监听到某个进程死掉,这将进程所用到的内存释放,做一些善后处理。

#include <libubox/list.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>

struct uapi_process {
  struct list_head list;
  struct uloop_process uloop;

  struct ustream_fd log;
  bool log_overflow;
  char process_name[64];
  char *mqtt_topic;
};


static void uapi_process_cb(struct uloop_process *proc, int ret)
{
  struct uapi_process *lp;
  lp = container_of(proc, struct uapi_process, uloop);

  if(WIFEXITED(ret)) {
    printf("%s (%d): exit with %d\n",
      lp->process_name, lp->uloop.pid, WEXITSTATUS(ret));
  } else if (WIFSIGNALED(ret)) {
    printf("%s (%d): exit with signal %d\n",
      lp->process_name, lp->uloop.pid, WTERMSIG(ret));
  } else if (WIFSTOPPED(ret)) {
    printf("%s (%d): stop with signal %d\n",
      lp->process_name, lp->uloop.pid, WSTOPSIG(ret));
  }

  uapi_delete_process(&lp->uloop);
}

int uapi_process_data(const char *process_argv)
{
  int pfds[2];
  pid_t pid;
  struct uapi_process *l_proc = NULL;

  if (pipe(pfds) < 0) {
    fprintf(stderr, "pipe failed for (%d)", errno);
    return -1;
  }

  /*
   * free after process stop
   */
  l_proc = malloc(sizeof(struct uapi_process));

  memset(l_proc, 0, sizeof(struct uapi_process));

  if (!l_proc) {
    fprintf(stderr, "malloc uapi_process failed");
    goto error;
  }

  if ((pid = fork()) < 0) {
    fprintf(stderr, "fork uapi_process failed for");
    goto error;
  }

  if (!pid) {
    /* child */
    int i;
    char app_path[PATH_MAX];

    /*
     * disable pipe read
     */
    close(pfds[0]);

    /*
     * stdin/sdtout/stderr to pipe write
     */
    for (i = 0; i <= 2; i++) {
      if (pfds[1] == i)
      continue;

      dup2(pfds[1], i);
    }

    if (pfds[1] > 2)
      close(pfds[1]);

    memset(app_path, 0, sizeof(app_path));
    snprintf(app_path, sizeof(app_path), "%s%s", UAPI_PATH, UAPI_PROCESS_NAME);
    execl(app_path, UAPI_PROCESS_NAME, UAPI_MQTT_ARGV, process_argv, (char *)NULL);
    fprintf(stderr, "failed execl %s", app_path);
    exit(127);
  }

  /*
   * parent disable pipe write
   */
  close(pfds[1]);
  memcpy(l_proc->process_name, UAPI_PROCESS_NAME, sizeof(l_proc->process_name));
  l_proc->uloop.cb = uapi_process_cb;
  l_proc->uloop.pid = pid;
  uloop_process_add(&l_proc->uloop);
  list_add_tail(&l_proc->list, &uapi_process_list);

  return 0;

error:
  if (l_proc) {
    free(l_proc);
  }

  close(pfds[0]);
  close(pfds[1]);
  return -1;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值