libevent学习笔记一:总体把握

分析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;
}

你可能感兴趣的:(libevent,libevent)