文章目录
- 前言
- 一、相关函数/系统调用
- 1. signal
- 2. kill
- 3. abort (库函数)
- 4. raise (库函数)
- 5. alarm
前言
现实生活中, 存在着诸多信号, 比如红绿灯, 上下课铃声…我们在接收到信号时, 就会做出相应的动作. 对于进程也是如此的, 进程也会收到来自 OS 发出的信号, 根据信号的不同也会做出不同的动作, 进程在收到信号时也并不一定会立即执行, 也可以在适当的时候在执行该信号对应的动作, 一般信号常见处理方式有如下三种:
- 忽略此信号.
- 执行该信号的默认处理动作.
- 提供一个信号处理函数, 要求内核在处理该信号时切换到用户态执行这个处理函数, 这种方式称为捕捉一个信号.
而在进程中用以保存信号的容器可以是一个位图, 通过 0, 1 来表示是否收到某信号.
在 Linux 中, 可以通过指令:
kill -l
来查看系统定义的信号列表:
通过指令:
man 7 signal
可以查看关于信号的详细说明:
在 Linux 中可以通过指令:
kill -信号编号 进程pid
来对指定进程发送指定信号.
一、相关函数/系统调用
1. signal
头文件: #include <signal.h>
函数声明: sighandler_t signal(int signum, sighandler_t handler);
- signum: 被设置的信号编号.
- handler: 被设置的信号的新的处理函数, 是一个回调函数, 通过用户传递.
- sighandler_t: 是一个函数指针, 可以指向一个返回值为 void, 参数为 int 的函数, 以下是系统中的 typedef:
typedef void (*sighandler_t)(int);
功能: 将指定的信号的处理函数覆盖为 handler.
示例代码:
#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
void sighandler(int signo)
{
cout << "void sighandler(int signo): " << signo << endl;
}
int main()
{
signal(2, sighandler);
while(1)
{
cout << "Hello" << endl;
sleep(1);
}
return 0;
}
运行结果:
实际 Ctrl + C 就是编号为 2 的信号, 平常通过 Ctrl + C 向进程发送 SIGINT(2) 号信号, 可以终止进程, 但是把信号 2 的处理函数换成了自定义的, 所以在终端按下 Ctrl + C 时执行我们自定义的函数.
PS: 9, 18, 19 号信号即时被重定向了新的处理函数也没用, 该信号仍然会执行原本的处理函数.
2. kill
头文件:
#include <sys/types.h>
#include <signal.h>
函数声明: int kill(pid_t pid, int sig);
- pid: 目标进程pid.
- sig: 向目标发送的信号编号.
功能: 向指定进程发送指定信号.
示例代码:
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
using namespace std;
int main()
{
for(int i = 0; i < 10; ++i)
{
if(i == 5)
{
kill(getpid(), 9);
}
cout << i << ":Hello" << endl;
sleep(1);
}
return 0;
}
运行结果:
在输出 5 条语句后, 向自己发送 9 号信号, 直接终止自己了.
3. abort (库函数)
头文件: #include <stdlib.h>
函数声明: void abort(void);
功能: 向调用进程发送终止信号.
示例代码:
#include <iostream>
#include <cstdlib>
#include <unistd.h>
using namespace std;
int main()
{
for(int i = 0; i < 10; ++i)
{
if(i == 5)
{
abort();
}
cout << i << ":Hello" << endl;
sleep(1);
}
return 0;
}
运行结果:
4. raise (库函数)
头文件: #include <signal.h>
函数声明: int raise(int sig);
- 返回值: 成功调用返回 0, 失败返回非零整数.
- sig: 信号编号.
功能: 向调用进程发送指定信号.
示例代码:
#include <iostream>
#include <csignal>
#include <unistd.h>
using namespace std;
int main()
{
for(int i = 0; i < 10; ++i)
{
if(i == 5)
{
raise(9);
}
cout << i << ":Hello" << endl;
sleep(1);
}
return 0;
}
运行结果:
5. alarm
头文件: #include <unistd.h>
函数声明: unsigned int alarm(unsigned int seconds);
- 返回值: 返回一个无符号整数, 表示前一个闹钟剩于的秒数, 打个比方, 闹钟设置为 30s 后响, 但是在 20s 的时候就收到了 SIGALRM(14) 信号, 此时闹钟会提前响, 返回值就为 30 - 20 = 10.
- seconds: 多少秒后响铃.
功能: 在过了 seconds 秒以后终止调用进程.
示例代码:
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
alarm(1);
for(int i = 0; ; i++)
{
cout << i << ":Hello" << endl;
}
return 0;
}
运行结果:
可以看到, 在 1 秒钟后闹钟响了, 进程也就被终止了.
接下来通过另一段代码查看返回值:
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;
void sighandler(int signo)
{
cout << "void sighandler(int signo): " << signo << endl;
int n = alarm(10);
cout << "n: " << n << endl;
}
int main()
{
cout << "pid:" << getpid() << endl;
signal(SIGALRM, sighandler);
alarm(10);
while(1);
return 0;
}
运行结果:
可以看到, 在闹钟设定后, 以我最快的速度给调用闹钟的进程发送 14 号信号之后, 返回的剩于秒数为 8s, 也就是说闹钟只跑了 2s, 而后又设置了一个 10s 后响的闹钟, 这次没有提前发送 14 号信号, 它正常跑完, 返回的剩于秒数为 0s, 合理.