现在linux使用的IPC(Inter-Process Communication,进程间通信)方式有以下几种: -(1)管道(pipe)和匿名管道(FIFO) -(2)信号(signal) -(3)消息队列 -(4)共享内存 -(5)信号量 -(6)套接字(socket)

什么是管道

管道是Unix中最古老的进程间通信的形式。我们把一个进程连接到另一个进程的一个数据流成为一个“管道”。

  • 管道是半双工的,数据只能向一个方向流动;需要双方通信的时候,需要建立其两个管道。
  • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)进行通信。

pipe函数

       #include <unistd.h>
       int pipe(int pipefd[2]);

功能:创建无名管道 参数:文件描述符组。fd[0]表示读端,fd[1]表示写端。 返回值:成功返回0,失败返回错误代码 也就是说,在fork()之前pipe(),就可以使得父子进程之间建立起一个管道,画个图: 父子进程都会打开5个文件描述符,除了默认的0、1、2。还有fd[0]、fd[1]。测试一下,就会知道这两个文件描述符为3、4。 写串代码用一用:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

	int main()
{
	int fd[2];
	int retByte;
	pid_t pid;
	char buf[20] = "";

	pipe(fd);	/*创建无名管道*/
	//printf("%d,%d\n",fd[0],fd[1]);

	pid = fork();
	if(pid == -1)
	{
		perror("create fork");
		return -1;
	}
	if(pid == 0)
	{
		//子进程,写端,使用fd[1]
		//close(fd[0]);
		//close(fd[1]);
		while(1)
		{
			scanf("%s",buf);
			if( write(fd[1],buf,strlen(buf)) == -1)
			{
				perror("write");
				return -1;
			}
			memset(buf,0,20);
			if(read(fd[0],buf,5) > 0 )
			{
				printf("child-read msg: %s\n",buf);
			}
		}
	}
	else
	{
		//父进程,读端,使用fd[0]
		while(1)
		{
			memset(buf,0,20);
			retByte = read(fd[0],buf,5);//每次只读5个
			if( retByte == -1)	
			{
				perror("read");
				return -1;
			}
			if(retByte > 0)
			{
				printf("parent-read msg: %s\n",buf);
			}
		}
	}
	return 0;
}

运行结果: 那么,如果没有读端呢?也就是父子进程的fd[0]都关闭了,会有什么现象呢?

	void handler(int no)
	{
		printf("SIGPIPE.\n");
	}

	int main()
	{
			int fd[2];
			int retByte;
			int rlt;
			pid_t pid;
			char buf[20] = "";

			rlt = pipe(fd);	/*创建无名管道*/
			//printf("%d,%d\n",fd[0],fd[1]);

			signal(SIGPIPE,handler);

			if(rlt != 0)
			{
				perror("pipe");
				return -1;
			}

			pid = fork();
			if(pid == -1)
			{
				perror("create fork");
				return -1;
			}
			if(pid == 0)
			{
				//子进程,写端,使用fd[1]
				close(fd[0]);
				//close(fd[1]);
				while(1)
				{
					scanf("%s",buf);
					if( write(fd[1],buf,strlen(buf)) == -1)
					{
						perror("write");
						return -1;
					}
					memset(buf,0,20);
				}
			}
			else
			{
				//父进程,读端,使用fd[0]
				close(fd[0]);
				while(1)
				{
				}
			}
			return 0;
}

运行结果: 会出现管道破裂!!(如果没有重写管道破裂的处理函数,系统默认的处理方式就是杀死进程,父子进程都over了)

管道读写规则

所以,总结一下读写规则: 读规则: 1)缓冲区没数据:阻塞 2)缓冲区的数据少于请求字节数:缓冲区有多少就读多少 3)缓冲区的数据多于请求字节数:只读取请求字节数,剩下的还在缓冲区 4)写端关闭:读端等待。 写规则: 1)缓冲区满了:写不进去 2)没有读端:管道破裂,父子进程都结束了。调试到write,发生SIGPIPE。 注意:读端和写端的对应关系可以是一对一、一对多、多对一、多对多的。

练习

上面讲到,缓冲区如果满了,就写不进去了。那么缓冲区有多大呢?换言之,如何检测linux中管道的容量? 代码如下:

int main()
{
	int fd[2];
	pipe(fd);
	char buf[4096]; //4k
	int i,loop,ret;
	
	for(i = 0 ; i < sizeof(buf) ; i++)
	{
		buf[i] = 'a';
	}
	
	loop = 100 ; //如果循环结束,还没阻塞,增加循环次数
	for(i = 0; i < loop ; i++)
	{
		ret = write(fd[1],buf,sizeof(buf));
		if(ret == -1)
		{
			perror("write error!\n");
			return 1;
		}
		else
		{
			printf("write successfully!    ");
			printf("size: %d K\n", (i+1)*4);
		}
	}

	close(fd[0]);
	close(fd[1]);

	return 0;
}

运行结果: 在写完64k的时候出现阻塞,说明管道已经满了。