本节主要介绍libevent对signal事件的具体处理框架,包括事件注册,删除和socket pair通知机制,以及是如何将Signal事件集成到事件主循环之中的。
1.继承策略–使用socket pair
Socket pair就是一个socket对,包含两个socket,一个读socket,一个写socket。
工作方式:
线程A-> W -> Write socket(经由Socket pair) -> Read socket -> R -> libevent线程
关于socket的实现方法:
(1)开始
(2)创建监听socket
(3)创建一个连接socket——-sock1
(4)调用connect()连接到监听socket监听的端口
(5)调用accept()取得连接成功后返回的socket——-sock2
(6)将sock1作为写socket,sock2作为读socket;返回
(7)结束
关于非Win32环境下的代码,流程可以与上文对应
int
evutil_ersatz_socketpair(int family, int type, int protocol,
evutil_socket_t fd[2])
{
/* 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.
*/
#ifdef WIN32
#define ERR(e) WSA##e
#else
#define ERR(e) e
#endif
evutil_socket_t listener = -1;
evutil_socket_t connector = -1;
evutil_socket_t acceptor = -1;
struct sockaddr_in listen_addr;
struct sockaddr_in connect_addr;
ev_socklen_t size;
int saved_errno = -1;
if (protocol
|| (family != AF_INET
#ifdef AF_UNIX
&& family != AF_UNIX
#endif
)) {
EVUTIL_SET_SOCKET_ERROR(ERR(EAFNOSUPPORT));
return -1;
}
if (!fd) {
EVUTIL_SET_SOCKET_ERROR(ERR(EINVAL));
return -1;
}
listener = socket(AF_INET, type, 0);
if (listener < 0)
return -1;
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
listen_addr.sin_port = 0; /* kernel chooses port. */
if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
== -1)
goto tidy_up_and_fail;
if (listen(listener, 1) == -1)
goto tidy_up_and_fail;
connector = socket(AF_INET, type, 0);
if (connector < 0)
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) == -1)
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)) == -1)
goto tidy_up_and_fail;
size = sizeof(listen_addr);
acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);
if (acceptor < 0)
goto tidy_up_and_fail;
if (size != sizeof(listen_addr))
goto abort_tidy_up_and_fail;
evutil_closesocket();
/* Now check we are talking to ourself by matching port and host on the
two sockets. */
if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
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[0] = connector;
fd[1] = acceptor;
return 0;
abort_tidy_up_and_fail:
saved_errno = ERR(ECONNABORTED);
tidy_up_and_fail:
if (saved_errno < 0)
saved_errno = EVUTIL_SOCKET_ERROR();
if (listener != -1)
evutil_closesocket(listener);
if (connector != -1)
evutil_closesocket(connector);
if (acceptor != -1)
evutil_closesocket(acceptor);
EVUTIL_SET_SOCKET_ERROR(saved_errno);
return -1;
#undef ERR
}
2.集成到事件主循环—通知event_base
为socket pair的读socket在libevent的event_base实例上注册一个persist的读事件。
这样当socket写入数据时,读socket就会得到通知,触发读事件,从而event_base就能得到相应的通知了。
完整的处理框架如下所示:
3.evsignal_info结构体
书上写的是evsignal_info,但是我看源代码找到的是evsig_info这个结构体
struct evsig_info {
/* Event watching ev_signal_pair[1] */
struct event ev_signal;
/* Socketpair used to send notifications from the signal handler */
evutil_socket_t ev_signal_pair[2];
/* True iff we've added the ev_signal event yet. */
int ev_signal_added;
/* Count of the number of signals we're currently watching. */
int ev_n_signals_added;
/* Array of previous signal handler objects before Libevent started
* messing with them. Used to restore old signal handlers. */
#ifdef _EVENT_HAVE_SIGACTION
struct sigaction **sh_old;
#else
ev_sighandler_t **sh_old;
#endif
/* Size of sh_old. */
int sh_old_max;
};
4.注册,注销signal事件
注册signal事件是通过evsignal_add(struct event *ev)函数完成的,libevent对所有的信号注册同一个处理函数evsignal_handle()
注册流程
(1)取得ev要注册到的信号signo
(2)如果信号signo为被注册,那么就为signo注册信号处理函数evsignal_handle();
(3)如果事件ev_signal还没注册,就注册它
(4)将事件ev添加到signo的event链表中
static void __cdecl
evsig_handler(int sig)
{
int save_errno = errno;
#ifdef WIN32
int socket_errno = EVUTIL_SOCKET_ERROR();
#endif
ev_uint8_t msg;
if (evsig_base == NULL) {
event_warnx(
"%s: received signal %d, but have no base configured",
__func__, sig);
return;
}
#ifndef _EVENT_HAVE_SIGACTION
signal(sig, evsig_handler);
#endif
/* Wake up our notification mechanism */
msg = sig;
send(evsig_base_fd, (char*)&msg, 1, 0);
errno = save_errno;
#ifdef WIN32
EVUTIL_SET_SOCKET_ERROR(socket_errno);
#endif
}