Linux定时器(setitimer定时)
定时器流程
1、itimerval——设置定时器的初值和周期值
2、setitimer——设置定时器
3、sigaction()/signal()——处理信号,执行定时功能
4、SIGALRM——触发信号
itimerval(设置定时器的初值和周期值)
itimerval
是一个结构体,它在 POSIX 兼容的操作系统中定义于 <sys/time.h>
头文件中。这个结构体用于 setitimer
系统调用,以便获取或设置进程的虚拟定时器和实时定时器。
结构体定义:
struct itimerval { |
成员解释:
-
it_interval
:这是一个timeval
结构,它指定了定时器的间隔时间,即定时器重复触发的时间周期。timeval
结构包含两个字段:tv_sec
(秒)和tv_usec
(微秒)。 -
it_value
:这也是一个timeval
结构,它指定了定时器的到期时间,即从当前时间到定时器第一次触发的剩余时间。
setitimer(设置定时器)
setitimer
是 UNIX 和类 UNIX 系统(包括 Linux)中的一个系统调用,用于设置进程的虚拟、实时或性能定时器。这个系统调用提供了一种机制,允许进程接收定时信号,例如,SIGALRM
。
函数原型:
int setitimer(int which, const struct itimerspec *new_value, struct itimerspec *old_value); |
参数:
which
:指定要设置的定时器类型,可以是以下之一:ITIMER_REAL
:实时定时器,基于系统时钟。ITIMER_VIRTUAL
:虚拟定时器,基于进程的虚拟执行时间。ITIMER_PROF
:性能定时器,用于采样程序的执行时间。
new_value
:指向itimerspec
结构的指针,该结构包含两个timespec
结构,分别指定定时器的初值和周期值。timespec
结构包含tv_sec
(秒)和tv_nsec
(纳秒)。old_value
:指向itimerspec
结构的指针,用于存储定时器被设置前的旧值。如果设置为NULL
,则忽略旧值。
要使用 setitimer
创建一个周期性执行的任务,你需要设置定时器的初值(it_value
)和周期值(it_interval
),这两者都定义在 itimerspec
结构体中。周期值设置了定时器触发的频率,而初值设置了第一次触发前的时间间隔。
signal()/ sigaction()函数
在 UNIX 和类 UNIX 系统中,signal
和 sigaction
都是用来处理信号的函数。信号是一种特殊的软件中断,当程序接收到某些事件(比如用户按下 Ctrl+C 或者定时器到期)时,操作系统会发送信号给程序。
signal
是一个用于处理异步事件的机制,它允许程序对操作系统发送的信号进行响应。在 UNIX 和类 UNIX 系统(包括 Linux)中,信号是一种软件中断,用于通知进程发生了某个事件,如用户按下中断键(通常是 Ctrl+C),或者系统检测到一个错误条件。
信号可以由多种不同的源触发,包括硬件事件、软件条件或系统调用。它们通常用于中断正在执行的进程,以便进程可以采取适当的行动,比如清理资源、记录日志或优雅地终止。
捕获并自定义处理信号:进程可以通过 signal
函数来捕获信号,并为其定义自定义的处理函数。这个处理函数将在信号到达时被调用,如下。
// SIGALRM 信号处理函数 |
sigaction
函数是 signal
函数的现代替代品,它提供了更多的功能和更好的可移植性。
|
-
signum
是要处理的信号的编号。 -
act
是指向sigaction
结构的指针,该结构定义了信号的处理方式。 -
oldact
是指向sigaction
结构的指针,用于存储旧的信号处理函数。
使用 sigaction
时,可以更安全地处理信号,并且可以设置信号处理函数的额外属性,如 sa_mask
(信号屏蔽字),sa_flags
(信号处理标志),以及 sa_handler
(信号处理函数)。
signal()与 sigaction()区别
signal
是较旧的函数,它有一些限制。例如,你不能在 signal
函数中使用某些会阻塞的函数,因为这可能会导致程序无法响应其他信号。而且,signal
函数本身的限制也比较多,比如它不能处理一些新的信号。
sigaction
是一个更新的函数,它解决了 signal
的一些问题。使用 sigaction
,你可以更安全地处理信号,并且可以设置更多的信号处理选项。例如,可以告诉操作系统在处理信号时是否应该暂停其他信号的接收,这样可以避免在处理一个信号时被另一个信号打断。
SIGALRM(定时器信号)
SIGALRM
(Signal Alarm)是一个在 UNIX 和类 UNIX 系统(包括 Linux)中广泛使用的信号,它用于处理定时器到期事件。当一个进程设置了闹钟(alarm clock),并在指定的时间间隔后,SIGALRM
信号会被发送给该进程。这个信号可以用来提醒进程执行某些操作,或者在特定的时间点触发某些事件。
示例
以下是一个简单的 C 程序示例,演示如何使用 setitimer
和 SIGALRM
信号来创建一个周期性的定时器:
|
在这个示例中,首先设置了一个信号处理函数 signal_handler
来处理 SIGALRM
信号。然后,使用 setitimer
函数设置了一个周期性的定时器,每隔 2 秒触发一次 SIGALRM
信号。最后,使用 pause
函数进入一个无限循环,等待信号的到来。
信号处理函数 signal_handler
是一个特殊的函数,它在信号到达时被操作系统调用。在这个例子中,signal_handler
函数只是简单地打印一条消息到控制台。每次定时器触发时,signal_handler
都会被调用一次。
注意事项 :
- 信号处理函数应该尽可能地短小,避免执行复杂的操作,因为这可能会影响系统的信号处理效率。
- 在信号处理函数中,应该避免使用可能会阻塞的系统调用或库函数。
- 如果需要长时间运行的操作,应该在信号处理函数中安排这些操作在进程的主体中异步执行。
设定定时器与 while
循环有本质的区别
-
异步执行: 信号处理函数是异步执行的,这意味着它们是由操作系统在信号到达时调用的,而不是程序的主执行流程(如
while
循环)的一部分。这意味着信号处理函数可以在程序的任何地方被调用,而不仅仅是在while
循环中。 -
实时性: 使用
setitimer
和信号处理函数可以确保某些操作以固定的频率发生,即使程序的主线程正在忙于其他任务。这在需要实时响应的情况下非常有用,例如在嵌入式系统或实时操作系统中。 -
主线程的阻塞:
while
循环会阻塞程序的主线程,直到循环条件不再满足。在循环内部执行的任务会连续不断地运行,直到循环结束。而信号处理函数则是在while
循环之外独立执行的,它们不会阻塞主线程,也不会影响主线程的执行顺序。 -
周期性任务的实现方式: 在
while
循环中实现周期性任务通常需要在循环内部使用延时函数(如sleep
),这会导致程序的整体执行速度降低,因为主线程必须等待延时结束才能继续执行。而使用setitimer
和信号处理函数,周期性任务是由操作系统在后台管理的,不会影响主线程的执行。
总结来说,setitimer
和信号处理函数提供了一种异步和实时的方式来周期性地执行任务,而 while
循环提供了一种同步和阻塞的方式来执行任务。选择哪种方式取决于具体需求和程序的设计。