linux C由signal和alarm实现的键盘监控和程序计时

本文介绍Linux环境下alarm和signal函数的应用,包括如何设置定时信号及处理按键中断等信号。通过实例展示了如何实现程序定时并响应Ctrl+C及kill命令。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.alarm()

函数原型:unsigned int alarm(unsigned int seconds);
头文件:#include<unistd.h>
函数说明: alarm()用来设置信号SIGALRM在经过参数seconds指定的秒数后,传送给目前的进程。

如果参数seconds为0,则之前设置的闹钟会被取消,并将剩下的时间返回。
返回值:如果调用此alarm()前,进程已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。 出错返回-1。

2.signal()

函数原型:void (*signal(int signum,void(* handler)(int)))(int);   
或者:typedef void(*sig_t) ( int );   
sig_t signal(int signum,sig_t handler);  

头文件:#include <signal.h>

函数说明:signal()会依参数signum 指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断。  
返回值:返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。


附加说明 :在信号发生跳转到自定的handler处理函数执行后,系统会自动将此处理函数换回原来系统预设的处理方式,如果要改变此操作请改用sigaction()。 

 
下面的情况可以产生Signal:  
1. 按下CTRL+C产生SIGINT   
2. 硬件中断,如除0,非法内存访问(SIGSEV)等等   
3. Kill函数可以对进程发送Signal   
4. Kill命令。实际上是对Kill函数的一个包装   
5. 软件中断。如当Alarm Clock超时(SIGURG),当Reader中止之后又向管道写数据(SIGPIPE),等等

一些常用的Signal 如下:

SignalDescription
SIGABRT由调用abort函数产生,进程非正常退出
SIGALRM用alarm函数设置的timer超时或setitimer函数设置的interval timer超时
SIGBUS某种特定的硬件异常,通常由内存访问引起
SIGCANCEL由Solaris Thread Library内部使用,通常不会使用
SIGCHLD进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略
SIGCONT当被stop的进程恢复运行的时候,自动发送
SIGEMT和实现相关的硬件异常
SIGFPE数学相关的异常,如被0除,浮点溢出,等等
SIGFREEZESolaris专用,Hiberate或者Suspended时候发送
SIGHUP发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送
SIGILL非法指令异常
SIGINFOBSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程
SIGINT由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程
SIGIO异步IO事件
SIGIOT实现相关的硬件异常,一般对应SIGABRT
SIGKILL无法处理和忽略。中止某个进程
SIGLWP由Solaris Thread Libray内部使用
SIGPIPE在reader中止之后写Pipe的时候发送
SIGPOLL当某个事件发送给Pollable Device的时候发送
SIGPROFSetitimer指定的Profiling Interval Timer所产生
SIGPWR和系统相关。和UPS相关。
SIGQUIT输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程
SIGSEGV非法内存访问
SIGSTKFLTLinux专用,数学协处理器的栈异常
SIGSTOP中止进程。无法处理和忽略。
SIGSYS非法系统调用
SIGTERM请求中止进程,kill命令缺省发送
SIGTHAWSolaris专用,从Suspend恢复时候发送
SIGTRAP实现相关的硬件异常。一般是调试异常
SIGTSTPSuspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程
SIGTTIN当Background Group的进程尝试读取Terminal的时候发送
SIGTTOU当Background Group的进程尝试写Terminal的时候发送
SIGURG当out-of-band data接收的时候可能发送
SIGUSR1用户自定义signal 1
SIGUSR2用户自定义signal 2
SIGVTALRMsetitimer函数设置的Virtual Interval Timer超时的时候
SIGWAITINGSolaris Thread Library内部实现专用
SIGWINCH当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程
SIGXCPU当CPU时间限制超时的时候
SIGXFSZ进程超过文件大小限制
SIGXRESSolaris专用,进程超过资源限制的时候发送

综合应用:程序定时10s,期间按下“ctrl+c” 输出“no”,并将程序剩余的定时时间打印输出,执行kill命令,程序立即退出,并输出当前程序执行时间。

要求:1.使用alarm定时;

    2.由于printf()的不安全性,不能在信号处理函数中直接打印输出;

    3.这里建议使用getdayoftime()来实现程序计时,并且只能使用2次,不能使用在循环内部。

#include <stdio.h>
#include <signal.h>
#include <signal.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

/*definetimeval for timing*/
struct timeval begin, end;
static unsigned long diff;

void PrintCtrlC(unsigned short *time)
{
	printf("no\n");
	printf("The Ctrl+C waited %d s to stop\n", *time);
	
}

void HandingCtrlC(int signo)
{
	/* get the rest of time */
	unsigned short  t = alarm(0);

	/*print the time*/
	PrintCtrlC(&t);

	/* start a new timer, the time is the rest of time*/
	alarm(t);
}

void PrintKill(unsigned long *time)
{
	printf("kill\n");
	printf("The program last %ld usec before 'kill'\n", *time);
}

void HandingKill(int signo)
{
	/*the grogram is stop, and timing is also stop.*/
	gettimeofday(&end, NULL);
	
	/*cal the time used*/
	diff = 1000000*(end.tv_sec-begin.tv_sec) + end.tv_usec-begin.tv_usec;

	PrintKill(&diff);
	exit(0);
}

void PrintTimeOut()
{
	printf("10s time out\n");
}
void sig_alarm()
{
	PrintTimeOut();
	exit(0);
}

void alive()
{
	char ch[4+1];

	/*start to timing the running time of the program*/
	gettimeofday(&begin, NULL);
	
	/*spin loop*/
	while(1)
	{
		/*create a timer for 10s*/
		signal(SIGALRM, sig_alarm);
		alarm(10);
	
		/* handing signal of 'Ctr+C', and check the signal.*/
		if(signal(SIGINT, HandingCtrlC) == SIG_ERR)
		{
			perror("SIGINT error\n");
		}

		/* handing signal of 'kill', and check the signal.*/
		if(signal(SIGTERM, HandingKill) == SIG_ERR)
		{
			perror("SIGTERM error\n");
		}

		scanf("%s", ch);
		if(strcmp(ch, "kill")==0)
		{
			break;
		}
	}
}

int main()
{
	alive();
	return 0;
}


注:程序在redhat 4.8上亲测通过;

ubuntu上可能按下ctrl+c输出的结果可能会输出时间有点差错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子建莫敌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值