libevent源码分析之信号处理
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;//这里我们把它称为事件2
/* Initalize the event library */
event_init();
/* 初始化事件2,设置相关信号,回调函数 */
event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,
&signal_int);
event_add(&signal_int, NULL);//激活信号
event_dispatch();//等待事件的触发
return (0);
}
evsignal_init(base);
struct evsignal_info {
struct event ev_signal;//向event_base注册读事件使用的event结构体,这里我们称为事件1
int ev_signal_pair[2];//sock pair对,也就是clientfd跟servfd
int ev_signal_added;//记录ev_signal信号是否已经注册
volatile sig_atomic_t evsignal_caught;//是否有信号发生
struct event_list evsigevents[NSIG];//注册到信号的事件链表的一个标志
sig_atomic_t evsigcaught[NSIG];//记录每个信号的触发的次数
#ifdef HAVE_SIGACTION
struct sigaction **sh_old;//记录旧的信号处理函数
#else
ev_sighandler_t **sh_old;
#endif
int sh_old_max;
};
int
evsignal_init(struct event_base *base)
{
int i;
/*
* Our signal handler is going to write to one end of the socket
* pair to wake up our event loop. The event loop then scans for
* signals that got delivered.
*///创建一个socketpair
if (evutil_socketpair(
AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {
#ifdef WIN32
/* Make this nonfatal on win32, where sometimes people
have localhost firewalled. */
event_warn("%s: socketpair", __func__);
#else
event_err(1, "%s: socketpair", __func__);
#endif
return -1;
}
////子进程不能访问该socketpair
FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
base->sig.sh_old = NULL;
base->sig.sh_old_max = 0;
base->sig.evsignal_caught = 0;
memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG);
/* initialize the queues for all events */
for (i = 0; i < NSIG; ++i)
TAILQ_INIT(&base->sig.evsigevents[i]);
//设置为非阻塞
evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);
//可读事件设置与fd相关(但还缺乏注册到base注册链表中,需要在event_add中才会被注册到链表中去)
event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1],
EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
base->sig.ev_signal.ev_base = base;//把信号对应的事件跟base相关联
base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
return 0;
}
//添加事件
static int
epoll_add(void *arg, struct event *ev)
{
struct epollop *epollop = arg;//获取epoll管理结构体
struct epoll_event epev = {0, {0}};//epoll事件
struct evepoll *evep;//读写事件指针
int fd, op, events;
if (ev->ev_events & EV_SIGNAL)//是否注册了信号
return (evsignal_add(ev));//是的话,添加信号到此事件
fd = ev->ev_fd;//获取对应的描述符
if (fd >= epollop->nfds) {//判断描述符是否大于最大值,是的话,扩充
/* Extent the file descriptor array as necessary */
if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
return (-1);
}
evep = &epollop->fds[fd];//获取事件指针
op = EPOLL_CTL_ADD;//默认是添加,其实还有修改等
events = 0;
if (evep->evread != NULL) {//这里epoll的修改与添加设置到一起了,如果不为空,说明本身已有事件了,那就只是修改器读写而已
events |= EPOLLIN;//修改
op = EPOLL_CTL_MOD;//设置为默认
}
if (evep->evwrite != NULL) {//为空
events |= EPOLLOUT;
op = EPOLL_CTL_MOD;
}
if (ev->ev_events & EV_READ)//是否可读
events |= EPOLLIN;//events设置为可读
if (ev->ev_events & EV_WRITE)//是否可写
events |= EPOLLOUT;//设置为可写,注意events只是int类型
epev.data.fd = fd;//epoll事件设置fd
epev.events = events;//epoll事件设置为是否可读可写
if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
return (-1);
/* Update events responsible */
if (ev->ev_events & EV_READ)//更新ev_events是否可读可写,如果是,那就更新evep读写事件指针,表示此事件可读可写
evep->evread = ev;
if (ev->ev_events & EV_WRITE)
evep->evwrite = ev;
return (0);
}
int
evsignal_add(struct event *ev)
{
int evsignal;
struct event_base *base = ev->ev_base;
struct evsignal_info *sig = &ev->ev_base->sig;//获取信号事件结构体
if (ev->ev_events & (EV_READ|EV_WRITE))//信号事件不可以是读写
event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
evsignal = EVENT_SIGNAL(ev);//信号的fd就是信号的number
assert(evsignal >= 0 && evsignal < NSIG); // //信号不能超过NSIG这个数
if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {////如果说该信号链表为空
event_debug(("%s: %p: changing signal handler", __func__, ev));
if (_evsignal_set_handler( //设置信号处理函数,同时,保存原来的信号处理函数到ev_base->sh_old中去
base, evsignal, evsignal_handler) == -1)
return (-1);
/* catch signals if they happen quickly */
evsignal_base = base;
if (!sig->ev_signal_added) {//判断是否已加入
if (event_add(&sig->ev_signal, NULL))//正式注册,添加到epoll_wait中去
return (-1);
sig->ev_signal_added = 1;//表示已经添加了
}
}
//把ev->ev_signal_next加入到sig->evsigevents[evsignal]的链表末端
/* multiple events may listen to the same signal */
TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);
return (0);
}
static int
epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
{
struct epollop *epollop = arg; //获取管理epoll的结构
struct epoll_event *events = epollop->events;//epoll的事件数组
struct evepoll *evep;
int i, res, timeout = -1;
if (tv != NULL)
timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;//设置超时事件
if (timeout > MAX_EPOLL_TIMEOUT_MSEC) {//不可以大于最大超时事件
/* Linux kernels can wait forever if the timeout is too big;
* see comment on MAX_EPOLL_TIMEOUT_MSEC. */
timeout = MAX_EPOLL_TIMEOUT_MSEC;
}
res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
if (res == -1) {
if (errno != EINTR) {
event_warn("epoll_wait");
return (-1);
}
evsignal_process(base);//处理信号事件
return (0);
} else if (base->sig.evsignal_caught) {
evsignal_process(base);//处理信号事件
}
event_debug(("%s: epoll_wait reports %d", __func__, res));
for (i = 0; i < res; i++) {
int what = events[i].events;
struct event *evread = NULL, *evwrite = NULL;
int fd = events[i].data.fd;
if (fd < 0 || fd >= epollop->nfds)
continue;
evep = &epollop->fds[fd];
if (what & (EPOLLHUP|EPOLLERR)) {
evread = evep->evread;
evwrite = evep->evwrite;
} else {
if (what & EPOLLIN) {//可读
evread = evep->evread;
}
if (what & EPOLLOUT) {//可写
evwrite = evep->evwrite;
}
}
if (!(evread||evwrite))
continue;
if (evread != NULL)//插入就绪链表
event_active(evread, EV_READ, 1);
if (evwrite != NULL)//插入就绪链表
event_active(evwrite, EV_WRITE, 1);
}
if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
/* We used all of the event space this time. We should
be ready for more events next time. */
int new_nevents = epollop->nevents * 2;
struct epoll_event *new_events;
new_events = realloc(epollop->events,
new_nevents * sizeof(struct epoll_event));
if (new_events) {
epollop->events = new_events;
epollop->nevents = new_nevents;
}
}
return (0);
}
void
evsignal_process(struct event_base *base)//遍历信号链表,是否有事件2挂载
{
struct evsignal_info *sig = &base->sig;//获取信号管理结构体
struct event *ev, *next_ev;
sig_atomic_t ncalls;
int i;
base->sig.evsignal_caught = 0;
for (i = 1; i < NSIG; ++i) {
ncalls = sig->evsigcaught[i];//是否有触发
if (ncalls == 0)//没有就可以滚了
continue;
sig->evsigcaught[i] -= ncalls;//有的话。。。清空
for (ev = TAILQ_FIRST(&sig->evsigevents[i]);
ev != NULL; ev = next_ev) {
next_ev = TAILQ_NEXT(ev, ev_signal_next);
if (!(ev->ev_events & EV_PERSIST))//没设置这个位,就只使用一次了
event_del(ev);
event_active(ev, EV_SIGNAL, ncalls);//插入就绪链表
}
}
}
libevent源码分析之信号处理的更多相关文章
- 【转】libevent源码分析
libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...
- Libevent源码分析 (1) hello-world
Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libeven ...
- libevent源码分析
这两天没事,看了一下Memcached和libevent的源码,做个小总结. 1.入门 1.1.概述Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络 ...
- Libevent源码分析系列【转】
转自:https://www.cnblogs.com/zxiner/p/6919021.html 1.使用libevent库 源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了 ...
- Libevent源码分析系列
1.使用libevent库 源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了,然后去看底层相应的源码,这样比较有条理,自上向下掌握.下面用libevent库写个程序,每隔1秒 ...
- Libevent源码分析—event_base_dispatch()
我们知道libevent是一个Reactor模式的事件驱动的网络库. 到目前为止,我们已经看了核心的event和event_base结构体的源码,看了初始化这两个结构体的源码,看了注册event的 ...
- libevent源码分析二--timeout事件响应
libevent不仅支持io事件,同时还支持timeout事件与signal事件,这篇文件将分析libevent是如何组织timeout事件以及如何响应timeout事件. 1. min_heap ...
- libevent源码分析一--io事件响应
这篇文章将分析libevent如何组织io事件,如何捕捉事件的发生并进行相应的响应.这里不会详细分析event与event_base的细节,仅描述io事件如何存储与如何响应. 1. select l ...
- Libevent源码分析—event_init()
下面开始看初始化event_base结构的相关函数.相关源码位于event.c event_init() 首先调用event_init()初始化event_base结构体 struct event_b ...
随机推荐
- Android 数据库的线程合作
前言:之前琢磨了很多线程相关的东西,一直摸不着头脑,直到学到了数据库,终于发现世界原来如此美好,任何事物都有存在的理由. 1.主线程,我把它比作长江,作为母亲河的长江,想必大家每个人都不会很陌生. 2 ...
- ansible-1
ansible与salt对比: 相同: 都是为了同时在多台机器上执行相同的命令 都是python开发 不同: agent(saltstack需要安装.ansible不需要) 配置(salt配置麻烦,a ...
- EVALUation mode running with code size limit:2k keil进行仿真过程中出现的报错
EVALUation mode running with code size limit:2k 如果keil软件未破解,会限制程序的存储大小.第一是你的软件没有破解,不能编译2K以上的程序:这种情况下 ...
- Tomcat详解及SNS系统的部署实现
Tomcat详解及SNS系统的部署实现 http://jungege.blog.51cto.com/4102814/1409290
- css改变光标
<span style="cursor:auto"> Auto</span><br /> <span style="cursor ...
- jenkins调用pom.xml文件
对于测试人员来说,大部分代码维护在本地,因此在用jenkins做持续集成时,我们只需要用Jenkins去直接调用pom.xml文件去执行我们的项目 这里主要是正对创建自由风格的工程来讲解的 一.Jen ...
- httpclient传参类型与响应参数接收
https://blog.csdn.net/qq_26562641/article/details/72817457 https://blog.csdn.net/chenjf0221/article/ ...
- 数据库——mysql内置功能(11)
1.视图 视图是一个虚拟表(非真实存在),其本质是(根据SQL语句获取动态的数据集,并未其命名),用户使用时只需使用(名称)即可获取结果集,可以将该结果集当做表来使用 使用视图我们可以把查询过程中的临 ...
- 从今天开始学Python
外部链接下载吧 1. Python 3.63.chm AIP 帮助文档 下载:https://pan.baidu.com/s/1lhpv8JTC3Z7B6aZ3qQi40g 2. VMwar ...
- PAT 甲级 1037 Magic Coupon
https://pintia.cn/problem-sets/994805342720868352/problems/994805451374313472 The magic shop in Mars ...