进程间通信——管道

为什么要学习进程通信?
因为复杂程序都是多任务的,进程间需要相互协作,但进程间的内存空间又是独立的,所以需要掌握进程的通信让他们相互协作。

管道的概念:

管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:

1. 其本质是一个伪文件(实为内核缓冲区)

2. 由两个文件描述符引用,一个表示读端,一个表示写端。

3. 规定数据从管道的写端流入管道,从读端流出。


管道分 无名管道有名管道
无名管道:只能用于父子进程之间通信
1.创建函数:
头文件:
         #include <unistd.h>
函数原型:
         int pipe(int pipefd[2]);
返回值:成功返回0,失败返回-1
pipefd[0]   ---》读端
pipefd[1]   ---》写端
注意: ①当管道中没有数据时,读阻塞
              ②当管道中的数据写满时,写阻塞
              ③管道文件不知lseek函数
              ④管道中的数据是先进先出的

管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?通常可以采用如下步骤:


1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。

2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。

3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。

-----------------------------------------------------------------------------------------

程序实例1:父进程写入数据到管道,子进程从管道读出数据并打印

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

int main()
{
	int pipefd[2];
	char *p = "hello world\n";
	char buf[100] = {0};
	if(pipe(pipefd) == -1)
	{
		printf("创建管道出错!\n");
		return 0;
	}
	pid_t pid  = fork();
	if(pid > 0)
	{
		close(pipefd[0]);//父进程关闭读端
		int ret1 = write(pipefd[1],p,strlen(p));//往管道写入数据
		if(ret1 < 0)
			printf("写入管道失败!\n");
		else	
			printf("写入成功!\n");
		wait(NULL);//等待子进程退出
		close(pipefd[1]);//写完数据后关闭写端
	}
	else if(pid == 0)
	{
		close(pipefd[1]);//子进程关闭写端
		int ret2 = read(pipefd[0],buf,sizeof(buf));//读取管道中的数据
		if(ret2 < 0)
			printf("读取管道失败!\n");
		else	
			printf("读取成功!\n");
		//write(STDOUT_FILENO,buf,ret2);//输出到标准输出,即打印显示,与下面等价
		printf("buf = %s\n",buf);
		close(pipefd[0]);//读取完数据后关闭读端
	}
	else
	{
		printf("创建进程失败!\n");
		return 0;
	}
	return 0;
}



-------------------------------------------------------------------------------------------------------
实例程序2:
 练习:使用管道实现父子进程间通信,完成:ls -l | wc –l。假定父进程实现ls,子进程实现wc。
#include <stdio.h>  
#include <unistd.h>  
#include <sys/wait.h>  
#include <stdlib.h>
int main(void)  
{  
    pid_t pid;  
    int fd[2];  
  
    pipe(fd);  
    pid = fork();  
  
    if (pid == 0) 
	{  	//子进程 
        close(fd[1]);                   //子进程从管道中读数据,关闭写端  
		dup2(fd[0], STDIN_FILENO);      //让wc从管道中读取数据  
        execlp("wc", "wc", "-l", NULL); //wc命令默认从标准读入取数据  ,wc -l 命令是统计行数
		close(fd[0]); 
    } 
	else if(pid >0)
	{  
		//父进程
        close(fd[0]);   //父进程向管道中写数据,关闭读端  
		dup2(fd[1], STDOUT_FILENO);     //将ls的结果写入管道中  
        execlp("ls", "ls","-l", NULL);       //ls输出结果默认对应屏幕
		close(fd[1]); 		
    }  
	else
	{
		printf("创建进程失败!\n");
		exit(1);
	}
    return 0;  
}  



可见运行程序和在命令行输入命令的结果是一样的。
----------------------------------------------------------------------
有名管道:可以实现同一个系统中任意两个进程通信
有名管道的创建有两种
①命令行创建:mkfifo   /home/gec/myfifo ---》创建管道文件


②管道创建函数
头文件:
        #include  <sys/types.h>
        #include <sys/stat.h>
函数原型:
        int mkfifo(const char *pathname,mode_t mode);
参数一:需要创建的管道路径
参数二:管道的权限,0666
返回值:成功返回0,失败返回-1
注意:①不能再windows文件夹中创建管道文件
         ②open函数当以只读方式或只写方式打开管道时,没有写端或读端打开管道,open会阻塞
        open()函数中O_NONBLOCK---》以不阻塞的方式打开管道,加入打开写端时没有读端被打开,则打开写端失败。
        ③read函数本身是阻塞的,当在管道中没有写端时,则read函数不阻塞。
        ④write函数,当管道文件写满时,write会阻塞,为了保护写入的数据
--------------------------------------------------------
创建一个有名管道,并实现两个进程间(不是父子进程)通信
进程写端:
#include <stdio.h>
#include <sys/types.h>
#include  <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
	mkfifo("/home/gec/pi",0666);
	int fd = open("/home/gec/pi",O_WRONLY);
	if(fd > 0)
		printf("open ok\n");
	printf("in while\n");
	while(1)
	{
		char buf[50] = {0};
		printf("input\n");
		scanf("%s",buf);
		write(fd,buf,strlen(buf));
	}
	close(fd);
}


进程读端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
	mkfifo("/home/gec/pi",0666);
	int fd = open("/home/gec/pi",O_RDONLY);
	if(fd >0)
		printf("open ok\n");
	printf("in while \n");
	while(1)
	{
		char buf[50] = {0};
		int ret = read(fd,buf,sizeof(buf));
		if(ret >0)
			printf("ret  = %s\n",buf);
	}
	close(fd);
}


进程写端编译:


进程读端编译:


在/home/gec目录下可以看到管道文件pi

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值