Linux---进程通信(二)---管道

系列文章目录

文章一: Linux—进程通信(一)—进程通信概述
文章二: Linux—进程通信(二)—管道
文章三: Linux—进程通信(三)—信号通信
文章四: Linux—进程通信(四)—IPC通信之共享内存
文章五: Linux—进程通信(五)—IPC通信之消息队列
文章六: Linux—进程通信(六)—IPC通信之信号灯



前言

本文主要介绍了有名管道和无名管道。
给出了Linux系统下的相关函数,并且编写了一部分例子


管道是一种特殊的文件,存在于内核中,是由队列来实现的。

一、无名管道

1.函数

管道的创建函数

函数原型;

int pipe(int fd[2])

头文件:

#include<unistd.h>

功能:

创建管道,为系统调用函数

参数:

1.参数是得到的文件描述符,特殊的是有两个文件描述符:fd[0]和fd[1]。
2.之所以有两个文件描述符,是因为管道是一个队列,出队是读文件,入队是写文件,就有两个文件描述符
3.fd[0]是读文件描述符,即出队
4.fd[1]是写文件描述符,即入队

返回值:

成功是0,失败是-1

注意:

1.管道是创建在内存中的,进程结束,空间释放,管道就不存在了。
2.管道中的东西,读完了就删除了,因为对于队列来说,出队意味着删除。
3.管道中没有东西可读,则会阻塞。
4.管道有一定的大小,如果写满就会处于写阻塞状态。
5.无名管道不能进行非父子进程之间的通信。

读,写,关闭函数

与传统文件操作函数一致:

1.读管道:read
2.写管道:write
3.关闭管道:close

具体的内容可以参考前面的文件操作系列文章。

例子1:验证读阻塞

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int fd[2];
	int ret;
	char writebuf[]="hello linux";
	char readbuf[128]={0};
	ret=pipe(fd);
	if(ret<0)
	{
		printf("create pipe failure\n");
		return -1;
	}
	printf("create pipe success fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
	write(fd[1],writebuf,sizeof(writebuf));

	read(fd[0],readbuf,128);
	printf("readbuf=%s\n",readbuf);

	memset(readbuf,0,128);
	read(fd[0],readbuf,128);
	printf("second read after\n");
	
	close(fd[0]);
	close(fd[1]);
	return 0;
}

执行结果是“second read after”这条语句没打印出来,程序阻塞在那里。
这是可以使用命令:

sudo   ps   -axj

来查看进程的运行状态,发现是S+状态,即睡眠状态,说明进程已经阻塞。

例子2:验证管道写满会不会写阻塞

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int fd[2];
	int ret;
	int i=0;
	char writebuf[]="hello linux";
	char readbuf[128]={0};
	ret=pipe(fd);
	if(ret<0)
	{
		printf("create pipe failure\n");
		return -1;
	}
	printf("create pipe success fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
	
	//这里的5457是在阻塞的边界上
	//如果是5456,那么就不会阻塞
	while(i<5457)
	{
		write(fd[1],writebuf,sizeof(writebuf));
		i++;
	}
	
	printf("write pipe end\n");
	
	close(fd[0]);
	close(fd[1]);
	return 0;
}

执行结果是不会打印“write pipe end”,程序处于S+状态,阻塞。

例子3:无名管道的进程通信

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	pid_t pid;
	int fd[2];
	int ret;
	char process_inter=0;
	ret=pipe(fd);
	if(ret<0)
	{
		printf("create pipe failure\n");
		return -1;
	}
	printf("create pipe success\n");
	
	pid=fork();
	if(pid==0)
	{
		int i=0;
		read(fd[0],&process_inter,1);
		while(process_inter==0);
		for(i=0;i<5;i++)
		{
			printf("this is child process i=%d\n",i);
			usleep(100);
		}
	}
	if(pid>0)
	{
		int i=0;
		for(i=0;i<5;i++)
		{
			printf("this is child process i=%d\n",i);
			usleep(100);
		}
		sleep(5);
		process_inter=1;
		write(fd[1],&process_inter,1);
	}
	return 0;
}

执行结果:父进程先运行,大概5秒后,子进程后运行。

二、有名管道

1.为什么需要有名管道?

很直观的意义上来讲:
有名管道可以进行非父子进程之间的通信,这是无名管道所做不到的。根本原因就是无名管道在用户空间没有名,不能让用户空间的两个无亲缘关系的进程调用

2.什么是有名管道

文件系统中存在这样一个文件结点,每个文件结点都有一个inode号,而且这时一个特殊的文件类型:管道类型

这里进行一个补充:Linux系统中的7种文件类型

1.普通文件
2.目录文件
3.链接文件
4.管道文件
5.Soket(套接字)文件
6.字符设备文件
7.块设备文件

3.有名管道的函数

有名管道的创建

函数形式:

int mkfifo(const char *filename,mode_t mode);

函数功能:

创建管道文件

参数:

1.第一个参数是文件的文件名
2.第二个参数是文件的权限,与具体的umask值有关(可以参考前面的文件操作系列文章)

返回值:

创建成功返回0,创建失败返回-1

注意:

1.管道文件只有inode号,不占磁盘空间,和套接字,字符设备文件块设备文件一样。
2.普通文件和符号链接文件及目录文件,不仅有inode号,还占磁盘空间。
3.mkfifo用来创建管道文件的节点,没有在内核中创建管道。
4.只有通过open函数打开这个文件时,才会在内核空间创建管道。
5.无名管道的规则也适用有名管道

打开,读,写,关闭函数

与传统文件操作函数一致:

1.打开管道:open
2.读管道:read
3.写管道:write
4.关闭管道:close

具体的内容可以参考前面的文件操作系列文章。

例子1:实现无亲缘关系进程间的通信

进程1:

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
int main()
{
	int i=0;
	char process_inter=0;
	int fd;
	fd=open("./myfifo",O_WRONLY);
	if(fd<0)
	{
		printf("open myfifo failure\n");
		return -1;
	}
	printf("open myfifo success\n");
	
	for(i=0;i<5;i++)
	{
		printf("this is first process i=%d\n",i);
		usleep(100);
	}
	process_inter=1;
	sleep(5);
	write(fd,&process_inter,1);
	while(1);
	return 0;
}

进程2:

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
int main()
{
	int i=0;
	char process_inter=0;
	int fd;
	fd=open("./myfifo",O_RDONLY);
	if(fd<0)
	{
		printf("open myfifo failure\n");
		return -1;
	}
	printf("open myfifo success\n");
	
	read(fd,&process_inter,1);
	while(process==0);
	
	for(i=0;i<5;i++)
	{
		printf("this is first process i=%d\n",i);
		usleep(100);
	}
	process_inter=1;
	write(fd,&process_inter,1);
	while(1);
	return 0;
}

执行步骤:

先打开程序1,再迅速打开程序2.

执行结果描述:

1.刚开始程序1什么都不显示,包括创建管道成功的信息也不显示。这是因为创建的管道没有读端,程序处于阻塞状态。

2.当程序2开始执行后,程序1会立即执行全部内容,程序2会打印出创建管道成功的语句,然后阻塞。

3.当过大概5秒后,程序2也顺利执行。这样两个进程间实现通信。


总结

对于open close read write函数有疑问的读者,可以参考下面的文章:

文章: Linux之read write open lseek用法

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SigmaBull

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值