ZUCC_操作系统实验_Lab6_进程通信--信号

进程通信——信号

一.改写Ctl+C的信号处理方式

1.代码:

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

void ouch(int sig){
	printf("OUCH! I got the signal %d\n",sig);
	(void)signal(SIGINT,SIG_DFL);		//恢复信号初始设定
}

int main(void){
	signal(SIGINT,ouch);		//改变信号处理方式

	while(1){
		printf("Hello world\n");
		sleep(1);
	}
}
//1.#include<signal.h>
//(void)signal(SIGINT,SIG_DEF)

//2.SIGINT:键盘中断  SIG_DFL:恢复信号初始处理方式

//3.注意(void)signal(SIGINT,ouch)  不同位置

运行结果如图:

2)发送信号:通过ctr+c发送终止信号,试试键盘其他信号,看看是否有效

无效,SIGINT为输入中断信号

3)预置信号的处理方式:通过signal设置信号的处理方式。消去注释1或者2,看程序变化

1.仅消去注释1:作用:该注释为恢复信号的默认处理方式 ;结果:则对中断输入信号操作仅printf(),
2.仅消去注释2:作用:设置遇到输入中断信号的处理方式为ouch(); 结果:一个ctr+c结束

4)接受信号的进程按照设定完成指定的事件处理:ouch(int sig)安排接到信号后的处理方式

1.(void)signal(SIGINT,SIGDFL) 恢复系统对输入中断信号的默认处理方式


二.闹钟,通过系统定时发送alarm()定时发送信号

1.代码:

#include<stdio.h>
#include<unistd.h>		//alarm()
#include<signal.h>		//signal()

void handler(){
	printf("hello\n");
}

int main(void){
	int i;
	signal(SIGALRM,handler);
	alarm(3);
	for(i=0;i<6;i++){
		printf("sleep is %d\n",i);
		sleep(1);	
	}

	return 0;
}

//1.SIGALRM:定时发送信号   alarm(n):具体定是发送的时间,
//2.alarm:多alarm只发送一次,且只发送最大时间间隔	

运行结果如图:

2)观察输出结果,理解程序用alarm定时发送信号

alarm()
1.头文件:#include<unistd.h>
2.作用:启动后,计时到了,发送信号给进程
3.函数原型:unsigned int alarm(unsigned int seconds);
4.函数参数:unsigned int seconds:指定秒数
5.返回值:成功:0(但是如果之前还设置其他alarm(),则返回上一个闹钟的剩余时间)
		失败:-1
6.注意:一个进程只能由一个alarm(),默认最长时间的
7.使用:
		#include<unistd.h>
		{
			signal(SIGALRM,handler);
			alarm(3);
		}



三.用setitimer设置定时器,getitimer获取定时器信息

1)代码:

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

int sec;

void sigroutine(int signo){
	switch(signo){
	case SIGALRM:printf("I got a signal-----SIGALRM\n");break;
	case SIGVTALRM:printf("I got a signal --------SIGVTALRM\n");break;
	}
	return ;
}
int main(){
	struct itimerval value,ovalue,value2;	//
	sec=5;
	printf("the process id is %d\n",getpid());
	signal(SIGALRM,sigroutine);
	signal(SIGVTALRM,sigroutine);

	value.it_value.tv_sec=1;
	value.it_value.tv_usec=0;
	value.it_interval.tv_sec=0;
	value.it_interval.tv_usec=500000;
	setitimer(ITIMER_REAL,&value,&ovalue);		//

	value2.it_value.tv_sec=0;
	value2.it_value.tv_usec=250000;
	value2.it_interval.tv_sec=0;
	value2.it_interval.tv_usec=250000;
	setitimer(ITIMER_VIRTUAL,&value2,&ovalue);
	
	for(;;);
	return 0;
}
//1.#include<sys/time.h>
//	struct itimerval
//	setitimer(,,)	(百度百科)
//	ITIMER_REAL:按照实际时间计时,计时到了,发送SIGLARM给进程
//	ITIMER_VIRTUAL:按照进程执行的时间计时,计时到了,发送SIGVTALRM给进程
//	value.it_value.tv_sec=1:计时器初始启动时间
//	value.it_interval.tv_usec=0:计时器启动后的时间间隔

运行结果如图:

2)了解sys/tims.h关于struct itimerval的定义

1.
struct itimerval{
	struct timeval it_interval;			//interval:间隔 
	struct timeval it_value;			//value:启动值
}
	struct timeval{
		long tv_sec;		//second,秒
		long tv_usec;		//usecond,微秒  1秒=1000000微秒
	}

3)了解定时器ITIMER_REAL和ITIMER_VITUAL的差异,明白实际时间和进程执行时间的差异

1.
	ITIMER_REAL:按照实际时间计时,计时到了,发送SIGLARM给进程
	ITIMER_VIRTUAL:按照进程执行的时间计时,计时到了,发送SIGVTALRM给进程

4)setitimer()函数

1.头文件:#include<sys/times.h>
2.函数作用:设置定时器
3.函数原型:int setitimer(int which,const struct itimerval *value,const struct itimerval *ovalue)
4.函数参数:
		a.int which:
			三种定时器类型: 
			ITIMER_REAL:按照实际时间计时,计时到了发送SIGALRM信号给进程
			ITIMER_VIRTUAL:按照进程执行时间计时,计时到了发送SIGVTALRM信号给进程
			ITIMER_PROF:按照进程执行和系统为该进程调用执行时间计时,计时到了发送SIGPROF信号给进程
		b.struct itimerval{
			struct timerval it_interval;		//interval:间隔
			struct timerval it_value;			//it:interval timer value
		}										//tv:time	value
				struct timerval{
					long tv_sec;
					long tv_usec;			//微秒:1秒=1000 000微秒
				}
5.返回值:成功:0
		失败:-1
6.使用:
		#include<sys/time.h>
		struct itimerval value,ovalue;
		{
			value.it_value.tv_sec=1;
			value.it_value.tv_usec=0;
			value.it_interval.tv_sec=0;
			value.it_interval.tv_usec=5000 00;
			setitimer(ITIMER_REAL,&value,&ovalue);
		}


四.父子进程间通过kill发送信号

1)代码

#include<stdio.h>		//printf()
#include<unistd.h>		//fork(),pause()
#include<stdlib.h>		//exit()
#include<signal.h>		//signal(),kill()

static int alarm_fired=0;

void ding(int sig){
	alarm_fired=1;
}

int main(void){
	int pid;
	
	printf("Alarm application starting\n");
	if((pid=fork())==0){
		sleep(5);
		kill(getppid(),SIGALRM);
		exit(0);
	}

	printf("waiting for alarm go off\n");
	(void) signal(SIGALRM,ding);
	pause();
//	wait(0);
	if(alarm_fired)
		printf("DING\n");
	printf("done\n");
}

//1.kill(getppid(),SIGALRM)
//	pause():头文件:#include<unistd.h>  释义:暂停   作用:休眠当前进程,直到信号到达

运行结果如图:

2)kill()的用法:

1.头文件:#include<signal.h>
2.作用:给指定进程发送指定信号
3.函数原型:void kill(pid_t pid,int sig)
4.函数参数:
  		1.pid_t pid:
			a.当pid>0,发送给该进程号进程信号
			b.当pid=0,发送给该进程组信号
			c.当pid=-1,发送给除1和本身外所有进程信号
			d.当pid<-1,发送给进程号为-pid的进程信号
		2.sig:
			a.1~31信号为不实时信号,34~64为实时信号(可靠信号)
5.返回值:成功:0
		失败:-1
6.使用:
		#include<signal.h>
		{
			(void) signal(SIGALRM,ding);
			kill(getpid(),SIGALRM);
		}

signal()的用法:

1.头文件:#include<signal.h>
2.作用:设置信号处理方式
3.函数原型:void(*signal(int signum,void(* handler)(int)))(int)
4.函数参数:
  		a.signum:信号编号
  		b.handler:
  			1.handler:函数,则按照函数执行
  			2.SIG_ING:忽略该信号执行
  			3.SIG_DFL:恢复系统信号初始设定处理方式
5.返回值:成功:0
		失败:-1
6.使用:
		#include<signal>
		{
			(void*)signal(SIGINT,ding);
			(void*)signal(SIGINT,SIGDFL);
		}
  		

3)掌握父子进程获取对方进程号的方法:

1.子获取父的进程号: getppid()
2.父进程获得子进程信号方法:fork()的父进程返回值

4)取消语句1,观察输出变化,并解释

1.pause是暂停父进程。去掉pause(),父进程执行,重设置了SIGALRM信号处理方式。但是因为信号是在子进程中发出的,父进程还未接受到信号就终止。子进程再向父进程发送信号时,父进程已经终止了。

5)取消语句1,保留语句2,观察输出变化,并解释原因

1.wait(0)是等待子进程完成,父进程重设SIGALRM信号的处理方式,wait(0),父进程等待子进程结束。子进程被调用执行,发送信号SIGALRM给父进程,父进程处理信号


五.使用信号进行进程间的通信

1.代码:
进程使用用 fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断信号(即 按CTRL+c 键);
捕捉到中断信号后,父进程用系统调用kill()向两个子进程发出信号,进程捕捉到信号后分别输出提示信息后终止;父进程等待两个子进程终止后,输出提示信息后终止。

#include<stdio.h>
#include<stdlib.h>		//exit()
#include<unistd.h>		//fork(),sleep()
#include<signal.h>		//kill(),signal()
#include<sys/types.h>
#include<sys/wait.h>			//wait()
//#include<semaphore.h>			

int wait_mark;
//sem_t sem;
void waiting(){
	while(wait_mark!=0);
}
void stop(){
	wait_mark=0;
}

int main(void){
	int p1,p2;

//	sem_init(&sem,0,1);
	signal(SIGINT,stop);
	while((p1=fork())==-1);
	if(p1>0){
		while((p2=fork())==-1);
		if(p2>0){		
		//主进程的处理
			wait_mark=1;
			waiting();
			kill(p1,16);//
			kill(p2,17);//
			wait(0);
			wait(0);			//同步
			printf("parents is killed\n");
			exit(0);
		}
		else{
		//p2进程的处理
			wait_mark=1;
			signal(17,stop);
			waiting();			//等待信号17

//			sem_wait(&sem);
			printf("p2 is killed by Parent1\n");
			fflush(stdout);
			sleep(1);
			printf("p2 is killed by Parent2\n");
			fflush(stdout);
//			sem_post(&sem);
			exit(0);
		}
	}
	else{
	//p1进程的处理
		wait_mark=1;
		signal(16,stop);
		waiting();				//等待信号16

//		sem_wait(&sem);
		printf("p1 is killed by parent1\n");
		fflush(stdout);
		sleep(1);
		printf("p1 is killed by parent2\n");
		fflush(stdout);
//		sem_post(&sem);
		exit(0);
	}
}

//1.fork():父进程中返回子进程ID

注意:运行后要CTR+C,发送信号后才会显示结果

运行结果如图:

2.了解父进程利用fork的返回值,向子进程发送信号

1.fork()函数,在父进程中返回子进程的ID,调用kill函数,向子进程发送信号

3.观察下图输出,使用互斥控制,使得输出结果不交替

去掉注释


六.使用sigaction注册信号

1.代码

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

void new_op(int signum,siginfo_t* info,void *myact){	//
	printf("receive the signum %d\n",signum);
	sleep(1);
}

int main(int argc,char **argv){
	int sig;
	struct sigaction act;

	sig=atoi(argv[1]);
	sigemptyset(&act.sa_mask);	//清空信号集
	act.sa_flags=SA_SIGINFO;		//设置信号处理其他相关操作
	act.sa_sigaction=new_op;		//信号处理函数

	if(sigaction(sig,&act,NULL)<0)		//*
		printf("install signal error\n");
	while(1){
		sleep(3);
		printf("wait for the signal\n");
	}
}

注意:按照2)说明进行

2)说明,命令行参数信号值;启动另一终端,运行ps -a可以获得其进程ID;运行 kill -s signo pid检验信号的发送接收及处理

运行结果如图:

3)了解sigaction的结构定义

strcut sigaction{
	void (*sa_handler)(int);
	void (*sa_sigaction)(int,siginfo_t*,void*);
	sigset_t mask;
	int sa_flags;
	void (*sa_restore)(void)
}

4)了解使用sigaction函数注册新信号

1.头文件:#include<signal.h>
2.作用:查询设置信号处理方式
3.函数原型:int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)
4.函数参数:
		1.int signum:依据signum指定的信号编号设置信号的处理函数
		2.strcut sigaction{
            void (*sa_handler)(int);		//处理函数
            void (*sa_sigaction)(int,siginfo_t*,void*);
            sigset_t mask;				//暂时将mask所指的信号集搁置
            int sa_flags;				//设置信号处理的其他相关操作
            void (*sa_restore)(void)
        }
5.返回值:成功:0
		失败:-1
6.使用:
#include<signal.h>

void new_op(int signum,siginfo_t* info,void *myact){	//
}

int main(int argc,char **argv){
	int sig=atoi(argv[1]);;
	struct sigaction act;

	sigemptyset(&act.sa_mask);	//清空信号集
	act.sa_flags=SA_SIGINFO;		//设置信号处理其他相关操作
	act.sa_sigaction=new_op;		//信号处理函数
	sigaction(sig,&act,NULL)	//*
}

5)理解信号注册,信号发送,信号处理三个环节



七.使用sigqueue发送信号并传递附加信息

1.代码:

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<signal.h>

void new_op(int signum,siginfo_t *info,void *myact){
	int i;
	for(i=0;i<10;i++){
		printf("%c ",(*((char*)((*info).si_ptr)+i)));		//*
		printf("\nhandle signal %d over\n\n",signum);
	}
}

int main(int argc,char **argv){
	int i;
	int sig;
	pid_t pid;
	struct sigaction act;		
	union sigval mysigval;		//

	char data[10];
	memset(data,0,sizeof(data));		//初始化函数,头文件string.h
	for(i=0;i<10;i++)
		data[i]='$';
	mysigval.sival_ptr=data;		//*

	sig=atoi(argv[1]);
	pid=getpid();
	sigemptyset(&act.sa_mask);		
	act.sa_flags=SA_SIGINFO;		//信息传递开关,允许传送参数信息给new_op
	act.sa_sigaction=new_op;		//三参数信号处理函数

	if(sigaction(sig,&act,NULL)<0)
		printf("install the signal error\n");
	while(1){
		sleep(2);
		printf("wait for the signal\n");
		sigqueue(pid,sig,mysigval);		//向本进程发送信号,并传递附加信息
	}
}

运行结果如图:

2)了解union sigval的定义

union sigval{
	int sival_int;
	void *sival_ptr;		//指向要传递的信号参数
}

3)了解sigqueue函数发送信号

1.头文件:#include<signal.h>
2.函数作用:在队列中向指定进程发送信号和数据
3.函数原型:int sigqueue(pid_t pid,int sig,const union sigval value)
4.函数参数:
		1.pid_t pid:进程号
		2.int sig:发送的信号
		3.const union sigval value:信号附带的顺序
		union sigval{
            int sival_int;
            void *sival_ptr;		//指向要传递的信号参数
        }
5.返回值:成功:0
		失败:-1
6.函数使用:
		#include<signal.h>
		{
			union struct sigval mysigval; 
			mysigval.sival_ptr=data;
			sigqueue(pid,sig,mysigval);
		}

4)观察进程给自己发送信号

1.sigqueue(pid,sig,mysigval)中,pid=getpid(),自己的进程


八.修改前面的程序,将信号发送和接收放在两个程序,并在发送过程中传递整型参数

1.代码:

//信号接收程序:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>

void new_op(int signum,siginfo_t *info,void *myact){
	printf("the int value is %d\n",info->si_int);
}

int main(int argc,char **argv){
	int sig;
	pid_t pid;
	struct sigaction act;

	sig=atoi(argv[1]);
	pid=getpid();
	
	sigemptyset(&act.sa_mask);
	act.sa_flags=SA_SIGINFO;
	act.sa_sigaction=new_op;
	if(sigaction(sig,&act,NULL)<0)
		printf("install the signal error\n");

	while(1){
		sleep(3);
		printf("wait for the signal\n");
	}
}
//信号发送程序:
#include<stdio.h>
#include<unistd.h>
#include<sys/time.h>
#include<sys/types.h>
#include<signal.h>

int main(int argc,char **argv){
	int signum;
	pid_t pid;
	union sigval mysigval; 		//
	
	signum=atoi(argv[1]);
	pid=(pid_t)atoi(argv[2]);
	mysigval.sival_int =8;
	if(sigqueue(pid,signum,mysigval)==-1)
		printf("send error\n");
	sleep(1);
	return 0;
}

注意:

1.先开一个终端,先运行信号接收程序:  ./t 35
2.再开一个终端,先查看进程:ps -a
			./p 35 进程号
3.具体如下图:

运行如图:

2)理解发送进程和接收进程

3)按照程序要求,在运行时附加合适的参数,并运行检验



八.编写程序

1.用 fork()创建一个子进程,在子进程中再次调用fork()创建孙子进程。调用系统调用 signal(),让父进程捕捉键盘上来的中断信号(即按^c 键);捕捉到中断信号后,父进程用系统调用kill(O向子进程发出信号,子进程捕捉到信号后,调用kill()向孙子进程发出信号

孙子进程捕捉到信号后,输出下列信息后终止:
Grandson processl is killed by son!
子进程等待孙子进程终止后,输出如下的信息后终止:
Child process is killed by parent!
父进程等待子进程终止后,输出如下的信息后终止:
Parent process is killed!
提示:过程如下所示。
父进程 子进程 孙进程
捕捉键盘信号: Kill() 接收信号:

#include<stdio.h>
#include<stdlib.h>		//exit()
#include<unistd.h>		//fork(),sleep()
#include<signal.h>		//kill(),signal()
#include<sys/types.h>
#include<sys/wait.h>			//wait()

int wait_mark;
void waiting(){
	while(wait_mark!=0);
}
void stop(){
	wait_mark=0;
}

int main(void){
	int p1,p2;

	signal(SIGINT,stop);
	while((p1=fork())==-1);
	if(p1>0){
		while((p2=fork())==-1);
		if(p2>0){		
		//主进程的处理
			wait_mark=1;
			waiting();
			kill(p1,16);//
			kill(p2,17);//
			wait(0);
			wait(0);			//同步
			printf("parent process is killed\n");
			exit(0);
		}
		else{
		//p2进程的处理
			wait_mark=1;
			signal(17,stop);
			waiting();			//等待信号17

			printf("Grandson process is killed by son\n");
			fflush(stdout);
			exit(0);
		}
	}
	else{
	//p1进程的处理
		wait_mark=1;
		signal(16,stop);
		waiting();				//等待信号16

		printf("child process is killed by parent\n");
		exit(0);
	}
}

//1.fork():父进程中返回子进程ID

注意:运行后ctl+c

运行如图:


2.父子进程间互相发送信号:父进程无限循环向子进程发送信号,子进程收到信号
向父进程发送相同信号,父子进程之间各自记录全局变量的变化,结果如:
child caught signal #1
parent caught signal #1
child caught signal #2
parent caught signal #2
child caught signal #3
parent caught signal #3

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

int i;
void p_handle(){
	printf("parent caught the signal # %d\n",i);
} 
void c_handle(){
	printf("child  caught the signal # %d \n",i);
}

int main(void){
	int p1;
	if((p1=fork())>0){
		(void)signal(17,p_handle);
		for(i=0;i<10;i++){
			pause();
			kill(p1,16);
		}
		exit(0);
	}
	else{
		(void)signal(16,c_handle);
		for(i=0;i<10;i++){
			kill(getppid(),17);
			pause();
		}
		exit(0);
	}

	return 0;
}
//1.先signal,再kill:先改变信号的处理方式,再发送信号
//2.pause():阻塞直到信号到达

运行如图:


3.将编程题1用sigaction()和sigqueue()再实现一次,并在信号传递时附带上
息。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<signal.h>

void op(int signum,siginfo_t *info,void *myact){
	printf("%s\n",(*info).si_ptr);
}

int main(int argc,char **argv){
	int sig=atoi(argv[1]);
	pid_t p1,p2;
	struct sigaction act;		
	union sigval mysigval_parent,mysigval_son,mysigval_grandson;		//

	char data_parent[40]="parent process is killed";
	mysigval_parent.sival_ptr=data_parent;
	char data_son[40]="child process is killed by parent";
	mysigval_son.sival_ptr=data_son;
	char data_grandson[50]="Grandson process is killed by son";
	mysigval_grandson.sival_ptr=data_grandson;

	sigemptyset(&act.sa_mask);		
	act.sa_flags=SA_SIGINFO;		//信息传递开关,允许传送参数信息给new_op
	act.sa_sigaction=op;		//三参数信号处理函数

	if(sigaction(sig,&act,NULL)<0)
		printf("install the signal error\n");

	if((p1=fork())==0){
		if((p2=fork())==0){
			sigqueue(getpid(),sig,mysigval_grandson);				//孙子 
			exit(0);			
		}
		else{
			wait(0);
			sigqueue(getpid(),sig,mysigval_son);				//儿子 
			exit(0);			
		}
	}
	else{
		wait(0);
		wait(0);
		sigqueue(getpid(),sig,mysigval_parent);			//父亲 
		exit(0); 		
	}
}
//1.sigaction与sigqueue间的信息怎么连接? 
//2.虚拟机在运行程序时为什么自动关闭虚拟机?
//注意: 
//1.不要使用getppid(),会直接终端虚拟机运行 :直接用getpid()
//2.wait()在sigqueue()前使用 

运行如图:


心得:1.在原程序的基础上进行渐变式的改变,以校验想法和错误修正。不要过大改变,会导致错误归因过多,无从下手

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值