管道:
1. 管道是Unix系统最古老的进程间通信方式,其实质是一个虚拟文件,是在内核中维护了一个消息队列。
2. 历史上的管道通常是指半双工管道,只允许数据单向流动。现代系统大都提供全双工管道,数据可以沿着管道双向流动。
1. 概念:基于有名文件(管道文件)的管道通信
2. 命令形式:
# mkfifo fifo 创建管道
# echo hello > fifo 读入hello到管道中
# cat fifo 查看管道中内容
演示:
注意:
1. 执行echo 命令式 第一个终端是阻塞在write上的,只有用第二个终端cat后,才解除阻塞
2.管道自身是不占内存的,可以用ls -l 命令查看 :第一个p表示piple
3. 编程模型:
4. 编程实现:写进程:wfifo.cpp
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#define FIFO_FILE "/tmp/fifo"
int main (void) {
printf ("创建管道...\n");
if (mkfifo (FIFO_FILE, 0666) == -1) {
perror ("mkfifo");
return -1;
}
printf ("打开管道...\n");
int fd = open (FIFO_FILE, O_WRONLY);
if (fd == -1) {
perror ("open");
return -1;
}
printf ("发送数据...\n");
for (;;) {
printf ("> ");
char buf[1024];
gets (buf);
if (! strcmp (buf, "!"))
break;
if (write (fd, buf, (strlen (buf) + 1) *
sizeof (buf[0])) == -1) {
perror ("write");
return -1;
}
}
printf ("关闭管道...\n");
if (close (fd) == -1) {
perror ("close");
return -1;
}
printf ("删除管道...\n");
<span style="white-space:pre"> </span>if (unlink (FIFO_FILE) == -1) {
perror ("unlink");
return -1;
}
printf ("大功告成!\n");
return 0;
}
读进程:rfifo.cpp
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define FIFO_FILE "/tmp/fifo"
int main (void) {
printf ("打开管道...\n");
int fd = open (FIFO_FILE, O_RDONLY);
if (fd == -1) {
perror ("open");
return -1;
}
printf ("接收数据...\n");
for (;;) {
char buf[1024];
ssize_t rb = read (fd, buf, sizeof (buf));
if (rb == -1) {
perror ("read");
return -1;
}
if (! rb)//rb=0说明管道的另外一段被关闭了
break;
printf ("< %s\n", buf);
}
printf ("关闭管道...\n");
if (close (fd) == -1) {
perror ("close");
return -1;
}
printf ("大功告成!\n");
return 0;
}
运行两个进程:
注意:
① 当管道是通的时候,open函数才会执行。
② 当管道两端都关闭的时管道才会被卸载
③当读到的是!时候,写端被关闭,此时read读到的是0,读端也被关闭,否则读端阻塞在read函数,写端阻塞在write函数
思考:当我们开三个终端,一个写两个读,会是导致什么结果?
当一个管道被多个进程去读的时候,不定谁读到,得看cpu的切换,看cpu给谁时间(开三个终端,一个写,俩个读),此时我们应该使用多客户机模型。
从图中可以看出:请求从共有管道发出,应答从私有管道发出,共有管道由服务器建立,私有管道由各自的客户端建立,保证数据发送不会混乱。
无名管道(pipe)
头文件:#include <unistd.h>
int pipe (int pipefd[2]);
1) 成功返回0,失败返回-1。
2) 通过输出参数pipefd返回两个文件描述符,其中pipefd[0]用于读,pipefd[1]用于写。
特别使用与父子进程之间的通信
使用方法:
A. 调用该函数在内核中创建管道文件(看不到,是个虚拟文件,是内核在虚拟内存中创建的),并通过其输出参数, 获得分别用于读和写的两个文件描述符;
B. 调用fork函数,创建子进程;
C. 写数据的进程关闭读端(pipefd[0]),读数据的进程关闭写端(pipefd[1]);
D. 传输数据;
E. 父子进程分别关闭自己的文件描述符。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int main (void) {
printf ("父进程:创建管道...\n");
int pipefd[2];
if (pipe (pipefd) == -1) {
perror ("pipe");
return -1;
}
printf ("父进程:创建进程...\n");
pid_t pid = fork ();
if (pid == -1) {
perror ("fork");
return -1;
}
if (pid == 0) {
printf ("子进程:关闭写端...\n");
close (pipefd[1]);
printf ("子进程:接收数据...\n");
for (;;) {
char buf[1024];
ssize_t rb = read (pipefd[0], buf, sizeof (buf));
if (rb == -1) {
perror ("read");
return -1;
}
if (! rb)
break;
puts (buf);
}
printf ("子进程:关闭读端...\n");
close (pipefd[0]);
printf ("子进程:大功告成!\n");
return 0;
}
printf ("父进程:关闭读端...\n");
close (pipefd[0]);
printf ("父进程:发送数据...\n");
for (;;) {
char buf[1024];
gets (buf);
if (! strcmp (buf, "!"))
break;
if (write (pipefd[1], buf, (strlen (buf) + 1) *
sizeof (buf[0])) == -1) {
perror ("write");
return -1;
}
}
printf ("父进程:关闭写端...\n");
close (pipefd[1]);
if (wait (0) == -1) {
perror ("wait");
return -1;
}
printf ("父进程:大功告成!\n");
return 0;
}
这个程序中最重要的就是对fork函数的理解,可以参考:
http://blog.csdn.net/meetings/article/details/47123359
有名管道和无名管道的区别:
无名管道是单工模式,命名管道是全双工模式
有名管道是通过一个有名的文件在读写两端建立联系,而无名管道则是通过文件描述符来在父子进程间建立联系的。