unix signal : signalfd, eventfd, timerfd

shiyang6017發表於2019-05-11

signalfd

建立一個用於所受訊號的檔案描述符 
int signalfd(int fd , const sigset_t* mask , int flags ); 
struct signalfd_siginfo {  
    uint32_t ssi_signo;   /* Signal number */  
    int32_t  ssi_errno;   /* Error number (unused) */  
    int32_t  ssi_code;    /* Signal code */  
    uint32_t ssi_pid;     /* PID of sender */  
    uint32_t ssi_uid;     /* Real UID of sender */  
    int32_t  ssi_fd;      /* File descriptor (SIGIO) */  
    uint32_t ssi_tid;     /* Kernel timer ID (POSIX timers)*/
    uint32_t ssi_band;    /* Band event (SIGIO) */  
    uint32_t ssi_overrun; /* POSIX timer overrun count */  
    uint32_t ssi_trapno;  /* Trap number that caused signal */  
    int32_t  ssi_status;  /* Exit status or signal (SIGCHLD) */  
    int32_t  ssi_int;     /* Integer sent by sigqueue(3) */  
    uint64_t ssi_ptr;     /* Pointer sent by sigqueue(3) */  
    uint64_t ssi_utime;   /* User CPU time consumed (SIGCHLD) */  
    uint64_t ssi_stime;   /* System CPU time consumed (SIGCHLD) */  
    uint64_t ssi_addr;    /* Address that generated signal (for hardware-generated signals) */  
    uint8_t  pad[X];      /* Pad size to 128 bytes (allow for additional fields in the future) */  
};  
int main() {
    sigset_t mask;
    int sigfd;
    struct signalfd_siginfo ssinfo;

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) print_err("sigprocmask err");
    if ((sigfd = signalfd(-1, &mask, 0)) < 0) print_err("signalfd err");
    
    while (1) {
        int len = read(sigfd, &ssinfo, sizeof(ssinfo));
        if (len < 0) print_err("read err");
        if (ssinfo.ssi_signo == SIGINT) printf("get SIGINT.
");
    }
    return 0;
}

eventfd

int eventfd(unsigned int initval, int flags);

核心相關的資料結構

struct eventfd_ctx {
    struct kref kref;   /* file計數用的,用於get/put */
    wait_queue_head_t wqh; /* 這個用來存放使用者態的程式wait項,有了它通知機制才成為可能 */
/*
* Every time that a write(2) is performed on an eventfd, the
* value of the __u64 being written is added to "count" and a
* wakeup is performed on "wqh". A read(2) will return the "count"
* value to userspace, and will reset "count" to zero. The kernel
* side eventfd_signal() also, adds to the "count" counter and
* issue a wakeup.
*/
    __u64 count;  /*計數器,read就是取出然後清空,write就是把value加上 */
    unsigned int flags;  /* 用來存放阻塞/非阻塞標識或是O_CLOEXEC之類的東西 */
};

值得注意的flag

EFD_SEMAPHORE (since Linux 2.6.30)

  • If EFD_SEMAPHORE was not specified and the eventfd counter has a nonzero value, then a read(2) returns 8 bytes containing that value, and the counter`s value is reset to zero.
  • If EFD_SEMAPHORE was specified and the eventfd counter has a nonzero value, then a read(2) returns 8 bytes containing the value 1, and the counter`s value is decremented by 1.
  • If the eventfd counter is zero at the time of the call to read(2), then the call either blocks until the counter becomes nonzero (at which time, the read(2) proceeds as described above) or fails with the error EAGAIN if the file descriptor has been made nonblocking.
void fun(int event_fd) {
    int epoll_fd = epoll_create1(EPOLL_CLOEXEC); // EPOLL_NONBLOCK
    struct epoll_event event;
    const int SIZE = 1024;
    struct epoll_event ready_events[SIZE];
    int ready_nums;

    memset(&event, 0, sizeof event);
    event.events = EPOLLIN;
    event.data.fd = event_fd;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, event_fd, &event);
    for(; ;) {
        ready_nums = epoll_wait(epoll_fd, ready_events, SIZE, -1);
        for (int i = 0; i < ready_nums; ++i) {
            int fd = ready_events[i].data.fd;
            if (fd == event_fd) {
                uint64_t val;
                read(fd, &val, sizeof val);
                printf("the value of val = %lu
", val);
            }
        }
    }
}
int main() {

    int event_fd = eventfd(0, EFD_NONBLOCK);

    pid_t child_pid;
    uint64_t val = 1314;
    if ((child_pid = fork()) == 0) {
        write(event_fd, &val, sizeof val);
        goto DONE;
    } else if (child_pid > 0) {
        sleep(1);
        fun(event_fd);
        goto DONE;
    }

DONE:
    return 0;
}

timerfd

 struct timespec {
               time_t tv_sec;                /* Seconds */
               long   tv_nsec;               /* Nanoseconds */
           };

           struct itimerspec {
               struct timespec it_interval;  /* Timer interval */
               struct timespec it_value;     /* Initial expiration */
           };

If new_value->it_value specifies a nonzero value (i.e., either subfield is nonzero),
then timer_settime() arms (starts) the timer, setting it to initially expire at the given time.
If the timer was already armed, then the previous settings are overwritten.
If new_value->it_value specifies a zero value (i.e., both subfields are zero), then the timer is disarmed.

The new_value->it_interval field specifies the period of the timer, in seconds and nanoseconds.
If this field is nonzero, then each time that an armed timer expires, the timer is reloaded from the
value specified in new_value->it_interval.
If new_value->it_interval specifies a zero value then the timer expires just once, at the time specified by it_value.
By default, the initial expiration time specified in new_value->it_value is interpreted relative to the current time on the timer`s clock at the time of the call. This can be modified by specifying
TIMER_ABSTIME in flags, in which case new_value->it_value is interpreted as an absolute value as measured on the timer`s clock;
that is, the timer will expire when the clock value reaches the value specified by new_value->it_value.
If the specified absolute time has already passed, then the timer expires immediately, and the overrun count (see timer_getoverrun(2)) will be set correctly.

If the value of the CLOCK_REALTIME clock is adjusted while an absolute timer based on that clock is armed, then the expiration of the timer will be appropriately adjusted. Adjustments to the
CLOCK_REALTIME clock have no effect on relative timers based on that clock.

If old_value is not NULL, then it points to a buffer that is used to return the previous interval of the timer (in old_value->it_interval) and the amount of time until the timer would previously have next expired (in old_value->it_value).

timer_gettime() returns the time until next expiration, and the interval, for the timer specified by timerid, in the buffer pointed to by curr_value.
The time remaining until the next timer expiration is returned in curr_value->it_value;
this is always a relative value, regardless of whether the TIMER_ABSTIME flag was used when arming the timer.
If the value returned in curr_value->it_value is zero, then the timer is currently disarmed. The timer interval is returned in curr_value->it_interval.
If the value returned in curr_value->it_interval is zero, then this is a “one-shot” timer.

int timer_settime(timer_t timerid, int flags,
                         const struct itimerspec *new_value,
                         struct itimerspec * old_value);
int main() {
    int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
/*
    struct timespec begin_time;
    struct timespec time_interval = {1, 0};
    if (clock_gettime(CLOCK_MONOTONIC, &begin_time) < 0) {
        print_err("clock_gettime error");
    }
*/
    struct itimerspec timer = {{0, 0}, {1, 0}};

    // if
    if (timerfd_settime(timer_fd, 0, &timer, NULL) < 0) {
        print_err("timerfd_settime error");
    }
    while (1) {
        static uint64_t time_out_count = 0;
        if (read(timer_fd, &time_out_count, sizeof time_out_count) < 0) {
            print_err("read error");
        } else {
            printf("time_out_count:= %lu
", time_out_count);
        }
    }
    return 0;
}

相關文章