LV024-定时器产生信号
一、定时器产生信号
1. alarm() 函数
1.1 函数说明
在linux下可以使用man 3 alarm命令查看该函数的帮助手册。
/* 需包含的头文件 */
#include <unistd.h>
/* 函数声明 */
unsigned int alarm(unsigned int seconds);【函数说明】该函数用于设置一个定时器, 在定时器超时的时候, 内核会向进程发送SIGALRM信号,此函数也称为闹钟函数,一个进程只能有一个闹钟时间。如果不忽略或捕捉此信号, 它的默认操作是终止调用该函数的进程。
【函数参数】
seconds:unsigned int类型,设置定时时间,以秒为单位;如果参数seconds等于0,则表示取消之前设置的alarm闹钟。
【返回值】unsigned int类型,如果在调用alarm()之前已经为该进程设置了alarm闹钟,但是还没有超时,则已经设置的闹钟的剩余值作为本次alarm()函数调用的返回值,之前设置的闹钟则被新的替代;否则返回0。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <unistd.h>
/* 至少应该有的语句 */
alarm(seconds);【注意事项】
(1)参数seconds的值是产生SIGALRM信号需要经过的时钟秒数,当这一刻到达时,由内核产生该信号,每个进程只能设置一个alarm闹钟;虽然SIGALRM信号的系统默认操作是终止进程。 (2)alarm闹钟并不能循环触发,只能触发一次,若想要实现循环触发,可以在SIGALRM信号处理函数中再次调用alarm()函数设置定时器。
1.2 使用实例1
该实例为单次触发alarm闹钟。
/* 头文件 */
#include <stdio.h> /* perror */
#include <unistd.h> /* alarm sleep*/
/* 主函数 */
int main(int argc, char *argv[])
{
int i = 0;
alarm(5);/* 5s后向本进程发送 SIGALRM 信号,将会终止程序*/
while(1)
{
printf("i = %d\n", ++i);
sleep(1);
}
return 0;
}在终端执行以下命令编译程序:
gcc test.c -Wall # 生成可执行文件 a.out
./a.out # 执行可执行程序然后,终端会显示如下信息:
i = 1
i = 2
i = 3
i = 4
i = 5
闹钟1.3 使用实例2
该实例为循环触发alarm闹钟。
/* 头文件 */
#include <stdio.h> /* perror */
#include <unistd.h> /* alarm sleep*/
#include <signal.h> /* signal */
void sigHandle(int sig);
/* 主函数 */
int main(int argc, char *argv[])
{
int i = 0;
signal(SIGALRM, sigHandle);
alarm(5);/* 5s后向本进程发送 SIGALRM 信号,将会终止程序*/
while(1)
{
printf("i = %d\n", ++i);
sleep(1);
}
return 0;
}
void sigHandle(int sig)
{
static int count = 1;
if (sig == SIGALRM)
{
printf("[%d] timer \n", ++count);
alarm(5);
}
}在终端执行以下命令编译程序:
gcc test.c -Wall # 生成可执行文件 a.out
./a.out # 执行可执行程序然后,终端会显示以下信息:
i = 1
i = 2
i = 3
i = 4
i = 5
[2] timer
i = 6
i = 7
i = 8
i = 9
i = 10
[3] timer
i = 11
# 后边的省略 ... ...2. ualarm() 函数
2.1 函数说明
在linux下可以使用man 3 ualarm命令查看该函数的帮助手册。
/* 需包含的头文件 */
#include <unistd.h>
/* 函数声明 */
useconds_t ualarm(useconds_t usecs, useconds_t interval);【函数说明】该函数用于设置一个循环发送SIGALRM信号的定时器,函数会在usecs微秒后,将SIGALRM信号发送给进程,并且之后每隔interval微秒再发送一次 SIGALRM信号。
【函数参数】
usecs:useconds_t类型,设置定时时间,以微秒为单位,不能大于1000 000us。interval:useconds_t类型,设置循环发送信号的间隔时间,以微妙为单位,不能大于1000 000us。
【返回值】unsigned int类型,如果在调用ualarm()之前已经为该进程设置了ualarm闹钟,但是还没有超时,则已经设置的闹钟的剩余值作为本次ualarm()函数调用的返回值,之前设置的闹钟则被新的替代;否则返回0(第一次调用该函数也返回0)。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <unistd.h>
/* 至少应该有的语句 */
ualarm(useconds_t usecs, useconds_t interval);【注意事项】在ualarm函数的手册中,返回值的错误信息中有这么一条:
ERRORS
EINTR Interrupted by a signal; see signal(7).
EINVAL usecs or interval is not smaller than 1000000. (On systems where that is considered an error.)这就意味着,我们的两个参数最好都不要大于1s,否则闹钟是不会生效的。
2.2 使用实例
/* 头文件 */
#include <stdio.h> /* perror */
#include <unistd.h> /* ualarm sleep*/
#include <signal.h> /* signal */
void sigHandle(int sig);
/* 主函数 */
int main(int argc, char *argv[])
{
int i = 0;
signal(SIGALRM, sigHandle);
ualarm(999999, 999999);
while(1)
{
printf("i = %d\n", ++i);
sleep(1);
}
return 0;
}
void sigHandle(int sig)
{
static int count = 1;
if (sig == SIGALRM)
{
printf("[%d] timer \n", ++count);
}
}在终端执行以下命令编译程序:
gcc test.c -Wall # 生成可执行文件 a.out
./a.out # 执行可执行程序然后,终端会显示如下信息:
i = 1
[2] timer
i = 2
[3] timer
i = 3
[4] timer
i = 4
# 后边的省略 ... ...3. setitimer() 函数
3.1 函数说明
在linux下可以使用man 2 setitimer命令查看该函数的帮助手册。
/* 需包含的头文件 */
#include <sys/time.h>
/* 函数声明 */
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);【函数说明】该函数用于设置一个定时器,可以定时发送信号。可替代alarm函数,比alarm函数精确度更高,可以实现周期定时。
【函数参数】
which:int类型,设置定时的方式,一共有三种定时方式,每种都工作在不同的时钟上,并且在定时器定时结束时产生不同的信号。which 取值如下:
| ITIMER_REAL | 按实际时间计时,到达时间的时候会产生SIGALRM信号 |
| ITIMER_VIRTUAL | 这种方式根据进程消耗的用户模式CPU时间进行计时。(测量包括进程中所有线程消耗的CPU时间)在每次到期时,都会生成一个SIGVTALRM信号。 |
| ITIMER_PROF | 根据进程消耗的总CPU时间(即用户和系统)进行倒计时,每次到期时,发送SIGPROF信号 |
struct itimerval
{
struct timeval it_interval; /* Interval for periodic timer */
struct timeval it_value; /* Time until next expiration */
};
struct timeval
{
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};(1)it_interval:struct timeval类型,表示定时器循环的时间间隔,即第一次计时it_value时长发送信号,再往后的信号每隔一个it_interval发送一次。它有两个成员,为tv_sec和tv_usec,用于设置时间,分别表示秒和微秒。
(2)it_value:struct timeval类型,表示定时器定时时长,算起来应该是第一次启动定时器的定时时间,它有两个成员,为tv_sec和tv_usec,分别表示秒和微秒。
old_value:struct itimerval类型的结构体指针变量,是传出参数,表示上一次定时剩余的时间,如果不关心上一次定时剩余时间,可以设置为NULL。例如第一次定时10s,但是过了6s后,再次用setitimer函数定时,此时第二次的计时会将第一次计时覆盖,而上一次定时的剩余时间则为4s。
【返回值】int类型,成功返回0,失败将返回-1,并设置errno。
【使用格式】一般情况下基本使用格式如下:
/* 需要包含的头文件 */
#include <sys/time.h>
/* 至少应该有的语句 */
struct itimerval timevalue;
timevalue.it_interval.tv_sec = 2; /* 触发周期 s */
timevalue.it_interval.tv_usec = 0; /* 触发周期 us */
timevalue.it_value.tv_sec = 5; /* 触发时间 s */
timevalue.it_value.tv_usec = 0; /* 触发时间 us */
setitimer(ITIMER_REAL, &timevalue, NULL);【注意事项】none
3.2 使用实例
/* 头文件 */
#include <stdio.h> /* perror */
#include <unistd.h> /* ualarm sleep*/
#include <signal.h> /* signal */
#include <sys/time.h>
void sigHandle(int sig);
/* 主函数 */
int main(int argc, char *argv[])
{
int i = 0;
struct itimerval timevalue;
/* 时间参数结构体变量初始化 */
timevalue.it_interval.tv_sec = 2; /* 触发周期 s */
timevalue.it_interval.tv_usec = 0; /* 触发周期 us */
timevalue.it_value.tv_sec = 5; /* 触发时间 s */
timevalue.it_value.tv_usec = 0; /* 触发时间 us */
/* 设置定时器,5秒后触发信号,之后每隔2秒触发一次 */
setitimer(ITIMER_REAL, &timevalue, NULL);
/* 捕捉信号 */
signal(SIGALRM, sigHandle);
while(1)
{
printf("i = %d\n", ++i);
sleep(1);
}
return 0;
}
void sigHandle(int sig)
{
static int count = 1;
if (sig == SIGALRM)
{
printf("[%d] timer \n", ++count);
}
}在终端执行以下命令编译程序:
gcc test.c -Wall # 生成可执行文件 a.out
./a.out # 执行可执行程序然后,终端会显示如下信息:
i = 1
i = 2
i = 3
i = 4
i = 5
[2] timer
i = 6
i = 7
[3] timer
i = 8
i = 9
[4] timer
i = 10
# 后边的省略 ... ...4. getitimer() 函数
4.1 getitimer()
在linux下可以使用man 2 getitimer命令查看该函数的帮助手册。
/* 需包含的头文件 */
#include <sys/time.h>
/* 函数声明 */
int getitimer(int which, struct itimerval *curr_value);【函数说明】该函数用于将定时器的当前值填写在curr_value指向的结构体中,也就是获取定时器的当前值,该函数不会发送信号。
【函数参数】
which:int类型,定时的方式,一共有三种定时方式,每种都工作在不同的时钟上,并且在定时器定时结束时产生不同的信号。 which 取值如下:
| ITIMER_REAL | 按实际时间计时,到达时间的时候会产生SIGALRM信号 |
| ITIMER_VIRTUAL | 这种方式根据进程消耗的用户模式CPU时间进行计时。(测量包括进程中所有线程消耗的CPU时间)在每次到期时,都会生成一个SIGVTALRM信号。 |
| ITIMER_PROF | 根据进程消耗的总CPU时间(即用户和系统)进行倒计时,每次到期时,发送SIGPROF信号 |
【返回值】int类型,成功返回0,失败将返回-1,并设置errno。
【使用格式】一般情况下基本使用格式如下:
#include <sys/time.h>
/* 至少应该有的语句 */
struct itimerval nowvalue;
getitimer(ITIMER_REAL, &nowvalue);【注意事项】none
4.2 使用实例
/* 头文件 */
#include <stdio.h> /* perror */
#include <unistd.h> /* ualarm sleep*/
#include <signal.h> /* signal */
#include <sys/time.h>
void sigHandle(int sig);
/* 主函数 */
int main(int argc, char *argv[])
{
int i = 0;
struct itimerval timevalue;
struct itimerval nowvalue;
/* 时间参数结构体变量初始化 */
timevalue.it_interval.tv_sec = 2; /* 触发周期 s */
timevalue.it_interval.tv_usec = 0; /* 触发周期 us */
timevalue.it_value.tv_sec = 5; /* 触发时间 s */
timevalue.it_value.tv_usec = 0; /* 触发时间 us */
/* 设置定时器,5秒后触发信号,之后每隔2秒触发一次 */
setitimer(ITIMER_REAL, &timevalue, NULL);
/* 捕捉信号 */
signal(SIGALRM, sigHandle);
while(1)
{
getitimer(ITIMER_REAL, &nowvalue);
printf("[i = %d]: it_interval=%lds %ldus;it_value=%lds %ldus.\n", ++i,
nowvalue.it_interval.tv_sec,
nowvalue.it_interval.tv_usec,
nowvalue.it_value.tv_sec,
nowvalue.it_value.tv_usec);
sleep(1);
}
return 0;
}
void sigHandle(int sig)
{
static int count = 1;
if (sig == SIGALRM)
{
printf("[%d] timer \n", ++count);
}
}在终端执行以下命令编译程序:
gcc test.c -Wall # 生成可执行文件 a.out
./a.out # 执行可执行程序然后,终端会显示如下信息:
[i = 1]: it_interval=2s 0us;it_value=4s 999993us.
[i = 2]: it_interval=2s 0us;it_value=3s 999406us.
[i = 3]: it_interval=2s 0us;it_value=2s 998224us.
[i = 4]: it_interval=2s 0us;it_value=1s 997261us.
[i = 5]: it_interval=2s 0us;it_value=0s 996504us.
[2] timer
[i = 6]: it_interval=2s 0us;it_value=1s 999322us.
[i = 7]: it_interval=2s 0us;it_value=0s 998594us.
# 后边的省略 ... ...