Linux下IPC方式之信号1

Linux下IPC方式之信号1

  • 1. 信号的基本属性
    • 1.1 信号的特点
    • 1.2 信号的机制
    • 1.3 信号的产生
    • 1.4 信号的状态
    • 1.5 信号的处理方式
    • 1.6 阻塞信号集和未决信号集
    • 1.7 信号的四要素
    • 1.8 Linux常规信号一览表
  • 2. 信号的产生
    • 2.1 终端按键产生信号
    • 2.2 硬件异常产生信号
    • 2.3 `kill`函数/命令产生信号
    • 2.4 `raise`和`abort`函数
    • 2.5 时钟产生信号
    • 2.5.1 alarm函数
    • 2.5.2 setitimer函数

1. 信号的基本属性

1.1 信号的特点

简单,不能携带大量信息,满足特定条件发生,信号也叫软中断,有可能会有延迟。

1.2 信号的机制

信号实际上是由内核发送,内核来处理收到的信号。收到信号的进程,必须对信号做出处理(忽略,捕获,默认动作都行)

1.3 信号的产生

  1. 按键产生,如:Ctrl+c、Ctrl+z、Ctrl+\
  2. 系统调用产生,如:kill、raise、abort
  3. 软件条件产生,如:定时器alarm
  4. 硬件异常产生,如:非法访问内存(段错误)、除0(浮点数例外)、内存对齐出错(总线错误)
  5. 命令产生,如:kill命令

1.4 信号的状态

  • 产生
  • 递达:信号到达并且处理完
  • 未决:产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态。

1.5 信号的处理方式

  • 执行默认动作
  • 忽略(丢弃)
  • 捕捉(调用户处理函数)

1.6 阻塞信号集和未决信号集

阻塞信号集(信号屏蔽字): 将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(解除屏蔽后)
未决信号集:

  • 信号产生,未决信号集中描述该信号的位立刻翻转为1,表信号处于未决状态。当信号被处理对应位翻转回为0。这一时刻往往非常短暂。
  • 信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态。
    Linux下IPC方式之信号1_第1张图片
    如果我们把2号信号设置成阻塞(即在阻塞信号集的对应位置设为1),那么来一个2号信号,则未信号集的对应值置为1,什么时候阻塞信号集中的对应位置变成0了,什么时候未决信号集才能去处理之前被阻塞的那个信号。

1.7 信号的四要素

  • 编号
  • 事件
  • 名称
  • 默认处理动作
    1 忽略
    2 终止
    3 终止+core
    4 暂停
    5 继续

默认处理动作有五个(linux 输入 man 7 signal 即可查看),如图所示:Linux下IPC方式之信号1_第2张图片
注意:9号,19号信号,不能捕捉,不能忽略,不能阻塞。
Linux下IPC方式之信号1_第3张图片

1.8 Linux常规信号一览表

编号 名称 作用
1 SIGHUP 用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程
2 SIGINT 当用户按下了组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。
3 SIGQUIT 当用户按下组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号。默认动作为终止进程。
4 SIGILL CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件
5 SIGTRAP 该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件。
6 SIGABRT 调用abort函数时产生该信号。默认动作为终止进程并产生core文件。
7 SIGBUS 非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。
8 SIGFPE 在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件。
9 SIGKILL 无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。
10 SIGUSE1 用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。
11 SIGSEGV 指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。
12 SIGUSR2 另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。
13 SIGPIPE Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。
14 SIGALRM 定时器超时,超时的时间 由系统调用alarm设置。默认动作为终止进程。
15 SIGTERM 程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行shell命令Kill时,缺省产生这个信号。默认动作为终止进程。
16 SIGSTKFLT Linux早期版本出现的信号,现仍保留向后兼容。默认动作为终止进程。
17 SIGCHLD 子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。
18 SIGCONT 如果进程已停止,则使其继续运行。默认动作为继续/忽略。
19 SIGSTOP 停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为暂停进程。
20 SIGTSTP 停止终端交互进程的运行。按下组合键时发出这个信号。默认动作为暂停进程。
21 SIGTTIN 后台进程读终端控制台。默认动作为暂停进程。
22 SIGTTOU 该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。
23 SIGURG 套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。
24 SIGXCPU 进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程。
25 SIGXFSZ 超过文件的最大长度设置。默认动作为终止进程。
26 SIGVTALRM 虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。
27 SGIPROF 类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。
28 SIGWINCH 窗口变化大小时发出。默认动作为忽略该信号。
29 SIGIO 此信号向进程指示发出了一个异步IO事件。默认动作为忽略。
30 SIGPWR 关机。默认动作为终止进程。
31 SIGSYS 无效的系统调用。默认动作为终止进程并产生core文件。
34~64 SIGRTMIN~ SIGRTMAX LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。

2. 信号的产生

2.1 终端按键产生信号

Ctrl + c → 2) SIGINT(终止/中断) “INT” ----Interrupt
Ctrl + z → 20) SIGTSTP(暂停/停止) “T” ----Terminal 终端。
Ctrl + \ → 3) SIGQUIT(退出)

2.2 硬件异常产生信号

除0操作 → 8) SIGFPE (浮点数例外) “F” -----float 浮点数。
非法访问内存 → 11) SIGSEGV (段错误)
总线错误 → 7) SIGBUS

2.3 kill函数/命令产生信号

kill命令产生信号:kill -SIGKILL pid
kill函数:给指定进程发送指定信号(不一定杀死)

 int kill(pid_t pid, int sig);	
pid > 0		要发送进程ID
pid = 0		代表当前调用进程中内所有进程
pid = -1	代表有权限发送的所有进场
pid < 0		代表 -pid 对应的组内所有进程
sig			对应的信号,不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致。

练习,让3号子进程杀死父进程

#include
#include
#include
#include
#include
 
int main(){
	int i;
	for(i=0; i<5; i++){
		pid_t pid=fork();
		if(pid==0){
			break;
		}
	}
	//我们使3号子进程杀死父进程
	if(i==2){
		printf("I will kill my father after 5 second!\n");
		sleep(5);
		kill(getppid(),SIGKILL);
	}
	if(i==5){
		while(1){
			printf("I am father!\n");
			sleep(1);
		}
	}
	return 0;
}

运行结果:
Linux下IPC方式之信号1_第4张图片
父进程把3号子进程杀死:

#include
#include
#include
#include
 
int main(){
	int i=0;
	pid_t pid3, pid;
	for(i=0; i<5; i++){
		pid=fork();
		if(pid==0) break;
		if(i==2) pid3=pid;
	}
	if(i<5){
		while(1){
			printf("I am child, pid=%d, ppid=%d\n", getpid(), getppid());
			sleep(3);
		}
	}
	else if(i==5){
		printf("I am father,pid=%d, I will kill xiao san pid3=%d\n", getpid(), pid3);
		sleep(5);
		kill(pid3, SIGKILL);
		while(1) sleep(1);
	}
	return 0;
}

运行结果:
Linux下IPC方式之信号1_第5张图片

2.4 raiseabort函数

raise 函数:给当前进程发送指定信号(自己给自己发)
raise(signo) == kill(getpid(), signo);

int raise(int sig); 
返回值:	成功:0,失败非0
#include
#include
#include
#include
 
int main(){
	printf("I will die!\n");
	sleep(2);
	raise(SIGKILL);//kill(getpid(), sig)
	return 0;
}

abort 函数:给自己发送异常终止信号 6) SIGABRT 信号,终止并产生core文件

void abort(void); 该函数无返回
#include
#include
#include
#include
#include 
 
int main(){
	printf("I will die!\n");
	sleep(2);
	//raise(SIGKILL);//kill(getpid(), sig)
    abort();
	return 0;
}

在这里插入图片描述

2.5 时钟产生信号

2.5.1 alarm函数

设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终止(Term)。
每个进程都有且只有唯一个定时器。
定时给自己发送SIGALRM
几秒后发送信号
返回值——上次闹钟剩余的秒数
特别的,如果传入参数秒为0,代表取消闹钟


#include
#include
 
int main(){
	alarm(6);
	while(1){
		printf("hello world \n");
		sleep(1);
	}
	return 0;
}

Linux下IPC方式之信号1_第6张图片

alarm函数的返回值

#include
#include
 
int main(){
	int ret=0;
	ret=alarm(6);
	printf("ret=%d\n", ret);
	sleep(2);
	ret=alarm(5);
	printf("ret=%d\n", ret);
	while(1){
		printf("hello world\n");
		sleep(1);
	}
	return 0;
}

运行结果:
Linux下IPC方式之信号1_第7张图片

2.5.2 setitimer函数

设置定时器(闹钟)。 可代替alarm函数。精度微秒us,可以实现周期定时

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);	
返回值		成功:0;失败:-1,设置errno

参数:
which:指定定时方式
		ITIMER_REAL 	自然定时法 			SIGALRM
		ITIMER_VIRTUAL 	计算进程执行时间 	SIGVTALRM
		ITIMER_PROF 	程序执行时间+调度时间 ITIMER_VIRTUAL
		
new_value 	要设置的闹钟时间

old_value 	原闹钟时间

Linux下IPC方式之信号1_第8张图片
Linux下IPC方式之信号1_第9张图片
setitimer实现alarm功能

#include
#include
#include
 
int main(){
	//setitimer是一个结构体中嵌套了两个结构体
	//两个结构体分别代表:周期性的时间设置,下次的闹钟时间
	//每个结构体里。,第一个参数是秒,第二个参数是微妙
	//这里定义的意思就是:三秒之后发送SIGALRM信号
	struct itimerval myit={{0,0},{3,0}};
	setitimer(ITIMER_REAL, &myit, NULL);
 
	while(1){
		printf("Who can kill me!\n");
		sleep(1);
	}
	return 0;
}

运行结果:
Linux下IPC方式之信号1_第10张图片
测试周期性发送信号功能
周期性发送SIGALRM信号杀死进程,进程利用catch_sig函数来捕获SIGALRM信号

#include
#include
#include
#include
 
void catch_sig(int num){
	printf("cat %d sig\n", num);
}
int main(){
	signal(SIGALRM, catch_sig);
	//第一次等待五秒,之后每隔三秒
	struct itimerval myit={{3,0},{5,0}};
	setitimer(ITIMER_REAL, &myit, NULL);
 
	while(1){
		printf("Who can kill me!\n");
		sleep(1);
	}
	return 0;
}

运行结果:
Linux下IPC方式之信号1_第11张图片

setitimer实现alarm

#include
#include
#include
 
unsigned int myalarm(unsigned int seconds){
	struct itimerval oldit,myit={{0,0},{0,0}};
	//second秒之后,给我发送一个SIGALRM信号
	myit.it_value.tv_sec=seconds;
	//old_value 原闹钟时间
	setitimer(ITIMER_REAL, &myit, &oldit);
	//打印秒和微秒
	printf("tv_sec=%ld, tv_microsec=%ld\n",
		oldit.it_value.tv_sec, oldit.it_value.tv_usec);
	return oldit.it_value.tv_sec;
 
}
 
int main(){
	int ret=0;
	ret=myalarm(5);
	printf("ret=%d\n", ret);
	sleep(3);
	ret=myalarm(3);
	printf("ret=%d\n", ret);
	while(1){
		printf("hello world\n");
		sleep(1);
	}
	return 0;
}

运行结果:
Linux下IPC方式之信号1_第12张图片
后接Linux下IPC方式之信号2

你可能感兴趣的:(C++系统编程)