Libevent在嵌入式設備中的串口、can等的應用

Libevent在嵌入式設備中的串口、can等的應用

0 引言

在實際開發過程中大家經常遇到各種各樣的事件,常見的有:讀、寫、定時器、信號等事件,一般處理方式有以下幾種:

  • 每個IO分配一個線程
  • 採用IO複用技術 select poll epoll IOCP等
  • NIO 採用輪詢的方式

各種各樣的事件庫有很多,比如說:Libevent、libev、libuv 等網絡庫,這些庫大多僅支持網絡,由於本人在使用場景比較特殊,設備中會搭載很多的串口設備、can設備、網絡、定時查詢的設備(spi、i2c、modbus等),通過調研分析,認爲libevent跟當前的使用場景貼合度非常高,本博文就是簡單介紹libevent的簡單使用,主要涉及:

  • Livent 處理讀事件:串口、網絡、can等 、
  • libevent 處理定事件
  • libevent 處理信號事件

1 搭建libevent使用環境

1.1 libevent是什麼

Libevent 是一個輕量級的開源高性能網絡庫,幾大優點:

  • 事件驅動(event-driven),
  • 高性能;
  • 輕量;
  • 跨平臺,支持 Windows、 Linux、 Mac Os;
  • 支持多種 I/O 多路複用技術, epoll、 poll、 dev/poll、 select 等;
  • 支持 I/O,定時器和信號等事件;

1.2 libevent 源碼下載、安裝

libevent 官網 : http://libevent.org
大家可以從官網下載源碼、文檔、以及其他相關資料,本次演示代碼版本:

libevent-2.1.11-stable.tar.gz

軟件環境 Ubuntu 18.04 64bit
解壓 編譯 安裝

tar xvzf libevent-2.1.11-stable.tar.gz
cd libevent-2.1.11-stable/
./configure
make 
make install

測試是否安裝成功:

cd sample
./time-test
timeout_cb called at 1582641217: 2.003 seconds elapsed.

1.3 在arm平臺下交叉編譯

直接運行以下命令即可

./configure --prefix=/root/wan/libevent --host=arm-linux-gnueabihf CC=zlg-linux-gcc

2 libevent 使用

創建了兩個類

Event.h

#ifndef EVENT_H
#define EVENT_H

#include "public_define.h"

typedef void (*EventCallBack)(int, short, void *);
const short ks16_event_read = EV_READ | EV_PERSIST;
const short ks16_event_timer = EV_TIMEOUT | EV_PERSIST;
typedef struct EventBase
{
    int fd = -1;;
    short events = 0;
    EventCallBack pfn_event_call_back = NULL;
    void * argv = NULL;
    std::string str_event_des;
    struct timeval st_timer_interval = {0xFFFF, 0};   
    struct event * st_event_handle = NULL;
}EventBase;

typedef struct EventSignal
{
    short events = 0;
    unsigned char u8_signal_no = 0;
    EventCallBack pfn_event_call_back = NULL;
    std::string str_event_des;  
    struct event * st_event_handle = NULL;
}EventSignal;

class Event
{
public:
    Event();
    ~Event();
    int AddEvnt(EventBase);
    int AddSignalEvent(EventSignal);
    int EventStart(void);
private:
    event_base *pBase = NULL;  // 整個Event的文件句柄
    std::map< std::string,EventBase > map_id_to_event_handle;
};

Event & GetEventHandle(void);
#endif

Event.cpp

#include "Event.h"

using namespace std;
Event::Event()
{
    pBase= event_base_new();
	if(NULL == pBase )
	{
		printf(" Create Event Base Error!\n");
        throw "event_base_new failed";
	}
    else
    {
        printf("Create Event Base Success!\n");
    }
}

Event::~Event()
{
    event_base_free(pBase);
    for(auto it = map_id_to_event_handle.begin();it != map_id_to_event_handle.end();it++)
    {
        event_del((it->second).st_event_handle);
        event_free((it->second).st_event_handle);    
    }
    pBase = NULL;
}
int Event::EventStart(void)
{
    event_base_dispatch(pBase);
}

int Event::AddEvnt(EventBase stEventBase)
{
    stEventBase.st_event_handle = event_new(pBase, stEventBase.fd, stEventBase.events, 
                                                   stEventBase.pfn_event_call_back,stEventBase.argv);
    map_id_to_event_handle.insert(make_pair(stEventBase.str_event_des,stEventBase)); 
    event_add(stEventBase.st_event_handle, &(stEventBase.st_timer_interval));
}

int Event:: AddSignalEvent(EventSignal stEventSignal)
{
    stEventSignal.st_event_handle = evsignal_new(pBase, stEventSignal.u8_signal_no, stEventSignal.pfn_event_call_back, event_self_cbarg());
    event_add(stEventSignal.st_event_handle, NULL);
}


Event & GetEventHandle(void)
{
    static Event gcls_event;
    return gcls_event;
}

測試主程序:

#include "public_define.h"
#include "SerialPort.h"
#include "UdpClient.h"
#include "Event.h"
#include <event.h>

void SerialPortCallBack(int s32_fd, short u16_what, void * p_argv)
{
    if(s32_fd > 0 && (u16_what & EV_READ))
    {
        SerialPort * pcls_serial = (SerialPort * )p_argv;
        char u8_read_buffer[PUBLIC_LEN_1024] = {0};
        // 讀取數據
        int s32_read_len = pcls_serial->Read(u8_read_buffer,PUBLIC_LEN_1024);
        if(s32_read_len > 0)
        {
            printf("%s %d len %d u8_read_buffer %s \n",__FUNCTION__,__LINE__,s32_read_len,u8_read_buffer);
        }
        else
        {
            printf("%s %d len %d \n",__FUNCTION__,__LINE__,s32_read_len);
        }
    }
    else
    {
        printf("%s %d fd %d what %d \n",__FUNCTION__,__LINE__,s32_fd,u16_what);
    }
    return;
}

void UdpCallBack(int s32_fd, short u16_what, void * p_argv)
{
    if(s32_fd > 0 && (u16_what & EV_READ))
    {
        UdpClient * pcls_udp = (UdpClient * )p_argv;
        unsigned char u8_read_buffer[PUBLIC_LEN_1024] = {0};
        // 讀取數據
        int s32_read_len = pcls_udp->Recive(u8_read_buffer,PUBLIC_LEN_1024);
        if(s32_read_len > 0)
        {
            printf("%s %d len %d u8_read_buffer %s \n",__FUNCTION__,__LINE__,s32_read_len,u8_read_buffer);
        }
        else
        {
            printf("%s %d len %d \n",__FUNCTION__,__LINE__,s32_read_len);
        }
    }
    else
    {
        printf("%s %d fd %d what %d \n",__FUNCTION__,__LINE__,s32_fd,u16_what);
    }
    return;
}

void TimerCallBack(int s32_fd, short u16_what, void * p_argv)
{
    if( u16_what & EV_TIMEOUT)
    {
         printf("%s %d  what %d \n",__FUNCTION__,__LINE__,u16_what);
    }
    else
    {
        printf("%s %d  what %d \n",__FUNCTION__,__LINE__,u16_what);
    }
    return;
}

void SignalCallBack(int s32_fd, short u16_what, void * p_argv)
{
    struct event * signal = (struct event * )p_argv;
    static int called = 0;
    printf("signal_cb: got signal %d\n", event_get_signal(signal));

    if (called >= 2)
    {
        event_del(signal);
    }    
    called++;
}

int main(void)
{

    SerialPort cls_serial_test;
	// 打開串口
    cls_serial_test.Open("/dev/ttyS1");
    cls_serial_test.SetSetup(115200,8,1,'N');
    cls_serial_test.SetReadConfig(0xFF,1);
    // 添加讀取串口的事件
	EventBase cls_event;
    cls_event.str_event_des = "read serialport ";
    cls_event.pfn_event_call_back = SerialPortCallBack;
    cls_event.argv = &cls_serial_test;
    cls_event.events = ks16_event_read;
    cls_event.fd = cls_serial_test.GetSerialPortFd();
    GetEventHandle().AddEvnt(cls_event);
    // UDP 測試
    UdpClient cls_udp_test;
    cls_udp_test.OpenSocket("192.168.1.37",9527,"192.168.1.239",9527);
    cls_event.fd = cls_udp_test.GetUdpHandle();
    cls_event.argv = &cls_udp_test;
    cls_event.str_event_des = "read udpclient";
    cls_event.pfn_event_call_back = UdpCallBack;
    cls_event.events =  ks16_event_read;
    GetEventHandle().AddEvnt(cls_event);
    //定時時間
    cls_event.str_event_des = "timer";
    cls_event.fd = -1;
    cls_event.argv = NULL;
    cls_event.pfn_event_call_back = TimerCallBack;
    cls_event.events =  ks16_event_timer;
    cls_event.st_timer_interval = {2,0};
    GetEventHandle().AddEvnt(cls_event);
    // 信號事件
    EventSignal cls_signal;
    cls_signal.str_event_des = "timer event";
    cls_signal.pfn_event_call_back = SignalCallBack;
    cls_signal.u8_signal_no = SIGINT;
    GetEventHandle().AddSignalEvent(cls_signal);

    GetEventHandle().EventStart();
}

編譯運行後:

TimerCallBack 59  what 1 
^Csignal_cb: got signal 2
UdpCallBack 41 len 20 u8_read_buffer http://www.cmsoft.cn 
SerialPortCallBack 17 len 28 u8_read_buffer ww.openedv.com
 
^Csignal_cb: got signal 2
SerialPortCallBack 17 len 28 u8_read_buffer ww.openedv.com
 
UdpCallBack 41 len 20 u8_read_buffer http://www.cmsoft.cn 
TimerCallBack 59  what 1 
SerialPortCallBack 17 len 28 u8_read_buffer ww.openedv.com
 
UdpCallBack 41 len 20 u8_read_buffer http://www.cmsoft.cn

程序可以同時接收udp和串口相關的數據

3 結束語

libevent 是一個很古老的庫,相關的資料,文檔、源碼分析特別多,所以在這裏不進行一一列舉,代碼中涉及可以去查看相關文檔
以下是幾個資料的網址:
LibEvent中文幫助文檔
libevent 官網 : http://libevent.org
libevent源碼深度剖析

4 題外話

涉及到libevent的話題很多,今後有時間會一一進行總結、歸納。主要涉及的topic如下:

  • Reactor模式和Proactor的區別
  • blocking IO(阻塞IO)
  • nonblocking IO(非阻塞IO)
  • IO multiplexing(多路複用IO)
  • signal driven IO(信號驅動IO)
  • asynchronous IO(異步IO)
  • select poll epoll IOCP對比 區別
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章