Linux new API signalfd timerfd eventfd instructions

  • 2020-05-12 06:42:14
  • OfStack

Three new versions of fd added to the linux kernel:

signalfd: 2.6.22

timerfd: 2.6.25

eventfd: 2.6.22

Three meanings of fd:

lsignalfd

The traditional signal processing method is to register the signal processing function. Since the signal occurs asynchronously, it is reentrant to solve the problem of concurrent access to data. signalfd can abstract the signal into a file descriptor, which can be used to monitor read when a signal occurs. In this way, the signal can be monitored in select, poll, epoll and other listening queues.

ltimerfd

You can implement the timer function, abstract the timer as a file descriptor, when the timer expires it can be read, which can also be placed in the main loop of the listening queue.

leventfd

It implements the way of event notification between threads and can also be used for user state and kernel communication. The buffer size of eventfd is sizeof(uint64_t); This counter can be incremented to its write, and the read operation can be read and reset. eventfd can also be placed in the listening queue, where a readable event occurs when the counter is not 0 and can be read.

All three of the new fd can listen for readable events to happen when an event is triggered.

signalfd involves API:

Click (here) to fold or open


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

Parameter fd: if it is -1, it means to create a new one; if it is 1 existing one, it means to modify the signal associated with signalfd;

Parameter mask: signal set;

Parameter flag: SFD_NONBLOCK, SFD_CLOEXEC are supported after kernel version 2.6.27;

The file descriptor was returned successfully, and the fd returned supports the following operations: read, select(poll, epoll), close

l example


#include <sys/signalfd.h> 
#include <signal.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#define handle_error(msg) \ 
do { perror(msg); exit(EXIT_FAILURE); } while (0) 
int main(int argc, char *argv[]) 
{ 
sigset_t mask; 
int sfd; 
struct signalfd_siginfo fdsi; 
ssize_t s; 
sigemptyset(&mask); 
sigaddset(&mask, SIGINT); 
sigaddset(&mask, SIGQUIT); 
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 
handle_error("sigprocmask"); 
sfd = signalfd(-1, &mask, 0); 
if (sfd == -1) 
handle_error("signalfd"); 
for (;;) { 
s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); 
if (s != sizeof(struct signalfd_siginfo)) 
handle_error("read"); 
if (fdsi.ssi_signo == SIGINT) { 
printf("Got SIGINT\n"); 
} else if (fdsi.ssi_signo == SIGQUIT) { 
printf("Got SIGQUIT\n"); 
exit(EXIT_SUCCESS); 
} else { 
printf("Read unexpected signal\n"); 
} 
} 
}
#include <sys/signalfd.h> 
#include <signal.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#define handle_error(msg) \ 
do { perror(msg); exit(EXIT_FAILURE); } while (0) 
int main(int argc, char *argv[]) 
{ 
sigset_t mask; 
int sfd; 
struct signalfd_siginfo fdsi; 
ssize_t s; 
sigemptyset(&mask); 
sigaddset(&mask, SIGINT); 
sigaddset(&mask, SIGQUIT); 
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) 
handle_error("sigprocmask"); 
sfd = signalfd(-1, &mask, 0); 
if (sfd == -1) 
handle_error("signalfd"); 
for (;;) { 
s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo)); 
if (s != sizeof(struct signalfd_siginfo)) 
handle_error("read"); 
if (fdsi.ssi_signo == SIGINT) { 
printf("Got SIGINT\n"); 
} else if (fdsi.ssi_signo == SIGQUIT) { 
printf("Got SIGQUIT\n"); 
exit(EXIT_SUCCESS); 
} else { 
printf("Read unexpected signal\n"); 
} 
} 
}

L17-L21: add the signal of interest to sigset_t;

L24: call signalfd, associate the signal set with fd, the first parameter is -1 means to create a new signalfd, not -1 and is a legal signalfd means to add a new signal to it.

L29: block waiting for the signal to occur and read. You can tell what's going on from the readings.

timerfd refers to API


#include <sys/timerfd.h> 
int timerfd_create(int clockid, int flags); 
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,struct itimerspec *old_value); 
int timerfd_gettime(int fd, struct itimerspec *curr_value);
#include <sys/timerfd.h> 
int timerfd_create(int clockid, int flags); 
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,struct itimerspec *old_value); 
int timerfd_gettime(int fd, struct itimerspec *curr_value);
timerfd_create : create 1 a timerfd ; The returned fd You can do the following: read , select(poll , epoll) , close
timerfd_settime Set: timer Period, and the start interval 
timerfd_gettime : get the due date. 
// The data structure of function parameters is as follows:  
struct timespec 
{ 
time_t tv_sec; /* Seconds */ 
long tv_nsec; /* Nanoseconds */ 
}; 
struct itimerspec 
{ 
struct timespec it_interval; /* Interval for periodic timer */ 
struct timespec it_value; /* Initial expiration */ 
};
// The data structure of function parameters is as follows:  
struct timespec 
{ 
time_t tv_sec; /* Seconds */ 
long tv_nsec; /* Nanoseconds */ 
}; 
struct itimerspec 
{ 
struct timespec it_interval; /* Interval for periodic timer */ 
struct timespec it_value; /* Initial expiration */ 
};

l example


#include <sys/timerfd.h> 
#include <sys/time.h> 
#include <time.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <stdint.h> /* Definition of uint64_t */ 
#define handle_error(msg) \ 
do { perror(msg); exit(EXIT_FAILURE); } while (0) 
void printTime() 
{ 
struct timeval tv; 
gettimeofday(&tv, NULL); 
printf("printTime: current time:%ld.%ld ", tv.tv_sec, tv.tv_usec); 
} 
int main(int argc, char *argv[]) 
{ 
struct timespec now; 
if (clock_gettime(CLOCK_REALTIME, &now) == -1) 
handle_error("clock_gettime"); 
struct itimerspec new_value; 
new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]); 
new_value.it_value.tv_nsec = now.tv_nsec; 
new_value.it_interval.tv_sec = atoi(argv[2]); 
new_value.it_interval.tv_nsec = 0; 
int fd = timerfd_create(CLOCK_REALTIME, 0); 
if (fd == -1) 
handle_error("timerfd_create"); 
if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1) 
handle_error("timerfd_settime"); 
printTime(); 
printf("timer started\n"); 
for (uint64_t tot_exp = 0; tot_exp < atoi(argv[3]);) 
{ 
uint64_t exp; 
ssize_t s = read(fd, &exp, sizeof(uint64_t)); 
if (s != sizeof(uint64_t)) 
handle_error("read"); 
tot_exp += exp; 
printTime(); 
printf("read: %llu; total=%llu\n",exp, tot_exp); 
} 
exit(EXIT_SUCCESS); 
}
#include <sys/timerfd.h> 
#include <sys/time.h> 
#include <time.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <stdint.h> /* Definition of uint64_t */ 
#define handle_error(msg) \ 
do { perror(msg); exit(EXIT_FAILURE); } while (0) 
void printTime() 
{ 
struct timeval tv; 
gettimeofday(&tv, NULL); 
printf("printTime: current time:%ld.%ld ", tv.tv_sec, tv.tv_usec); 
} 
int main(int argc, char *argv[]) 
{ 
struct timespec now; 
if (clock_gettime(CLOCK_REALTIME, &now) == -1) 
handle_error("clock_gettime"); 
struct itimerspec new_value; 
new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]); 
new_value.it_value.tv_nsec = now.tv_nsec; 
new_value.it_interval.tv_sec = atoi(argv[2]); 
new_value.it_interval.tv_nsec = 0; 
int fd = timerfd_create(CLOCK_REALTIME, 0); 
if (fd == -1) 
handle_error("timerfd_create"); 
if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1) 
handle_error("timerfd_settime"); 
printTime(); 
printf("timer started\n"); 
for (uint64_t tot_exp = 0; tot_exp < atoi(argv[3]);) 
{ 
uint64_t exp; 
ssize_t s = read(fd, &exp, sizeof(uint64_t)); 
if (s != sizeof(uint64_t)) 
handle_error("read"); 
tot_exp += exp; 
printTime(); 
printf("read: %llu; total=%llu\n",exp, tot_exp); 
} 
exit(EXIT_SUCCESS); 
}

Code L25-L29: initializes timer parameters, initial interval and timing interval.

L32: create timer fd, CLOCK_REALTIME: real time type, modify the clock will affect the timer; CLOCK_MONOTONIC: relative time type. Changing the clock does not affect the timer.

L35: sets the value of the timer.

L44: block until timer expires. The return value is the number of unprocessed due dates. For example, if the timing interval is 2 seconds, but it takes 10 seconds to read, the value read is 5.

Compile run: add rt library (g++ -lrt timerfd. cc-o timerfd)


[root@localhost appTest]# ./timerfd 5 2 10
printTime: current time:1357391736.146196 timer started
printTime: current time:1357391741.153430 read: 1; total=1
printTime: current time:1357391743.146550 read: 1; total=2
printTime: current time:1357391745.151483 read: 1; total=3
printTime: current time:1357391747.161155 read: 1; total=4
printTime: current time:1357391749.153934 read: 1; total=5
printTime: current time:1357391751.157309 read: 1; total=6
printTime: current time:1357391753.158384 read: 1; total=7
printTime: current time:1357391755.150470 read: 1; total=8
printTime: current time:1357391757.150253 read: 1; total=9
printTime: current time:1357391759.149954 read: 1; total=10
[root@localhost appTest]#

The first parameter 5 is the interval of the first timer expiration, the second parameter 2 is the interval of the timer, and the third parameter is the interval of the timer expiration for 10 times, then exit. Program run (5+2*10)S exit.

Details can be: man timerfd_create

eventfd involves API:


#include <sys/eventfd.h> 
int eventfd(unsigned int initval, int flags);
#include <sys/eventfd.h> 
int eventfd(unsigned int initval, int flags);

Create 1 eventfd, which is the fd associated with 1 counter. If the counter is not zero, a readable event will occur. After read, the counter will be reset and write will increment the counter. The returned fd can be done as follows: read, write, select(poll, epoll), close.

This function creates an event object (eventfd object) to implement the wait/notification mechanism between processes (wait/notify). The kernel maintains a 64-bit counter (uint64_t) for this object. And initialize the counter with the first parameter (initval). Calling this function returns a new file descriptor (event object). The second parameter (flags) can be set by bit starting with version 2.6.27. The following macros are available:

lEFD_NONBLOCK

open(2) O_NONBLOCK sets the object to a non-blocking state. If this state is not set, read(2) reads eventfd, and if the counter value is 0, then 1 is blocked in the read call. If this flag is set, an EAGAIN error (errno = EAGAIN) is returned. The effect is the same as if it were an extra call to select(2).

lEFD_CLOEXEC

If this id is set, the exec call will automatically close the file descriptor to prevent leaks. If the kernel is 2.6.26 or earlier, flags must be set to 0.
Once you have created this object, you can do the following:

1) write: add the 8-byte integer value written by the buffer to the kernel counter.

2) read: read the 8-byte value and reset the counter to 0. If the counter is 0 when read is called, if eventfd is blocked, read will block here, otherwise you will get an EAGAIN error. If the length of buffer is less than 8 then read will fail and the error code will be set to EINVAL.

3) poll select epoll

4) close: when eventfd is not needed, close can be called to close. When all handles of this object are closed, the kernel will release resources. Why not just release close if you call fork to create
The process copies the handle to the new process and inherits all state.

l example


#include <sys/eventfd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#define handle_error(msg) \
do { perror(msg); exit(1); } while (0)
int main( int argc, char **argv ){
uint64_t u;
ssize_t s;5 int j;
if ( argc < 2 ) {
fprintf(stderr, "input in command argument");
exit(1);
}
int efd;
if ( (efd = eventfd(0, EFD_NONBLOCK)) == -1 )
handle_error("eventfd failed");
switch (fork()) {
case 0:
for( j = 1; j < argc; j ++ ) {
printf("Child writing %s to efd\n", argv[j] );
u = strtoull(argv[j], NULL, 0); /* analogesly atoi */
s = write(efd, &u, sizeof(uint64_t));/*append u to counter */
if ( s != sizeof(uint64_t) )
handle_error("write efd failed");
}
printf("child completed write loop\n");
exit(0);
default:
sleep (2);
printf("parent about to read\n");
s = read(efd, &u, sizeof(uint64_t));
if ( s != sizeof(uint64_t) ) {
if (errno = EAGAIN) {
printf("Parent read value %d\n", s);
return 1;
}
handle_error("parent read failed");
}
printf("parent read %d , %llu (0x%llx) from efd\n",
s, (unsigned long long)u, (unsigned long long) u);
exit(0);
case -1:
handle_error("fork ");
}
return 0;
}

#include <sys/eventfd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#define handle_error(msg) \
do { perror(msg); exit(1); } while (0)
int main( int argc, char **argv ){
uint64_t u;
ssize_t s;5 int j;
if ( argc < 2 ) {
fprintf(stderr, "input in command argument");
exit(1);
}
int efd;
if ( (efd = eventfd(0, EFD_NONBLOCK)) == -1 )
handle_error("eventfd failed");
switch (fork()) {
case 0:
for( j = 1; j < argc; j ++ ) {
printf("Child writing %s to efd\n", argv[j] );
u = strtoull(argv[j], NULL, 0); /* analogesly atoi */
s = write(efd, &u, sizeof(uint64_t));/*append u to counter */
if ( s != sizeof(uint64_t) )
handle_error("write efd failed");
}
printf("child completed write loop\n");
exit(0);
default:
sleep (2);
printf("parent about to read\n");
s = read(efd, &u, sizeof(uint64_t));
if ( s != sizeof(uint64_t) ) {
if (errno = EAGAIN) {
printf("Parent read value %d\n", s);
return 1;
}
handle_error("parent read failed");
}
printf("parent read %d , %llu (0x%llx) from efd\n",
s, (unsigned long long)u, (unsigned long long) u);
exit(0);
case -1:
handle_error("fork ");
}
return 0;
}

Related articles: