Linux信号与定时器应用实例

本文介绍了一个使用C语言实现的多定时器计时实验,程序注册了三个定时器,分别用于跟踪实际时间、进程使用CPU时间和用户使用CPU时间。通过用户信号控制定时输出各个计时器的时间值,并计算内核所用的时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


转自:http://hi.baidu.com/armlinuxqt/blog/item/7340a9efa0cccb2963d09ff4.html


程序注册三个定时器,分别对实际时间、进程使用CPU时间、用户使用CPU时间进行即时,计时时间为10秒。同时设定一个用户信号,当该信号在用户指定的空计次循环后到来。在用户信号到来后,打印各个计时器走过的时间值,并计算出内核所用的时间。到实际时间计时器到达10秒后产生定时器信号时,程序打印各计时器走过的时间,并退出程序。

因为需要获得定时器的时间,因此,我们需要使用setitimer和getitimer计时器。使用这两个计时器要频繁的对itimerval和timeval这两个结构体进行操作。首先,应该对这两个结构体在本实例中常用到的操作进行封装。封装的函数包括,timeval的比较,计算两个timeval的时间差,计算itimerval走过的时间(因为itimerval记录的是定时器剩余时间)。为了显示的需要,我们需要封装一个对timeval打印的函数。

之后,我们需要设计信号和信号处理函数,这里我们一共需要两个信号:SIGALRM和SIGUSR1。因为信号处理函数有一个int型参数,该参数可以用来区分信号。考虑到,两个信号到来以后都需要进行时间计算。因此可以考虑将两个信号写成一个函数,通过参数识别,在不同的位置进行不同的处理。


编写程序代码如下:

#include

#include

#include

#include


/* 函数声明 */

/* 信号处理函数 */

static void SigHandler(int signo);


/* 操作辅助函数 */

void TimerPassed(const struct itimerval *itimer, struct timeval *tv);

void TimeSubstract(const struct timeval *tv1, const struct timeval *tv2, struct timeval *tvres);

int TimeCompare(const struct timeval *tv1, const struct timeval *tv2);

void PrintTimeval(const char *str, const struct timeval *tv);


/* 主函数,程序入口点 */

int main()

{

/* 变量声明 */

struct itimerval myitimer;

long i, count;


/* 注册信号处理函数 */

/* 注册用户信号1处理函数 */

if (signal(SIGUSR1, SigHandler) == SIG_ERR) {

printf("Unable to create handler for SIGUSR1/n");

exit(0);

}

/* 定时器信号处理函数 */

/* 这里将同一个函数注册给两个信号,函数内部存在区分信号类型的逻辑 */

if (signal(SIGALRM, SigHandler) == SIG_ERR){

printf("Unable to create handler for SIGALRM/n");

exit(0);

}


/* 运行参数输入 */

printf("Loop times between timer info outputs (300 Recommanded):/n");

scanf("%ld", &count);

count *= 1000000;


/* 初始化定时器参数 */

myitimer.it_interval.tv_sec = 10;

myitimer.it_interval.tv_usec = 0;

myitimer.it_value.tv_sec = 10;

myitimer.it_value.tv_usec = 0;


/* 注册定时器 */

setitimer(ITIMER_REAL, &myitimer, NULL); /* 实时定时器 */

setitimer(ITIMER_VIRTUAL, &myitimer, NULL); /* 用户定时器 */

setitimer(ITIMER_PROF, &myitimer, NULL); /* CPU定时器 */


/* 无限循环,等待信号处理 */

while (1)

{

for (i=0; i < count; i++);

/* 每进行指定次数的循环后,发送用户信号1给自己 */

raise(SIGUSR1);

}

}


* 信号处理函数 */

static void SigHandler(int signo)

{

/* 变量声明 */

struct itimerval tmp_itimer;

struct timeval realtv, cputv, usertv, kerneltv;


/* 获得实时定时器时间 */

getitimer(ITIMER_REAL, &tmp_itimer);

TimerPassed(&tmp_itimer, &realtv);


/* 获得CPU定时器时间 */

getitimer(ITIMER_PROF, &tmp_itimer);

TimerPassed(&tmp_itimer, &cputv);


/* 获得用户定时器时间 */

getitimer(ITIMER_VIRTUAL,&tmp_itimer);

TimerPassed(&tmp_itimer, &usertv);


/* 计算Linux内核使用CPU时间 */

TimeSubstract(&cputv, &usertv, &kerneltv);

/* 按照信号进行处理 */

switch (signo)

{

/* 用户信号1 */

case SIGUSR1:

/* 输出各种时间值 */

PrintTimeval("Real Time ", &realtv);

PrintTimeval("CPU Time ", &cputv);

PrintTimeval("User Time ", &usertv);

PrintTimeval("Kernel Time", &kerneltv);

printf("/n");

break;


/* 定时器信号 */

case SIGALRM:

/* 输出时间值后退出程序 */

printf("Time up, the application will escape./n");

PrintTimeval("CPU Time ", &cputv);

PrintTimeval("User Time ", &usertv);

PrintTimeval("Kernel Time", &kerneltv);

exit(0);

break;

}

}


/* 计算时间的流逝 */

void TimerPassed(const struct itimerval *itimer, struct timeval *tv)

{

TimeSubstract(&(itimer->it_interval), &(itimer->it_value), tv);

}


/* 计算两个时间的差值 */

void TimeSubstract(const struct timeval *tv1, const struct timeval *tv2, struct timeval *tvres)

{

/* 变量声明 */

const struct timeval *tmptv1, *tmptv2;

int cmpres;


/* 比较tv1和tv2,将较大值赋给tmptv1,较小值赋给tmptv2 */

cmpres = TimeCompare(tv1, tv2);

if (cmpres > 0) {

tmptv1 = tv1;

tmptv2 = tv2;

} else {

tmptv1 = tv2;

tmptv2 = tv1;

}


/* 做差时存在借位的情况 */

if (tmptv1->tv_usec < tmptv2->tv_usec) {

/* 结果的秒数多减1,借给微秒 */

tvres->tv_sec = tmptv1->tv_sec - tmptv2->tv_sec - 1;

/* 微秒做减法时,先加上借来的一秒(1000000微秒) */

tvres->tv_usec = tmptv1->tv_usec + 1000000 - tmptv2->tv_usec;

/* 不存在借位的情况 */

} else {

/* 对应的秒和微秒分别做差 */

tvres->tv_sec = tmptv1->tv_sec - tmptv2->tv_sec;

tvres->tv_usec = tmptv1->tv_usec - tmptv2->tv_usec;

}

}


/* 时间值比较大小 */

int TimeCompare(const struct timeval *tv1, const struct timeval *tv2)

{

/* 如果秒值不一致则秒值大者较大 */

if (tv1->tv_sec > tv2->tv_sec)

return 1;

else if (tv1->tv_sec < tv2->tv_sec)

return -1;

/* 秒值相同的,微秒值较大者较大 */

else if (tv1->tv_usec > tv2->tv_usec)

return 1;

else if (tv1->tv_usec < tv2->tv_usec)

return -1;

/* 秒值和微秒值皆相同者等值 */

else

return 0;

}


/* 打印时间 */

void PrintTimeval(const char *str, const struct timeval *tv)

{

printf("%s = %ld sec %ld usec/n", str, tv->tv_sec, tv->tv_usec);

}

程序会提示你输入空循环的次数,2.7GHz的CPU在空闲的时候使用300至500时大约会1秒钟显示一次时间。数值越大显示时间的间隔越长,根据你的计算机的具体情况而定。此外输出的数据也各你当前系统状态而定。结果如下所示。

[root@localhost amiselect]# ./timerexp
Loop times between timer info outputs (300 Recommanded):
400
Real Time = 1 sec 224567 usec
CPU Time = 1 sec 223814 usec
User Time = 1 sec 223814 usec
Kernel Time = 0 sec 0 usec

Real Time = 2 sec 457899 usec
CPU Time = 2 sec 447628 usec
User Time = 2 sec 445628 usec
Kernel Time = 0 sec 2000 usec

Real Time = 3 sec 683384 usec
CPU Time = 3 sec 672442 usec
User Time = 3 sec 670442 usec
Kernel Time = 0 sec 2000 usec

Real Time = 4 sec 908240 usec
CPU Time = 4 sec 897255 usec
User Time = 4 sec 894256 usec
Kernel Time = 0 sec 2999 usec

Powered by Zoundry Raven

### Linux 信号处理机制 在 Linux 系统中,信号是一种异步通信方式,允许操作系统向进程发送通知消息。通过 `sigaction` 函数可以设置信号处理器的行为[^1]。 对于信号的捕获响应,通常会定义一个自定义函数作为信号处理器,并将其关联到特定类型的信号上: ```c struct sigaction sa; sa.sa_handler = handler_function; // 自定义的信号处理函数 sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGALRM, &sa, NULL) == -1) { perror("Error setting signal action"); } ``` 这段代码展示了如何配置 SIGALRM 信号的动作,其中 `handler_function` 是用户编写的回调函数,在接收到定时器到期产生的 SIGALRM 后会被调用执行。 ### 定时器实现方法 #### 使用 `alarm()` 实现一次性定时器 `alarm()` 提供了一种简单的创建一次性的计时器的方式,它会在指定秒数后给当前进程发送一个 SIGALRM 信号[^2]: ```c unsigned int seconds = 5; alarm(seconds); // 设置五秒钟后的闹钟 pause(); // 阻塞等待直到SIGALRM到来 ``` 此段代码设置了五个单位时间长度的一次性定时器;当到达设定的时间点时,系统自动发出 SIGALRM 给该进程。 #### 利用 `setitimer()` 创建更精确的间隔定时器 如果需要更加精细控制以及重复触发的能力,则应该考虑采用 `setitimer()` 来代替 `alarm()`。它可以用来建立具有固定频率的周期性事件源。 ```c struct itimerval timer; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 500 * 1000; /* Initial delay */ timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 500 * 1000; /* Interval between signals */ // Set the ITIMER_REAL value to start a real-time interval timer. if (setitimer(ITIMER_REAL, &timer, NULL)) { perror("Failed to set timer"); } while(1){ pause(); printf("Timer expired\n"); } ``` 上述实例说明了怎样利用 `setitimer()` 建立每半秒发生一次的实时定时器,每次超时时都会激活 SIGALRM 并打印一条信息。 #### 用户态下的高级定时器管理方案 除了基本 API 外,还有其他更为复杂的解决方案适用于高并发场景下大量活跃定时任务的情况。例如,基于红黑树的数据结构被广泛应用于高效地维护多个待定事件列表之中[^4]。 这些数据结构能够有效地支持动态增删节点操作的同时保持较好的性能特性,从而使得应用程序能够在面对成千上万个独立运行的小型延时期间依然维持良好的反应速度服务质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值