一、管道的概念:
管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。
二、管道的本质:
1、其本质是一个伪文件(实为内核缓冲区)。
2、有两个文件描述符引用,一个表示读端,一个表示写端。
3、规定数据由管道的写端流入管道,由读端流出。
三、管道的原理:
管道实为内核使用环形队列机制,借助内核缓冲区(4K)实现。
四、管道的局限:
1、数据自己读但不能自己写。
2、数据一旦被独奏,便不在管道中存在,不可反复读取。
3、由于管道采用半双工通讯方式,因此数据只能在一个方向流动。
4、只能在公共祖先的进程间使用管道。
5、常用的通信方式有,单工通信,半双工通信,全双工通信。
五、pipe函数:
1、创建管道:int pipe(int fd[2]);成功返回:0;失败返回-1,设置errno。函数调用成功返回r/w两个文件描述符。无需open,但需要手动的close。规定:fd[0]->r;fd[1]->w,就像0对应标准输入,1对应标准输出一样。向管道文件写数据其实是在读写内核缓冲区。
练习:父子进程使用管道通信,父进程写入字符串,子进程读出字符串。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main()
{
pid_t pid;
char buf[1024];
int fd[2];
char *p = "test for pipe\n";
if (pipe(fd) == -1)
sys_err("pipe");
pid = fork();
if (pid < 0)
sys_err("fork err");
else if(pid == 0)
{
#include<string.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main()
{
pid_t pid;
char buf[1024];
int fd[2];
char *p = "test for pipe\n";
if (pipe(fd) == -1)
sys_err("pipe");
pid = fork();
if (pid < 0)
sys_err("fork err");
else if(pid == 0)
{
close(fd[1]);
int len = read(fd[0],buf,sizeof(buf));
write(STDOUT_FILENO,buf,len);
close(fd[0]);
}
else
{
close(fd[0]);
write(fd[1],p,strlen(p));
wait(NULL);
close(fd[1]);
}
return 0;
}
int len = read(fd[0],buf,sizeof(buf));
write(STDOUT_FILENO,buf,len);
close(fd[0]);
}
else
{
close(fd[0]);
write(fd[1],p,strlen(p));
wait(NULL);
close(fd[1]);
}
return 0;
}
管道的读写行为:
1、如果所指向的管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read返回0,就像读到文件末尾一样。
2、如果有指向管道写端的文件描述符没关闭(管道写端引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余数据都被读取后,再次read阻塞,直到管道中有数据可读了才读取数据并返回。
3、如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数为0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。当然也可以对SIGPIPE信号实施捕捉,不终止进程。
4、如果有指向管道读端的文件描述符没关闭(管道读端引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程相管道中写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置写入数据并返回。
七、练习一个读端,多个写端(父进程读数据,两个子进程写入数据)。
include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int fd[2],i,n;
char buf[1024];
int ret = pipe(fd);
if (ret == -1)
{
perror("pipe error");
exit(1);
}
for (i = 0;i < 2;i++)
{
if ((pid = fork()) == 0)
break;
else if (pid == -1)
{
perror("fork error");
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int fd[2],i,n;
char buf[1024];
int ret = pipe(fd);
if (ret == -1)
{
perror("pipe error");
exit(1);
}
for (i = 0;i < 2;i++)
{
if ((pid = fork()) == 0)
break;
else if (pid == -1)
{
perror("fork error");
exit(1);
}
}
if (i == 0)
{
close(fd[0]);
write(fd[1],"1.hello\n",strlen("1.hello\n"));
}
else if(i == 1)
{
sleep(1);
close(fd[0]);
write(fd[1],"2.hello\n",strlen("2.hello\n"));
}
else
{
close(fd[1]);
sleep(2);
n = read(fd[0],buf,1024);
write(STDOUT_FILENO,buf,n);
}
return 0;
}
}
}
if (i == 0)
{
close(fd[0]);
write(fd[1],"1.hello\n",strlen("1.hello\n"));
}
else if(i == 1)
{
sleep(1);
close(fd[0]);
write(fd[1],"2.hello\n",strlen("2.hello\n"));
}
else
{
close(fd[1]);
sleep(2);
n = read(fd[0],buf,1024);
write(STDOUT_FILENO,buf,n);
}
return 0;
}