什么是管道?
在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为:限制管道的大小。实际上,管道是一个固定大小的缓冲区。
代码
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
int main()
{
int _pipe[2];
int ret = pipe(_pipe);
if(ret==-1)
{
printf("creat pipe error!errno code is :%d\n",errno);//错误码
return 1;
}
pid_t id = fork();
if(id<0)
{
printf("fork error!");
return 2;
}
else if(id==0)
{
//child
close(_pipe[0]);
int i = 0;
char *_mesg = NULL;
while(i<100)
{
_mesg = "I am child!";
write(_pipe[1],_mesg,strlen(_mesg)+1);//xie
sleep(1);
i++;
}
}
else
{
//father
close(_pipe[1]);
char _mesg_c[100];
int j = 0;
while(j<100)
{
memset(_mesg_c,'\0',sizeof(_mesg_c));
read(_pipe[0],_mesg_c,sizeof(_mesg_c));
printf("%s\n",_mesg_c);
j++;
}
}
}
管道特点及4种情况
- 单向通信。
- 只有在具有亲缘关系的进程间通信。
- 具有同步机制。
- 它是一种面向字节流的通信服务。
- 生命周期随进程
使用管道需要注意以下四种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志)
- 如果所有指向管道写端的文件描述符都关闭了,(管道写端的引用计数为0),而仍然有进程从管道的读端读取数据,那么管道中剩余的数据都被读取之后,再次read将会返回0,就像读到文件结尾一样。也就是说,写端不会写,读端读完之后就会再等着写端去写,但是写端关闭了啊,不会写了,所以就出现上面说的情况。这就体现出了管道的同步机制。
- 如果有指向管道写端的文件描述符没有关闭,(管道写端的引用计数大于0)而持有管道写端的进程也没有向管道中写数据,这时有进程管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。通俗讲就是,读端读数据,一直读,但是写端不写了,而且写端并没有关闭,所以这时读端就会一直等着写端去写。这就造成了阻塞式等待。
- 如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数为0),这时有进程向管道的写端写数据,那么该进程会收到SIGPIPE,通常会导致进程异常终止。所以进程就会异常退出了。
- 如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0)而持有管道读端的进程也没有从管道中读取数据,这时有进程向管道写端写数据,那么在管道写满时再写将会阻塞,直到管道中有了空位置才写入并返回,也就是管道的同步机制。
命名管道
命名管道(NamedPipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是:命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。而且,FIFO总是按照先进先出的原则工作,第一个被写入的数据首先从管道中读出。
命名管道的使用和匿名管道基本相同,只是在使用命名管道之前首先要使用open函数打开,因为命名管道是存在于硬盘上的文件,而管道是存在于内存中的特殊文件。
需要注意,使用open的几点:
- 调用open()打开命名管道可能会被阻塞,但是如果同时用读写方式(O_RDWR)打开,则一定不会造成阻塞。
- 如果以制度方式(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写才能打开管道。
- 同样,以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。
//相关代码
//client.c 也就是 管道的写端
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
umask(0);
if(mkfifo("./mypipe",0666 | S_IFIFO)<0)
{
perror("mkfifo error");
return 1;
}
int fd = open("./mypipe",O_RDONLY);
if(fd<0)
{
printf("open file error!\n");
return 2;
}
char buf[1024];
while(1)
{
ssize_t ret = read(fd,buf,sizeof(buf)-1);
if(ret>0)//error or end of file
{
buf[ret] = 0;
printf("client say# %s\n",buf);
}
else if(ret==0)
{
printf("client quit !server begin quit!\n");
break;
}
}
close(fd);
return 0;
}
//server.c 也就是管道的读端
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd = open("./mypipe",O_WRONLY);
if(fd<0)
{
printf("open file error!\n");
return 2;
}
char buf[1024];
while(1)
{
printf("please enter # ");
fflush(stdout);
ssize_t ret = read(0,buf,sizeof(buf)-1);
if(ret>0)
{
buf[ret-1] = 0;
write(fd,buf,strlen(buf));
}
}
close(fd);
return 0;
}