管道(本质是内核的缓存):一个进程到另一个进程的数据流。大小一般为4K。(一页也是4K,高效).
限制:
1.半双工通信
2.只能在有亲缘关系的进程间使用(有公共祖先),通常,一个管道有一个进程创建,然后该进程调用fork,此后父、子进程就可应用该管道。
管道的创建:
int pipe(int fildes[2]);//filedes[0]为读而打开,filedes[1]为了写而打开。 filedes[1]的输出是filedes[0]的输入。
返回值:成功返回0,若出错返回-1;
简单说明:一般而言,进程退出,管道释放,所以管道的生命周期随进程。
一般而言,内核会对管道操作进行同步与互斥
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
//建立管道
int fds[2];
if (-1 == pipe(fds)) perror("pipe"), exit(1);
pid_t pid = fork();
if(-1 == pid){
perror("fork");
exit(1);
}
else if(0 == pid) //子进程读
{
close(fds[1]); //不写
char buf[1024] = {};
while(1)
{
int r;
memset(buf, 0x00, sizeof(buf));
r = read(fds[0], buf, sizeof(buf));
if(-1 == r)perror("read"), exit(1);
if(0 == r) break;
printf("child read = %s\n", buf);
}
}else //父进程写
{
close(fds[0]);
char buf[1024] = {};
while(fgets(buf, sizeof(buf), stdin) != NULL)
{
int w;
w = write(fds[1], buf, strlen(buf));
if(-1 == w) perror("write"), exit(1);
memset(buf, 0x00, sizeof(buf));
}
}
return 0;
}
shell管道的实现:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
//建立管道
int fds[2];
if(-1 == pipe(fds)) perror("pipe"), exit(1);
pid_t pid = fork();
if(-1 == pid){
perror("fork");
exit(1);
}
else if(0 == pid)//子进程写
{
close(fds[0]); //不读
close(1); //关掉标准输出
dup(fds[1]); //写到管道的写端
execlp("ls", "ls", "-l", NULL);
close(fds[1]);
printf("替换进程失败\n");
}else //父进程读
{
close(fds[1]); //不写
close(0); //关掉标准输出
dup(fds[0]); //输出到管道的读端
execlp("wc", "wc", "-l", NULL);
close(fds[0]);
printf("替换进程失败\n");
}
return 0;
}
命名管道:
管道对操作的进程有个限制,必须具有同一个公共祖先的进才能使用。要是没有任何关系的进程要进行的通信,我们就要使用到命名管道。命名管道是一种特殊的文件类型。
命名管道可以使用命令行创建:
mkfifo filename
也可以从程序里创建,相关函数为:
int mkfifo(const char* pathname, mode_t mode); 返回值:成功返回0, 出错返回-1 出错的原因一般是创建文件的目录没有写权限。
当没有进程往管道中写时,从管道中读会发生阻塞,当没有进程从管道中读的时候,往管道中写会阻塞。
示例程序1: 打开一个命名管道,write.c程序对管道进行写操作,read.c对管道进行读操作
#include <stdio.h>
#include <stdlib.h> //打开管道
int main()
{
if(mkfifo("mypipe.p", 0644) == -1)
{
perror("mkfifo");
exit(1);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h> //写操作
int main()
{
int fd = open("mypipe.p", O_WRONLY);
if(-1 == fd)
{
perror("open");
exit(1);
}
int i = 1;
while(1)
{
if(-1 == write(fd, &i, sizeof i))
{
perror("read");
break;
}
i++;
printf("write %d ok!\n", i - 1);
sleep(1);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> //读操作
int main()
{
int fd = open("mypipe.p", O_RDONLY);
if(-1 == fd)
{
perror("open");
exit(1);
}
while(1)
{
int i;
if(0 == read(fd, &i, sizeof i))
{
printf("pipe close\n");
break;
}
printf("i = %d\n", i);
}
return 0;
}
示例程序2:利用命名管道实现文件的拷贝
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h> //读操作
int main()
{
if(-1 == mkfifo("fifo.p", 0644)) perror("mkfifo"), exit(1);
int infd;
infd = open("aaa", O_RDONLY);
if(-1 == infd) perror("open"), exit(1);
int outfd;
outfd = open("fifo.p", O_WRONLY);
if(-1 == outfd) perror("open"), exit(1);
char buf[1024];
int n;
while((n = read(infd, buf, 1024)) > 0)
{
write(outfd, buf, n);
}
close(infd);
close(outfd);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h> //写操作
int main()
{
int outfd;
outfd = open("COPY", O_WRONLY|O_CREAT|O_TRUNC, 0644);
if(-1 == outfd) perror("open"), exit(1);
int infd;
infd = open("fifo.p", O_RDONLY);
if(-1 == infd) perror("open"), exit(1);
char buf[1024];
int n;
while((n = read(infd, buf, 1024)) > 0)
{
write(outfd, buf, n);
}
close(infd);
close(outfd);
unlink("fifo.p");
return 0;
}: