linux c定时器与休眠

2018-06-09 12:18:34

定时器是进程规划自己在未来某一时刻接获通知的一种机制。休眠则能是进程(或线程)暂停执行一段时间。

间隔定时器

系统调用 setitimer() 创建一个间隔式定时器,一定时间后到期,这个定时器也可以每个一段时间到期一次。

#include <sys/time.h>

struct itimerval {
    struct timeval it_interval; /* 间隔定时器 */
    struct timeval it_value; /* 多少时间到期 */
};

struct timeval {
    time_t tv_sec; /* Seconds */
    suseconds_t tv_usec; /* Microseconds (long int) */
};

int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);

//Returns 0 on success, or –1 on error

通过制定which 值,进程可以创建3中不同类型的定时器:

ITIMER_REAL 创建真实时间倒计时定时器。到期会产生 SIGALARM 信号。
ITIMER_VIRTUAL 创建以进程虚拟时间(用户CPU时间)倒计时定时器。到期会产生SIGVTALRM 信号。
ITIMER_PROF 创建以进程时间(用户态CPU和内核态CPU总和)倒计时。到期产生SIGPROF信号。

这些信号的默认处置(disposition)都是终止进程。除非真的期望如此,要不然都要自己为这些信号创建处理器函数。

new_value 是指定定时间到期时间以及是否为周期性定时器。
old_value 则返回之前的定时器设置。 

如果第二次调用setitimer() 将new_value.it_value 中的2个字段都设置为0, 那么会屏蔽任何已有的定时器。

struct itimerval itv;
//延时1.1s后生成SIGALRM
itv.it_value.tv_sec = 1;
itv.it_value.tv_usec = 100000;
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 0;

setitimer(ITIMER_REAL, &itv, NULL);

//延时1.2s后生成SIGALRM,接下来每隔2s生成SIGALRM
itv.it_value.tv_sec = 1;
itv.it_value.tv_usec = 200000;
itv.it_interval.tv_sec = 2;
itv.it_interval.tv_usec = 0;

setitimer(ITIMER_REAL, &itv, NULL);

可以在任意时刻调用 getitimer() 来了解定时器当前的状态、距离下次到期的剩余时间。

#include <sys/time.h>

int getitimer(int which, struct itimerval *curr_value);

//Returns 0 on success, or –1 on error

curr_value 的信息跟setitimer() 借由参数old_value 所返回信息一样。

setitimer() 和 alarm() 创建的定时器可以跨越exec() 调用得以保存, 但由fork() 创建的子进程并不继承该定时器。

更为简单的定时器:alarm()

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

seconds 为定时器到期的秒数。到期会发送 SIGALARM 信号。重复调用会覆盖之前的设置。alarm(0) 可屏蔽现有定时器。

alarm() 返回值是前一设置到期的剩余秒数,如未设置返回0.

在linux 中,alarm() 和 setitimer()针对同一进程共享同一定时器,所以会相互覆盖。

例子

#include <signal.h>
#include <sys/types.h>  /* Type definitions used by many programs */
#include <stdio.h>      /* Standard I/O functions */
#include <stdlib.h>     /* Prototypes of commonly used library functions,
                           plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h>     /* Prototypes for many system calls */
#include <errno.h>      /* Declares errno and defines error constants */
#include <string.h>     /* Commonly used string-handling functions */

#define BUF_SIZE 200

static void handler(int sig){
    printf("Caught signal\n");          /* UNSAFE (see Section 21.1.2) */
}

int
main(int argc, char *argv[])
{
    struct sigaction sa;
    char buf[BUF_SIZE];
    ssize_t numRead;
    int savedErrno;

    if (argc > 1 && strcmp(argv[1], "--help") == 0){
        printf("%s [num-secs [restart-flag]]\n", argv[0]);
        return 1;
    }

    //如果指定,则标记系统调用自动重启
    sa.sa_flags = (argc > 2) ? SA_RESTART : 0;
    
    //改变SIGALRM信号的处置
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = handler;
    if (sigaction(SIGALRM, &sa, NULL) == -1)
        perror("sigaction");
    
    //设置定时器
    alarm((argc > 1) ? atoi(argv[1]) : 10);

    //阻塞并从标准输入读取
    numRead = read(STDIN_FILENO, buf, BUF_SIZE);

    savedErrno = errno;    /* 万一 alarm() 改变了它 */
    alarm(0);              /* 确保关闭了定时器 */
    errno = savedErrno;    // 还原错误

    /* Determine result of read() */

    if (numRead == -1) {
        if (errno == EINTR)
            printf("Read timed out\n");
        else
            perror("read");
    } else {
        printf("Successful read (%ld bytes): %.*s",
                (long) numRead, (int) numRead, buf);
    }

    return 0;
}
#3秒后read系统调用会被信号打断
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 3
Caught signal
Read timed out

#加入SA_RESTART标记,那么系统调用被信号打断后
#会自动恢复,也就是read() 会继续阻塞。
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 3 1
Caught signal
111
Successful read (4 bytes): 111


#在3秒内输入 111
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 3
111
Successful read (4 bytes): 111


暂停运行(休眠)

将前述定时器函数与sigsuspend() 相结合固然可以达到这一目的,但是使用休眠函数更为简单。

低分辨率休眠函数:sleep()

#include <unistd.h>

unsigned int sleep(unsigned int seconds);

如果休眠正常结束,返回0.如果因信号而中断休眠,sleep() 将返回剩余的秒数。

linux 实现sleep() 其实是对nanosleep() 的调用,而一些老系统可能会用alarm() 来实现sleep(),所以为了可移植,应该避免sleep()、alarm()、setitimer()混用。

高分辨率休眠:nanosleep()

#define _POSIX_C_SOURCE 199309
#include <time.h>

struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* 纳秒 0-999999999*/
};

int nanosleep(const struct timespec *request, struct timespec *remain);

//成功返回0, 被信号打断返回-1

如果参数remain 不为NULL,则该指针指向的缓冲区将返回剩余的休眠时间。

例子

#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>  /* Type definitions used by many programs */
#include <stdio.h>      /* Standard I/O functions */
#include <stdlib.h>     /* Prototypes of commonly used library functions,
                           plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h>     /* Prototypes for many system calls */
#include <errno.h>      /* Declares errno and defines error constants */
#include <string.h>     /* Commonly used string-handling functions */

static void sigintHandler(int sig){
    return;    //不做处理,只为了打断休眠
}

int
main(int argc, char *argv[]){
    struct timeval start, finish;
    struct timespec request, remain;
    struct sigaction sa;
    int s;

    if (argc != 3 || strcmp(argv[1], "--help") == 0){
        printf("%s secs nanosecs\n", argv[0]);
        return 1;
    }

    request.tv_sec = atoi(argv[1]);
    request.tv_nsec = atoi(argv[2]);

    //允许SIGINT 来打断睡眠
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = sigintHandler;
    if (sigaction(SIGINT, &sa, NULL) == -1)
        perror("sigaction");

    if (gettimeofday(&start, NULL) == -1)
        perror("gettimeofday");

    for (;;) {
        s = nanosleep(&request, &remain);
        if (s == -1 && errno != EINTR)
            perror("nanosleep");

        if (gettimeofday(&finish, NULL) == -1)
            perror("gettimeofday");
        printf("Slept for: %9.6f secs\n", finish.tv_sec - start.tv_sec +
                        (finish.tv_usec - start.tv_usec) / 1000000.0);

        if (s == 0)
            break;                      /* nanosleep() completed */

        printf("Remaining: %2ld.%09ld\n", (long) remain.tv_sec, remain.tv_nsec);
        request = remain; //如果是被信号打断,则继续休眠剩余的时间
    }

    printf("Sleep complete\n");
    return 0;
}
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 10 1000000

^CSlept for:  2.159698 secs
Remaining:  7.841367684

^CSlept for:  4.081253 secs
Remaining:  5.919940545

^CSlept for:  5.983243 secs
Remaining:  4.018029111

^CSlept for:  8.151804 secs
Remaining:  1.849547349
Slept for: 10.001454 secs
Sleep complete


POSIX间隔定时器

未来用来代替上面的这些定时器。

创建定时器:timer_create()

#define _POSIX_C_SOURCE 199309
#include <signal.h>
#include <time.h>

int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid);

//Returns 0 on success, or –1 on error

clockid 可以用下表的任意值,函数返回时会在timerid 出放置定时器句柄供后续使用。evp 可决定定时器到期对应用程序的通知方式。

 时钟id描述 
 CLOCK_REALTIME 可设定的系统级实时时钟
 CLOCK_MONOTONIC 不可设定的恒定态时钟
 CLOCK_PROCESS_CPUTIME_ID 每进程CPU时间时钟
 CLOCK_THREAD_CPUTIME_ID 每线程CPU时间的时钟
union sigval {
    int sival_int; /* Integer value for accompanying data */
    void *sival_ptr; /* Pointer value for accompanying data */
};

struct sigevent {
    int sigev_notify; /* Notification method */
    int sigev_signo; /* Timer expiration signal */
    union sigval sigev_value; /* Value accompanying signal or
passed to thread function */

    union {
    pid_t _tid; /* ID of thread to be signaled /
        struct {
            void (*_function) (union sigval);
/* Thread notification function */
            void *_attribute; /* Really 'pthread_attr_t *' */
        } _sigev_thread;
    } _sigev_un;
};
#define sigev_notify_function _sigev_un._sigev_thread._function
#define sigev_notify_attributes _sigev_un._sigev_thread._attribute
#define sigev_notify_thread_id _sigev_un._tid

sigevent 结构中 sigev_notify 字段的值分别为:

SIGEV_NONE 不提供定时器到期通知。进程可以使用timer_gettime() 来监控定时器。
SIGEV_SIGNAL 定时器到期时,生成 sigev_signo 中的信号。如果为实时信号,sigev_value 字段则指定了信号的额外数据。
SIGEV_THREAD 调用sigev_notify_function 作为新线程的启动函数。
SIGEV_THREAD_ID 发送 sigev_signo 信号给 sigev_notify_thread_id 所标识的线程。

配备和解除定时器:timer_settime()

一旦创建了定时器,就可以用 timer_settime() 对其进程启动或停止

#define _POSIX_C_SOURCE 199309
#include <time.h>

struct itimerspec {
    struct timespec it_interval; /* Interval for periodic timer */
    struct timespec it_value; /* First expiration */
};

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

int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
struct itimerspec *old_value);

//Returns 0 on success, or –1 on error

timerid 由timer_create()调用返回的句柄。old_value 返回之前的设置。若将flags置为0,则会将value.it_value视为始于timer_settime()调用时间点的相对值。设为TIMER_ABSTIME 代表绝对值。

获取定时器的当前值:timer_gettime()

#define _POSIX_C_SOURCE 199309
#include <time.h>

int timer_gettime(timer_t timerid, struct itimerspec *curr_value);

//Returns 0 on success, or –1 on error

删除定时器:timer_delete()

#define _POSIX_C_SOURCE 199309
#include <time.h>

int timer_delete(timer_t timerid);

//Returns 0 on success, or –1 on error

定时器溢出

所谓的定时器溢出就是,到期多次后才接收到信号。比如到期7次才接收到信号,那么溢出值为6。一旦选择了通过信号来接收定时器到期通知,则不对信号进行排队,即便是实时信号。获取定时器溢出值由2中方法:

1.调用timer_getoverrun()。
2.使用随信号一同返回的结构siginfo_t中的si_overrun字段值。(利用这个可以减少一次系统调用的开销)。

#define _POSIX_C_SOURCE 199309
#include <time.h>

int timer_getoverrun(timer_t timerid);

//成功返回溢出次数, 失败-1

返回0代表没溢出。


例子-信号来通知

#define _POSIX_C_SOURCE 199309
#include <signal.h>
#include <time.h>
#include <string.h>     /* Commonly used string-handling functions */
#include <sys/types.h>  /* Type definitions used by many programs */
#include <stdio.h>      /* Standard I/O functions */
#include <stdlib.h>     /* Prototypes of commonly used library functions,
                           plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h>     /* Prototypes for many system calls */
#include <errno.h>      /* Declares errno and defines error constants */


#define TIMER_SIG SIGRTMAX              /* Our timer notification signal */
#define BUF_SIZE 1000

char *currTime(const char *fmt);
void itimerspecFromStr(char *str, struct itimerspec *tsp);

void
itimerspecFromStr(char *str, struct itimerspec *tsp){
    char *dupstr ,*cptr, *sptr;

    dupstr = strdup(str);

    cptr = strchr(dupstr, ':');
    if (cptr != NULL)
        *cptr = '\0';

    sptr = strchr(dupstr, '/');
    if (sptr != NULL)
        *sptr = '\0';

    tsp->it_value.tv_sec = atoi(dupstr);
    tsp->it_value.tv_nsec = (sptr != NULL) ? atoi(sptr + 1) : 0;

    if (cptr == NULL) {
        tsp->it_interval.tv_sec = 0;
        tsp->it_interval.tv_nsec = 0;
    } else {
        sptr = strchr(cptr + 1, '/');
        if (sptr != NULL)
            *sptr = '\0';
        tsp->it_interval.tv_sec = atoi(cptr + 1);
        tsp->it_interval.tv_nsec = (sptr != NULL) ? atoi(sptr + 1) : 0;
    }
    free(dupstr);
}

char * currTime(const char *format){
    static char buf[BUF_SIZE];  /* Nonreentrant */
    time_t t;
    size_t s;
    struct tm *tm;

    t = time(NULL);
    tm = localtime(&t);
    if (tm == NULL)
        return NULL;

    s = strftime(buf, BUF_SIZE, (format != NULL) ? format : "%c", tm);

    return (s == 0) ? NULL : buf;
}

static void
handler(int sig, siginfo_t *si, void *uc)
{
    timer_t *tidptr;

    tidptr = si->si_value.sival_ptr;

    /* UNSAFE: This handler uses non-async-signal-safe functions
       (printf(); see Section 21.1.2) */

    printf("[%s] Got signal %d\n", currTime("%T"), sig);
    printf("    *sival_ptr         = %ld\n", (long) *tidptr);
    
    //获取定时器溢出值
    printf("    timer_getoverrun() = %d\n", timer_getoverrun(*tidptr));
}

int
main(int argc, char *argv[]){
    struct itimerspec ts;
    struct sigaction  sa;
    struct sigevent   sev;
    timer_t *tidlist;
    int j;

    //a.out 10/111:5/222
    //代表延时10秒111纳秒后到期,之后每5秒222纳秒到期一次
    if (argc < 2){
        printf("%s secs[/nsecs][:int-secs[/int-nsecs]]...\n", argv[0]);
        return 1;
    }
        

    tidlist = calloc(argc - 1, sizeof(timer_t));
    if (tidlist == NULL)
        perror("malloc");

    /* Establish handler for notification signal */

    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = handler;
    sigemptyset(&sa.sa_mask);
    if (sigaction(TIMER_SIG, &sa, NULL) == -1)
        perror("sigaction");

    /* Create and start one timer for each command-line argument */

    sev.sigev_notify = SIGEV_SIGNAL;   //通过信号通知
    sev.sigev_signo = TIMER_SIG;     //用 TIMER_SIG这个信号

    for (j = 0; j < argc - 1; j++) {
        
        //通过命令行参数生成itimerspec结构体来设置定时器
        itimerspecFromStr(argv[j + 1], &ts);

        //允许信号处理器函数获取定时器id
        sev.sigev_value.sival_ptr = &tidlist[j];

        //创建
        if (timer_create(CLOCK_REALTIME, &sev, &tidlist[j]) == -1)
            perror("timer_create");
        printf("Timer ID: %ld (%s)\n", (long) tidlist[j], argv[j + 1]);

        //开始计时
        if (timer_settime(tidlist[j], 0, &ts, NULL) == -1)
            perror("timer_settime");
    }

    //等待信号
    for (;;)
        pause();
}
[root@izj6cfw9yi1iqoik31tqbgz c]# gcc main.c -lrt

#2秒后到期,后面每5秒到期一次
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 2:5
Timer ID: 21635120 (2:5)
#2s后
[12:02:46] Got signal 64
    *sival_ptr         = 21635120
    timer_getoverrun() = 0
#又过了5s
[12:02:51] Got signal 64
    *sival_ptr         = 21635120
    timer_getoverrun() = 0
#这里按下ctrl + z暂停
^Z
[1]+  Stopped                 ./a.out 2:5

#过个10几秒,恢复
[root@izj6cfw9yi1iqoik31tqbgz c]# fg
./a.out 2:5
[12:03:10] Got signal 64
    *sival_ptr         = 21635120
    timer_getoverrun() = 2         #可以看出溢出了2次


通过线程来通知

SIGEV_THREAD标志允许程序从一个独立的线程中调用函数来获取定时器到期通知。线程将在后面文章讲解。

#define _POSIX_C_SOURCE 199309
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <string.h>     /* Commonly used string-handling functions */
#include <sys/types.h>  /* Type definitions used by many programs */
#include <stdio.h>      /* Standard I/O functions */
#include <stdlib.h>     /* Prototypes of commonly used library functions,
                           plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h>     /* Prototypes for many system calls */
#include <errno.h>      /* Declares errno and defines error constants */

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

static int expireCnt = 0;           /* Number of expirations of all timers */

#define TIMER_SIG SIGRTMAX              /* Our timer notification signal */
#define BUF_SIZE 1000

char *currTime(const char *fmt);
void itimerspecFromStr(char *str, struct itimerspec *tsp);

void
itimerspecFromStr(char *str, struct itimerspec *tsp){
    char *dupstr ,*cptr, *sptr;

    dupstr = strdup(str);

    cptr = strchr(dupstr, ':');
    if (cptr != NULL)
        *cptr = '\0';

    sptr = strchr(dupstr, '/');
    if (sptr != NULL)
        *sptr = '\0';

    tsp->it_value.tv_sec = atoi(dupstr);
    tsp->it_value.tv_nsec = (sptr != NULL) ? atoi(sptr + 1) : 0;

    if (cptr == NULL) {
        tsp->it_interval.tv_sec = 0;
        tsp->it_interval.tv_nsec = 0;
    } else {
        sptr = strchr(cptr + 1, '/');
        if (sptr != NULL)
            *sptr = '\0';
        tsp->it_interval.tv_sec = atoi(cptr + 1);
        tsp->it_interval.tv_nsec = (sptr != NULL) ? atoi(sptr + 1) : 0;
    }
    free(dupstr);
}

char * currTime(const char *format){
    static char buf[BUF_SIZE];  /* Nonreentrant */
    time_t t;
    size_t s;
    struct tm *tm;

    t = time(NULL);
    tm = localtime(&t);
    if (tm == NULL)
        return NULL;

    s = strftime(buf, BUF_SIZE, (format != NULL) ? format : "%c", tm);

    return (s == 0) ? NULL : buf;
}


static void                         /* Thread notification function */
threadFunc(union sigval sv)
{
    timer_t *tidptr;
    int s;

    tidptr = sv.sival_ptr;

    printf("[%s] Thread notify\n", currTime("%T"));
    printf("    timer ID=%ld\n", (long) *tidptr);
    printf("    timer_getoverrun()=%d\n", timer_getoverrun(*tidptr));

    /* Increment counter variable shared with main thread and signal
       condition variable to notify main thread of the change. */

    s = pthread_mutex_lock(&mtx);
    if (s != 0)
        perror("pthread_mutex_lock");

    expireCnt += 1 + timer_getoverrun(*tidptr);

    s = pthread_mutex_unlock(&mtx);
    if (s != 0)
        perror("pthread_mutex_unlock");

    s = pthread_cond_signal(&cond);
    if (s != 0)
        perror("pthread_cond_signal");
}

int
main(int argc, char *argv[])
{
    struct sigevent sev;
    struct itimerspec ts;
    timer_t *tidlist;
    int s, j;

    if (argc < 2){
        printf("%s secs[/nsecs][:int-secs[/int-nsecs]]...\n", argv[0]);
        return 1;
    }

    tidlist = calloc(argc - 1, sizeof(timer_t));
    if (tidlist == NULL)
        perror("malloc");

    sev.sigev_notify = SIGEV_THREAD;    //线程
    sev.sigev_notify_function = threadFunc;     //线程开始函数
    sev.sigev_notify_attributes = NULL;
            /* 这里可以指向 pthread_attr_t 结构体 */

    /* Create and start one timer for each command-line argument */

    for (j = 0; j < argc - 1; j++) {
        itimerspecFromStr(argv[j + 1], &ts);

        //传递给threadFunc()
        sev.sigev_value.sival_ptr = &tidlist[j];

        if (timer_create(CLOCK_REALTIME, &sev, &tidlist[j]) == -1)
            perror("timer_create");
        printf("Timer ID: %ld (%s)\n", (long) tidlist[j], argv[j + 1]);

        if (timer_settime(tidlist[j], 0, &ts, NULL) == -1)
            perror("timer_settime");
    }

    /* The main thread waits on a condition variable that is signaled
       on each invocation of the thread notification function. We
       print a message so that the user can see that this occurred. */

    s = pthread_mutex_lock(&mtx);
    if (s != 0)
        perror("pthread_mutex_lock");

    for (;;) {
        s = pthread_cond_wait(&cond, &mtx);
        if (s != 0)
            perror("pthread_cond_wait");
        printf("main(): expireCnt = %d\n", expireCnt);
    }
}

创建和配备所有定时器后,主程序进入循环并等待定时器到期。每次循环,程序都会调用 pthread_cond_wait(),等待处理定时器通知的线程就条件变量(cond)发出信号。

[root@izj6cfw9yi1iqoik31tqbgz c]# gcc main.c -lpthread -lrt

[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 2:5 10:10
Timer ID: 9863824 (2:5)
Timer ID: 9863920 (10:10)

[15:33:26] Thread notify
    timer ID=9863824
    timer_getoverrun()=0
main(): expireCnt = 1

[15:33:31] Thread notify
    timer ID=9863824
    timer_getoverrun()=0
main(): expireCnt = 2

^Z
[1]+  Stopped                 ./a.out 2:5 10:10
[root@izj6cfw9yi1iqoik31tqbgz c]# fg
./a.out 2:5 10:10
[15:33:55] Thread notify
    timer ID=9863920
    timer_getoverrun()=2
main(): expireCnt = 5
[15:33:55] Thread notify
    timer ID=9863824
    timer_getoverrun()=3
main(): expireCnt = 9
[15:33:56] Thread notify
    timer ID=9863824
    timer_getoverrun()=0
main(): expireCnt = 10
^C


利用文件描述符进行通知的定时器:timerfd API

linux特有的timerfd API可以从文件描述符中读取定时器到期通知,可以使用select()、poll()、epoll()(后面文章讲解)来进行统一监控,非常使用。

创建新的定时器对象。

#include <sys/timerfd.h>

int timerfd_create(int clockid, int flags);

//返回文件描述符, 失败-1

flags可以设置为0,也可以设置一下2种:

TFD_CLOEXEC 类似于open() 的标志O_CLOEXEC。
TFD_NONBLOCK 非阻塞读。

启动或停止定时器。

#include <sys/timerfd.h>

int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,
struct itimerspec *old_value);

//Returns 0 on success, or –1 on error

fd是创建的文件描述符,flags可以为0-相对时间,TFD_TIMER_ABSTIME-绝对时间。其他2个参数跟上面讲的定时器作用类似。

返回文件描述符定时器的间隔和剩余时间。

#include <sys/timerfd.h>

int timerfd_gettime(int fd, struct itimerspec *curr_value);

//Returns 0 on success, or –1 on error

timerfd 与 fork()及 exec() 之间的交互

调用fork() 期间,子进程会继承文件描述符,可以读取到定时器到期信息。

timerfd_create() 创建的文件描述符能跨越exec() 得以保存。所以在exec() 之后会继续生成到期通知。

从timerfd 文件描述符读取

可以用read() 来读取到期信息。传给read() 缓冲区必须足以容纳无符号8字节整型(uint64_t)。

例子

#include <sys/timerfd.h>
#include <time.h>
#include <stdint.h>                     /* Definition of uint64_t */
#include <string.h>     /* Commonly used string-handling functions */
#include <sys/types.h>  /* Type definitions used by many programs */
#include <stdio.h>      /* Standard I/O functions */
#include <stdlib.h>     /* Prototypes of commonly used library functions,
                           plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h>     /* Prototypes for many system calls */
#include <errno.h>      /* Declares errno and defines error constants */

#define BUF_SIZE 1000

char *currTime(const char *fmt);
void itimerspecFromStr(char *str, struct itimerspec *tsp);

void
itimerspecFromStr(char *str, struct itimerspec *tsp){
    char *dupstr ,*cptr, *sptr;

    dupstr = strdup(str);

    cptr = strchr(dupstr, ':');
    if (cptr != NULL)
        *cptr = '\0';

    sptr = strchr(dupstr, '/');
    if (sptr != NULL)
        *sptr = '\0';

    tsp->it_value.tv_sec = atoi(dupstr);
    tsp->it_value.tv_nsec = (sptr != NULL) ? atoi(sptr + 1) : 0;

    if (cptr == NULL) {
        tsp->it_interval.tv_sec = 0;
        tsp->it_interval.tv_nsec = 0;
    } else {
        sptr = strchr(cptr + 1, '/');
        if (sptr != NULL)
            *sptr = '\0';
        tsp->it_interval.tv_sec = atoi(cptr + 1);
        tsp->it_interval.tv_nsec = (sptr != NULL) ? atoi(sptr + 1) : 0;
    }
    free(dupstr);
}

char * currTime(const char *format){
    static char buf[BUF_SIZE];  /* Nonreentrant */
    time_t t;
    size_t s;
    struct tm *tm;

    t = time(NULL);
    tm = localtime(&t);
    if (tm == NULL)
        return NULL;

    s = strftime(buf, BUF_SIZE, (format != NULL) ? format : "%c", tm);

    return (s == 0) ? NULL : buf;
}


int
main(int argc, char *argv[]){
    struct itimerspec ts;
    struct timespec start, now;
    int maxExp, fd, secs, nanosecs;
    uint64_t numExp, totalExp;
    ssize_t s;

    if (argc < 2 || strcmp(argv[1], "--help") == 0){
        printf("%s secs[/nsecs][:int-secs[/int-nsecs]] [max-exp]\n", argv[0]);
        return 1;
    }
        

    itimerspecFromStr(argv[1], &ts);
    maxExp = (argc > 2) ? atoi(argv[2]) : 1;

    fd = timerfd_create(CLOCK_REALTIME, 0);
    if (fd == -1)
        perror("timerfd_create");

    if (timerfd_settime(fd, 0, &ts, NULL) == -1)
        perror("timerfd_settime");

    if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
        perror("clock_gettime");

    for (totalExp = 0; totalExp < maxExp;) {
        s = read(fd, &numExp, sizeof(uint64_t));
        if (s != sizeof(uint64_t))
            perror("read");

        totalExp += numExp;

        if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
            perror("clock_gettime");

        secs = now.tv_sec - start.tv_sec;
        nanosecs = now.tv_nsec - start.tv_nsec;
        if (nanosecs < 0) {
            secs--;
            nanosecs += 1000000000;
        }

        printf("%d.%03d: expirations read: %llu; total=%llu\n",
                secs, (nanosecs + 500000) / 1000000,
                (unsigned long long) numExp, (unsigned long long) totalExp);
    }

    return 0;
}
#2s后到期,到期后每5s到期一次,总共允许到期5次
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 2:5 5
2.000: expirations read: 1; total=1
^Z
[1]+  Stopped                 ./a.out 2:5 5
[root@izj6cfw9yi1iqoik31tqbgz c]# fg
./a.out 2:5 5
13.928: expirations read: 2; total=3

17.000: expirations read: 1; total=4
22.000: expirations read: 1; total=5


总结

下一篇开始讲解进程的创建,如果有疑问可以给我留言。


备注
1.编译器版本gcc4.8,运行环境centos7 64位
2.原文地址http://www.freecls.com/a/2712/50

©著作权归作者所有
收藏
推荐阅读
  • linux c信号(3)-高级特性

    core dump文件coredump 文件 内含进程终止时内存映像的一个文件,把它加载到调试器中,即可查明信号到达时程序代码和数据的状态。 引发core dump文件除了程序中调用 abort()外...

  • err
    linux c信号(2)-处理函数

    一般而言信号处理函数设计的越简单越好。其中的一个重要原因在于,这将降低引发竞争条件的风险。下面是信号处理函数的两种常见设计。1.信号处理器函数设置全局性标记变量并退出。...

  • err
    linux c信号(1)-基本概念

    信号是事件发生时对进程的通知机制。有时也称之为软件中断。信号与硬件中断的相似之处在于打断了程序执行的正常流程,大多数情况下,无法预测信号到达的精确时间。一个(具有合适权限...

  • err
    linux c监控文件事件

    某些应用程序需要对文件或目录进行监控,来侦测其发生特定事件。例如当文件加入或移出一目录,图形化文件管理器应能判定此目录是否在其当前显示之列,而守护进程可能也要监控自己的配...

  • err
    linux c目录与链接

    相关系统调用:link(),unlink(),rename(),symlink(),readlink(),mkdir(),rmdir(),remove(),opendir...

  • nginx模块 ngx_http_headers_module

    ngx_http_headers_module 模块是用来增加 Expires 和 Cache-control,或者是任意的响应头。Syntax: add_header name value [alw...

  • nginx模块 ngx_http_gunzip_module、ngx_http_gzip_module、ngx_http_gzip_static_module

    ngx_http_gunzip_module 模块将文件解压缩后并在响应头加上 "Content-Encoding: gzip" 返回给客户端。为了解决客户端不支持gzip压缩。编译的时候带上 --w...

  • nginx模块 ngx_http_flv_module、ngx_http_mp4_module

    ngx_http_flv_module模块提供了对 flv 视频的伪流支持。编译的时候带上 --with-http_flv_module。它会根据指定的 start 参数来指定跳过多少字节,并在返回数...

  • nginx模块 ngx_http_fastcgi_module

    ngx_http_fastcgi_module 模块使得nginx可以与 fastcgi 服务器通信。比如目前要使得 nginx 支持 php 就得使用 fastcgi技术,在服务器上装上 nginx...

  • nginx模块 ngx_http_autoindex_module

    ngx_http_autoindex_module 模块可以将uri以 / 结尾时,列出里面的文件和目录。Syntax: autoindex on | off; Default: autoindex ...

简介
天降大任于斯人也,必先苦其心志。