管道分为无名管道(只能在具有公共祖先的两个进程中使用----父子进程)和有名管道(可用于不同进程)
管道特点:
- 半双工,数据在同一时刻只能在同一方向流动
- 数据只能从一端写入一端读出(先入先出)
- 管道不是普通的文件,不属于某个文件系统,其只存在于内存中。
无名管道
通过pipe函数创建无名管道
参数:
pipefd : 为 int 型数组的首地址,其存放了管道的文件描述符 pipefd[0]、pipefd[1]。
当创建一个管道它会创建两个文件描述符fd[0]和fd[1]
- fd[0]用于读管道
- fd[1]用于写管道
一般的IO函数都可以用来操作管道(lseek()除外----因为管道先进先出特点 )
成功 返回0
失败 返回-1
单个进程管道
#include<stdio.h>
#include<unistd.h>
int main()
{
int fd[2];
int ret=-1;
char buf[1024];
ret=pipe(fd);
if(ret==-1)
{
perror("pipe");
return 1;
}
//写管道fd[1]
write(fd[1],"asdfgh",6);
//读管道
read(fd[0],buf,6);
printf("buf:%s\n",buf);
return 0;
}
父子进程管道
进程要先调用pipe,再调用fork,从而创建父进程到子进程的ipc通道
如果要父进程写子进程读 关闭父进程读fd[0] 关闭子进程写fd[1]
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid=-1;
int ret=-1;
int fd[2];
char buf[1024];
//先创建管道
ret=pipe(fd);
if(ret==-1)
{
perror("pipe");
return 1;
}
//创建父子进程
pid=fork();
if(pid==0)
{
//子进程 读 关闭写
close(fd[1]);
read(fd[0],buf,sizeof(buf));
printf("buf:%s\n",buf);
exit(0);
}
else{
//parent
close(fd[0]);
write(fd[1],"123456678",9);
}
return 0;
}
当管道的一端被关闭后 有以下规则
读管道
- 管道有数据,read返回读到的字节数
- 无数据
- 当写管道没有关闭时,,read阻塞,(这里可以设置为非阻塞,没有数据直接返回-1)
- 关闭时,read返回0(相当于读到末尾)
写管道
读管道没有关闭
- 管道满了,write阻塞
- 未满,返回实际写入字节数
读管道关闭
- 进程会终止(回产生SIGPIPE,如果忽略该信号或者捕捉该信号并从其处理程序返回,则write返回-1,error设置为EPIPE)
设置为非阻塞
//先获取原来的flags
int flags=fcntl(fd[0],F_GETFL)
//这只新的flags
flsgs |= O_NONBLOCK
//设置非阻塞
fcntl(fd[0],F_SETFL,flags)
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
int main()
{
pid_t pid=-1;
int ret=-1;
int fd[2];
char buf[1024];
//先创建管道
ret=pipe(fd);
if(ret==-1)
{
perror("pipe");
return 1;
}
//创建父子进程
pid=fork();
if(pid==0)
{
//子进程 读 关闭写
close(fd[1]);
memset(buf,0,1024);
printf("子进程读取内容......\n");
int flags=fcntl(fd[0],F_GETFL);
flags|=O_NONBLOCK;
fcntl(fd[0],F_SETFL,flags);
ret= read(fd[0],buf,sizeof(buf));
if(ret==-1)
{
perror("read");
return 1;
}
printf("buf:%s\n",buf);
close(fd[0]);
exit(1);
}
else{
//parent
close(fd[0]);
sleep(3);
write(fd[1],"123456678",9);
}
return 0;
}
查看缓冲区的命令
可以使用ulimit -a命令查看当前系统中创建管道文件所对应的内核缓冲区大小
查看缓冲区的函数
#include<unistd.h>
long fpathconf(int fd,int name)
功能:
通过name查看不同属性值
name:
- _PC_PIPE_BUF,查看管道缓冲区大小
- _PC_NAME_MAX,文件名字字节数的上限
成功根据name返回不同
失败返回-1
#include<unistd.h>
#include<stdio.h>
int main()
{
int fd[2];
int ret=-1;
ret=pipe(fd);
if(ret==-1)
{
perror("pipe");
return 1;
}
long num=fpathconf(fd[0],_PC_PIPE_BUF);
printf("num=%ld\n",num);
num=fpathconf(fd[1],_PC_NAME_MAX);
printf("num=%ld\n",num);
}
有名管道
命名管道提供一个路径名与之关联,以FIFO的文件系统存在于文件系统中,两个进程彼此通过FIFO相互通信.
通过命令创建管道
两个进程 一个写管道一个读管道
命名管道(FIFO)和无名管道(pipe)有一些特点是相同的,不一样的地方在于:
1) FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在内存中。
2) 当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。
3) FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。