Linux信号专题——信号是如何产生的
- 1. 终端按键产生信号
- 2. 硬件异常信号
- 3. 函数产生信号
- 3.1 kill函数
- 3.2 raise函数
- 3.3 abort函数
- 4. 时钟信号
- 4.1 alarm函数
- 4.2 setitimer函数
专栏传送门 :《Linux从小白到大神》 系统学习Linux开发、VIM/GCC/GDB/Make工具、Linux文件IO、进程管理、进程通信、多线程等,请关注专栏免费学习。
当程序出现硬件异常会产生信号:
kill命令和kill函数都可以产生信号来杀死进程。kill命令产生信号:kill -SIGKILL pid;kill函数:给指定进程发送指定信号(不一定杀死)。
#include
#include
int kill(pid_t pid, int sig);
函数功能
The kill() system call can be used to send any signal to any process group or process. 给指定进程发送指定信号。
函数参数
pid:进程ID
sig:信号名,不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致。
If sig is 0, then no signal is sent, but error checking is still performed; this can be used to check for the existence of a process ID or process group ID.
函数返回值
#include
int raise(int sig);
函数功能
he raise() function sends a signal to the calling process or thread. 给当前进程发送指定信号(自己给自己发) 。
函数参数
函数返回值
raise() returns 0 on success, and non-zero for failure. 成功返回0,失败返回非0值。
/************************************************************
>File Name : test.c
>Author : Mindtechnist
>Company : Mindtechnist
>Create Time: 2022年05月23日 星期一 14时20分42秒
************************************************************/
#include
#include
#include
#include
int main(int argc, char* argv[])
{
printf("pid: %d\n", getpid());
sleep(1);
raise(SIGKILL); /* 相当于
kill(getpid(), SIGKILL); */
return 0;
}
#include
void abort(void);
函数功能
The abort() first unblocks the SIGABRT signal, and then raises that signal for the calling process. 给自己发送异常终止信号 6) SIGABRT 信号,终止并产生core文件。
函数参数
函数返回值
The abort() function never returns.
#include
unsigned int alarm(unsigned int seconds);
函数功能
设置定时器(闹钟),定时给调用进程(也就是自己)发送SIGALRM,来约定进程几秒钟后结束。在指定seconds后,内核会给当前进程发送14)SIGALRM信号,进程收到该信号,默认动作终止。 每个进程都有且只有唯一一个定时器。定时与进程状态无关(自然定时法),就绪、运行、挂起(阻塞、暂停)、终止、僵尸等等无论进程处于何种状态,alarm都会计时。
alarm() arranges for a SIGALRM signal to be delivered to the calling process in seconds seconds.
If seconds is zero, no new alarm() is scheduled.
In any event any previously set alarm() is canceled.
Signal | Value | Action | Comment |
---|---|---|---|
SIGALRM | 14 | Term | Timer signal from alarm(2) |
Term Default action is to terminate the process. /*终止进程*/
函数参数
函数返回值
alarm() returns the number of seconds remaining until any previously scheduled alarm was due to be delivered, or zero if there was no previously scheduled alarm. 返回上次定时器剩余的秒数。我们实现约定好多少秒时候发送一个信号,alarm()函数返回距离发送信号还剩余的秒数,如果没有剩余时间或没有约定发送信号返回0。可以这么理解,如果是第一次开启定时器,返回0;如果上一次设定了alarm(5),两秒之后又设置了alarm(3),那么这个alarm()返回上一次定时器剩余的时间,也就是5-2=3秒。
用法示例:
/************************************************************
>File Name : test.c
>Author : Mindtechnist
>Company : Mindtechnist
>Create Time: 2022年05月23日 星期一 14时20分42秒
************************************************************/
#include
#include
int main(int argc, char* argv[])
{
int ret = alarm(3);
printf("first alarm(3) return: %d\n", ret);
sleep(2);
ret = alarm(5);
printf("second alarm(5) return: %d\n", ret);
while(1)
{
printf("pid: %d\n", getpid());
sleep(1);
}
return 0;
}
编译运行得到结果
**示例2:**time命令计时与IO优化
#include
#include
int main(int argc, char* argv[])
{
int count = 0;
alarm(1);
while(1)
{
printf("%d\n", count++);
}
return 0;
}
编译运行,使用time命令可以查看程序执行的时间(实际执行时间 = 系统时间 + 用户时间 + 等待时间),time ./a.out
在上面的时间中:
real:总共的时间(自然时间);
user:用户使用时间;
sys:系统时间;
可以看到最大计数到了7572,并且user几乎没有分配到时间,这是因为IO操作(打印屏幕)造成的,我们可以重定向一下输出
可以看到user的时间增加了,并且最大计数达到了306087。实际上程序运行的瓶颈大部分在于IO,优化程序,首先优化IO。
#include
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
函数功能
The system provides each process with three interval timers, each decrementing in a distinct time domain. 设置定时器(闹钟),可代替alarm函数,精度微秒us,可以实现周期定时。
函数参数
which:指定定时方式
new_value:要设置的定时器时间
struct itimerval {
struct timeval it_interval; /* next value 周期性的时间*/
struct timeval it_value; /* current value 下一次闹钟到来的时间 */
};
struct timeval {
long tv_sec; /* seconds 秒*/
long tv_usec; /* microseconds 微秒*/
}; /*秒+微妙才是真正的时间,微妙是为了更精确*/
old_value:原来的定时器时间
函数返回值
**示例1:**使用setitimer实现alarm函数定时功能
#include
#include
#include
int main(int argc, char* argv[])
{
struct itimerval temp = {{0, 0}, {3, 0}};
setitimer(ITIMER_REAL, &temp, NULL); /*ITIMER_REAL,3秒后发送SIGALRM信号*/
while(1)
{
printf("pid: %d\n", getpid());
sleep(1);
}
return 0;
}
编译运行,3秒后闹钟
**示例2:**周期性定时器
#include
#include
#include
#include
/*信号处理回调函数*/
void m_catch(int sig)
{
/*捕捉到信号执行此函数,不杀死进程*/
printf("catch signal: %d\n", sig);
}
int main(int argc, char* argv[])
{
signal(SIGALRM, m_catch/*函数指针做函数参数*/);
/*signal信号捕捉函数,当产生SIGALRM信号的时候,去执行m_catch函数*/
struct itimerval temp = {{2, 0}, {4, 0}}; /*第一次等待4秒,以后每隔2秒发送一个信号*/
setitimer(ITIMER_REAL, &temp, NULL);
while(1)
{
printf("pid: %d\n", getpid());
sleep(1);
}
return 0;
}
编译执行,可以看到第一次隔了4秒捕获到信号,后面周期性的每隔2秒捕获一次信号,不会杀死进程,可以通过ctrl+c杀掉进程。
**示例3:**setitimer实现alarm函数
#include
#include
#include
#include
unsigned int malarm(unsigned int seconds)
{
struct itimerval temp = {{0, 0}, {0, 0}};
struct itimerval ret;
temp.it_value.tv_sec = seconds;
setitimer(ITIMER_REAL, &temp, &ret);
printf("tv_sec: %ld, tv_mirsec: %ld\n", ret.it_value.tv_sec, ret.it_value.tv_usec);
return ret.it_value.tv_sec;
}
int main(int argc, char* argv[])
{
int ret = malarm(5);
printf("malarm() return: %d\n", ret);
sleep(2);
ret = malarm(6);
printf("malarm() return: %d\n", ret);
while(1)
{
printf("pid: %d\n", getpid());
return 0;
}
return 0;
}
编译运行,时间可能会不太准确,这是正常的