Linux下两个子进程通过管道(pipe)通信,接受父进程的信号(signal)停止(完整代码)

题目描述:

算法设计:

多进程:

这里主要是利用系统调用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;
		}
	}

信号机制:

这里需要用到几个函数:

		signal(SIGINT,SIG_IGN);

		signal(SIGUSR1,fun_chid);
		signal(SIGUSR2,fun_chid);
		
		kill(childpid[1],SIGUSR1);
		kill(childpid[2],SIGUSR2);

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
使用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:杀死信号
  
用户进程对信号的响应方式:
忽略信号
捕捉信号
执行缺省操作

相关函数:
int kill(pid_t pid, int sig);
  功能:信号发送
  参数:pid:指定进程
  sig:要发送的信号
  返回值:成功 0;失败 -1

管道通信:

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

	
	write(fd[1],child,strlen(child)+1);
	read(fd[0],msg,sizeof(msg))
  • 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()从管道读出

实现代码:


#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;
}

运行截图:

Linux下两个子进程通过管道(pipe)通信,接受父进程的信号(signal)停止(完整代码)

上一篇:Linux Signal及Golang中的信号处理


下一篇:【Linux学习笔记】kill及kill -9的用法及如何实现进程的优雅退出