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等系统调用管理事件机制.著名分布式缓存软件 ...
随机推荐
- .NET加密技术概述
微软.NET 的System.Security.Cryptography中的类实现了各种具体的加密算法和技术.这些类,有一些是非托管 Microsoft CryptoAPI 的包装,而另一些则是纯粹的 ...
- jmeter(6)——集合点与检查点
集合点 1.概念 集合点:我们所说的并发不会是真正的并发, 集合点可以理解成,所有的用户在进行某一操作时在同一时间点一起执行,比如:抢票或者促销抢购,集合点可以帮助我们使并发更加有效可控 2.位置 位 ...
- WEBPACK & BABEL 打包项目
本文首发于 BriFuture's Blog. 最近在用 Vue 重写之前的一个项目 Compass,这个项目以前是用 QML + JavaScript 在 Qt 平台上搭建的.这是我本科毕设时做的一 ...
- jquery.form.js ie 下下载文件已经ie8失效问题解决方案
https://github.com/malsup/form/blob/master/jquery.form.js在使用这个插件时遇到的问题1.ie下会变成下载文件,解决方案是在后端返回时设置'Con ...
- step2: 爬取廖雪峰博客
#https://zhuanlan.zhihu.com/p/26342933 #https://zhuanlan.zhihu.com/p/26833760 scrapy startproject li ...
- Tidb 离线Ansible方式部署实践
1.最近浏览到一个比较新的分布式数据库Tidb,开源看起来比较牛的样子,一时手痒就动手试试部署 2.参考官方 Ansible 离线方式部署 :https://pingcap.com/docs-cn/o ...
- 修改django 后台admin用户的密码
python manage.py shellfrom django.contrib.auth.models import User from django.contrib.auth.models im ...
- 前端定位Position属性四个值
1.static(静态定位):默认值.没有定位,元素出现在正常的流中. 2.relative(相对定位):生成相对定位的元素,通过top,bottom,left,right的设置相对于其正常(原先本身 ...
- 简单的maven项目
手动建立一个简单的maven项目,不使用eclipse 好吧,第一步:建立一个项目路径,比如在d盘新建一个mavenTest文件夹,在mavenTest文件夹中建立HelloMaven的文件夹,作为项 ...
- python简单的爬虫
def baidu_tieba(url,begin_page,end_page): for i in range(begin_page, end_page+1): sName = string.zfi ...