Linux提供了alarm系统调用和SIGALRM信号。
alarm系统调用
unsigned int alarm(unsigned int seconds);
- 功能:设置定时器(闹钟)。函数调用,开始倒计时,当倒计时为0的时候,函数会给当前的进程发送一个信号:SIGALARM
- 参数:
seconds: 倒计时的时长,单位:秒。如果参数为0,定时器无效(不进行倒计时,不发信号)。
取消一个定时器,通过alarm(0)。
- 返回值:
- 之前没有定时器,返回0
- 之前有定时器,返回之前的定时器剩余的时间
例如:
alarm(10); -> 返回0
过了1秒
alarm(5); -> 返回9
SIGALARM :默认终止当前的进程,每一个进程都有且只有唯一的一个定时器。
新手实例
注意 :alarm函数不阻塞程序,即与进程的状态无关(自然定时法)
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
int main()
{
int seconds = alarm(5);
printf("seconds = %d\n", seconds); // 0
sleep(2); // 意味着不阻塞闹钟
seconds = alarm(2); // 不阻塞,就是指睡眠之后立马执行下面的打印
printf("seconds = %d\n", seconds); // 3
while (1)
{
}
return 0;
}
小的案例:计算机1秒可以数数多少?
我们将每个数打印输出在终端时,由于终端也有缓存,需要耗费磁盘io的时间,所以当我们在屏幕上观察时程序可能运行了不止1秒。计算机数数是考虑计算机内核数数的时间,可以考虑重定向,>>
,这样只操作了一次。同时切换内核和用户区也都会造成时间损耗。
实际的时间 = 内核时间 + 用户时间 + 消耗的时间
进行文件IO操作的时候比较浪费时间。
setitimer 定时器
使用定时器,要安装SIGALRM信号。假设不安装SIGALRM信号,则进程收到SIGALRM信号后。缺省的动作就是终止当前进程。
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
- 功能:设置定时器(闹钟)。精度微妙us,可以实现周期性定时
- 参数:
- which : 定时器以什么时间计时
ITIMER_REAL: 真实时间,时间到达,发送 SIGALRM 常用
ITIMER_VIRTUAL: 用户时间,时间到达,发送 SIGVTALRM
ITIMER_PROF: 以该进程在用户态和内核态下所消耗的时间来计算,时间到达,发送 SIGPROF
- new_value: 设置定时器的属性
struct itimerval { // 定时器的结构体
struct timeval it_interval; // 每个阶段的时间,间隔时间
struct timeval it_value; // 延迟多长时间执行定时器
};
struct timeval { // 时间的结构体
time_t tv_sec; // 秒数
suseconds_t tv_usec; // 微秒
};
- old_value :记录上一次的定时的时间参数,一般不使用,指定NULL
- 返回值:
成功 0
失败 -1 并设置错误号
信号捕捉
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
- 功能:设置某个信号的捕捉行为
- 参数:
- signum: 要捕捉的信号
- handler: 捕捉到信号要如何处理
- SIG_IGN : 忽略信号
- SIG_DFL : 使用信号默认的行为
- 回调函数 : 这个函数是内核调用,程序员只负责写,捕捉到信号后如何去处理信号。
回调函数:
- 需要程序员实现,提前准备好的,函数的类型根据实际需求,看函数指针的定义
- 不是程序员调用,而是当信号产生,由内核调用
- 函数指针是实现回调的手段,函数实现之后,将函数名放到函数指针的位置就可以了。
- 返回值:
成功,返回上一次注册的信号处理函数的地址。第一次调用返回NULL
失败,返回SIG_ERR,设置错误号
SIGKILL SIGSTOP不能被捕捉,不能被忽略。
新手案例
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
// 回调函数
void myalarm(int num)
{
printf("捕捉到了信号的编号是:%d\n", num);
printf("xxxxx\n");
}
// 3秒过后,每隔两秒定时一次
int main()
{
// 注册信号捕捉,要放在设置定时器之前
// signal(SIGALRM, SIG_IGN);
// signal(SIGALRM, SIG_DFL);
// void (*sighandler_t)(int); 函数指针,int类型的参数表示捕捉到的信号的值。
signal(SIGALRM, myalarm);
struct itimerval new_value; // 分配在栈空间,会随机赋值
// 设置间隔时间
new_value.it_interval.tv_sec = 2;
new_value.it_interval.tv_usec = 0;
// 设置延迟时间,3秒之后开始第一次定时
new_value.it_value.tv_sec = 3;
new_value.it_value.tv_usec = 0; // 毫秒数需要设置,否则默认随机值
int ret = setitimer(ITIMER_REAL, &new_value, NULL); // 非阻塞
printf("定时器开始了...\n");
if (ret == -1)
{
perror("setitimer");
exit(0);
}
getchar();
return 0;
}
程序结果: