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對比 區別