Skip to content

LV024-定时器产生信号

一、定时器产生信号

1. alarm() 函数

1.1 函数说明

linux下可以使用man 3 alarm命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <unistd.h>

/* 函数声明 */
unsigned int alarm(unsigned int seconds);

函数说明】该函数用于设置一个定时器, 在定时器超时的时候, 内核会向进程发送SIGALRM信号,此函数也称为闹钟函数,一个进程只能有一个闹钟时间。如果不忽略或捕捉此信号, 它的默认操作是终止调用该函数的进程。

函数参数

  • secondsunsigned int类型,设置定时时间,以秒为单位;如果参数seconds等于0,则表示取消之前设置的alarm闹钟。

返回值unsigned int类型,如果在调用alarm()之前已经为该进程设置了alarm闹钟,但是还没有超时,则已经设置的闹钟的剩余值作为本次alarm()函数调用的返回值,之前设置的闹钟则被新的替代;否则返回0

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <unistd.h>

/* 至少应该有的语句 */
alarm(seconds);

注意事项

(1)参数seconds的值是产生SIGALRM信号需要经过的时钟秒数,当这一刻到达时,由内核产生该信号,每个进程只能设置一个alarm闹钟;虽然SIGALRM信号的系统默认操作是终止进程。 (2)alarm闹钟并不能循环触发,只能触发一次,若想要实现循环触发,可以在SIGALRM信号处理函数中再次调用alarm()函数设置定时器。

1.2 使用实例1

该实例为单次触发alarm闹钟。

c
/* 头文件 */
#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;
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

然后,终端会显示如下信息:

shell
i = 1
i = 2
i = 3
i = 4
i = 5
闹钟

1.3 使用实例2

该实例为循环触发alarm闹钟。

c
/* 头文件 */
#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);
	}
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

然后,终端会显示以下信息:

shell
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命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <unistd.h>

/* 函数声明 */
useconds_t ualarm(useconds_t usecs, useconds_t interval);

函数说明】该函数用于设置一个循环发送SIGALRM信号的定时器,函数会在usecs微秒后,将SIGALRM信号发送给进程,并且之后每隔interval微秒再发送一次 SIGALRM信号。

函数参数

  • usecsuseconds_t类型,设置定时时间,以微秒为单位,不能大于1000 000us
  • intervaluseconds_t类型,设置循环发送信号的间隔时间,以微妙为单位,不能大于1000 000us

返回值unsigned int类型,如果在调用ualarm()之前已经为该进程设置了ualarm闹钟,但是还没有超时,则已经设置的闹钟的剩余值作为本次ualarm()函数调用的返回值,之前设置的闹钟则被新的替代;否则返回0(第一次调用该函数也返回0)。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <unistd.h>

/* 至少应该有的语句 */
ualarm(useconds_t usecs, useconds_t interval);

注意事项】在ualarm函数的手册中,返回值的错误信息中有这么一条:

shell
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 使用实例

c
/* 头文件 */
#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);
	}
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

然后,终端会显示如下信息:

shell
i = 1
[2] timer 
i = 2
[3] timer 
i = 3
[4] timer 
i = 4
# 后边的省略 ... ...

3. setitimer() 函数

3.1 函数说明

linux下可以使用man 2 setitimer命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <sys/time.h>

/* 函数声明 */
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

函数说明】该函数用于设置一个定时器,可以定时发送信号。可替代alarm函数,比alarm函数精确度更高,可以实现周期定时。

函数参数

  • whichint类型,设置定时的方式,一共有三种定时方式,每种都工作在不同的时钟上,并且在定时器定时结束时产生不同的信号。which 取值如下:
ITIMER_REAL按实际时间计时,到达时间的时候会产生SIGALRM信号
ITIMER_VIRTUAL这种方式根据进程消耗的用户模式CPU时间进行计时。(测量包括进程中所有线程消耗的CPU时间)在每次到期时,都会生成一个SIGVTALRM信号。
ITIMER_PROF根据进程消耗的总CPU时间(即用户和系统)进行倒计时,每次到期时,发送SIGPROF信号
- `new_value`:`struct itimerval`类型的结构体指针变量,是传入参数,表示设定定时的时长,也就是超时时间。它有两个成员`it_interval`和`it_value`,分别用于设置间隔时间和定时时间,如果`it_value`为0,那么定时器将不不会启动;如果计时器`it_value`过期之后,`it_interval`为0,那么定时器也将停止工作。使用`man 2 setitimer`的时候,显示的帮助手册中会有这个结构体成员详情:
c
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_intervalstruct timeval类型,表示定时器循环的时间间隔,即第一次计时it_value时长发送信号,再往后的信号每隔一个it_interval发送一次。它有两个成员,为tv_sectv_usec,用于设置时间,分别表示秒和微秒。

(2)it_valuestruct timeval类型,表示定时器定时时长,算起来应该是第一次启动定时器的定时时间,它有两个成员,为tv_sectv_usec,分别表示秒和微秒。

  • old_valuestruct itimerval类型的结构体指针变量,是传出参数,表示上一次定时剩余的时间,如果不关心上一次定时剩余时间,可以设置为NULL。例如第一次定时10s,但是过了6s后,再次用setitimer函数定时,此时第二次的计时会将第一次计时覆盖,而上一次定时的剩余时间则为4s

返回值int类型,成功返回0,失败将返回-1,并设置errno

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#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 使用实例

c
/* 头文件 */
#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);
	}
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

然后,终端会显示如下信息:

shell
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命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <sys/time.h>

/* 函数声明 */
int getitimer(int which, struct itimerval *curr_value);

函数说明】该函数用于将定时器的当前值填写在curr_value指向的结构体中,也就是获取定时器的当前值,该函数不会发送信号。

函数参数

  • whichint类型,定时的方式,一共有三种定时方式,每种都工作在不同的时钟上,并且在定时器定时结束时产生不同的信号。 which 取值如下:
ITIMER_REAL按实际时间计时,到达时间的时候会产生SIGALRM信号
ITIMER_VIRTUAL这种方式根据进程消耗的用户模式CPU时间进行计时。(测量包括进程中所有线程消耗的CPU时间)在每次到期时,都会生成一个SIGVTALRM信号。
ITIMER_PROF根据进程消耗的总CPU时间(即用户和系统)进行倒计时,每次到期时,发送SIGPROF信号
- `curr_value`:`struct itimerval`类型的结构体指针变量,用于保存当前定时器的值。它有两个成员`it_interval`和`it_value`,分别用于存放已开启的定时器的间隔时间和定时时间。

返回值int类型,成功返回0,失败将返回-1,并设置errno

使用格式】一般情况下基本使用格式如下:

c
#include <sys/time.h>

/* 至少应该有的语句 */
struct itimerval nowvalue;
getitimer(ITIMER_REAL, &nowvalue);

注意事项none

4.2 使用实例

c
/* 头文件 */
#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);
	}
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

然后,终端会显示如下信息:

shell
[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.
# 后边的省略 ... ...