Linux 进程间通信~管道

管道

(1)什么是管道

当一个进程连接数据到另一个进程时,这种连接方式被称为管道。
管道实际上在内核创建了一个i node,和一个指向它的固定大小内核缓冲区(可以理解为类似cache,但读写不分开),传入的两个文件描述符就都指向这段缓冲区,这样读管道的0文件描述符和写它的1文件描述符,就好像读写这个文件一样,实现了进程间的通信。
管道借助于文件系统,根据是否要创建管道文件分为有名管道和无名管道。有名管道的用法是创建一个管道文件,然后利用文件的读写操作利用其进行通信。无名管道是在父子进程或者兄弟进程之间利用文件描述符进行通信,这里并不需要创建文件。

(2)管道的优缺点

无名管道是一种非永久性的管道通信机构.当它访问的进程全部终止时,它也将随之被撤消。无名管道只能用在具有家族联系的进程之间。
有名管道可以长期存在于系统之中.而且提供给任意关系的进程使用,但是使用不当容易导致出错。
管道是所有进程间通讯最简单的方式,这样也导致了管道只能传递固定格式的数据。一旦遭遇一对多,多对一,多对多的情况管道就不再适用了。

(3)无名管道的实现

无名管道利用pipe函数进行创建管道,其原型为:
int pipe(int file_descriptor[2]);
其中,file_descriptor[1]用来写入,file_descriptor[0]用来读取。
父子进程间代码实现如下

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
int main()
{
	int data_processed;
	int file_pipes[2];
	const char some_data[] = "123";
	char buffer[128];

	pid_t fork_result;

	memset(buffer, '\0', sizeof(buffer));

	if(pipe(file_pipes) == 0)
	{
		fork_result = fork();
		assert(fork_result != -1);
		if(fork_result == 0)
		{
			// 子进程
			data_processed = read(file_pipes[0], buffer, 127);
			printf("READ %d bytes: %s \n",data_processed, buffer);
			exit(0);
		}
		else
		{
			//父进程
			data_processed = write(file_pipes[1], some_data,
					strlen(some_data));
			printf("Wrote %d bytes \n",data_processed);
		}
	}
	exit(0);
}

无名管道也可以应用于兄弟进程之间,这样就需要利用exec函数进行进程替代了。
函数实现如下

//pipe3.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
int main()
{
	int data_processed;
	int file_pipes[2];
	const char some_data[] = "123";
	char buffer[128];

	pid_t fork_result;

	memset(buffer, '\0', sizeof(buffer));

	if(pipe(file_pipes) == 0)
	{
		fork_result = fork();
		assert(fork_result != -1);
		if(fork_result == 0)
		{
			// 子进程
			sprintf(buffer, "%d", file_pipes[0]);
			(void)execl("pipe4", "pipe4", buffer, (char*)0);
			exit(0);
		}
		else
		{
			//父进程
			data_processed = write(file_pipes[1], some_data,
					strlen(some_data));
			printf("%d - wrote %d bytes \n",getpid(),data_processed);
		}
	}
	exit(0);
}

其中用到了pipe4.c这个文件,这个文件只是一个简单的输出文件。代码如下

//pipe4.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
	int data_processed;
	char buffer[128];
	int file_descriptor;

	memset(buffer, '\0', sizeof(buffer));
	sscanf(argv[1], "%d", &file_descriptor);
	data_processed = read(file_descriptor, buffer, 127);

	printf("%d  - read %d bytes : %s \n",getpid(), data_processed, buffer);
	exit(0);
}

(4)有名管道的实现

有名管道需要一个共同的管道文件,他可以在命令行窗口由mkfifo filename 命令创建,也可以在程序中利用mkfifo()函数创建,其格式为:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char* filename,mode_t mode);
其中,filename 是文件名,mode是文件权限。
代码如下所示:
fifo3.c文件是写进程,向管道里写数据

//fifo3.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "./test"
#define BUFFER_SIZE 128
#define TEN_MEG (1024*1024*10)

int main()
{
	int pipe_fd;
	int res;
	int open_mode = O_WRONLY;
	int bytes_sent = 0;
	char buffer[128];
	if(access(FIFO_NAME, F_OK) == -1)
	{
		res = mkfifo(FIFO_NAME, 0777);
		if(res != 0)
		{
			fprintf(stderr,"COULD not creat fifo %s \n",FIFO_NAME);
			exit(0);
		}
		printf("process %d opening FIFO O_WRONLY\n",getpid());
		pipe_fd = open(FIFO_NAME, open_mode);
		printf("Process %d result %d\n",getpid(),pipe_fd);

		if(pipe_fd != -1)
		{
			while(bytes_sent < TEN_MEG)
			{
				res = write(pipe_fd, buffer, BUFFER_SIZE);
				if(res == -1)
				{
					fprintf(stderr, "Write errer on pipe \n");
					exit(0);
				}
				bytes_sent += res;
			}
			(void)close(pipe_fd);
		}
		else
		{
			exit(EXIT_FAILURE);
		}
		printf("Process %d finished\n ",getpid());
		exit(EXIT_SUCCESS);
	}
	exit(0);
}

fifo4.c文件是读文件。

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "./test"
#define BUFFER_SIZE 128
#define TEN_MEG (1024*1024*10)

int main()
{
	int pipe_fd;
	int res;
	int open_mode = O_RDONLY;
	char buffer[128];
	int bytes_read = 0;
	memset(buffer, '\0', sizeof(buffer));

	printf("process %d opening FIFO O_RDONLY\n",getpid());
	pipe_fd = open(FIFO_NAME, open_mode);
	printf("Process %d result %d\n",getpid(), pipe_fd);

	if(pipe_fd != -1)
	{
		do
		{
			res = read(pipe_fd, buffer, BUFFER_SIZE);
			bytes_read += res;
		}while(res > 0);
		(void)close(pipe_fd);
	}
	else
	{
		exit(EXIT_FAILURE);
	}
	printf("Process %d finishe, %d bytes read\n",getpid(),bytes_read);
	exit(EXIT_SUCCESS);
}

写入时,如果没有读取段,则会阻塞,以等待读进程。

猜你喜欢

转载自blog.csdn.net/ichliebecamb/article/details/88948688