通常 ppollpselect 會搭配 sigprocmask 使用,先透過 sigprocmask block signal,然後在 ppollpselect 這邊再 unblock signal 做處理,例如常見的 daemon reload 可以這樣做:

// C
volatile sig_atomic_t reload_flag = 0, terminate_flag = 0;

static void sig_handler(int sig)
{
    if (sig == SIGHUP) {
        reload_flag = 1;
    }
    else if (sig == SIGTERM) {
        terminate_flag = 1;
    }
}

// A
sigset_t mask, orig_mask;
sigemptyset (&mask);
sigaddset (&mask, SIGTERM);
sigaddset (&mask, SIGHUP);

if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
    exit(EXIT_FAILURE);
}

// B
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = sig_handler;
if (sigaction(SIGHUP, &sa, NULL) == -1) {
    exit(EXIT_FAILURE);
}
if (sigaction(SIGTERM, &sa, NULL) == -1) {
    exit(EXIT_FAILURE);
}


// Set socket blah blah

// D
while (1) {
    int poll_ret = ppoll(fds, MAX_FDS_SIZE, tv, orig_mask);
    if (poll_ret == -1) {
        if (errno != EINTR) {
            exit(EXIT_FAILURE);
        } else if (reload_flag) {
            // process reload
            reload_flag = 0;
        } else if (terminate_flag) {
            // process terminate
            terminate_flag = 0;
            break;
        }
    } else if (poll_ret > 0) {
        // process socket
    } else if (poll_ret == 0) {
        // process timeout
    } else {
        exit(EXIT_FAILURE);
    }
}

A 這段先將 SIGTERMSIGHUP block 起來,接著在 B 這邊設定這兩種 signal 的 callback,C 這裏的 callback function 中則去設定 flag,這裏的 sig_atomic_t 是表示這個 type 變數的操作保證是 atomic 的,通常會是 int 就是,資料的部分可以參考:

D 的部分,就是在 interrupt 進來之後,判斷這些 flag,做更進一步的處理。

至於 pselect 的部分可以參考這邊:

另外,如果要用 ppoll 的話,要先 define 一個變數,不然會出現 implicit definition 的警告:

#ifndef __USE_GNU
/* Define this to avoid a warning about implicit definition of ppoll.*/
#define __USE_GNU
#endif
#include <poll.h>

為啥不用 epoll? 因為連線數很少,用 epoll 不見得比較好,libevent 差不多一樣道理。

Ref: