Linux 新的API signalfd、timerfd、eventfd使用說(shuō)明
三種新的fd加入linux內(nèi)核的的版本:
signalfd:2.6.22
timerfd:2.6.25
eventfd:2.6.22
三種fd的意義:
lsignalfd
傳統(tǒng)的處理信號(hào)的方式是注冊(cè)信號(hào)處理函數(shù);由于信號(hào)是異步發(fā)生的,要解決數(shù)據(jù)的并發(fā)訪(fǎng)問(wèn),可重入問(wèn)題。signalfd可以將信號(hào)抽象為一個(gè)文件描述符,當(dāng)有信號(hào)發(fā)生時(shí)可以對(duì)其read,這樣可以將信號(hào)的監(jiān)聽(tīng)放到select、poll、epoll等監(jiān)聽(tīng)隊(duì)列中。
ltimerfd
可以實(shí)現(xiàn)定時(shí)器的功能,將定時(shí)器抽象為文件描述符,當(dāng)定時(shí)器到期時(shí)可以對(duì)其read,這樣也可以放到監(jiān)聽(tīng)隊(duì)列的主循環(huán)中。
leventfd
實(shí)現(xiàn)了線(xiàn)程之間事件通知的方式,也可以用于用戶(hù)態(tài)和內(nèi)核通信。eventfd的緩沖區(qū)大小是sizeof(uint64_t);向其write可以遞增這個(gè)計(jì)數(shù)器,read操作可以讀取,并進(jìn)行清零;eventfd也可以放到監(jiān)聽(tīng)隊(duì)列中,當(dāng)計(jì)數(shù)器不是0時(shí),有可讀事件發(fā)生,可以進(jìn)行讀取。
三種新的fd都可以進(jìn)行監(jiān)聽(tīng),當(dāng)有事件觸發(fā)時(shí),有可讀事件發(fā)生。
signalfd涉及API:
點(diǎn)擊(此處)折疊或打開(kāi)
#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);
參數(shù)fd:如果是-1則表示新建一個(gè),如果是一個(gè)已經(jīng)存在的則表示修改signalfd所關(guān)聯(lián)的信號(hào);
參數(shù)mask:信號(hào)集合;
參數(shù)flag:內(nèi)核版本2.6.27以后支持SFD_NONBLOCK、SFD_CLOEXEC;
成功返回文件描述符,返回的fd支持以下操作:read、select(poll、epoll)、close
l例子
#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:將感興趣的信號(hào)加入到sigset_t中;
L24:調(diào)用signalfd,把信號(hào)集與fd關(guān)聯(lián)起來(lái),第一個(gè)參數(shù)為-1表示新建一個(gè)signalfd,不是-1并且是一個(gè)合法的signalfd表示向其添加新的信號(hào)。
L29:阻塞等待信號(hào)的發(fā)生并讀取。根據(jù)讀取的結(jié)果可以知道發(fā)生了什么信號(hào)。
timerfd涉及的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:創(chuàng)建一個(gè)timerfd;返回的fd可以進(jìn)行如下操作:read、select(poll、epoll)、close
timerfd_settime:設(shè)置timer的周期,以及起始間隔
timerfd_gettime:獲取到期時(shí)間。
//函數(shù)參數(shù)中數(shù)據(jù)結(jié)構(gòu)如下:
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 */
};
//函數(shù)參數(shù)中數(shù)據(jù)結(jié)構(gòu)如下:
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例子
#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);
}
代碼L25-L29:初始化定時(shí)器的參數(shù),初始間隔與定時(shí)間隔。
L32:創(chuàng)建定時(shí)器fd,CLOCK_REALTIME:真實(shí)時(shí)間類(lèi)型,修改時(shí)鐘會(huì)影響定時(shí)器;CLOCK_MONOTONIC:相對(duì)時(shí)間類(lèi)型,修改時(shí)鐘不影響定時(shí)器。
L35:設(shè)置定時(shí)器的值。
L44:阻塞等待定時(shí)器到期。返回值是未處理的到期次數(shù)。比如定時(shí)間隔為2秒,但過(guò)了10秒才去讀取,則讀取的值是5。
編譯運(yùn)行:編譯時(shí)要加rt庫(kù)(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]#
第一個(gè)參數(shù)5為第一次定時(shí)器到期間隔,第二個(gè)參數(shù)2為定時(shí)器的間隔,第三個(gè)參數(shù)為定時(shí)器到期10次則退出。程序運(yùn)行(5+2*10)S退出。
詳細(xì)信息可以:man timerfd_create
eventfd涉及API:
#include <sys/eventfd.h> int eventfd(unsigned int initval, int flags); #include <sys/eventfd.h> int eventfd(unsigned int initval, int flags);
創(chuàng)建一個(gè)eventfd,這是一個(gè)計(jì)數(shù)器相關(guān)的fd,計(jì)數(shù)器不為零是有可讀事件發(fā)生,read以后計(jì)數(shù)器清零,write遞增計(jì)數(shù)器;返回的fd可以進(jìn)行如下操作:read、write、select(poll、epoll)、close。
這個(gè)函數(shù)會(huì)創(chuàng)建一個(gè)事件對(duì)象 (eventfd object), 用來(lái)實(shí)現(xiàn),進(jìn)程(線(xiàn)程)間的等待/通知(wait/notify) 機(jī)制. 內(nèi)核會(huì)為這個(gè)對(duì)象維護(hù)一個(gè)64位的計(jì)數(shù)器(uint64_t)。并且使用第一個(gè)參數(shù)(initval)初始化這個(gè)計(jì)數(shù)器。調(diào)用這個(gè)函數(shù)就會(huì)返回一個(gè)新的文件描述符(event object)。2.6.27版本開(kāi)始可以按位設(shè)置第二個(gè)參數(shù)(flags)。有如下的一些宏可以使用:
lEFD_NONBLOCK
功能同open(2)的O_NONBLOCK,設(shè)置對(duì)象為非阻塞狀態(tài),如果沒(méi)有設(shè)置這個(gè)狀態(tài)的話(huà),read(2)讀eventfd,并且計(jì)數(shù)器的值為0 就一直堵塞在read調(diào)用當(dāng)中,要是設(shè)置了這個(gè)標(biāo)志, 就會(huì)返回一個(gè) EAGAIN 錯(cuò)誤(errno = EAGAIN)。效果也如同 額外調(diào)用select(2)達(dá)到的效果。
lEFD_CLOEXEC
這個(gè)標(biāo)識(shí)被設(shè)置的話(huà),調(diào)用exec后會(huì)自動(dòng)關(guān)閉文件描述符,防止泄漏。如果是2.6.26或之前版本的內(nèi)核,flags 必須設(shè)置為0。
創(chuàng)建這個(gè)對(duì)象后,可以對(duì)其做如下操作:
1) write: 將緩沖區(qū)寫(xiě)入的8字節(jié)整形值加到內(nèi)核計(jì)數(shù)器上。
2) read: 讀取8字節(jié)值, 并把計(jì)數(shù)器重設(shè)為0. 如果調(diào)用read的時(shí)候計(jì)數(shù)器為0, 要是eventfd是阻塞的, read就一直阻塞在這里,否則就得到 一個(gè)EAGAIN錯(cuò)誤。如果buffer的長(zhǎng)度小于8那么read會(huì)失敗, 錯(cuò)誤代碼被設(shè)置成 EINVAL。
3) poll select epoll
4) close: 當(dāng)不需要eventfd的時(shí)候可以調(diào)用close關(guān)閉, 當(dāng)這個(gè)對(duì)象的所有句柄都被關(guān)閉的時(shí)候,內(nèi)核會(huì)釋放資源。 為什么不是close就直接釋放呢, 如果調(diào)用fork 創(chuàng)建
進(jìn)程的時(shí)候會(huì)復(fù)制這個(gè)句柄到新的進(jìn)程,并繼承所有的狀態(tài)。
l例子
#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;
}
以上所述是小編給大家介紹的Linux 新的API signalfd、timerfd、eventfd使用說(shuō)明,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
詳解虛擬機(jī)下CentOS 6.5配置IP地址的三種方法
本篇文章主要介紹了虛擬機(jī)下CentOS 6.5配置IP地址的三種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
centos開(kāi)機(jī)自動(dòng)啟動(dòng)RabbitMq軟件的方法
本文詳細(xì)講解了centos開(kāi)機(jī)自動(dòng)啟動(dòng)RabbitMq軟件的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以收藏下2021-11-11
Linux使用partprobe更新分區(qū)表的示例代碼
partprobe 是一個(gè)用于在 Linux 操作系統(tǒng)中通知內(nèi)核分區(qū)表變化的命令,它請(qǐng)求操作系統(tǒng)重新讀取磁盤(pán)的分區(qū)表,以便及時(shí)反映對(duì)分區(qū)所做的修改,本文給大家介紹了Linux使用partprobe更新分區(qū)表的方法,需要的朋友可以參考下2025-03-03
centos 7中添加一個(gè)新用戶(hù)并授權(quán)的步驟詳解
這篇文章主要給大家介紹了關(guān)于在centos 7中添加一個(gè)新用戶(hù)并授權(quán)的步驟,文中將實(shí)現(xiàn)的步驟介紹的非常詳細(xì),通過(guò)文中介紹的步驟可以輕松的創(chuàng)建一個(gè)新用戶(hù)并授權(quán),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-08-08
Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問(wèn)題
這篇文章主要介紹了Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
CentOS7?minimal?最小化安裝網(wǎng)絡(luò)設(shè)置過(guò)程
這篇文章主要介紹了CentOS7?minimal?最小化安裝網(wǎng)絡(luò)設(shè)置過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
解決CentOS7虛擬機(jī)無(wú)法上網(wǎng)并設(shè)置CentOS7虛擬機(jī)使用靜態(tài)IP上網(wǎng)
這篇文章主要介紹了解決CentOS7虛擬機(jī)無(wú)法上網(wǎng)并設(shè)置CentOS7虛擬機(jī)使用靜態(tài)IP上網(wǎng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02

