IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> unix-linux 信号处理 -> 正文阅读

[系统运维]unix-linux 信号处理

进程之间的通信方式(数据的传递):

1.普通的方式
	文件 内存映射(mmap) 映射到同一个物理空间
	父进程传递给子进程:
	main函数参数列表   环境列表
2.信号  kill -sig pid
	给指定进程pid发生一个sig信号
3.传统古老的方式 pipe 管道
4.IPC进程间通信
	共享内存 sharemomery
	消息队列 message
	信号量集 semoper
5.网络通信
	socket

信号提供了一种异步处理的机制

查看各类信号 kill -l

一张信号图

信号分为可靠信号(实时信号)和不可靠信号

可靠信号:

位于[SIGRTMIN(34),SIGRTMAX(64)]区间的信号都是可靠信号。
支持排队,不会丢失。
无论可靠信号还是不可靠信号,都可以通过sigqueue/sigaction函数发送/安装,以获得比其早期版本kill/signal函数更可靠的使用效果。

不可靠信号:

那些建立在早期机制上的信号被称为“不可靠信号”,小于SIGRTMIN(34)的信号都是不可靠信号。
不支持排队,可能会丢失。同一个信号产生多次,进程可能只收到一次该信号。
进程每次处理完这些信号后,对相应信号的响应被自动恢复为默认动作,除非显示地通过signal函数重新设置一次信号处理程序。

信号的处理

忽略。
终止进程。
终止进程同时产生core文件。 core文件就是段错误
捕获并处理。当信号发生时,内核会调用一个事先注册好的用户函数(信号处理函数)。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
//信号处理函数  当接收对应的信号时 执行的代码
void sigtstp_proc(int sig){
	printf("%d进程接收到一个信号:%d\n",getpid(),sig);	
	signal(sig,sigtstp_proc);//确保永久有效
}
void sig_proc(int sig){
	printf("%d进程接收到一个信号:%d\n",getpid(),sig);	
}
int main(){
	printf("进程:%d\n",getpid());
	//SIGINT  忽略  终止  终止+core  添加处理方式
	if(signal(SIGINT,SIG_IGN)==SIG_ERR){//忽略
		perror("singal");
		return -1;
	}
	if(signal(SIGQUIT,SIG_DFL)==SIG_ERR){//默认处理
		perror("signal");
		return -1;
	}
	//当进程接收到SIGTSTP信号时 会去执行sigtstp_proc函数
	if(signal(SIGTSTP,sigtstp_proc)==SIG_ERR){//自定义处理方式
		perror("signal");
		return -1;
	}
	if(signal(6,sig_proc)==SIG_ERR){
		perror("signal");
		return -1;
	}
	if(signal(9,SIG_IGN)==SIG_ERR){
		perror("signal");	
	}
	for(;;);
	return 0;	
}

signal

sighandler_t signal (int signum,sighandler_t handler); 注册信号的函数

成功返回原来的信号处理函数指针或SIG_IGN/SIG_DFL常量,
SIG_IGN: 忽略该信号;
SIG_DFL: 默认处理。

失败返回SIG_ERR

父子signal

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

void sigint_proc(int sig){
	printf("%d进程接收到%d信号!\n",getpid(),sig);	
}

int main(){
	//父进程的注册的信号处理函数
	if(signal(SIGINT,sigint_proc)==SIG_ERR){
		perror("signal");
		return -1;
	}
	pid_t id = fork();
	if(id == -1){
		perror("fork");
		return -1;
	}
	if(id == 0){//子进程继承父进程的信号处理函数
		printf("子进程:%d\n",getpid());	
		
	}else{
		printf("父进程:%d\n",getpid());	
	}
	for(;;);
	
	return 0;	
}

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

void sigint_proc(int sig){
	printf("%d进程接收到%d信号!\n",getpid(),sig);	
}

int main(){
	//父进程的注册的信号处理函数
	if(signal(SIGINT,sigint_proc)==SIG_ERR){
		perror("signal");
		return -1;
	}
	pid_t id = fork();
	if(id == -1){
		perror("fork");
		return -1;
	}
	if(id == 0){//子进程继承父进程的信号处理函数
		printf("子进程:%d\n",getpid());	
		execve("rundead",NULL,NULL);		
	}else{
		printf("父进程:%d\n",getpid());	
	}
	for(;;);
	
	return 0;	
}

子进程会继承父进程的信号处理方式
//子程调用exec函数后, exec函数将被父进程设置为捕获的信号恢复至默认处理, 其余保持不变。

子进程结束会向父进程发送SIGCHLD

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

void sigchild_proc(int sig){
	printf("%d 进程收到%d信号!\n",getpid(),sig);	
	//可以在这个地方等待子进程结束
	pid_t id = wait(NULL);//处理子进程结束  不会阻塞了
	printf("%d 进程的子进程%d结束了!\n",getpid(),id);
}

int main(){
	if(signal(SIGCHLD,sigchild_proc)==SIG_ERR){
		perror("signal");
		return -1;
	}
	pid_t id[3];
	int i;
	for(i=0;i<3;i++){
		id[i] = fork();
		if(id[i]==-1)return-1;
		if(id[i]==0)
			break;
	}
	if(i<3){
		printf(" 我是快乐的子进程:%d\n",getpid());
		sleep(i*i+3);
		return i;//子进程结束 会向父进程发送17信号SIGCHLD
	}
	/*
	pid_t ret = 0;
	while((ret = wait(NULL))>=0){
		printf(" 我是父进程%d,子进程%d结束了\n",getpid(),ret);
	}
	*/
	for(;;);//模拟父进程很忙

	return 0;	
}

命令

mykill 函数实现命令行功能

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc,char *argv[]){
	if(argc < 3){
		printf("%s -sig pid\n",argv[0]);
		return -1;
	}
	int sig = -atoi(argv[1]);
	pid_t pid = atoi(argv[2]);
	
	int ret = kill(pid,sig);
	if(ret == -1){
		if(sig == 0 && errno == ESRCH){
			printf("%d进程不存在!\n",pid);
		}
		perror("kill");
		return -1;
	}
	
	return 0;	
}

gcc mykill.c -o kl
kl -2 2668

raise.c

向调用进程自身发送sig信号。成功返回0,失败返回-1。
类似kill(getpid(),pid)  
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>

void sigint_proc(int sig){
	printf("%d进程收到%d信号!\n",getpid(),sig);	
}

int main(){
	if(signal(SIGINT,sigint_proc)==SIG_ERR){
		perror("signal");
		return -1;
	}

	raise(SIGINT);//向本进程发送一个信号
	kill(getpid(),SIGINT);//向自己发送一个信号SIGINT
	for(;;);
	return 0;	
}

pause.c

进入睡眠状态
只有调用了信号处理函数并从中返回以后,该函数才会返回
ps aux | grep a.out
可查看程序状态 T是暂停状态
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void sigint_proc(int sig){
	
}

int main(){
	if(signal(SIGINT,sigint_proc)==SIG_ERR){
		perror("signal");
		return -1;
	}
	printf("begin...\n");
	pause();//停住了  收到信号之后被唤醒
	printf("end!\n");
	return 0;	
}

sleep

该函数要么返回0(睡够),要么返回剩余秒数(被信号中断)。 
只有睡够seconds秒,或调用了信号处理函数并从中返回以后,该函数才会返回。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void sigint_proc(int sig){
	
}

int main(){
	if(signal(SIGINT,sigint_proc)==SIG_ERR){
		perror("signal");
		return -1;
	}
	unsigned int ret = sleep(10);//返回剩余的秒数  可能被信号唤醒
	printf("ret = %u\n",ret);
	return 0;	
}

alarm

一个计时器,结果又有点像时钟
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>

void sigalarm_proc(int sig){
	system("clear");
	time_t t = time(NULL);
	struct tm *lt = localtime(&t);
	printf("	%2d:%2d:%2d \n",lt->tm_hour,lt->tm_min,lt->tm_sec);
	alarm(1);//1秒之后又发出SIGALRM信号
}

int main(){
	if(signal(SIGALRM,sigalarm_proc)==SIG_ERR){
		perror("signal");
		return -1;
	}
	alarm(3);
	int ret = alarm(1);
	//两个alarm 结束上面的,只执行下面的,但是返回上面剩余的秒数
	printf("%d \n",ret);
	for(;;);
	return 0;	
}

信号集与信号阻塞(信号屏蔽)

#include <stdio.h>
#include <signal.h>

void test(sigset_t *t,int sig){
	if(sigismember(t,sig)==1){
		printf("信号集中有%d信号!\n",sig);
	}else{
		printf("信号集中没有%d信号!\n",sig);	
	}
}

int main(){
	
	sigset_t set;//信号集  不能直接初始化
	sigemptyset(&set);//信号集中所有的信号清除
	test(&set,2);
	test(&set,3);
	test(&set,4);
	sigaddset(&set,SIGINT);//将信号SIGINT添加到信号集中
	test(&set,2);
	test(&set,3);
	sigfillset(&set);//将所有信号都添加到信号集中
	test(&set,2);
	test(&set,3);
	test(&set,4);
	sigdelset(&set,SIGINT);//从信号集中删除SIGINT信号
	test(&set,2);
	test(&set,3);
	test(&set,4);

	
	return 0;	
}

结果:

信号集中没有2信号!
信号集中没有3信号!
信号集中没有4信号!
信号集中有2信号!
信号集中没有3信号!
信号集中有2信号!
信号集中有3信号!
信号集中有4信号!
信号集中没有2信号!
信号集中有3信号!
信号集中有4信号!


sigemptyset(&set);//信号集中所有的信号清除
sigaddset(&set,SIGINT);//将信号SIGINT添加到信号集中
sigfillset(&set);//将所有信号都添加到信号集中
sigdelset(&set,SIGINT);//从信号集中删除SIGINT信号

sigpromask 信号屏蔽

一个终端运行
另一个终端 
kill -2 pid
kill -3 pid
kill -40 pid
kill -40 pid
kill -40 pid
kill -5 pid
只有当捕获到-5的信号,前面才会执行,并且不可靠信号相同的只能执行一次,可靠信号可全部重复执行
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

sigset_t set;
sigset_t oldset;
void sig_proc(int sig){
	printf("收到了%d信号\n",sig);	
}
void sig5_proc(int sigs){
	//获取处于未决状态的信号集
	sigset_t sigpset;
	sigpending(&sigpset);
	int sig = 1;
	for(;sig<65;sig++){
		if(sigismember(&sigpset,sig)==1){
			printf("%d 信号处于未决!\n",sig);	
		}	
	}
	//解除信号掩码
	sigprocmask(SIG_SETMASK,&oldset,NULL);
	//在屏蔽期间的信号将会被递送
}
int main(){
	printf("进程:%d\n",getpid());
	sigemptyset(&set);
	//不可靠信号
	sigaddset(&set,2);
	sigaddset(&set,3);
	sigaddset(&set,4);
	//可靠信号
	sigaddset(&set,40);
	sigaddset(&set,41);
	sigaddset(&set,42);
	signal(2,sig_proc);
	signal(3,sig_proc);
	signal(4,sig_proc);
	signal(40,sig_proc);
	signal(41,sig_proc);
	signal(42,sig_proc);
	signal(5,sig5_proc);//收到5信号 解除信号掩码 解除之后,在屏蔽期间收到的信号就会被递送
	//int ret = sigprocmask(SIG_SETMASK,&set,&oldset);
	int ret = sigprocmask(SIG_BLOCK,&set,NULL);
	if(ret == -1){
		perror("sigprocmask");	
	}
	for(;;);
	//sigprocmask(SIG_SETMASK,&oldset,NULL);//恢复
	sigprocmask(SIG_UNBLOCK,&set,NULL);//不太科学
	return 0;	
}

sigaction

?1) 缺省情况下,在信号处理函数的执行过程中,会自动屏蔽这个正在被处理的信号。
而对于其它信号则不会屏蔽,通过sigaction::sa_mask成员可以人为指定。
在信号处理函数的执行过程中,需要加入进程信号掩码中的信号。 并在信号处理函数执行完之后,自动解除对这些信号的屏蔽。
?2)sigaction::sa_flags可为以下值的位或: SA_ONESHOT/SA_RESETHAND执行完一次信号处理函数后,即将对此信号的处理恢复为 默认方式(这也是老版本signal函数的缺省行为。
SA_NODEFER/SA_NOMASK 在信号处理函数的执行过程中,不屏蔽这个正在被处理的信号。 ?SA_NOCLDSTOP
若signum参数取SIGCHLD,则当子进程暂停时,不通知父进程。
SA_RESTART 系统调用一旦被signum参数所表示的信号中断,会自行重启。
SA_SIGINFO 使用信号处理函数指针2,通过该函数的第二个参数,提供更多信息。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void sigint_proc(int sig){//在处理sig信号时 自动屏蔽该信号

	printf("收到信号%d\n",sig);
	sleep(10);
	printf("proc end!\n");
}


int main(){
	//signal(2,sigint_proc);
	sigset_t set;
	sigemptyset(&set);
	struct sigaction act={};
	act.sa_handler = sigint_proc;
	act.sa_mask = set;//没有屏蔽任何信号
	act.sa_flags = SA_NOMASK;
	int ret = sigaction(2,&act,NULL);
	if(ret == -1){
		perror("sigaction");
		return -1;
	}

	for(;;);
	
	return 0;	
}

和上面一个相比较而言flags处多 | 了SA_ONESHOT
//在处理sig这个信号时,自动屏蔽该信号,2(SIGINT)一次,后面的2就会被屏蔽
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void sigint_proc(int sig){//在处理sig信号时 自动屏蔽该信号

	printf("收到信号%d\n",sig);
	sleep(10);
	printf("proc end!\n");
}


int main(){
	//signal(2,sigint_proc);
	sigset_t set;
	sigemptyset(&set);
	struct sigaction act={};
	act.sa_handler = sigint_proc;
	act.sa_mask = set;//没有屏蔽任何信号
	act.sa_flags = SA_NOMASK|SA_ONESHOT;
	int ret = sigaction(2,&act,NULL);
	if(ret == -1){
		perror("sigaction");
		return -1;
	}

	for(;;);
	
	return 0;	
}


SA_SIGINFO 多一个函数可以传递其他参数
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void sigint_proc(int sig){//在处理sig信号时 自动屏蔽该信号

	printf("收到信号%d\n",sig);
	sleep(10);
	printf("proc end!\n");
}

void sigaction_proc(int sig,siginfo_t *info,void *ptr){
	printf("%d进程收到一个信号:%d\n",getpid(),sig);
	printf("该信号来自进程:%d,并且携带数据:%d\n",info->si_pid,info->si_value.sival_int);
}

int main(){
	printf("进程:%d\n",getpid());
	//signal(2,sigint_proc);
	sigset_t set;
	sigemptyset(&set);
	struct sigaction act={};
	//act.sa_handler = sigint_proc;
	act.sa_sigaction = sigaction_proc;
	act.sa_mask = set;//没有屏蔽任何信号
	act.sa_flags = SA_NOMASK|SA_SIGINFO;
	int ret = sigaction(2,&act,NULL);
	if(ret == -1){
		perror("sigaction");
		return -1;
	}

	for(;;);
	
	return 0;	
}

sigqueue

因为kill不能带数据  
所以有sigqueue 可以传递数据
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

//sigq -sig pid data
int main(int argc,char *argv[]){
	if(argc < 4){
		printf("%s -sig pid data\n",argv[0]);
		return -1;
	}
	printf("%d\n",getpid());
	int sig = -atoi(argv[1]);
	int pid = atoi(argv[2]);
	int data = atoi(argv[3]);
	union sigval val;
	val.sival_int = data;
	int ret = sigqueue(pid,sig,val);
	if(ret == -1){
		perror("sigqueue");
		return -1;
	}


	return 0;
}

和sigaction联合使用 
两个终端
sigqueue取别名sigq
运行sigaction 等待信号
另一个终端 sigq -2  3814 100
sigaction输出数据 
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void sigint_proc(int sig){//在处理sig信号时 自动屏蔽该信号
//在处理sig这个信号时,自动屏蔽该信号,2(SIGINT)一次,后面的2就会被屏蔽
	printf("收到信号%d\n",sig);
	sleep(10);
	printf("proc end!\n");
}

void sigaction_proc(int sig,siginfo_t *info,void *ptr){
	printf("%d进程收到一个信号:%d\n",getpid(),sig);
	printf("该信号来自进程:%d,并且携带数据:%d\n",info->si_pid,info->si_value.sival_int);
}

int main(){
	printf("进程:%d\n",getpid());
	//signal(2,sigint_proc);
	sigset_t set;
	sigemptyset(&set);
	struct sigaction act={};
	//act.sa_handler = sigint_proc;
	act.sa_sigaction = sigaction_proc;
	act.sa_mask = set;//没有屏蔽任何信号
	act.sa_flags = SA_NOMASK|SA_SIGINFO;
	int ret = sigaction(2,&act,NULL);
	if(ret == -1){
		perror("sigaction");
		return -1;
	}

	for(;;);
	
	return 0;	
}

time

计时器
SIGALRM - 真实计时器 
SIGVTALRM - 虚拟计时器 
SIGPROF - 实用计时器 
ITIMER_REAL: 真实计时器;
ITIMER_VIRTUAL: 虚拟计时器;
ITIMER_PROF: 实用计时器。
定时任务
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
//看电影 打游戏  以一定的频率刷新
//定时任务   每隔一个时间间隔需要执行一次
//alarm
void sigalarm_proc(int sig){
	system("clear");
	time_t t = time(NULL);	
	struct tm *lt = localtime(&t);
	printf("%2d:%2d:%2d\n",lt->tm_hour,lt->tm_min,lt->tm_sec);
	//alarm(1);
}

int main(){
	//定时器  
	if(signal(SIGALRM,sigalarm_proc)==SIG_ERR){
		perror("signal");
		return -1;
	}
	struct itimerval t = {{1,0},{3,0}};//5每隔5秒发送一次 10:10秒之后发送第一个
	//{10,5},//10.5
	int ret = setitimer(ITIMER_REAL,&t,NULL);
	if(ret == -1){
		perror("setitmer");
		return -1;
	}
	for(;;);
	return 0;	
}

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-08-07 12:29:14  更:2021-08-07 12:31:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 6:29:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码