libevent源码学习_event_test

对应的sample文件中提供了event_test.c,里面就是关于事件的简单示例,具体如下:

 1 /*
 2  * Compile with:
 3  * cc -I/usr/local/include -o event-test event-test.c -L/usr/local/lib -levent
 4  */
 5 
 6 #ifdef HAVE_CONFIG_H
 7 #include "config.h"
 8 #endif
 9 
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <sys/queue.h>
13 #include <unistd.h>
14 #include <sys/time.h>
15 #include <fcntl.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <errno.h>
20 
21 #include <event.h>
22 
23 static void
24 fifo_read(int fd, short event, void *arg)
25 {
26     char buf[255];
27     int len;
28     struct event *ev = arg;
29 
30     /* Reschedule this event */
31     event_add(ev, NULL);
32 
33     fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
34         fd, event, arg);
35 
36     len = read(fd, buf, sizeof(buf) - 1);
37 
38     if (len == -1) {
39         perror("read");
40         return;
41     } else if (len == 0) {
42         fprintf(stderr, "Connection closed\n");
43         return;
44     }
45 
46     buf[len] = '\0';
47 
48     fprintf(stdout, "Read: %s\n", buf);
49 }
50 
51 int
52 main (int argc, char **argv)
53 {
54     struct event evfifo;
55 
56     struct stat st;
57     const char *fifo = "event.fifo";
58     int socket;
59  
60     if (lstat (fifo, &st) == 0) {
61         if ((st.st_mode & S_IFMT) == S_IFREG) {
62             errno = EEXIST;
63             perror("lstat");
64             exit (1);
65         }
66     }
67 
68     unlink (fifo);
69     if (mkfifo (fifo, 0600) == -1) {
70         perror("mkfifo");
71         exit (1);
72     }
73 
74 
75     socket = open (fifo, O_RDWR | O_NONBLOCK, 0);
76 
77     if (socket == -1) {
78         perror("open");
79         exit (1);
80     }
81 
82     fprintf(stderr, "Write data to %s\n", fifo);
83 
84     /* Initalize the event library */
85     event_init();
86 
87     /* Initalize one event */
88     event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo);
89 
90     /* Add it to the active events, without a timeout */
91     event_add(&evfifo, NULL);
92     
93     event_dispatch();
94 
95     return (0);
96 }

从这个例子中,我们可以看到使用libevent的基本步骤:

event_init --> event_set --> event_add --> event_dispatch

下面分步来解析这些函数。

1、函数event_init()

位于:event.c

 1 struct event_base *
 2 event_init(void)
 3 {
 4     struct event_base *base = event_base_new();
 5 
 6     if (base != NULL)
 7         current_base = base;
 8 
 9     return (base);
10 }

比较简单,就是调用了函数event_base_new初始化一个event_base类型的变量,并且将这个变量赋值给一个全局变量current_base。

 

2、函数event_base_new()

位于:event.c

 1 struct event_base *
 2 event_base_new(void)
 3 {
 4     int i;
 5     struct event_base *base;
 6 
 7     /*
 8      * 在堆上分配内存存储event_base,所有字段初始化为0
 9      */
10     if ((base = calloc(1, sizeof(struct event_base))) == NULL)
11         event_err(1, "%s: calloc", __func__);
12 
13     /*
14      * 设置use_monotonic变量
15      */
16     detect_monotonic();
17     
18     /*
19      * 得到当前时间
20      */
21     gettime(base, &base->event_tv);
22     
23     /*
24      * 初始化小根堆
25      */
26     min_heap_ctor(&base->timeheap);
27     
28     /*
29      * 初始化注册时间队列
30      */
31     TAILQ_INIT(&base->eventqueue);
32 
33     /*
34      * 初始化socketpair
35      */
36     base->sig.ev_signal_pair[0] = -1;
37     base->sig.ev_signal_pair[1] = -1;
38     
39     /*
40      * C语言实现多态
41      * 根据所支持的系统调用进行对应的初始化
42      */
43     base->evbase = NULL;
44     for (i = 0; eventops[i] && !base->evbase; i++) {
45         base->evsel = eventops[i];
46 
47         base->evbase = base->evsel->init(base);
48     }
49 
50     if (base->evbase == NULL)
51         event_errx(1, "%s: no event mechanism available", __func__);
52 
53     if (evutil_getenv("EVENT_SHOW_METHOD")) 
54         event_msgx("libevent using: %s\n",
55                base->evsel->name);
56 
57     /*
58      * 设置优先级base->nactivequeues,分配数组base->activequeues
59      * 数组大小和优先级相同
60      */
61     /* allocate a single active event queue */
62     event_base_priority_init(base, 1);
63 
64     return (base);
65 }

主要做了以下事情:

(1)给event_base类型变量分配空间

(2)初始化小根堆【struct min_heap timeheap,位于位于结构体event_base】

(3)初始化注册事件队列【struct event_list eventqueue,位于位于结构体event_base】

(4)根据系统支持的系统调用初始化后面真正干活的eventop实例对象【void *evbase,位于结构体event_base】

(5)调用函数event_base_priority_init() 初始化优先队列,确认的说应该是活跃事件的队列,它是带优先级的,因为这里是最开始的初始化,所以就初始化一个队列,并且它的优先级为1,这个优先级就作为初始化队列的数量。具体见下面函数event_base_priority_init() 的分析

 

上面的init以epoll为例,位于:epoll.c

 1 static void *
 2 epoll_init(struct event_base *base)
 3 {
 4     int epfd;
 5     struct epollop *epollop;
 6 
 7     /* Disable epollueue when this environment variable is set */
 8     if (evutil_getenv("EVENT_NOEPOLL"))
 9         return (NULL);
10 
11     /* Initalize the kernel queue */
12     if ((epfd = epoll_create(32000)) == -1) {
13         if (errno != ENOSYS)
14             event_warn("epoll_create");
15         return (NULL);
16     }
17 
18     FD_CLOSEONEXEC(epfd);
19 
20     if (!(epollop = calloc(1, sizeof(struct epollop))))
21         return (NULL);
22 
23     epollop->epfd = epfd;
24 
25     /* Initalize fields */
26     epollop->events = malloc(INITIAL_NEVENTS * sizeof(struct epoll_event));
27     if (epollop->events == NULL) {
28         free(epollop);
29         return (NULL);
30     }
31     epollop->nevents = INITIAL_NEVENTS;
32 
33     epollop->fds = calloc(INITIAL_NFILES, sizeof(struct evepoll));
34     if (epollop->fds == NULL) {
35         free(epollop->events);
36         free(epollop);
37         return (NULL);
38     }
39     epollop->nfds = INITIAL_NFILES;
40 
41     evsignal_init(base);
42 
43     return (epollop);
44 }

调用了系统调用epoll_create,创建出ep_fd,然后初始化了结构体epollop的成员变量。

1 struct epollop {
2     struct evepoll *fds;
3     int nfds;
4     struct epoll_event *events;
5     int nevents;
6     int epfd;
7 };

 

1 struct evepoll {
2     struct event *evread;
3     struct event *evwrite;
4 };

 

3、函数event_base_priority_init()

位于:event.c

 1 int
 2 event_base_priority_init(struct event_base *base, int npriorities)
 3 {
 4     int i;
 5 
 6     /*
 7      * 当前base上有活跃的events则不能设置优先级,返回
 8      */
 9     if (base->event_count_active)
10         return (-1);
11 
12     /*
13      * 不同,则先释放原先的activequeues数组
14      */
15     if (base->nactivequeues && npriorities != base->nactivequeues) {
16         for (i = 0; i < base->nactivequeues; ++i) {
17             free(base->activequeues[i]);
18         }
19         free(base->activequeues);
20     }
21 
22     /*
23      * 设置新的优先级
24      * 设置和优先级值相同大小的event_list数组
25      */
26     /* Allocate our priority queues */
27     base->nactivequeues = npriorities;
28     base->activequeues = (struct event_list **)
29         calloc(base->nactivequeues, sizeof(struct event_list *));
30     
31     if (base->activequeues == NULL)
32         event_err(1, "%s: calloc", __func__);
33 
34     /*
35      * 初始化activequeues数组中每个元素
36      */
37     for (i = 0; i < base->nactivequeues; ++i) {
38         base->activequeues[i] = malloc(sizeof(struct event_list));
39         if (base->activequeues[i] == NULL)
40             event_err(1, "%s: malloc", __func__);
41         TAILQ_INIT(base->activequeues[i]);
42     }
43 
44     return (0);
45 }

初始化结构体event_base里面的struct event_list **activequeues成员,这是个2维数组,其中的元素activequeues[priority]是一个链表,这个链表里面对应的是相同优先级的事件。

为了更清晰上面的初始化,附上UML图如下:

 

4、函数event_set()

位于:event.c

 1 void
 2 event_set(struct event *ev, int fd, short events,
 3       void (*callback)(int, short, void *), void *arg)
 4 {
 5     /* Take the current base - caller needs to set the real base later */
 6     ev->ev_base = current_base;
 7 
 8     ev->ev_callback = callback;
 9     ev->ev_arg = arg;
10     ev->ev_fd = fd;
11     ev->ev_events = events;
12     ev->ev_res = 0;
13     ev->ev_flags = EVLIST_INIT;
14     ev->ev_ncalls = 0;
15     ev->ev_pncalls = NULL;
16 
17     min_heap_elem_init(ev);
18 
19     /* by default, we put new events into the middle priority */
20     if(current_base)
21         ev->ev_pri = current_base->nactivequeues/2;
22 }

 

5、函数event_add()

位于:event.c

 1 int
 2 event_add(struct event *ev, const struct timeval *tv)
 3 {
 4     /*
 5      * 要注册到的event_base
 6      * 得到ev对应的反应堆实例event_base
 7      */
 8     struct event_base *base = ev->ev_base;
 9     const struct eventop *evsel = base->evsel;
10     void *evbase = base->evbase;
11     int res = 0;
12 
13     event_debug((
14          "event_add: event: %p, %s%s%scall %p",
15          ev,
16          ev->ev_events & EV_READ ? "EV_READ " : " ",
17          ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
18          tv ? "EV_TIMEOUT " : " ",
19          ev->ev_callback));
20 
21     assert(!(ev->ev_flags & ~EVLIST_ALL));
22     
23     /*
24      * ev->ev_events表示事件类型
25      * 如果ev->ev_events是读/写/信号事件,而且ev不在已注册队列或已就绪队列
26      * 那么调用evbase注册ev事件
27      */
28     if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
29         !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
30         res = evsel->add(evbase, ev);
31         if (res != -1)
32             /*
33              * 注册成功,插入event到已注册链表中
34              */
35             event_queue_insert(base, ev, EVLIST_INSERTED);
36     }
37 
38     return (res);
39 }

只留下了关键逻辑,精简了和定时器相关的代码。

 

上面的add以epoll为例,位于:epoll.c

 1 static int
 2 epoll_add(void *arg, struct event *ev)
 3 {
 4     struct epollop *epollop = arg;
 5     struct epoll_event epev = {0, {0}};
 6     struct evepoll *evep;
 7     int fd, op, events;
 8 
 9     if (ev->ev_events & EV_SIGNAL)
10         return (evsignal_add(ev));
11 
12     fd = ev->ev_fd;
13     if (fd >= epollop->nfds) {
14         /* Extent the file descriptor array as necessary */
15         if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
16             return (-1);
17     }
18 
19     /*
20      * 获得地址,后面给它赋值
21      */
22     evep = &epollop->fds[fd];
23     op = EPOLL_CTL_ADD;
24     events = 0;
25 
26     /*
27      * 如果原先存在就EPOLL_CTL_MOD而不是EPOLL_CTL_ADD
28      */
29     if (evep->evread != NULL) {
30         events |= EPOLLIN;
31         op = EPOLL_CTL_MOD;
32     }
33     if (evep->evwrite != NULL) {
34         events |= EPOLLOUT;
35         op = EPOLL_CTL_MOD;
36     }
37 
38     /*
39      * 设置关注的事件
40      */
41     if (ev->ev_events & EV_READ)
42         events |= EPOLLIN;
43     if (ev->ev_events & EV_WRITE)
44         events |= EPOLLOUT;
45 
46     epev.data.fd = fd;
47     epev.events = events;
48     if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
49             return (-1);
50 
51     /* Update events responsible */
52     if (ev->ev_events & EV_READ)
53         evep->evread = ev;
54     if (ev->ev_events & EV_WRITE)
55         evep->evwrite = ev;
56 
57     return (0);
58 }

如果新加入的fd大小大于了之前分配的fd最大个数,则需要调用函数epoll_recalc()重新分配空间,否则就是更新相关的结构体变量,并调用系统调用epoll_ctl来EPOLL_CTL_ADD或EPOLL_CTL_MOD对应的事件。

 

函数epoll_recalc()解析如下:

 1 static int
 2 epoll_recalc(struct event_base *base, void *arg, int max)
 3 {
 4     struct epollop *epollop = arg;
 5 
 6     /*
 7      * 当前的fd大于了之前根据最大fd分配的结构体evepoll个数,重新分配,否则直接返回
 8      */
 9     if (max >= epollop->nfds) {
10         struct evepoll *fds;
11         int nfds;
12 
13         /*
14           * 每次以2倍大小扩充
15          */
16         nfds = epollop->nfds;
17         while (nfds <= max)
18             nfds <<= 1;
19 
20         /*
21          * 扩充
22          */
23         fds = realloc(epollop->fds, nfds * sizeof(struct evepoll));
24         if (fds == NULL) {
25             event_warn("realloc");
26             return (-1);
27         }
28 
29         /*
30          * 更新成员变量的值,并且把新扩充的内存清空
31          */
32         epollop->fds = fds;
33         memset(fds + epollop->nfds, 0,
34             (nfds - epollop->nfds) * sizeof(struct evepoll));
35         epollop->nfds = nfds;
36     }
37 
38     return (0);
39 }

 

在上面的add完毕后,要把对应的事件放到已注册事件的链表里面。

 1 void
 2 event_queue_insert(struct event_base *base, struct event *ev, int queue)
 3 {
 4     /*
 5      * ev可能已经在激活列表中了,避免重复插入  
 6      */
 7     if (ev->ev_flags & queue) {
 8         /* Double insertion is possible for active events */
 9         if (queue & EVLIST_ACTIVE)
10             return;
11 
12         event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
13                ev, ev->ev_fd, queue);
14     }
15 
16     if (~ev->ev_flags & EVLIST_INTERNAL)
17         base->event_count++;
18 
19     /*
20      * 记录queue标记
21      */
22     ev->ev_flags |= queue;
23     switch (queue) {
24     /*
25      * I/O或Signal事件,加入已注册事件链表  
26      */
27     case EVLIST_INSERTED:
28         TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
29         break;
30     /*
31      * 就绪事件,加入激活链表
32      */
33     case EVLIST_ACTIVE:
34         base->event_count_active++;
35         TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
36             ev,ev_active_next);
37         break;
38     /*
39      * 定时事件,加入堆
40      */
41     case EVLIST_TIMEOUT: {
42         min_heap_push(&base->timeheap, ev);
43         break;
44     }
45     default:
46         event_errx(1, "%s: unknown queue %x", __func__, queue);
47     }
48 }

 

 

6、函数event_dispatch()

位于:event.c

1 int
2 event_dispatch(void)
3 {
4     return (event_loop(0));
5 }

 

1 /* not thread safe */
2 
3 int
4 event_loop(int flags)
5 {
6     return event_base_loop(current_base, flags);
7 }

可以看到,用了全局变量current_base,所以它并不是线程安全的。

 

  1 int
  2 event_base_loop(struct event_base *base, int flags)
  3 {
  4     const struct eventop *evsel = base->evsel;
  5     void *evbase = base->evbase;
  6     struct timeval tv;
  7     struct timeval *tv_p;
  8     int res, done;
  9 
 10     /* clear time cache */
 11     base->tv_cache.tv_sec = 0;
 12     
 13     done = 0;
 14     while (!done) {        
 15         /*
 16          * 校正系统时间,如果系统使用的是非MONOTONIC时间,用户可能会向后调整了系统时间  
 17          * 在timeout_correct函数里,比较last wait time和当前时间
 18          * 如果当前时间< last wait time  表明时间有问题
 19          * 这时需要更新timer_heap中所有定时事件的超时时间。  
 20          */
 21         timeout_correct(base, &tv);
 22 
 23         /*
 24          * 根据timer heap中事件的最小超时时间,计算系统I/O demultiplexer的最大等待时间 
 25          */
 26         tv_p = &tv;
 27         if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
 28             timeout_next(base, &tv_p);
 29         } else {
 30             /*
 31              * 依然有未处理的就绪事件,就让I/O demultiplexer立即返回,不必等待  
 32              * 下面会提到,在libevent中,低优先级的就绪事件可能不能立即被处理  
 33              */
 34             /* 
 35              * if we have active events, we just poll new events
 36              * without waiting.
 37              */
 38             evutil_timerclear(&tv);
 39         }
 40         
 41         /* If we have no events, we just exit */
 42         if (!event_haveevents(base)) {
 43             event_debug(("%s: no events registered.", __func__));
 44             return (1);
 45         }
 46 
 47         /* update last old time */
 48         gettime(base, &base->event_tv);
 49 
 50         /* clear time cache */
 51         base->tv_cache.tv_sec = 0;
 52 
 53         /*
 54          * 调用系统I/O demultiplexer等待就绪I/O events,可能是epoll_wait,或者select等;  
 55          * 在evsel->dispatch()中,会把就绪signal event、I/O event插入到激活链表中  
 56          */
 57         res = evsel->dispatch(base, evbase, tv_p);
 58 
 59         if (res == -1)
 60             return (-1);
 61 
 62         /*
 63          * 将time cache赋值为当前系统时间
 64          */
 65         gettime(base, &base->tv_cache);
 66 
 67         /*
 68          * 检查heap中的timer events
 69          * 将就绪的timer event从heap上删除,并插入到激活链表中
 70          */
 71         timeout_process(base);
 72 
 73         /*
 74          * 调用event_process_active()处理激活链表中的就绪event,调用其回调函数执行事件处理  
 75          * 该函数会寻找最高优先级(priority值越小优先级越高)的激活事件链表,  
 76          * 然后处理该链表中的所有就绪事件;  
 77          * 因此低优先级的就绪事件可能得不到及时处理
 78          *
 79          * 见函数event_process_active()的注释:
 80          * Active events are stored in priority queues.  Lower priorities are always
 81          * process before higher priorities.  Low priority events can starve high
 82          * priority ones.
 83          * 
 84          * 在函数event_process_active()里面就是寻找priority值最小的已就绪事件队列
 85          * 找到一个就开始处理里面所有的事件回调了,其他的队列根本就不管了......
 86          * 所以原作者用的Low priority events can starve high priority ones非常贴切
 87          */
 88         if (base->event_count_active) {
 89             /*
 90              * 处理event_base的活跃链表中的事件
 91              * 调用event的回调函数,优先级高的event先处理
 92              */
 93             event_process_active(base);
 94             
 95             if (!base->event_count_active && (flags & EVLOOP_ONCE)) {
 96                 done = 1;
 97             }
 98         } else if (flags & EVLOOP_NONBLOCK) {
 99             done = 1;
100         }
101     }
102 
103     /* clear time cache */
104     base->tv_cache.tv_sec = 0;
105 
106     event_debug(("%s: asked to terminate loop.", __func__));
107     return (0);
108 }

1、去掉了一些无用的业务逻辑代码。

2、调用epoll_dispatch来进行事件的分发,激活就绪的事件都弄到激活的链表里面去。

3、调用timeout_process把超时的事件也弄到激活的链表里面去。

4、调用event_process_active开始处理,调用对应事件的回调函数;需要注意的是:就是寻找priority值最小的已就绪事件队列,找到一个就开始处理里面所有的事件回调了,其他的队列根本就不管了......所以原作者用的Low priority events can starve high priority ones非常贴切。

5、关于定时器、时间相关的函数未仔细看。

 

函数epoll_dispatch(),位于epoll.c

 1 static int
 2 epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
 3 {
 4     struct epollop *epollop = arg;
 5     struct epoll_event *events = epollop->events;
 6     struct evepoll *evep;
 7     int i, res, timeout = -1;
 8 
 9     if (tv != NULL)
10         timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
11 
12     if (timeout > MAX_EPOLL_TIMEOUT_MSEC) {
13         /* Linux kernels can wait forever if the timeout is too big;
14          * see comment on MAX_EPOLL_TIMEOUT_MSEC. */
15         timeout = MAX_EPOLL_TIMEOUT_MSEC;
16     }
17 
18     res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
19 
20     if (res == -1) {
21         if (errno != EINTR) {
22             event_warn("epoll_wait");
23             return (-1);
24         }
25 
26         evsignal_process(base);
27         return (0);
28     } else if (base->sig.evsignal_caught) {
29         evsignal_process(base);
30     }
31 
32     event_debug(("%s: epoll_wait reports %d", __func__, res));
33 
34     for (i = 0; i < res; i++) {
35         int what = events[i].events;
36         struct event *evread = NULL, *evwrite = NULL;
37         int fd = events[i].data.fd;
38 
39         if (fd < 0 || fd >= epollop->nfds)
40             continue;
41         evep = &epollop->fds[fd];
42 
43         if (what & (EPOLLHUP|EPOLLERR)) {
44             evread = evep->evread;
45             evwrite = evep->evwrite;
46         } else {
47             if (what & EPOLLIN) {
48                 evread = evep->evread;
49             }
50 
51             if (what & EPOLLOUT) {
52                 evwrite = evep->evwrite;
53             }
54         }
55 
56         if (!(evread||evwrite))
57             continue;
58 
59         if (evread != NULL)
60             event_active(evread, EV_READ, 1);
61         if (evwrite != NULL)
62             event_active(evwrite, EV_WRITE, 1);
63     }
64 
65     if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
66         /* We used all of the event space this time.  We should
67            be ready for more events next time. */
68         int new_nevents = epollop->nevents * 2;
69         struct epoll_event *new_events;
70 
71         new_events = realloc(epollop->events,
72             new_nevents * sizeof(struct epoll_event));
73         if (new_events) {
74             epollop->events = new_events;
75             epollop->nevents = new_nevents;
76         }
77     }
78 
79     return (0);
80 }

 

函数event_process_active()

 1 static void
 2 event_process_active(struct event_base *base)
 3 {
 4     struct event *ev;
 5     struct event_list *activeq = NULL;
 6     int i;
 7     short ncalls;
 8 
 9     /*
10      * 寻找最高优先级(priority值越小优先级越高)的已就绪事件队列
11      */
12     for (i = 0; i < base->nactivequeues; ++i) {
13         if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
14             activeq = base->activequeues[i];
15             break;
16         }
17     }
18 
19     assert(activeq != NULL);
20 
21     for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
22         /*
23          * 如果有persist标志,则只从激活队列中移除此事件
24          */
25         if (ev->ev_events & EV_PERSIST) {
26             event_queue_remove(base, ev, EVLIST_ACTIVE);
27         }
28         /*
29          * 否则则从激活事件列表、已注册事件、监听事件的兴趣列表中全部干掉此事件
30          */
31         else {
32             event_del(ev);
33         }
34         
35         /* Allows deletes to work */
36         ncalls = ev->ev_ncalls;
37         /*
38          * 每个事件的回调函数的调用次数
39          */
40         ev->ev_pncalls = &ncalls;
41         while (ncalls) {
42             ncalls--;
43             ev->ev_ncalls = ncalls;
44             /*
45              * 回调
46              */
47             (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
48             if (base->event_break)
49                 return;
50         }
51     }
52 }

 

至此,看了2个下午的libevent事件处理流程收官!

event-test.c例子中使用一个命名管道(也被称为FIFO文件),它通过读的方式打开一个命名管道,并且监听这个命名管道是否有数据可读,当有数据可读时会执行fifo_read函数,把读取的内容打印出来。

可以搞一个往这个命名管道写内容的简单的程序,进行测试:

 1 #include <sys/types.h>  
 2 #include <sys/stat.h>  
 3 #include <sys/queue.h>  
 4 #include <sys/time.h>  
 5 #include <fcntl.h>  
 6 #include <stdlib.h>  
 7 #include <stdio.h>  
 8 #include <string.h>  
 9 #include <unistd.h>  
10 #include <errno.h>  
11   
12   
13 int main(int argc, char **argv)  
14 {  
15     char *input = argv[1];  
16     if (argc != 2)  
17     {  
18         input = "hello";  
19     }  
20     int fd ;  
21     fd = open("event.fifo",O_WRONLY);  
22     if(fd == -1){  
23         perror("open error");  
24         exit(EXIT_FAILURE);  
25     }  
26   
27   
28     write(fd, input, strlen(input));  
29     close(fd);  
30     printf("write success\n");  
31     return 0;  
32 }  

 

 

本文参考自:

http://blog.csdn.net/lyh66/article/details/46328531

http://www.cnblogs.com/zxiner/category/1010504.html

http://blog.csdn.net/sparkliang/article/category/660506

 

转载于:https://www.cnblogs.com/abc-begin/p/7603130.html

libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。著名分布式缓存软件memcached也是libevent based,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。 编辑本段 详细   编译库代码,编译脚本会判断OS支持哪种类型的事件机制(select、epoll或kqueue),然后条件编译相应代码,供上层使用的接口仍然是保持统一的(否则也不能所谓的跨平台了)。在linux redhat as 4 u 2 上编译相当容易,configure以后make,make install就可以了,windows上编译似乎有点小麻烦,不过稍微改点东西也就通过了。   从代码中看,libevent支持用户使用三种类型的事件,分别是网络IO、定时器、信号三种,在定时器的实现上使用了RB tree的数据结构,以达到高效查找、排序、删除定时器的目的,网络IO上,主要关注了一下linux上的epoll(因为目前的开发主要在linux平台),结果发现libeventepoll居然用的EPOLLLT,水平触发的方式用起来比较方便,不容易出错,但是在效率上可能比EPOLLET要低一些。   跟网络无关的,libevent也有一些缓冲区管理的函数,而且是c风格的函数,实用性不是太大。libevent没有提供缓存的函数。   虽然libevent实用上的价值不大,但它提供的接口形式还是不错的,实现类似的lib的时候仍然是可以参考的。   Libevent定时器的数据结构自version 1.4起已由红黑树改为最小堆(Min Heap),以提高效率;网络IO和信号的数据结构采用了双向链表(TAILQ)。在实现上主要有3种链表: EVLIST_INSERTED, EVLIST_ACTIVE, EVLIST_TIMEOUT,一个ev在这3种链表之间被插入或删除,处于EVLIST_ACTIVE链表中的ev最后将会被调度执行。   Libevent提供了DNS,HTTP Server,RPC等组件,HTTP Server可以说是Libevent的经典应用。从http.c可看到Libevent的很多标准写法。写非阻塞式的HTTP Server很容易将socket处理与HTTP协议处理纠缠在一起,Libevent在这点上似乎也有值得推敲的地方。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值