linux系统编程之管道通信

目录

一【无名管道】

1、无名管道的简述

2、管道的特点:

3、无名管道的创建

案例:父进程写,子进程读,使用无名管道

4、无名管道读写的特点

案例:使用无名管道实现ps -A |grep bash

二【有名管道(命名管道)FIFO】

1、有名管道的概述

2、FIFO文件的创建

案例:实现两个进程通信:


一【无名管道】

1、无名管道的简述

1)无名管道又简称为管道。是一种特殊类型的文件,在应用层体现为两个打开的文件描述符。

2、管道的特点:

管道是最古老的的UNIX IPC.方式.

1、半双工,数据在同一时刻只能在一个方向上流动。

2、数据只能从管道的一端写入,从另一端读出。

3、写入管道中的数据遵循先入先出的规则。

4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。

5、管道不是普通的文件,不属于某个文件系统,其只存在于内存中。

6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

7、从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。

8、管道没有名字,只能在具有公共祖先的进程之间使用。

3、无名管道的创建

头文件:include<unistd.h>

函数原型:int pipe( int filedes[2] )

功能

经过由参数filedes返回的两个文件描述符

参数

filedes为int类型的数组的首地址,其存放了管道的文件描述符fd[0],fd[1]

filedes[0]为读而打开,filedes[1] 为写而打开管道,filedes[0]的输出是filedes[1]的输入。

返回值:成功返回1,失败返回-1

案例:父进程写,子进程读,使用无名管道

#include <stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include <string.h>
int main(int argc, char const *argv[])
{
	int fd[2];
	pipe(fd);

	pid_t pid = fork();
	if(pid==0)
	{
		close(fd[1]);
		printf("子进程正在等待读取父进程\n");
		char buf[128];
		memset(buf,0,128);
		read(fd[0],buf,sizeof(buf));
		printf("子进程读到消息为:%s\n",buf);
		close(fd[0]);
		_exit(-1);
	}

	if(pid>0)
	{
		close(fd[0]);
		printf("请输入你要发送的信息:\n");
		char buf[128];
		scanf("%s",&buf);
		write(fd[1],buf,sizeof(buf));
		close(fd[1]);
		wait(NULL);
	}
	return 0;
}

运行

4、无名管道读写的特点

1)默认使用read函数从管道中读数据是堵塞的。

2)调用write函数从管道中写数据,当缓存区满的时候,也会阻塞。

3)通信过程中,读端口全部关闭后,写进程向管道写数据的时候,写进程会收到SIGPIPE信号退出。

案例:使用无名管道实现ps -A |grep bash

#include <stdio.h>
#include<unistd.h>
#include<sys/wait.h>
int main(int argc, char const *argv[])
{
	 int fd[2];
	 pipe(fd);
	 int i =0;
	 for( i;i<2;i++)
	 {
	 	pid_t pid = fork();
	 	if(pid==0) 	
	 		break;
	 }
	 //子进程1 ps -A 写  //让标准输出1 作为管道写口。
	 if(i == 0)
	 {
	 	close(fd[0]);
	 	dup2(fd[1],1);
	 	execlp("ps","ps","-A",NULL);
	 	_exit(-1);
	 }
	 //子进程2 grep bash 读的标准输入0,所以让标准输入0作为管道读口
	 if(i==1)
	 {	
	 	close(fd[1]);
	 	dup2(fd[0],0);
	 	execlp("grep","grep","bash",NULL);
	 	_exit(-1);
	 }
	 //父进程
	 if(i==2)
	 {
	 	close(fd[0]);
	 	close(fd[1]);
	 	while(1)
	 	{
	 		pid_t pid = waitpid(-1,NULL,WNOHANG);
	 		if(pid<0)
	 		{
	 			break;
	 		}
	 		if(pid==0)
	 		{
	 			continue;
	 		}
	 		if(pid>0)
	 		{
	 			printf("子进程%d已经被回收\n",pid);
	 		}
	 	}
	 }
	return 0;
}

二【有名管道(命名管道)FIFO】

主要用于不相关的进程间通信。抽象为物理内存的一个文件。

1、有名管道的概述

特点

1、半双工,数据在同一时刻只能在一个方向上流动。

2、写入FIFO中的数据遵循先入先出的规则。

3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。

4、FIFO在文件系统中作为一个特殊的文件而存在,但FIFO中的内容却存放在内存中。

5.管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

6、从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更多的数据。

7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。

8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信

2、FIFO文件的创建

头文件

#include <sys/types.h>

#include <sys/stat.h>

函数原型:int mkfifo( ..const char *pathname,mode .t mode) ;

参数:

pathname:.E.IFO.的路径多土文件多。

mode: mode_t类型的权限描述符。

返回值

成功:返回0。

失败:如果文件已经存在,则会出错且返回-1。

阻塞方式打开管道:

1、open以只读方式打开FIFO时,要阻塞到某个进程为写而打开此FIFO2、open以只写方式打开FIFO时,要阻塞到某个进程为读而打开此FIFO。

3、open以只读、只写方式打开FIFO时会阻塞,调用read函数从FIFO里读数据时read也会阻塞。

4、通信过程中若写进程先退出了,则调用read函数从FIFO里读数据时不阻塞;若写进程又重新运行,则调用read函数从FIFO里读数据时又恢复阻塞。

5、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到SIGPIPE信号)退出。

6、调用write函数向FIFO里写数据,当缓冲区已满时write也会阻塞。

案例:实现两个进程通信:

写进程:

#include <stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include<fcntl.h>
#include <sys/stat.h>
#include<string.h>
int main(int argc, char const *argv[])
{
	mkfifo("my_fifo",0666);
	int fd1 = open("my_fifo",O_WRONLY);//以写的方式打开,会阻塞到对方以读的方式打开
	if(fd1 == -1)
	{
		perror("open");
		return -1;
	}
	printf("写端open\n");
	while(1)
	{
		char buf[128]="";
		printf("请输入要发送的数据:");
		//获取数据
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1]=0;
		//发送
		write(fd1,buf,strlen(buf));
		
		if(strcmp(buf,"bye")==0)
		{
			break;
		}
	}
	close(fd1);
	return 0;
}

读进程:

#include <stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include<fcntl.h>
#include <sys/stat.h>
#include<string.h>
int main(int argc, char const *argv[])
{
	mkfifo("my_fifo",0666);
	int fd1 = open("my_fifo",O_RDONLY);//会阻塞到对方以写的方式打开
	if(fd1 == -1)
	{ 
		perror("open");
		return -1;
	}
	printf("写端open\n");
	while(1)
	{
		char buf[128]="";
		read(fd1,buf,sizeof(buf));
		printf("收到的数据为:%s\n",buf);

		if((strcmp(buf,"bye"))==0)
		{
			break;
		}
	} 
	close(fd1);
	return 0;
}

拓展:条件编译一份代码实现

#include <stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include<fcntl.h>
#include <sys/stat.h>
#include<string.h>
int main(int argc, char const *argv[])
{
    
#ifdef WRITE
	mkfifo("my_fifo",0666);
	int fd1 = open("my_fifo",O_WRONLY);
#endif

#ifdef READ
	mkfifo("my_fifo",0666);
	int fd1 = open("my_fifo",O_RDONLY);
#endif

	if(fd1 == -1)
	{
		perror("open");
		return -1;
	}
 
#ifdef WRITE
	printf("写端open\n");
#endif

#ifdef READ
	printf("读端open\n");
#endif

#ifdef READ
	while(1)
	{
		char buf[128]="";
		read(fd1,buf,sizeof(buf));
		printf("收到的数据为:%s\n",buf);

		if((strcmp(buf,"bye"))==0)
		{
			break;
		}
	} 
#endif

#ifdef WRITE
	while(1)
	{
		char buf[128]="";
		printf("请输入要发送的数据:");
		//获取数据
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1]=0;
		//发送
		write(fd1,buf,strlen(buf));

		if(strcmp(buf,"bye")==0)
		{
			break;
		}
	}
#endif

close(fd1);
	return 0;
	
}

以非阻塞方式打开管道:

1、先以只读方式打开:如果没有进程已经为写而打开一个FIFO,只读open成功,并且open不阻塞。

2、先以只写方式打开:如果没有进程已经为读而打开一个FIFO,只写open将出错返回-1。

3、read、write读写命名管道中读数据时不阻塞。

4、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到SIGPIPE信号)退出。

注意: open函数以可读可写方式打开FIFO文件时的特点:

1、open不阻塞。

2、调用read函数从FIFO里读数据时read会阻塞。

3、调用write函数向FIFO里写数据,当缓冲区已满时write也会阻塞

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值