Linux下多进程通信(signal,pipe)

题目描述:

  • 父进程使用系统调用pipe()建立一个管道,然后使用系统调用fork()创建两个子进程:子进程1和子进程2
  • 子进程1每隔1秒通过管道向子进程2发送数据:I send message x times.(x初值为1,以后发送一次后做加一操作)
  • 子进程2从管道读出信息,并显示在屏幕上
  • 父进程用系统调用signal()来捕捉来自键盘的中断信号SIGINT(即按Ctrl+C键,);当捕捉到中断信号后,父进程用系统调用kill()向两个子进程发出信号,
  • 子进程捕捉到信号后分别输出如下信息后终止:
    Child Process 1 is killed by Parent!
    Child Process 2 is killed by Parent!
  • 父进程等待两个子进程终止后,释放管道并输出如下的信息后终止
    Parent Process is Killed!

算法设计:

多进程:

这里主要是利用系统调用fork:

  • fork是Linux下创建进程的一个系统调用
    调用fork的进程为主进程,一次调用会产生一个子进程。
  • fork的特点:一次调用两次返回:
    主进程和子进程的差异就从fork这条语句开始,fork给调用他的主进程的返回值是子进程的PID (若成功),给子进程的返回值是0,故可由此判断当前进程是子进程还是父进程,如:
int pid = fork();
if(pid==0){
//说明是子进程,这里写子进程的相关操作
}
else{
//说明是父进程,这里写父进程的相关操作
}
如何创建多个子进程?

错误的方法:

for(int i=0;i<10:i++)
	fork();

这显然不正确,因为不仅有父进程可以执行fork()语句,子进程也能执行,这就会导致实际创建的进程比预期的要多,因为有类似子进程的子进程存在。
避免这个问题也很简单,只在父进程执行fork()就行了,区分父子进程的方法上面已经给出。
我的具体实现代码:

int childpid[10];//用来保存所有进程的PID,0号是主进程
childpid[0]=getpid();//012分别是父进程和两个子进程的pid
for(int i=1;i<=2;i++){
		int pid=fork();
		if(pid!=0)
			childpid[i] =pid;
		else{
			childpid[i] =getpid();
			break;
		}
	}
最终,主程序的逻辑大概如下:

if(getpid()==childpid[0]){
}
if(getpid()==childpid[1]){
}
if(getpid()==childpid[2]){
}


信号机制:

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式

使用signal()函数处理时,只需指出要处理的信号处理函数即可。
signal()原型:
/*signal-handler*/* signal(int sig, /*signal-handler*/* handler);

  • 参数:
    sig:要设置信号处理函数的信号。它可以是实现定义值或预定义的宏(见下方):
    handler:信号处理函数。这必须是下列之一:
      SIG_DFL 宏。信号处理函数被设为默认信号处理函数。
      SIG_IGN 宏。忽略信号。
      指向函数指针。
  • 返回值
    成功时为先前的信号处理函数,失败时为 SIG_ERR (某些实现上能禁用设置信号处理函数)。
  • 信号处理函数的预定义宏

SIGINT:ctrl+c 终止信号   
SIGQUIT:ctrl+\ 终止信号   
SIGTSTP:ctrl+z 暂停信号
SIGALRM:闹钟信号 收到此信号后定时结束,结束进程   
SIGCHLD:子进程状态改变,父进程收到信号   
SIGKILL:杀死信号

  • 用户进程对信号的响应方式:
      忽略信号(利用SIG_IGN 宏)
      捕捉信号
      执行缺省操作

  • 其他需要的相关函数:

int kill(pid_t pid, int sig);
  功能:发送信号
  参数:pid:指定进程
     sig:要发送的信号
  返回值:成功 0;失败 -1
这里需要用到几个函数:

程序功能的具体实现:
  • 1.父进程接收Ctrl+C信号
signal(SIGINT,fun_pare);//发出中断信号
  • 2.父进程响应Ctrl+C:向子进程发出信号
void fun_pare(int sig)//父进程响应Ctrl C 信号函数
{
	kill(childpid[1],SIGUSR1);
	kill(childpid[2],SIGUSR2);
}
  • 3.子进程接收父进程发来的信号
		signal(SIGUSR1,fun_chid);
		signal(SIGUSR2,fun_chid);

注意在子进程中应该屏蔽Ctrl+C信号,否则,在父进程响应Ctrl+C时,子进程就会调用默认的响应函数而退出了:

signal(SIGINT,SIG_IGN);
  • 4.子进程对父进程发来的信号进行处理
void fun_chid(int sig)//子进程1响应信号函数
{
	if(sig==SIGUSR1)
		printf("\nChild Process 1 is killed by parent!\n");
	if(sig==SIGUSR2)
	printf("\nChild Process 2 is killed by parent!\n");
		
	exit(822);//默认正常退出返回0
}

这里利用响应函数的参数对接收到的函数进行判断,从而将两个进程的响应放在一块实现。

管道通信:

  • pipe函数的原型:
    int pipe (int fd[2]); //成功返回0,出错返回-1
    fd参数是数组/指针,可以带回两个值(文件描述符),
    fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。

  • pipe通信(父子进程间)的流程:
    1.父进程创建管道,得到两个件描述符指向管道的两端
    2.发进程关闭fd[1](读端),利用write()向管道写入
    3.收进程关闭fd[0](写端),利用read()从管道读出

  • 我这里的实现:

	int flg,fd[2];
	flg = pipe(fd); //创建管道
    if (flg == -1)
        perror("pipe error");
    
    int *child,*msg,len;
	write(fd[1],child,len);//表示向fd[1]写入child字符串的前len位
	read(fd[0],msg,sizeof(msg))//表示从fd[0]读出一个字符串

整体实现代码:


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


int childpid[10]={-1,-1,-1,-1},status=999;

    

void fun_pare(int sig)//父进程响应Ctrl C 信号函数
{
	kill(childpid[1],SIGUSR1);
	kill(childpid[2],SIGUSR2);
}
void fun_chid(int sig)//子进程1响应信号函数
{
	if(sig==SIGUSR1)
		printf("\nChild Process 1 is killed by parent!\n");
	if(sig==SIGUSR2)
	printf("\nChild Process 2 is killed by parent!\n");
		
	exit(822);//默认正常退出返回0
}


int main(){

	int ret,fd[2];
	ret = pipe(fd); //创建管道
    if (ret == -1)
        perror("pipe error");

	
	

	childpid[0]=getpid();//012分别是父进程和两个子进程的pid

	for(int i=1;i<=2;i++){
		int pid=fork();
		if(pid!=0)
			childpid[i] =pid;
		else{
			childpid[i] =getpid();
			break;
		}
	}


		

	if(getpid()==childpid[0]){//父进程
		printf("我是父进程,PID:%d PPID:%d\n",getpid(),getppid());
		printf("\t子进程1是:PID:%d\n",childpid[1]);
		printf("\t子进程2是:PID:%d\n",childpid[2]);

		signal(SIGINT,fun_pare);//发出中断信号
			
		int child_num = 2;//子进程个数
		while(child_num --){
		int t = wait(&status);//等待两个子进程结束
		printf("一个进程结束了,他的 PID:%d 返回码 :%d\n",t,status);
		}
		

		printf("Parent process is killed!\n");//两个子进程都结束后主进程才可以结束
		exit(822);//默认正常退出返回0
	}
	else{//否则是子进程
		signal(SIGINT,SIG_IGN);
		signal(SIGUSR1,fun_chid);
		signal(SIGUSR2,fun_chid);
	}


	if(getpid()==childpid[1]){//子进程1
		int i=0;
        close(fd[0]);
        char child[100]="I send message x times !\0";
		
        while(++i)
        {
			child[15] = '0' + i;
            write(fd[1],child,strlen(child)+1);
            sleep(1);
        }
	}
	if(getpid()==childpid[2]){//子进程2
	 	close(fd[1]);
        char msg[100];
        while(1)
        {
            if(read(fd[0],msg,sizeof(msg)))
            	printf("%s\n",msg);
        }
	}	
	return 0;
}

运行截图:

在这里插入图片描述

posted @ 2022-03-05 16:00  Cheney822  阅读(401)  评论(0)    收藏  举报