libevent-signal(1)
现在已经知道,libevent有三种事件类型,分别是时钟事件,信号事件,i/o事件。今天就分析一下信号事件,下面是一个简单的信号事件demo
#include <sys/types.h> #ifdef HAVE_CONFIG_H
#include "config.h"
#endif #include <sys/stat.h>
#ifndef WIN32
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
#else
#include <windows.h>
#endif
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h> #include <event.h> int called = 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)); if (called >= 2)
event_del(signal); called++;
} int
main (int argc, char **argv)
{
struct event signal_int; /* Initalize the event library */
struct event_base* base = event_base_new(); /* Initalize one event */
event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,
&signal_int);
event_base_set(base, &signal_int); event_add(&signal_int, NULL); event_base_dispatch(base);
event_base_free(base); return (0);
}
从代码看,这里event_set第二个参数是一个中断类型的信号(ctrl+c可触发),第三个参数代表这是一个信号事件并长存
event_base_new中会调用base->evsel->init(这里先不放代码,末尾会放流程图),而这个函数会根据当前后端选择初始化函数,这里是win32_init,最终调用 evutil_socketpair(int family, int type, int protocol, int fd[2]),这个函数中用 tcp-socket将 两个socket句柄绑定到fd数组上

具体代码如下
int
evutil_socketpair(int family, int type, int protocol, int fd[])
{
#ifndef WIN32
return socketpair(family, type, protocol, fd);
#else
/* This code is originally from Tor. Used with permission. */ /* This socketpair does not work when localhost is down. So
* it's really not the same thing at all. But it's close enough
* for now, and really, when localhost is down sometimes, we
* have other problems too.
*/
int listener = -;
int connector = -;
int acceptor = -;
struct sockaddr_in listen_addr;
struct sockaddr_in connect_addr;
int size;
int saved_errno = -; if (protocol
#ifdef AF_UNIX
|| family != AF_UNIX
#endif
) {
EVUTIL_SET_SOCKET_ERROR(WSAEAFNOSUPPORT);
return -;
}
if (!fd) {
EVUTIL_SET_SOCKET_ERROR(WSAEINVAL);
return -;
} listener = socket(AF_INET, type, );
if (listener < )
return -;
memset(&listen_addr, , sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
listen_addr.sin_port = ; /* kernel chooses port. */
if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
== -)
goto tidy_up_and_fail;
if (listen(listener, ) == -)
goto tidy_up_and_fail; connector = socket(AF_INET, type, );
if (connector < )
goto tidy_up_and_fail;
/* We want to find out the port number to connect to. */
size = sizeof(connect_addr);
if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -)
goto tidy_up_and_fail;
if (size != sizeof (connect_addr))
goto abort_tidy_up_and_fail;
if (connect(connector, (struct sockaddr *) &connect_addr,
sizeof(connect_addr)) == -)
goto tidy_up_and_fail; size = sizeof(listen_addr);
acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);
if (acceptor < )
goto tidy_up_and_fail;
if (size != sizeof(listen_addr))
goto abort_tidy_up_and_fail;
EVUTIL_CLOSESOCKET(listener);
/* Now check we are talking to ourself by matching port and host on the
two sockets. */
if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -)
goto tidy_up_and_fail;
if (size != sizeof (connect_addr)
|| listen_addr.sin_family != connect_addr.sin_family
|| listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
|| listen_addr.sin_port != connect_addr.sin_port)
goto abort_tidy_up_and_fail;
fd[] = connector;
fd[] = acceptor; return ; abort_tidy_up_and_fail:
saved_errno = WSAECONNABORTED;
tidy_up_and_fail:
if (saved_errno < )
saved_errno = WSAGetLastError();
if (listener != -)
EVUTIL_CLOSESOCKET(listener);
if (connector != -)
EVUTIL_CLOSESOCKET(connector);
if (acceptor != -)
EVUTIL_CLOSESOCKET(acceptor); EVUTIL_SET_SOCKET_ERROR(saved_errno);
return -;
#endif
}

//下面另一篇再写 event_add的代码如下
int
event_add(struct event *ev, const struct timeval *tv)
{
struct event_base *base = ev->ev_base;
const struct eventop *evsel = base->evsel;
void *evbase = base->evbase;
int res = ; event_debug((
"event_add: event: %p, %s%s%scall %p",
ev,
ev->ev_events & EV_READ ? "EV_READ " : " ",
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
tv ? "EV_TIMEOUT " : " ",
ev->ev_callback)); assert(!(ev->ev_flags & ~EVLIST_ALL)); /*
* prepare for timeout insertion further below, if we get a
* failure on any step, we should not change any state.
*/
if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT))
{
if (min_heap_reserve(&base->timeheap,
+ min_heap_size(&base->timeheap)) == -)
return (-); /* ENOMEM == errno */
} if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
res = evsel->add(evbase, ev);
if (res != -)
event_queue_insert(base, ev, EVLIST_INSERTED);
} /*
* we should change the timout state only if the previous event
* addition succeeded.
*/
if (res != - && tv != NULL) {
struct timeval now; /*
* we already reserved memory above for the case where we
* are not replacing an exisiting timeout.
*/
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(base, ev, EVLIST_TIMEOUT); /* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
* removes it from the active list. */
if ((ev->ev_flags & EVLIST_ACTIVE) &&
(ev->ev_res & EV_TIMEOUT)) {
/* See if we are just active executing this
* event in a loop
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = ;
} event_queue_remove(base, ev, EVLIST_ACTIVE);
} gettime(base, &now);
evutil_timeradd(&now, tv, &ev->ev_timeout); event_debug((
"event_add: timeout in %ld seconds, call %p",
tv->tv_sec, ev->ev_callback)); event_queue_insert(base, ev, EVLIST_TIMEOUT);
} return (res);
}
第五行,代表的是当前系统所支持的后端模式,base->evsel和base->evbase先不要纠结,都是用在一个地方
第23行关于最小堆的逻辑先跳过
第30行,当事件ev不在已注册或者激活链表中,则调用evbase注册事件,这里的ev_events,ev_flags分别代表event关注的事件类型与当前的状态
ev_events有四种类型
I/O事件: EV_WRITE和EV_READ
定时事件:EV_TIMEOUT
信号: EV_SIGNAL
辅助选项:EV_PERSIST,表明是一个永久事件
ev_flags有以下几种状态
#define EVLIST_TIMEOUT 0x01 // event在time堆中
#define EVLIST_INSERTED 0x02 // event在已注册事件链表中
#define EVLIST_SIGNAL 0x04 // 未见使用
#define EVLIST_ACTIVE 0x08 // event在激活链表中
#define EVLIST_INTERNAL 0x10 // 内部使用标记
#define EVLIST_INIT 0x80 // event已被初始化
重点分析一下res = evsel->add(evbase, ev);我所在为win32平台,实际调用的是int win32_insert(void *op, struct event *ev)
int
win32_insert(void *op, struct event *ev)
{
struct win32op *win32op = op;
struct event_entry *ent; if (ev->ev_events & EV_SIGNAL) {
if (win32op->signals_are_broken)
return (-);
return (evsignal_add(ev));
}
if (!(ev->ev_events & (EV_READ|EV_WRITE)))
return ();
ent = get_event_entry(win32op, ev->ev_fd, );
if (!ent)
return (-); /* out of memory */ event_debug(("%s: adding event for %d", __func__, (int)ev->ev_fd));
if (ev->ev_events & EV_READ) {
if (do_fd_set(win32op, ent, )<)
return (-);
ent->read_event = ev;
}
if (ev->ev_events & EV_WRITE) {
if (do_fd_set(win32op, ent, )<)
return (-);
ent->write_event = ev;
}
return ();
}
libevent-signal(1)的更多相关文章
- libevent源码分析三--signal事件响应
libevent支持io事件,timeout事件,signal事件,这篇文件将分析libevent是如何组织signal事件,以及如何实现signal事件响应的. 1. sigmap 类似于io事件 ...
- windows下编译及使用libevent
Libevent官网:http://libevent.org/ windows 7下编译: 编译环境: windows 7 + VS2010 (1)解压libevent到F:\libevent\lib ...
- libevent源码分析:http-server例子
http-server例子是libevent提供的一个简单web服务器,实现了对静态网页的处理功能. /* * gcc -g -o http-server http-server.c -levent ...
- libevent源码分析:hello-world例子
hello-world是libevent自带的一个例子,这个例子的作用是启动后监听一个端口,对于所有通过这个端口连接上服务器的程序发送一段字符:hello-world,然后关闭连接. /* * gcc ...
- libevent源码分析:signal-test例子
signal-test是libevent自带的一个例子,展示了libevent对于信号事件的处理方法. #include <sys/types.h> #include <event2 ...
- libevent源码分析:event_assign、event_new
在libevent中,获取event类型对象的方法有两种,event_assign.event_new 1.event_assign() /** Prepare a new, already-allo ...
- [转]windows下编译及使用libevent
http://www.cnblogs.com/luxiaoxun/p/3603399.html Libevent官网:http://libevent.org/ windows 7下编译: 编译环境: ...
- 【转】libevent源码分析
libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...
- libevent源码分析(一)
分析libevent的源代码,我的想法的是先分析各种结构体,struct event_base.struct event,然后是event_base_new函数.event_new函数.event_a ...
- Libevent 定时器
先摘一点网上的介绍 libevent是一个事件触发的网络库,适用于windows.linux.bsd等多种平台,内部使用select.epoll.kqueue等系统调用管理事件机制.著名分布式缓存软件 ...
随机推荐
- 面试题目: 获取服务器IP和客户端IP
[面试题目] 怎么获取服务器IP和客户端IP地址? I. PHP获取客户端IP, 可通过下面系统变量 1. $_SERVER['Remote_Addr'] 2. $_SERVER['HTTP_CLIE ...
- Python基础(6) - 基本语句
Python print(在Python 3.0中就变成了函数了) print语句是把对象用文本化的形式输出到标准的输出流上. Operation Interpretation print spam ...
- Golang教程:goroutine信道
在上一篇教程中,我们讨论了如何使用协程实现并发.在这篇教程中,我们将讨论信道以及如何使用信道实现协程间通信. 什么是信道 信道(Channel)可以被认为是协程之间通信的管道.与水流从管道的一端流向另 ...
- MyBatis 指定的转换无效
表字段Pay类型设置的是float,生成类的属性如下: public double Pay{get;set;} 读取列表时出现如下错误: 错误信息: 查看堆栈跟踪信息, get_Decimal()提示 ...
- <td>标签scope属性
HTML <td> 标签的 scope 属性 HTML <td> 标签 实例 下面的例子把两个 th 元素标识为列的表头,把两个 td 元素标识为行的表头: <table ...
- Spring cloud ReadTimeout 问题解决
今天使用Spring cloud @FeignClient 调用远程服务的时候,出现readTimeout问题,通过找资料解决方式如下 在Spring.properties 配置文件中添加如下属性解决 ...
- unity3d之如何控制人物移动、旋转和动画播放
代码源自噩梦射手,记录一下方便后续使用,顺便将老师的解释给备注上去_(:з」∠)_ using UnityEngine; using UnitySampleAssets.CrossPlatformIn ...
- 第11章 Media Queries 与Responsive 设计
Media Queries--媒体类型(一) 随着科学技术不断的向前发展,网页的浏览终端越来越多样化,用户可以通过:宽屏电视.台式电脑.笔记本电脑.平板电脑和智能手机来访问你的网站.尽管你无法保证一个 ...
- 来谈谈你对CSS盒模型的认识?
任何一个网页的搭建都离不开盒模型的堆砌.应该说css模型是web的一个根基,最后呈现出来的效果不同无非就是在高宽.内容与背景删的区别而已. 那么CSS模型有什么认识的呢? 首先,css盒模型有几种呢? ...
- 小白学flask之静态文件
引入css的方式有两种 1 那在flask中,如何处理静态文件? 做法很简单,只要在你的包或模块旁边创建一个名为 static 的文件夹就行了. flask的静态文件是位于应用的 /static 中的