libevent学习笔记一:总体把握

来源:互联网 发布:淘宝的延长收货是什么 编辑:程序博客网 时间:2024/05/22 08:11

分析libevent

  1. 如何使用 (官方手册,心得体会)
  2. 源码分析 (event, event_base 为核心)

引言

  • 标准c库便有许多参用了回调函数,让用户制定处理过程,比如常用的 qsort(3), bsearch(3)
  • 基本的socket编程时阻塞/同步的,线程在默认情况下占2~8M栈空间。posix的select(2)采用了轮循的方法来判断某个fd是否激活?故时间需要O(n),效率并不高
  • 进而各个系统提出了异步的callback系统调用。 比如: linux的epoll(2),BSD 的kqueue(2),Windows的IOCP。 进而能够用 O(1)效率查找到激活的fd

libevent就是对这些高效IO的封装,提供统一的API,便于开发。

libevent默认为单线程,可以配置为多线程。
一个线程一个event_base,对应一个 struct event_base 和时间管理器,用 schedule 托管给它一系列的event。
当时间发生后,event_base 会在合适的时间(不一定立即)调用绑定于该事件的函数,直到函数运行完,再返回schedule其余事件。

struct event_base *base = event_base_new();assert(base != NULL);

event_base 内部阻塞于 epoll / kqueue
每个事件对应一个 struct event ,可以是监听一个 fd / posix 信号量
struct event 使用 event_new 创建绑定, event_add 来启用


补充下epoll的两个触发模式

  • Edge Trigger 边缘触发: 状态变化时产生 IO 事件
  • Level Trigger 水平(条件)触发: 满足条件就产生 IO 事件

对于server而言,大致过程如下:

  1. listener = socket() bind() listen() 设置fd为非阻塞non-blocking(fcntl(2)) 或者用libevent封装的evutil_make_socket_nonblocking
  2. 创建一个 event_base
  3. event_init() event_set() : 创建一个 event , 将该 socket 托管给event_base , 指定监听类型,绑定相应的回调函数和参数。 对于 listener socket 而言,只需要监听 EV_READ | EV_PERSIST , PERSIST是保持存在的选项
  4. event_add() : 添加该事件,设置超时时间
  5. event_dispatch() : 进入事件循环
  6. (异步) 当有client发起请求 / 事件发生,便会调用该回调函数进行处理

WHY ? 为何不在listen()之后立即调用 accept() ?

如果accept 获得和client通信的sockfd之后,马上进行 recv() / send() , 线程就会阻塞于此。
因此应该创建一个event来托管这个sockfd。

缓冲区管理 bufferevent

从libevent2开始,提供了bufferevent
struct bufferevent 内建立了两个event(read / write)和对应的缓冲区:

  1. struct evbuffer *input;
  2. struct evbuffer *output;

当有数据被读入 input 时,read_cb 立刻调用
当output被输出完毕的时候,write_cb 立刻调用


基本库

* I/O 事件 *

void get_time(int fd, short event, struct event *arg){    localtime_r(&now, &t);    asctime_r(&t, buf);    write(fd, buf, strlen(buf));    //get system time, then write it}void connect_accept(int fd, short event, void *arg){    //offer a callback function to the event, accept a connection    struct sockaddr_in socket_in;    socklen_t len = sizeof(socket_in);    int accept_fd = accept(fd, (struct sockaddr *) &socket_in, &len);    struct event *ev = (struct event *)malloc(sizeof(struct event));    event_set(ev, accept_fd, EV_WRITE, (void *)get_time, ev);    event_add(ev, NULL);}int main(void){    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    struct sockaddr_in socket_in;    bind(sockfd, (struct sockaddr *)&socket_in, sizeof(socket_in));    listen(sockfd, 5);    event_init();    //libevent initialization    struct event ev;     // 设置事件为可读,持续,监听的是sockfd,回调函数connect_accept    event_set(&ev, sockfd, EV_READ|EV_PERSIST, connect_accept, &ev);    //添加事件,未设置超时时间    event_add(&ev, NULL);    //进入libevent主循环,等待事件发生    event_dispatch();    return 0;}

信号处理事件

static void signal_cb(int fd, short event, void *arg){    struct event *signal = arg;    printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal));    // __func__ 是调用的函数名    if (called >= 2)        event_del(signal);    called++;}int main (int argc, char **argv){    struct event signal_int;    event_init();   //libevent initialization    event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb, &signal_int);     //设置事件属性为信号触发、持续,回调函数为con_accept()    event_add(&signal_int, NULL); //添加事件    event_dispatch();//进入libevent主循环    return 0;}

常规超时处理

static void timeout_catch(int fd, short event, void *arg){    struct timeval tv;    struct event *timeout = arg;    int newtime = time(NULL);    printf("%s : called at %d\n", __func__, newtime - lasttime);    lasttime = newtime;    evutil_timerclear(&tv);    tv.tv_sec = 1;    event_add(timeout, &tv);}int main(void){    struct event timeout;    struct timeval tv;    event_init();    evtimer_set(&timeout, timeout_catch, &timeout);    //等价于    event_set(timeout, -1, 0, timeout_catch, &timeout);    evutil_timerclear(&tv);    tv.tv_sec = 1;    event_add(&timeout, &tv);    lasttime = time(NULL);    event_dispath();    //enter the loop    return 0;}
0 0
原创粉丝点击