对应的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