目录
1.信号的概念
信号是一种异步通信的方式
1.1信号的5种默认处理动作
2.各种函数
2.1 kill函数
- 功能:给任何的进程或者进程组pid, 发送任何的信号 sig
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
- 参数:
- pid :
> 0 : 将信号发送给指定的进程
= 0 : 将信号发送给当前的进程组
= -1 : 将信号发送给每一个有权限接收这个信号的进程
< -1 : 这个pid=某个进程组的ID取反 (-12345)
- sig : 需要发送的信号的编号或者是宏值,0表示不发送任何信号
kill(getppid(), 9);//终止当前进程组
kill(getpid(), 9);//终止当前进程
int main() {
pid_t pid = fork();
if(pid == 0) {
// 子进程
int i = 0;
for(i = 0; i < 5; i++) {
printf("child process\n");
sleep(1);
}
} else if(pid > 0) {//注意的是,fork函数对父进程返回的是子进程的pid
// 父进程
printf("parent process\n");
sleep(2);
printf("kill child process now\n");
kill(pid, SIGINT);
}
return 0;
}
2.2 raise函数
- 功能:给当前进程发送任何信号
int raise(int sig);
- 功能:给当前进程发送信号
- 参数:
- sig : 要发送的信号
- 返回值:
- 成功 0
- 失败 非0
2.3 abort函数
- 功能: 发送SIGABRT信号(6)给当前的进程,杀死当前进程,产生core
void abort(void);
kill(getpid(), SIGABRT);
2.4 alarm函数
- 功能:设置定时器(闹钟)。函数调用,开始倒计时,当倒计时为0的时候,
函数会给当前的进程发送一个信号:SIGALARM(14,定时器超时)
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
- 参数:
seconds: 倒计时的时长,单位:秒。如果参数为0,定时器无效(不进行倒计时,不发信号)。
取消一个定时器,通过alarm(0)。
- 返回值:
- 之前没有定时器,返回0
- 之前有定时器,返回之前的定时器剩余的时间
- SIGALARM :默认终止当前的进程,每一个进程都有且只有唯一的一个定时器。
alarm(10); -> 返回0
过了1秒
alarm(5); -> 返回9
注意:alarm函数是非阻塞的
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) {//10秒后被终止
}
return 0;
}
这里有个细节,第一次写没有两个\n,因此终端没有打印任何信息,这是因为linux需要换行符来刷新I\O缓冲区
实际的时间 = 内核时间(1s) + 用户时间 + 消耗的时间
进行文件IO操作的时候比较浪费时间
因此如果我们设置让进程运行一秒,期间伴随着用户态和内核态的切换以及I\O操作,所用的时间会不止一秒
2.5 setitimer函数
- 功能:设置定时器(闹钟)。可以替代alarm函数。精度微秒us,可以实现周期性定时
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
- 参数:
- which : 定时器以什么时间计时
ITIMER_REAL: 真实时间,时间到达后发送 SIGALRM 常用
ITIMER_VIRTUAL: 用户时间,时间到达,发送 SIGVTALRM,只计算进程占用cpu的使用时间
ITIMER_PROF: 以该进程在用户态和内核态下所消耗的时间来计算,时间到达,发送 SIGPROF
ITIMER_REAL、ITIMER_VIRTUAL和ITIMER_PROF是Linux操作系统中的三个定时器类型,它们各自有不同的用途。
1. ITIMER_REAL
ITIMER_REAL用于设置一个实际时间计时器。当计时器到期后,会向进程发送SIGALRM信号。这种计时器通常用于在指定时间间隔内执行某些任务或超时检测。
2. ITIMER_VIRTUAL
ITIMER_VIRTUAL用于设置一个虚拟时间计时器。这种计时器只在进程运行用户空间程序代码(即CPU处于用户态)期间进行计数,当该计数达到设定值后,也会向进程发送SIGVTALRM信号。
这种计时器通常用于测量进程使用CPU的时间片数量,并且可以防止某些恶意程序长时间占据CPU资源。
另外需要注意,在多核处理器中,如果进程在不同的核心上运行,则ITIMER_VIRTUAL计时器只能记录当前正在使用 CPU 的那个核心上所花费的 CPU 时间,并不能记录整个进程占用 CPU 的总时间。
3. ITIMER_PROF
ITIMER_PROF是一个混合型定时器,在同时跟踪实际和虚拟处理时间方面都很有用。每次此类定制周期结束之前将生成 SIGPROF 信号并启动新周期。
总结一下:
- ITMER_REAL:实际物理时间。
- ITMER_VIRTUAL:虚拟 CPU 时间。
- ITMER_PROF: 实现了两者混合模式的功能
- *new_value: 设置定时器的属性,他是一个地址
struct itimerval { // 定时器的结构体
struct timeval it_interval; // 每个阶段的时间,间隔时间
struct timeval it_value; // 延迟多长时间执行定时器
};
struct timeval { // 时间的结构体
time_t tv_sec; // 秒数
suseconds_t tv_usec; // 微秒
};
过10秒后,每个2秒定时一次
- old_value :记录上一次的定时的时间参数,一般不使用,指定NULL
- 返回值:
成功 0
失败 -1 并设置错误号
测试代码
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
// 过3秒以后,每隔2秒钟定时一次
int main() {
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;
}
2.6 signal信号捕捉函数
- 功能:设置某个信号的捕捉行为
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
- 参数:
- signum: 要捕捉的信号
- handler: 捕捉到信号要如何处理
- SIG_IGN : 忽略信号
- SIG_DFL : 使用信号默认的行为
- 回调函数 : 这个函数是内核调用,程序员只负责写,捕捉到信号后如何去处理信号。
回调函数:
- 需要程序员实现,提前准备好的,函数的类型根据实际需求,看函数指针的定义
- 不是程序员调用,而是当信号产生,由内核调用
- 函数指针是实现回调的手段,函数实现之后,将函数名放到函数指针的位置就可以了。
- 返回值:
成功,返回上一次注册的信号处理函数的地址。第一次调用返回NULL
失败,返回SIG_ERR,设置错误号
SIGKILL(9) SIGSTOP(19)不能被捕捉,不能被忽略。
typedef void (*sighandler_t)(int);
定义了一个sighandler_t类型的函数指针,指向返回值为void,参数为int的一类函数,当然我们也可以自己写一个返回值为void,参数为int的函数,将函数的地址传递给signal函数
测试代码:
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;
}
对于
void myalarm(int num) {
printf("捕捉到了信号的编号是:%d\n", num);
printf("xxxxxxx\n");
}
我们也可以用函数指针的方式去写
typedef void (*sighandler_t)(int);
-定义一个sighanler_t的函数指针类型,指向一类返回值为void,参数为int的函数
int main(){
sighandler_t func;
func = myalarm;
signal(SIGALRM,func);
}