Just enjoy it...
__cplusplus
         在编写网络应用程序中,我们经常判断连接请求是否有数据收等待接收或可以发送数据,通常我们可以采用selectpoll去判断一个fd_set中是否有数据可读或可写的文件描述符,如果有数据可读,创建相应的线程去读取数据等等。

同时,我们可能要注册信号处理函数,以实现对某个信号进行处理。在linux下通常做法是通过调用signalsigaction函数来注册信号处理服务函数。然而这种做法就是我们没有办法传送一个context给我们信号处理函数。在libevent给我们带来了一个完美的解决方案,那就是将对signal的处理转换成与文件描述符一样的处理方案,采用select,poll,epoll,kqueneI/O模型进行处理。

在分析其中的技巧之前,我们先来看下libevent里几个重要的数据结构:struct event_basestruct evsig_infostruct eventop

下面是struct event_base的部分成员:

/* signal handling info */
const struct eventop *evsigsel;
void *evsigbase;
struct evsig_info sig;

struct eventop结构定义如下:

struct eventop {
    
const char *name;
    
void *(*init)(struct event_base *);
    
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
    
int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
    
int (*dispatch)(struct event_base *struct timeval *);
    
void (*dealloc)(struct event_base *);
    
int need_reinit;
    
enum event_method_feature features;
    size_t fdinfo_len;
}
;

 

struct eventop结构封装了各I/O模型的处理方式,在主程序中,我们不关心采用的是select,poll等,只需要调用dispatch方法,将一些处于就绪态的events添加到active_event list中。

 

结构struct evsig_info定义如下:

struct evsig_info {
struct event ev_signal;
evutil_socket_t ev_signal_pair[
2];
int ev_signal_added;
volatile sig_atomic_t evsig_caught;
sig_atomic_t evsigcaught[NSIG];
#ifdef _EVENT_HAVE_SIGACTION
struct sigaction **sh_old;
#else
ev_sighandler_t 
**sh_old;
#endif
int sh_old_max;
}
;
 

下面我们将重点分析struct evsig_info结构体。

ev_signal成员将所有的信号集看成一个event,它对应的文件描述符是ev_signal_pair[1],在文件signal.c中初始如下:

//base就是current_base全局变量

event_assign(&base->sig.ev_signal, basebase->sig.ev_signal_pair[1],

              EV_READ 
| EV_PERSIST, evsig_cb, &base->sig.ev_signal);

struct evsig_info中的sig.ev_signal_pair[2]通过unix socket 域连接起来,对于每个要添加处理函数的signal,我们都给它注册同一个信号处理函数,实现如下:

static void evsig_handler(int sig)
{
    
int save_errno = errno;
#ifdef WIN32
    
int socket_errno = EVUTIL_SOCKET_ERROR();
#endif

    
if (evsig_base == NULL) {
        event_warn(
            
"%s: received signal %d, but have no base configured",
            __func__, sig);
        
return;
    }

    evsig_base
->sig.evsigcaught[sig]++;
    evsig_base
->sig.evsig_caught = 1;
#ifndef _EVENT_HAVE_SIGACTION
    signal(sig, evsig_handler);
#endif
    
/* Wake up our notification mechanism */
    send(evsig_base
->sig.ev_signal_pair[0], "a"10);
    errno 
= save_errno;
#ifdef WIN32
    EVUTIL_SET_SOCKET_ERROR(socket_errno);
#endif

 

      在该函数中,我们可以看到,它首先对struct evsig_infosig_evsigcaught[sig]进行加一(通过这个数,后续程序知道哪个信号已经被触发了)。接着去写sig_ev_signal_pair[0],在之前我们讲过sig_ev_signal_pair[0,1]是一对unix域套接字的两端,因此,这里写sig_ev_signal_pair[0],那么sig_ev_signal_pair[1]读就处于active状态。在dispatch中,将evsig_info->ev_signal添加入active_event list中,然后调用ev_signalcall_back函数。该call_back函数仅仅是去读取套接字,使它仍处于dis-active状态。

       整个过程下来,我们就获取了哪些信号已经触发,接下来我们只需要轮回event_list,查找包含active signalevent,调用该event的回调函数即可。

     下面是一个使用libevent去处理响应信号的例子。

static void signal_cb(int fd, short eventvoid *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 */
    event_init();
    
/* Initalize one event */
    event_set(
&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,
        
&signal_int);
    event_add(
&signal_int, NULL);
    event_dispatch();
    
return (0);
}


在该例子中,通过创建一个event,该event的文件描述符为信号量sigint.并添加到event_list中。然后调用event_dispatch启动循环。

注意:struct event_base中的sig->ev_sig eventevent_init里事先已初始好,并添加到event_list当中了。

posted on 2009-05-09 22:43 我不是达人 阅读(2727) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理