匿名管道和命名管道

一:匿名管道(pipe)。
    是一种最基本的IPC机制,由pipe函数创建。
    调用pipe函数时在内核中开辟一块缓冲区用于通信。而我们把这块缓冲区叫做管道。
  
    #include<unistd.h>
    int pipe(int fd[2]);
    其参数:为一个输出型的参数。其中fd[0]中保存读端口的文件描述符,fd[1]中保存写端口的文件描述符。
    其返回值:成功返回0,失败返回-1。
   
    其特征是:
    1>、只能进行单向通信。
    2>、只能用于具有血缘关系的进程之间的通信(常用于父子进程之间的通信)。
    3>、进程的读写操作是以字节流方式来完成。(可以以任意大小读写)
    4>、它是依赖于文件系统的,其生命周期随进程(随进程的结束而结束)。
    5>、进程之间有默认的同步关系。

   
   1. 验证:(eg:创建一个父子进程,假设子进程用来写数据,父进程用来读数据)(正常的读写,边读边写)
   
    调用pipe后,fd[0]会指向管道的读端,而fd[1]会指向管道的写端。
    由于子进程会复制父进程的进程地址空间,因此在fork后,会得到上图。
    由于该管道是只能用于单向通信,所以需要吧其他不用端口对应的文件描述符关闭。以保证安全性。
   

    代码实现:
   
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
	int fd[2]={0,0};
	if(pipe(fd)==0)
	{
		pid_t id = fork();
		if(id == 0)
		{//child
			close(fd[0]);//关闭读
			const char* str="i am a student";
			while(1)
			{
				write(fd[1],str,strlen(str));
				sleep(1); //1s写一次
			}
		}
		else 
		{//father
			close(fd[1]);//关闭写
			while(1)
			{
				char buf[1024];
				int s = read(fd[0],buf,sizeof(buf));
				if(s>0)
				{
					buf[s]=0;
					printf("child  ->  father:%s\n",buf);
				}
			}
		}
	}	
	else
	{
		perror("pipe");
		return 1;
	}		

	return 0;
}

    执行程序后,发现没间隔1s就会输出一次。
   

    2.当父进程不关闭读端口,而每间隔500s读取一次数据时,并且子进程不断的每次以一个字节的数据不停的写:会发现,管道会被写满,子进程阻塞等待。经过测试发现其管道底层缓冲区大小为65536字节。(没有关闭读端口,但是没有读取数据,可能会将管道写满,导致写进程的阻塞)
    代码实现:
   
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
	int fd[2]={0,0};
	if(pipe(fd)==0)
	{
		pid_t id = fork();
		if(id == 0)
		{//child
			close(fd[0]);
			const char* str="i";
			int n=0;
			while(1)
			{
				write(fd[1],str,strlen(str));
				++n;
				printf("%d %d\n",n,n*strlen(str));
			}
		}
		else 
		{//father
			close(fd[1]);
			while(1)
			{
			        sleep(500);
				char buf[1024];
				int s = read(fd[0],buf,sizeof(buf));
				if(s>0)
				{
					buf[s]=0;
					printf("child  ->  father:%s\n",buf);
				}
			}
		}
	}	
	else
	{
		perror("pipe");
		return 1;
	}		

	return 0;
}

    执行结果:
 
总共写入65536个字节后,子进程阻塞等待父进程读数据。

    3.当子进程不停的写数据,而父进程读5次数据后,关闭读端口时。我们发现,子进程会立即收到13号信号,从而也被终止。(这么做是因为:管道本身是用来通信的,既然没有读的进程,那也没有通信,那将数据写进管道又有什么用呢?)(如果关闭所有读端口,写端口对应的进程会收到信号异常终止)
    代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
	int fd[2]={0,0};
	if(pipe(fd)==0)
	{
		pid_t id = fork();
		if(id == 0)
		{//child
			close(fd[0]);
			const char* str="i am child!";
			while(1)
			{
				write(fd[1],str,strlen(str));
				sleep(1);
			}
		}
		else 
		{//father
			close(fd[1]);

			int count=0;
			while(1)
			{
				char buf[1024];
				int s = read(fd[0],buf,sizeof(buf));
				if(s>0)
				{
					buf[s]=0;
					printf("child  ->  father:%s\n",buf);
				}
				if(++count >= 5)
				{
					close(fd[0]);
					break;
				}
			}
			
			int status;
			waitpid(id,&status,0);

			printf("father is quit! child quit! sig = %d exitCode =  %d\n",status&0xff,(status>>8)&0xff);
		}
	}	
	else
	{
		perror("pipe");
		return 1;
	}		

	return 0;
}
    结果:

    4.当父进程不停的读数据,子进程写5条数据并且关闭写端口后,父进程在读数据的过程中,一旦读完管道中的数据,read就会返回一个0(相当于文件结尾)。并且不再读数据。(如果关闭所以写端口,读端口会一直读,直到文件尾)
    代码测试:
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
	int fd[2]={0,0};
	if(pipe(fd)==0)
	{
		pid_t id = fork();
		if(id == 0)
		{//child
			close(fd[0]);
			const char* str="i am child!";
			int n=0;
			while(1)
			{
				write(fd[1],str,strlen(str));
				if(++n>=5)
				{
					close(fd[1]);
					break;
				}
				sleep(1);
			}
		}
		else 
		{//father
			close(fd[1]);

			int count=0;
			while(1)
			{
				char buf[1024];
				int s = read(fd[0],buf,sizeof(buf));
				if(s>0)
				{
					buf[s]=0;
					printf("child  ->  father:%s\n",buf);
				}
				else if(s == 0)
				{
					printf("write is quit! s=%d\n",s);
					break;
				}
			}
			
			int status;
			waitpid(id,&status,0);
		}
	}	
	else
	{
		perror("pipe");
		return 1;
	}		

	return 0;
}
    结果:
 
    发现其read的返回值为0,代表已读取到文件尾(管道依赖于文件系统,因此可以将其看作为一个文件)。

    eg:linux中的“|”(竖划线)就是一个匿名管道。(没有创建任何文件、并且进程之间具有血缘关系。(都属于bash的子进程))


二:命名管道(FIFO)
    所谓命名管道:是一个设备文件(p)。
    与匿名管道最大的区别是:会事先创建一个管道文件,来实现没有任何关系的两个进程直接的通信。
    原理:创建一个管道文件,然后就可以讲问题转化为两个不同的进程对该管道文件的读写工作。
   
    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char* pathname,mode_t mode);

    参数:第一个为要创建的管道文件的名称,可以带路径,如果没有带路径,则默认为当前路径。
              第二个为创建的该文件的权限大小。


    eg:创建两个进程:server和client来实现这两个进程之间的通信。
   
    代码:
server.c
#include <stdio.h> //读
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
	umask(0);
	if(mkfifo("./myfifo",S_IFIFO | 0666) == -1)
	{
		perror("fifo");
		return 1;
	}
	else
	{
		int fd = open("myfifo",O_RDONLY);
		if(open < 0)
		{
			perror("open");
			return 2;
		}
		while(1)
		{
			char buf[1024];
			int s = read(fd,buf,sizeof(buf));
			if(s>0)
			{
				buf[s-1]=0;
				printf("%s\n",buf);
			}
			else
			{
				perror("read");
				return 3;
			}
		}

	}

	return 0;
}

cilent.c
#include <stdio.h>   //写
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
		int fd = open("myfifo",O_WRONLY);
		if(open < 0)
		{
			perror("open");
			return 1;
		}
		while(1)
		{
			char buf[1024];
			int s = read(0,buf,sizeof(buf));
			if(s>0)
			{
				buf[s-1]=0;
				write(fd,buf,sizeof(buf));
			}
			else
			{
				perror("read");
				return 2;
			}
		}


	return 0;
}



结果如下:
cilent:(输入)
server:(输出)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值