linux-定时器-基于C语言

ITIMER_REAL 实时计数;ITIMER_VIRTUAL 统计进程在用户模式(进程本身执行) 执行的时间;ITIMER_PROF 统计进程在用户模式(进程本身执行)和核心模式(系统代表 进程执行)下的执行时间,与 ITIMER_VIRTUAL 比 较,这个计时器记录的时间多了该进程 核心模式执行过程中消耗的时间。

一个简单的定时器例子

设置 一个 ITIMER_REAL 类型的定时器,它每过一秒都会发出一个信号,等到定时到达的时候 (即定时器时间值减到 0),程序将统计已经经过的时间。

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

/*声明信号处理函数, 这个函数是在进程收到信号的时候调用就可以了*/
static void sig_handler(int signo);

long lastsec, countsec; /*这两个变量分别用来保存上一秒的时间和总共花去的时间*/

int main(void) {
    struct itimerval v; /*定时器结构体*/
    long nowsec, nowusec; /*当前时间的秒数和微秒数*/


/*注册 SIGUSR1 和 SIGALARM 信号的处理函数为 sig_handler*/ 
    if (signal(SIGUSR1, sig_handler) == SIG_ERR) {
        printf("Unable to create handler for SIGUSR1\n");
        exit(0);
    }
    if (signal(SIGALRM, sig_handler) == SIG_ERR) {
        printf("Unable to create handler for SIGALRM\n");
        exit(0);
    }
/*初始化定时器初值和当前值*/
    v.it_interval.tv_sec = 9; // 周期时间
    v.it_interval.tv_usec = 999999;
    v.it_value.tv_sec = 9;
    v.it_value.tv_usec = 999999;

/*调用 setitimer 设置定时器,并将其挂到定时器链表上,这个函数的三个参数的含义分 别是设置 ITIMER_REAL 类型的定时器,
要设置的值存放在变量 v 中,该定时器设置前 的值在设置后保存的地址,如果是这个参数为 NULL,那么就放弃保存设置前的值*/
    setitimer(ITIMER_REAL, &v, NULL);

    lastsec = v.it_value.tv_sec;
    countsec = 0;

/*该循环首先调用 getitimer 读取定时器当前值,再与原来的秒数比较,当发现已经过了 一秒后产生一个 SIGUSR1 信号,
程序就会进入上面注册过的信号处理函数*/ 
    while (1) {
	getitimer(ITIMER_REAL, &v);
	nowsec = v.it_value.tv_sec;
	nowusec = v.it_value.tv_usec;
	if (nowsec == lastsec - 1) {
/*每过一秒,产生一个 SIGUSR1 信号*/
	      raise(SIGUSR1);
	      lastsec = nowsec;
	      countsec++; /*记录总的秒数*/
	}
    }
}

/*信号处理函数*/
static void sig_handler(int signo) {
    switch (signo) {
/*接收到的信号是 SIGUSR1, 打印 One second passed*/
        case SIGUSR1:
            printf("One second passed\n");
            break;
/*定时器定时到达*/
        case SIGALRM: {
            printf("Timer has been zero,elapsed %d seconds\n", countsec);
            lastsec = countsec;
            countsec = 0;
            break;
        }
    }
}

运行结果

统计关于进程的时间

ITIMER_REAL 实时计数, ITIMER_VIRTUAL 统计进程在用户模式(进程本身执行)执行的时间;ITIMER_PROF 统 计 进程在用户模式(进程 本身执 行)和 核心 模式( 系统 代表进程执行)下的执 行时间 ,与 ITIMER_VIRTUAL 比较,这个计时器记录的时间多了该进程核心模式执行过程中消耗的时 间。通过在一个进程中设定这三个定时器,我们就可以了解到一个进程在用户模式、核心模 式以及总的运行时间。下面的这个程序除了定义了三个定时器和信号处理过程以外,其它的地方和上面的程序完全相同。

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

static void sig_handler(int signo);

long countsec, lastsec, nowsec;

int main(void) {
    struct itimerval v;

/*注册信号处理函数*/
    if (signal(SIGUSR1, sig_handler) == SIG_ERR) {
        printf("Unable to create handler for SIGUSR1\n");
        exit(0);
    }
    if (signal(SIGALRM, sig_handler) == SIG_ERR) {
        printf("Unable to create handler for SIGALRM\n");
        exit(0);
    }

    v.it_interval.tv_sec = 10;
    v.it_interval.tv_usec = 0;
    v.it_value.tv_sec = 10;
    v.it_value.tv_usec = 0;

/*调用 setitimer 设置定时器,并将其挂到定时器链表上,这个函数的三个参数的含义分别是设置何种类型的定时器;
要设置的值存放在变量 v 中;该定时器设置前的值在设置 后保存的地址,如果是这个参数为 NULL,那么就放弃保存设置前的值*/
    setitimer(ITIMER_REAL, &v, NULL);
    setitimer(ITIMER_VIRTUAL, &v, NULL);
    setitimer(ITIMER_PROF, &v, NULL);
    countsec = 0;
    lastsec = v.it_value.tv_sec;
    while (1) {
        getitimer(ITIMER_REAL, &v);
        nowsec = v.it_value.tv_sec;
        if (nowsec == lastsec - 1) {
            if (nowsec < 9) {
/*同上面一样,我们每隔一秒发送一个 SIGUSR1 信号*/
                raise(SIGUSR1);
                countsec++;
            }
            lastsec = nowsec;
        }
    }
}

static void sig_handler(int signo) {
    struct itimerval u, v;
    long t1, t2;

    switch (signo) {
        case SIGUSR1:
/*显示三个定时器的当前值*/
            getitimer(ITIMER_REAL, &v);
            printf("real time=%.ld secs %ld usecs\n", 9 - v.it_value.tv_sec, 999999 - v.it_value.tv_usec);
            getitimer(ITIMER_PROF, &u);
            printf("cpu time=%ld secs %ld usecs\n", 9 - u.it_value.tv_sec, 999999 - u.it_value.tv_usec);
            getitimer(ITIMER_VIRTUAL, &v);
            printf("user time=%ld secs %ld usecs\n", 9 - v.it_value.tv_sec, 999999 - v.it_value.tv_usec);

/*当前 prof timer 已经走过的微秒数*/
            t1 = (9 - u.it_value.tv_sec) * 1000000 + (1000000 - u.it_value.tv_usec);
/*当前 virtual timer 已经走过的微秒数*/
            t2 = (9 - v.it_value.tv_sec) * 1000000 + (1000000 - v.it_value.tv_usec);
/*计算并显示 kernel time*/
            printf("kernel time=%ld secs %ld usecs\n\n", (t1 - t2) / 1000000, (t1 - t2) % 1000000);
            break;
        case SIGALRM:
            printf("Real Timer has been zero,elapsed %d seconds\n", countsec);
            exit(0);
            break;
    }
}

运行结果

更进一步的进程时间统计

上面的程序只在很短的时间内统计了进程在各种状态的执行时间。但是进程并没有真正 的负载作业,和现实中的进程差距比较大。下面我们要继续修改上面的程序,让这个进程做 点“事情”,然后我们再来看看在和实际情况比较相近的状态下定时器统计到的进程在各个 状态下的时间。在这个程序里面我们将创建两个子进程,加上父进程总共三个进程,这三个 进程分别调用 fibonacci()计算 fibonacci 数。在计算之前我们初始化定时器,完成之后,我们 将读取定时器,然后来统计进程相关的各种时间。

#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>

long unsigned int fibonacci(unsigned int n); /*计算 fibonacci 数的函数*/
static void par_sig(int signo); /*父进程的信号处理函数*/
static void c1_sig(int signo); /*子进程 1 的信号处理函数*/
static void c2_sig(int signo); /*子进程 2 的信号处理函数*/

/*用于分别记录父,子 1,子 2 进程 real time 过的总秒数*/
static long p_realt_secs = 0, c1_realt_secs = 0, c2_realt_secs = 0;
/*用于分别记录父,子 1,子 2 进程 virtual time 过的总秒数*/
static long p_virtt_secs = 0, c1_virtt_secs = 0, c2_virtt_secs = 0;
/*用于分别记录父,子 1,子 2 进程 proft time 过的总秒数*/
static long p_proft_secs = 0, c1_proft_secs = 0, c2_proft_secs = 0;
/*用于分别取出父,子 1,子 2 进程的 real timer 的值*/
static struct itimerval p_realt, c1_realt, c2_realt;
/*用于分别取出父,子 1,子 2 进程的 virtual timer 的值*/
static struct itimerval p_virtt, c1_virtt, c2_virtt;
/*用于分别取出父,子 1,子 2 进程的 proft timer 的值*/
static struct itimerval p_proft, c1_proft, c2_proft;

int main() {
    long unsigned fib = 0;
    int pid1, pid2;
    unsigned int fibarg = 39;
    int status;
    struct itimerval v;
    long moresec, moremsec, t1, t2;

    pid1 = fork();
    if (pid1 == 0) {
/*设置子进程 1 的信号处理函数和定时器初值*/
        signal(SIGALRM, c1_sig);
        signal(SIGVTALRM, c1_sig);
        signal(SIGPROF, c1_sig);
        v.it_interval.tv_sec = 10;
        v.it_interval.tv_usec = 0;
        v.it_value.tv_sec = 10;
        v.it_value.tv_usec = 0;
        setitimer(ITIMER_REAL, &v, NULL);
        setitimer(ITIMER_VIRTUAL, &v, NULL);
        setitimer(ITIMER_PROF, &v, NULL);
        fib = fibonacci(fibarg); /*计算 fibonacci 数*/
/*取出子进程 1 的定时器值*/
        getitimer(ITIMER_PROF, &c1_proft);
        getitimer(ITIMER_REAL, &c1_realt);
        getitimer(ITIMER_VIRTUAL, &c1_virtt);
/*通过定时器的当前值和各信号发出的次数计算子进程 1 总共用的 real time,cpu time,user time 和 kernel time。
moresec 和 moremsec 指根据定时器的当前值计算 出的自上次信号发出时过去的 real time,cpu time,user time 和 kernel time。
计算 kernel time 时, moresec 和 moremsec 为 kernel time 的实际秒数+毫秒数*/ 
        moresec = 9 - c1_realt.it_value.tv_sec;
        moremsec = (1000000 - c1_realt.it_value.tv_usec) / 1000;
        printf("Child 1 fib=%ld , real time=%ld sec,%ld msec\n", fib, c1_realt_secs + moresec, moremsec);
        moresec = 9 - c1_proft.it_value.tv_sec;
        moremsec = (1000000 - c1_proft.it_value.tv_usec) / 1000;
        printf("Child 1 fib=%ld , cpu time=%ld sec,%ld msec\n", fib, c1_proft_secs + moresec, moremsec);
        moresec = 9 - c1_virtt.it_value.tv_sec;
        moremsec = (1000000 - c1_virtt.it_value.tv_usec) / 1000;
        printf("Child 1 fib=%ld , user time=%ld sec,%ld msec\n", fib, c1_virtt_secs + moresec, moremsec);
        t1 = (9 - c1_proft.it_value.tv_sec) * 1000 + (1000000 - c1_proft.it_value.tv_usec) / 1000 + c1_proft_secs * 10000;
        t2 = (9 - c1_virtt.it_value.tv_sec) * 1000 + (1000000 - c1_virtt.it_value.tv_usec) / 1000 + c1_virtt_secs * 10000;
        moresec = (t1 - t2) / 1000;
        moremsec = (t1 - t2) % 1000;
        printf("Child 1 fib=%ld , kernel time=%ld sec,%ld msec\n", fib, moresec, moremsec);
        fflush(stdout);
        exit(0);

    } else {

        pid2 = fork();
        if (pid2 == 0) {
/*设置子进程 2 的信号处理函数和定时器初值*/ 
            signal(SIGALRM, c2_sig);
            signal(SIGVTALRM, c2_sig);
            signal(SIGPROF, c2_sig);
            v.it_interval.tv_sec = 10;
            v.it_interval.tv_usec = 0;
            v.it_value.tv_sec = 10;
            v.it_value.tv_usec = 0;
            setitimer(ITIMER_REAL, &v, NULL);
            setitimer(ITIMER_VIRTUAL, &v, NULL);
            setitimer(ITIMER_PROF, &v, NULL);
            fib = fibonacci(fibarg);
/*取出子进程 2 的定时器值*/
            getitimer(ITIMER_PROF, &c2_proft);
            getitimer(ITIMER_REAL, &c2_realt);
            getitimer(ITIMER_VIRTUAL, &c2_virtt);
/*通过定时器的当前值和各信号发出的次数计算子进程 2 总共用的 real time,cpu time,user time 和 kernel time。
moresec 和 moremsec 指根据定时 器的当前值计算出的自上次信号发出时过去的 real time,cpu time,user time 和 kernel time。
计算 kernel time 时, moresec 和 moremsec 为 kernel time 的实际秒数+毫秒数*/
            moresec = 9 - c2_realt.it_value.tv_sec;
            moremsec = (1000000 - c2_realt.it_value.tv_usec) / 1000;
            printf("Child 2 fib=%ld , real time=%ld sec,%ld msec\n", fib, c2_realt_secs + moresec, moremsec);
            moresec = 9 - c2_proft.it_value.tv_sec;
            moremsec = (1000000 - c2_proft.it_value.tv_usec) / 1000;
            printf("Child 2 fib=%ld , cpu time=%ld sec,%ld msec\n", fib, c2_proft_secs + moresec, moremsec);
            moresec = 9 - c2_virtt.it_value.tv_sec;
            moremsec = (1000000 - c2_virtt.it_value.tv_usec) / 1000;
            printf("Child 2 fib=%ld , user time=%ld sec,%ld msec\n", fib, c2_virtt_secs + moresec, moremsec);

            t1 = (9 - c2_proft.it_value.tv_sec) * 1000 + (1000000 - c2_proft.it_value.tv_usec) / 1000 +
                 c2_proft_secs * 10000;


            t2 = (9 - c2_virtt.it_value.tv_sec) * 1000 + (1000000 - c2_virtt.it_value.tv_usec) / 1000 +
                 c2_virtt_secs * 10000;
            moresec = (t1 - t2) / 1000;
            moremsec = (t1 - t2) % 1000;
            printf("Child 2 fib=%ld , kernel time=%ld sec,%ld msec\n", fib, moresec, moremsec);
            fflush(stdout);
            exit(0);

        } else {

/*设置父进程的信号处理函数和定时器初值*/
            signal(SIGALRM, par_sig);
            signal(SIGVTALRM, par_sig);
            signal(SIGPROF, par_sig);
            v.it_interval.tv_sec = 10;
            v.it_interval.tv_usec = 0;
            v.it_value.tv_sec = 10;
            v.it_value.tv_usec = 0;
            setitimer(ITIMER_REAL, &v, NULL);
            setitimer(ITIMER_VIRTUAL, &v, NULL);
            setitimer(ITIMER_PROF, &v, NULL);
            fib = fibonacci(fibarg);
/*取出父进程的定时器值*/
            getitimer(ITIMER_PROF, &p_proft);
            getitimer(ITIMER_REAL, &p_realt);
            getitimer(ITIMER_VIRTUAL, &p_virtt);
/*通过定时器的当前值和各信号发出的次数计算子进程 1 总共用的 real time,cpu time,user time 和 kernel time。
moresec 和 moremsec 指根据 定时器的当前值计算出的自上次信号发出时过去的 real time,cpu time,user time 和 kernel time。
计算 kernel time 时, moresec 和 moremsec 为 kernel time 的实际秒数+毫秒数*/ 
            moresec = 9 - p_realt.it_value.tv_sec;
            moremsec = (1000000 - p_realt.it_value.tv_usec) / 1000;
            printf("Parent fib=%ld , real time=%ld sec,%ld msec\n", fib, p_realt_secs + moresec, moremsec);
            moresec = 9 - p_proft.it_value.tv_sec;
            moremsec = (1000000 - p_proft.it_value.tv_usec) / 1000;
            printf("Parent fib=%ld , cpu time=%ld sec,%ld msec\n", fib, p_proft_secs + moresec, moremsec);
            moresec = 9 - p_virtt.it_value.tv_sec;
            moremsec = (1000000 - p_virtt.it_value.tv_usec) / 1000;
            printf("Parent fib=%ld , user time=%ld sec,%ld msec\n", fib, p_virtt_secs + moresec, moremsec);

            t1 = (9 - p_proft.it_value.tv_sec) * 1000 + (1000000 - p_proft.it_value.tv_usec) / 1000 +
                 p_proft_secs * 10000;

            t2 = (9 - p_virtt.it_value.tv_sec) * 1000 + (1000000 - p_virtt.it_value.tv_usec) / 1000 +
                 p_virtt_secs * 10000;
            moresec = (t1 - t2) / 1000;
            moremsec = (t1 - t2) % 1000;
            printf("Parent fib=%ld , kernel time=%ld sec,%ld msec\n", fib, moresec, moremsec);
            fflush(stdout);
            waitpid(0, &status, 0);
            waitpid(0, &status, 0);
            exit(0);
        }
        printf("this line should never be printed\n");
    }
}

long unsigned fibonacci(unsigned int n) {
    if (n == 0)
        return 0;
    else if (n == 1 || n == 2)
        return 1;
    else
        return (fibonacci(n - 1) + fibonacci(n - 2));
}

/*父进程信号处理函数;每个 timer 过 10 秒减为 0,激活处理函数一次,相应的计数器加 10*/
static void par_sig(int signo) {

    switch (signo) {
        case SIGALRM:
            p_realt_secs += 10;
            break;
        case SIGVTALRM:
            p_virtt_secs += 10;
            break;
        case SIGPROF:
            p_proft_secs += 10;
            break;
    }
}

/*子进程 1 的信号处理函数,功能与父进程的信号处理函数相同*/
static void c1_sig(int signo) {

    switch (signo) {
        case SIGALRM:
            c1_realt_secs += 10;
            break;
        case SIGVTALRM:
            c1_virtt_secs += 10;
            break;
        case SIGPROF:
            c1_proft_secs += 10;
            break;
    }
}

/*子进程 2 的信号处理函数,功能与父进程的信号处理函数相同*/
static void c2_sig(int signo) {

    switch (signo) {
        case SIGALRM:
            c2_realt_secs += 10;
            break;
        case SIGVTALRM:
            c2_virtt_secs += 10;
            break;
        case SIGPROF:
            c2_proft_secs += 10;
            break;
    }
}

运行结果:

posted @ 2020-09-02 20:47  漫漫长夜何时休  阅读(253)  评论(0编辑  收藏  举报