1.alarm函数:设置发送信号的闹钟
通过 man 2 alarm 查看alarm函数的详细信息。
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:设置定时器(闹钟)。从调用这个函数开始进行倒计时,倒计时结束之后(变为0),函数会给当前的进程发送SIGALARM信号。
参数:
seconds: 倒计时的时长,单位:秒。如果参数为0,定时器无效(不进行倒计时,不发信号)。取消一个定时器,通过alarm(0)。
返回值: 之前没有定时器,返回0;之前有定时器,返回之前的定时器剩余的时间。
SIGALARM信号:
默认终止当前的进程,每一个进程都有且只有唯一的一个定时器。
alarm(10); -> 返回0
过了1秒
alarm(5); -> 返回9
测试代码:
#include <stdio.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) {
} //alarm该函数是不阻塞的,如果不设置这个,这个程序就结束了,看不到SIGALARM信号的效果,还有两秒 才能看到
return 0;
}
使用alarm函数测试电脑一秒钟能数多少个数
alarm和setitimer(ITIMER_PROF) 共享同一个定时器。即alarm的定时时间包含的是:用户+系统内核的运行时间
进行文件的IO操作会比较浪费时间,因此在执行的时候 ./alarm1 >> a.txt 写道这个文件中,向终端输出会消耗文件IO的时间。
此外需要说明的是,定时器与进程的状态无关,无论进程处于什么状态,alarm都会计时。
测试代码:
#include <stdio.h>
#include <unistd.h>
int main() {
alarm(1); //1秒后进程终止
int i = 0;
while(1) {
printf("%i\n", i++);
}
return 0;
}
通过vim 打开a.txt,然后shift + G到最后一行,可以看到1秒钟在1000万左右,当然向文件中写入也会花费时间,真实的情况比这还要高。
2.setitimer函数
通过 man 2 setitimer 命令查看setitimer的详细描述
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
这些系统调用提供了对间隔计时器的访问,也就是说,这些计时器最初在将来的某个时间点过期,之后(可选地)定期过期。当计时器过期时,将为调用进程生成一个信号,并将计时器重置为指定的间隔(如果间隔非零)。
功能:
设置定时器(闹钟)。可以替代alarm函数。精确到微妙us,并可以实现周期性的定时。
参数:
which: 定时器以什么时间定时
-
ITIMER_REAL :这个计时器以真实时间(即挂钟)倒数。在每次到期时,生成一个SIGALRM信号。(常用,这个真实时间包括很多其他的处理时间)
-
ITIMER_VIRTUAL :该计时器根据进程消耗的用户模式CPU时间进行倒计时。(该度量包括进程中所有线程所消耗的CPU时间。)在每次到期时,生成一个SIGVTALRM信号。
-
ITIMER_PROF :这个计时器根据进程消耗的总(即用户和系统)CPU时间进行计数。(该度量包括进程中所有线程所消耗的CPU时间。)在每次到期时,生成一个SIGPROF信号。
new_value: 设置定时器的属性,是一个结构体类型
struct itimerval { // 定时器的结构体
struct timeval it_interval; // 每个阶段的时间,即间隔时间 2
struct timeval it_value; // 延迟多长时间执行定时器 10
};
struct timeval { // 时间的结构体
time_t tv_sec; // 秒数
suseconds_t tv_usec; // 微秒 精确到微妙
};
过10秒后,每个2秒定时一次
old_value :记录上一次的定时的时间参数,一般不使用,指定NULL
返回值:成功返回0, 失败返回-1并设置错误号
测试代码:实现过3秒后,每隔2秒钟定时一次
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
// 过3秒以后,每隔2秒钟定时一次
int main() {
struct itimerval new_value;
// 设置间隔的时间2秒
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(); //获取键盘录入,什么也不做,否则还不到3秒程序就结束了
//在获取键盘录入时,进程处于阻塞态,但是定时器与进程的状态无关,
//无论进程处于什么状态,都会计时
return 0;
}
我们发现,执行这个程序之后,3秒钟之后,这个程序就截止了,似乎没有每间隔2秒定时一次。原因是,当3秒钟之后,会向进程发送SIGALRM信号,进程收到这个信号后终止。因此,并没有执行每隔2秒定时一次这样的操作。这就需要信号捕捉函数了!!!捕捉这个3秒之后的信号,不让它杀死当前进程。
3.signal信号捕捉函数
通过man 2 signal 查看signal函数的详细描述
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead. (signal()的行为在不同的UNIX版本之间是不同的,在不同的Linux版本之间也是不同的。避免使用它:使用sigaction(2)代替。)
功能:简单来说就是 捕捉到某个信号,然后执行一些处理(可以是系统自带的,也可以是自定义的函数)
signal() sets the disposition of the signal signum to handler, which is either SIG_IGN, SIG_DFL, or the address of a programmer-defined function (a "signal handler"). (将信号signum的处置设置为handler,它可以是SIG_IGN, SIG_DFL,或者是程序员定义的函数(“信号处理程序”)的地址。)
参数:
signum: 要捕捉的信号
handler: 捕捉到信号要如何处理
-
SIG_IGN : 忽略这个信号,什么也不做
-
SIG_DFL : 使用信号默认的行为,相当于没有捕捉
-
自定义(回调函数):这个函数是由内核调用,程序员只负责写,捕捉到信号后如何去处理信号。
回调函数:(类似于C++中的仿函数)
需要程序员实现,提前准备好的,函数的类型根据实际需求,看函数指针的定义
不是程序员调用,而是当信号产生,由内核调用
函数指针是实现回调的手段,函数实现之后,将函数名放到函数指针的位置就可以了。
返回值:
成功,返回上一次注册的信号处理函数的地址。第一次调用返回NULL
失败,返回SIG_ERR,设置错误号
SIGKILL SIGSTOP不能被捕捉,不能被忽略。(SIGKILL是强制杀死进程,不能捕捉它,如果一旦捕捉并设置为SIG_IGN,那么这个进程就结束不了了)
测试代码:过3秒以后,每隔2秒钟定时一次
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void myalarm(int num) {
printf("捕捉到了信号的编号是:%d\n", num);
printf("xxxxxxx\n");
}
// 过3秒以后,每隔2秒钟定时一次
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;
}
3秒钟之后,捕捉到了SIGALRM信号,然后每隔2秒,再次捕捉到SIGALRM信号。
4.sigaction信号捕捉函数
通过man 2 sigaction查看sigaction函数的具体信息
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
功能:
The sigaction() system call is used to change the action taken by a process on receipt of a specific signal. (See signal(7) for an overview of signals.) sigaction()系统调用用于更改进程在接收到特定信号时所采取的操作。(信号的概述见信号(7)。) 【检查或者改变信号的处理,实现信号捕捉】
参数:
signum : 需要捕捉的信号的编号或者宏值(信号的名称);signum specifies the signal and can be any valid signal except SIGKILL and SIGSTOP.
If act is non-NULL, the new action for signal signum is installed from act. If oldact is non-NULL, the previous action is saved in oldact.如果act为非null,则从act安装信号signum的新动作。如果oldact为非null,则先前的操作保存在oldact中。
act :捕捉到信号之后的处理动作
oldact : 上一次对信号捕捉相关的设置,一般不使用,传递NULL
返回值:成功0,失败-1
参数 act 和 oldact是结构体类型
The sigaction structure is defined as something like:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
struct sigaction {
// 函数指针,指向的函数就是信号捕捉到之后的处理函数
/*
sa_handler specifies the action to be associated with signum and may be SIG_DFL for the default action, SIG_IGN to ignore this signal, or a pointer to a signal handling function. This function receives the signal number as its only argument.sa_handler
指定与signum相关联的操作,默认操作可以是SIG_DFL,忽略该信号的SIG_IGN,或者指向信号处理函数的指针。这个函数接收信号号作为它的唯一参数。
*/
void (*sa_handler)(int);
// 不常用
void (*sa_sigaction)(int, siginfo_t *, void *);
// 临时阻塞信号集,在信号捕捉函数执行过程中,临时阻塞某些信号。
sigset_t sa_mask;
// 使用哪一个信号处理对捕捉到的信号进行处理
// 这个值可以是0,表示使用sa_handler,也可以是SA_SIGINFO表示使用sa_sigaction
//On some architectures a union is involved: do not assign to both sa_handler and sa_sigaction.在某些体系结构中涉及到联合:不要同时分配给sa_handler和sa_sigaction。
int sa_flags;
// 被废弃掉了
void (*sa_restorer)(void);
};
测试代码:过3秒以后,每隔2秒钟定时一次
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void myalarm(int num) {
printf("捕捉到了信号的编号是:%d\n", num);
printf("xxxxxxx\n");
}
// 过3秒以后,每隔2秒钟定时一次
int main() {
struct sigaction act;
act.sa_handler = myalarm;
act.sa_flags = 0;
sigemptyset(&act.sa_mask); // 清空临时阻塞信号集,全0
// 注册信号捕捉,捕捉SIGALRM信号,输出myalarm中的信号
sigaction(SIGALRM, &act, NULL);
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();
while(1);
return 0;
}
通过act.sa_mask设置SIGINT信号阻塞,这样程序就不能通过ctrl c停止了:文章来源:https://www.toymoban.com/news/detail-769023.html
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void myalarm(int num) {
printf("捕捉到了信号的编号是:%d\n", num);
printf("xxxxxxx\n");
}
// 过3秒以后,每隔2秒钟定时一次
int main() {
struct sigaction act;
act.sa_handler = myalarm;
act.sa_flags = 0;
sigemptyset(&act.sa_mask); // 清空临时阻塞信号集,全0
sigaddset(&act.sa_mask, SIGINT);
// 修改内核中的阻塞信号集
sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
//设置在信号捕捉函数执行过程中,临时阻塞SIGINT信号。
//这样的话ctrl c就停止不了进程了
// 注册信号捕捉,捕捉SIGALRM信号,输出myalarm中的信号
sigaction(SIGALRM, &act, NULL);
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();
while(1);
return 0;
}
文章来源地址https://www.toymoban.com/news/detail-769023.html
到了这里,关于Linux中的alarm和setitimer定时器函数以及信号捕捉函数signal和sigaction的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!