Linux-视频监控系统(3)-Epoll框架的实现

来源:互联网 发布:sniffer java 报错 编辑:程序博客网 时间:2024/06/07 22:56
采集端分为以下几个子系统:
1、采集端子系统
2、存储子系统
3、压缩子系统
4、传输子系统
5、配置子系统

6、主程序


首先对整体的程序框架做一个划分,对于每个子系统都建立一个.c文件,同时对每个子系统都建立一个结构体来描述,然后把所有需要用到的头文件都放到include文件夹中。


然后提炼出哪些事件是需要等待的,可以把这些事件加入Epoll池中,分析如下:

1、摄像头可读(摄像头的设备文件已经有了一帧图像)

2、socket可写(可以通过socket发送数据了)

3、存储设备可写(可以往SD卡中存放数据)

可能分析的不够全面,会把有些事件漏掉,想起了以后再加好了。


对于Epoll的事件结构是这样的:

struct epoll_event{    __uint32_t   events;    epoll_data_t data;};
如果需要保存一些信息,比如说设备文件fd,对应的处理函数和其他标志位,就是采用data里面的结构来保存的:

typedef union epoll_data{    void *ptr;    int fd;    __uint32_t u32;    __uint64_t u64;};
由于结构里的数据只可以用一个,因此为了方便起见,我们自己定义一个结构来保存设备文件fd、一些标志位和处理函数,并把这个结构的指针挂接早ptr指针中:

struct event_ext{int fd;//监控文件的fdbool epolled;//事件是否在池中的标志uint32_t events;//事件类型void (*handler)(int fd, void *arg);//处理函数void *arg;//处理函数的参数};

这个结构定义在main.c中,然后在main.h中定义:

struct server{int epfd;//指向创建的Epollstruct cam *cam;//指向摄像头子系统struct tcp_srv *tcp_srv;//指向网络子系统struct cfg *cfg;//指向配置子系统};struct server *srv_main;

在cam.c中定义:

struct cam{};struct cam *cam_sys_init(){//初始化采集子系统//将采集子系统中的事件加入Epoll池return NULL;}
在net.c中定义:

struct tcp_srv{};struct tcp_srv *net_sys_init(){//初始化传输子系统//将传输子系统的事件加入Epoll池}
在配置文件中定义:

struct cfg{};



定义好每个子系统的结构后开始设计主程序的框架,在主程序中主要实现Epoll的初始化,各个子模块的初始化,然后添加事件,最后等待事件的发生,具体事件的处理交给各个子系统。同时为了使整个框架更灵活,把Epoll事件的添加交给子系统完成,具体需要添加/修改哪个事件有子系统觉得,同时事件的添加放在子系统初始化中完成。然后给子系统提供事件初始化函数、事件添加/修改、事件删除的接口。最后main.c文件如下:

#include <stdio.h>#include <unistd.h>#include <stdbool.h>#include <fcntl.h>#include <sys/epoll.h>#include "main.h"struct event_ext{int fd;//监控文件的fdbool epolled;//事件是否在池中的标志uint32_t events;//事件类型void (*handler)(int fd, void *arg);//处理函数void *arg;//处理函数的参数};//初始化事件的接口struct event_ext *epoll_event_create(int fd, uint32_t type, void (*handler)(int , void *), void *arg){struct event_ext *e = calloc(1,sizeof(struct event_ext));e->fd = fd;e->events = type;e->handler = handler;e->arg = arg;return e;}//添加事件的接口int epoll_add_event(int epfd, struct event_ext *ev){struct  epoll_event epv;int op;//初始化epoll_eventepv.data.ptr = ev;epv.events = ev->events;if(ev->epolled){op = EPOLL_CTL_MOD;}else{op = EPOLL_CTL_ADD;ev->epolled = true;}//将epoll_event加入epollepoll_ctl(epfd, op, ev->fd, &epv);return 0;}//删除事件int epoll_del_event(int epfd, struct event_ext *ev){epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, NULL);ev->epolled = false;return 0;}int main(){struct epoll_event events[512];int fds;int i;uint32_t event;struct event_ext *e;srv_main = calloc(1,sizeof(struct server));//创建Epollsrv_main->epfd = epoll_create(512);//子系统初始化srv_main->cam = cam_sys_init();srv_main->tcp_srv = net_sys_init();//等待事件发生并处理while(1){fds = epoll_wait(srv_main->epfd, events, 512, 1000);for(i=0; i<fds; i++){event = events[i].events;e = events[i].data.ptr;if((event & EPOLLIN) && (e->events & EPOLLIN)){e->handler(e->fd, e->arg);}if((event & EPOLLOUT) && (e->events & EPOLLOUT)){e->handler(e->fd, e->arg);}if((event & EPOLLERR) && (e->events & EPOLLERR)){e->handler(e->fd, e->arg);}}}return -1;}


然后是Makefile的编写:

BIN = wcamsrvINC = -Iinclude/SRC = $(wildcard *.c)OBJS = $(patsubst %.c, %.o, $(SRC))CC = arm-linux-gccCFLAGS = $(INC) -g$(BIN):$(OBJS)$(CC) -o $@ $^clean:rm $(OBJS) $(BIN)
BIN一个变量,保存目标的二进制文件

INC是头文件的路径,包含include下的所有文件

SRC是需要编译的源文件,使用wildcard函数来寻找所有的.c文件

OBJS保存生成的.o文件

CC是编译工具,它是arm-linux-gcc

下面2行是编译和清除的命令。