Libevent源碼分析-timer和signal處理

來源:互聯網
上載者:User

標籤:libevent   timer   signal   

  • timer處理
  • Signal處理
  • timerfd和signalfd
    • timerfd
    • signalfd

timer處理

在Libevent源碼分析-event處理流程中,使用了定時器,來看一下源碼:

evtimer_set(&ev, time_cb, NULL);//設定定時器事件

其中evtimer_set是個宏定義

#define evtimer_set(ev, cb, arg)    event_set((ev), -1, 0, (cb), (arg))//event_set原型voidevent_set(struct event *ev, evutil_socket_t fd, short events,      void (*callback)(evutil_socket_t, short, void *), void *arg)

可以看到定時器事件中,fd=-1,event=0。fd為-1的事件不會發生。在將event添加到event_base時:

event_add(&ev, &tv);//註冊事件

可以看到有個逾時時間tv。在epoll_wait時,會有個逾時時間timeout

int epoll_wait(int epfd,struct epoll_event* events, int maxevents,int timeout)

當timeout到時,事件還沒發生,epoll_wait還是會返回。
在Libevent中沒這麼簡單,因為監控了多個事件,有許多逾時時間,event_base中專門有個管理timeout的堆

struct min_heap timeheap;

這個堆是小根堆。堆得插入和刪除複雜度都是Log(N)。在epoll_wait返回後會處理堆中的定時器事件

static voidtimeout_process(struct event_base *base){    /* Caller must hold lock. */    struct timeval now;    struct event *ev;    if (min_heap_empty(&base->timeheap)) {//堆為空白        return;    }    gettime(base, &now);    while ((ev = min_heap_top(&base->timeheap))) {        if (evutil_timercmp(&ev->ev_timeout, &now, >))//依次從堆頂去值,找到逾時時間隨後處理            break;        /* delete this event from the I/O queues */        event_del_internal(ev);        event_debug(("timeout_process: call %p",             ev->ev_callback));        event_active_nolock(ev, EV_TIMEOUT, 1);//將逾時時間加入active隊列    }}

這就是Libevent中的Timer處理原理。

Signal處理

Libevent中,訊號的處理不像timer那麼簡單了。因為timer可以藉助等待逾時實現,訊號的發送是隨機的,如何轉換為IO?
在Libevent中用了一個socket pair,先看一下event_base中的一個資料結構

struct evsig_info {    struct event ev_signal;//檢測ev_signal_pair[1]的event    evutil_socket_t ev_signal_pair[2];//這裡是2個socket fd    int ev_signal_added;//訊號是否註冊    int ev_n_signals_added;//總共添加了幾個訊號    #ifdef _EVENT_HAVE_SIGACTION    struct sigaction **sh_old;    #else    ev_sighandler_t **sh_old;    #endif    int sh_old_max;/sh_old的大小};

這個資料結構中的ev_signal_pair[2]就是把signal轉換為IO事件的關鍵。這其實是一對socket fd,它們的兩端都在本地。ev_signal_pair[0]作為寫入端,ev_signal_pair[1]作為fd事件。當訊號發生時,寫入ev_signal_pair[0],即可檢測到ev_signal_pair[1]的可讀事件。

evsig_init為訊號做初始化,並把ev_signal_pair[1]作為監聽的socket fd。這個函數調用了evutil_socketpair(
AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair)
初始化ev_signal_pair

intevutil_ersatz_socketpair(int family, int type, int protocol,    evutil_socket_t fd[2]){    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)) {        EVUTIL_SET_SOCKET_ERROR(ERR(EAFNOSUPPORT));        return -1;    }    if (!fd) {        EVUTIL_SET_SOCKET_ERROR(ERR(EINVAL));        return -1;    }    listener = socket(AF_INET, type, 0);//設定監聽的listen    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;    /* 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;    evutil_closesocket(listener);    fd[0] = connector;//寫入端    fd[1] = acceptor;//等待對方寫入的fd    return 0;}

通過這一對socket pair就把signal事件轉換為IO事件了。

timerfd和signalfdtimerfd

在Linux 2.6核心以後新增了timerfd來做計時器,和IO相關聯
這節介紹muduo中定時器的實現。先看一個2.6核心新增的有關定時的系統調用,基於這幾個系統調用可以實現基於檔案描述符的定時器。即可是定時,使檔案描述符在某一特定時間可讀。

#include <sys/timerfd.h>int timerfd_create(int clockid, int flags);int timerfd_settime(int fd, int flags,     onst struct itimerspec *new_value,     struct itimerspec *old_value);int timerfd_gettime(int fd, struct itimerspec *curr_value);

1、timerfd_create用於建立一個定時器檔案,函數傳回值是一個檔案控制代碼fd。
2、timerfd_settime用於設定新的逾時時間,並開始計時。flag為0表示相對時間,為1表示絕對時間。new_value為這次設定的新時間,old_value為上次設定的時間。返回0表示設定成功。
3、timerfd_gettime用於獲得定時器距離下次逾時還剩下的時間。如果調用時定時器已經到期,並且該定時器處於迴圈模式(設定逾時時間時struct itimerspec::it_interval不為0),那麼調用此函數之後定時器重新開始計時。

signalfd

signal是把訊號轉換為IO事件的系統內建的方法。

#include <sys/signalfd.h>int signalfd(int fd, const sigset_t *mask, int flags);

參數fd:如果是-1,表示建立一個fd;否則應該是一個存在的檔案描述符。
參數mask:通過這fd檢測的訊號集合。
參數flags:改變signal()的行為。

參考
linux新API—signalfd的使用方法

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

Libevent源碼分析-timer和signal處理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.