进程间通信——管道

1、进程间通信

    每个进程有独立的用户地址空间,所以一个进程是看不到另一个进程中的数据包括全局变量。进程间通信是需要通过内核来进行的,内核中开辟一块缓存,不同的进程将数据写到这里或从这里读取,从而实现不同进程之间的通信,内核提供的这种机制称为进程间通信。进程间通信方式有多种,不同的方式提供的共享资源形式不同或者提供者不同。管道就是这其中的一种。

2、管道的创建

    管道的创建使用pipe()系统调用,关于此系统调用可参见另一篇文章中的描述——高级IO函数

3、管道实现进程间通信

    管道一般用于有关系的进程之间的通信,例如父子进程之间的通信:

  • 父进程创建了一个管道,f[0]、f[1]分别为管道两端的文件描述符
  • 父进程通过fork函数创建子进程,此时子进程继承了父进程的管道,也有一对文件描述符f[0]、f[1]。
  • 由于管道只能单向传输数据,所以父子进程必须一个关闭f[0],另一个关闭f[1],才能实现通信。如下图所示,即为管道实现进程间通信的抽象描述。



    管道实现进程间通信代码示例:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
	int fd[2];
	int ret = pipe(fd);
	if(ret == -1)
	{
		printf("pipe error\n");
		return 1;
	}

	pid_t id = fork();
	if(id == 0)   //子进程
	{
		close(fd[1]);
		char buf[100];
		while(1)
		{
			memset(buf, '\0', sizeof(buf));
			size_t s = read(fd[0], buf, sizeof(buf));
			if(s > 0)
			{
				buf[s - 1] = '\0';
				printf("%s", buf);
			}
			else
				break;
		}
		close(fd[0]);
		return 0;
	}
	else if(id > 0)  //父进程
	{
		close(fd[0]);
		char* wrbuf = "hello world";
		int i = 5;
		while(i--)
		{
			ret = write(fd[1], wrbuf, strlen(wrbuf));
			if(ret < 0)
			{
				printf("write error\n");
				break;
			}
			sleep(2);
		}
		close(fd[1]);
		int stat_loc;
		wait(&stat_loc);
		return 0;
	}
	else
	{
		printf("fork error\n");
		return 1;
	}
}

4、管道的读写状态

  • 当管道容量满时,且读端文件描述符引用计数大于0,则再次调用write会阻塞;
  • 当读端文件描述符引用计数等于0,写端再次调用write时,进程会收到SIGPIPE信号,导致进程终止;
  • 当管道数据为空时,且写端文件描述符引用计数大于0,则再次调用read会阻塞,直到有数据可读;
  • 当写端文件描述符引用计数等于0,读端再次调用read时,返回0,如同读到文件末尾(EOF)。

5、管道容量

    管道有一个容量限制,规定了如果应用程序没有将数据从管道中读走的话,该管道最多能被写入的数据字节数。当前的Linux,管道容量默认为65536字节,也可以使用fcntl函数修改管道容量。

    测试管道容量,可以采用对管道只写不读,当write阻塞时说明管道已满,计算write进去的数据字节数,即为管道容量。

posted @ 2018-05-17 17:19  落影无痕  阅读(171)  评论(0编辑  收藏  举报