一、概念
进程间通信是在不同进程间传播或者交换消息。管道,也就是匿名管道,是linux系统下最常见的进程间通信方式之一,它是在两个进程间实现一个数据流通的通道,优点:简单易用;缺点:功能简单。
管道是linux/unix系统间比较原始的进程间通信方式,实现数据以一种数据流的方式在进程间流动。匿名管道在系统中没有实名的,并不可以在文件系统中以任何方式看到该管道,管道只是进程的一种资源,随着进程的结束被系统清除。创建一个管道时生成了两个文件描述符,但是没有路径名,也就是不存在有意义的文件,此处的文件描述符只是在内存中跟某一个索引节点相关联的两个文件描述符。
二、管道通信特点:
1,无名字,是匿名管道
2,半双工通信,需要双工通信时,需要建立起连个管道
3,只能用于父子进程和兄弟进程之间
4,单独构成一种文件系统,存在于内存中
5,写入的内容在管道缓冲区的末尾,然后从管道缓冲区的头部读出
6,缓冲区大小有限
7,管道所传送的是无格式字节流。
三、管道的创建与关闭
Linux环境下使用pipe函数创建一个管道:intpipe(int fd[2]); 若成功返回0,若失败返回-1.参数fd[2]是一个长度为2的文件描述符数组,fd[0]是读出端的文件描述符,fd[1]是写入端的文件描述符。当函数成功返回后,自动维护了一个从fd[1]到fd[0]的通道。管道的关闭使用close函数。
程序一演示了如何使用创建和关闭管道,并使用管道传输数据,程序结束时,释放掉管道占用的文件资源。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
intfd[2]; /* 管道的文件描述符数组 */
char str[256];
if((pipe(fd)) < 0)
{
printf("create the pipe failed!\n");
exit(1); /*pipe出错退出*/
}
write(fd[1],"create the pipe successfully!\n", 31 ); /*向管道写入端写入数据*/
read(fd[0],str, sizeof(str) ); /*从管道读出端读出数据*/
printf("%s", str );
printf("pipe file descriptors are%d,%d \n", fd[0], fd[1]) ;
close(fd[0]); /* 关闭管道的读出端文件描述符*/
close(fd[1]); /* 关闭管道的写入端文件描述符*/
return 0;
}
四、管道的读写
可以使用read和write函数对管道进行读写,需要注意的是,管道的两段式固定了的,即管道的读出端只能用于读取数据,管道的写入端只能用于写入数据,一般的文件函数都可以用于管道,如:close read write
管道的写入规则:向管道中写入时,管道缓冲区一旦有空闲区域,写进程就立即试图向管道中写入数据,如果读进程不读走管道缓冲区的数据,写操作会一直被阻塞。单独一个进程操作一个管道时没有意义的,管道应用一般是在父子进程或者兄弟进程之间。
如果要建立一个父进程到子进程的管道,可以先调用pipe函数紧接着调用fork函数,由于子进程自动继承父进程的数据段,则父子进程同时拥有管道的所有权。当想要实现父进程到子进程的数据通道,那么就要在父进程中关闭通道的读出端,在子进程中关闭通道的写入端。反之,在父进程中关闭通道的写入端,在子进程中关闭通道的读出端。总之使用pipe和fork组合,可以构造出所有的父进程与子进程,或子进程到兄弟进程的管道。
程序二关于管道在父子进程中的应用示例,先使用pipe函数建立通道,在使用fork函数创建子进程,在父子进程中维护管道的数据流动方向,并在父进程中项子进程附送消息,在进程中接收消息并在输出到标准输出。
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <limits.h>
#define BUFSIZE PIPE_BUF /* PIPE_BUF:管道默认一次性读写的数据长度*/
void err_quit(char *msg)
{
printf(msg);
exit(1);
}
int main(void)
{
int fd[2];
charbuf[BUFSIZE] = "hello my child!\n"; /*写入管道的缓冲区*/
pid_t pid;
int len;
if((pipe(fd)) < 0) /*创建管道*/
{
err_quit("pipe failed\n");
}
if ((pid =fork()) < 0) /*创建一个子进程*/
{
err_quit("fork failed\n");
}
else if (pid> 0)
{
close (fd[0] ); /*父进程中关闭管道的读出端*/
write(fd[1], buf, strlen(buf)); /*父进程向管道写入数据*/
exit(0);
}
else
{
close (fd[1] ); /*子进程关闭管道的写入端*/
len =read (fd[0], buf, BUFSIZE); /*子进程从管道中读出数据*/
if (len< 0)
{
err_quit ("process failed when read a pipe\n");
}
else
{
write(STDOUT_FILENO, buf, len); /*输出到标准输出*/
}
exit(0);
}
}
程序三是管道在兄弟进程间的应用示例,在父进程中创建管道,并使用fork函数创建两个子进程,在第一个子进程中发送消息到第二个子进程,在第二个子进程中读出消息并处理。在父进程中,什么都不做,需要关闭通道的两端。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <limits.h>
#define BUFSIZE PIPE_BUF /* PIPE_BUF:管道默认一次性读写的数据长度*/
void err_quit(char * msg)
{
printf ( msg);
exit(1);
}
int main(void)
{
int fd[2];
charbuf[BUFSIZE] = "hello my brother!\n"; /* 缓冲区 */
pid_t pid;
int len;
if ((pipe(fd)) < 0 ) /*创建管道*/
{
err_quit("pipe failed\n");
}
if ( (pid =fork()) < 0 ) /*创建第一个子进程*/
{
err_quit("fork failed\n");
}
else if (pid == 0 ) /*子进程中*/
{
close (fd[0] ); /*关闭不使用的文件描述符*/
write(fd[1], buf, strlen(buf)); /*写入消息*/
exit(0);
}
if ( (pid =fork()) < 0 ) /*创建第二个子进程*/
{
err_quit("fork failed\n");
}
else if (pid > 0 ) /*父进程中*/
{
close (fd[0] );
close (fd[1] );
exit ( 0);
}
else /*第二个子进程中*/
{
close (fd[1] ); /*关闭不使用的文件描述符*/
len =read (fd[0], buf, BUFSIZE); /*读取消息*/
write(STDOUT_FILENO, buf, len); /*将消息输出到标准输出*/
exit(0);
}
return 0;
}
在上述代码中,父进程分别建立了两个子进程,在子进程中关闭了管道的读出段,在另一个子进程中关闭了管道的写入端,在父进程中关闭了通道的两段。在程序中父进程在创建了第一个子进程时没有关闭管道两端,而是在创建了第二进程之后才关闭了两端,这是为了在创建第二个子进程时,子进程可以继承完整的通道,而不是已经关闭了两段的通道。