1. 匿名管道
这里说的管道是指匿名管道(无名管道),是 UNIX 系统IPC最古老的形式。
1. 管道的特点:
其本质是一个伪文件(实为内核缓冲区)。
它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
数据一旦被读走,便不在管道中存在,不可反复读取。
管道的大小有限制,当要写⼊的数据量⼤于
PIPE_BUF
时,linux将不再保证写⼊入的原⼦子性。一般而言,内核会对管道操作进行同步与互斥。
一般而言,进程退出,管道释放,所以管道的生命周期随进程。
2. 创建管道
int pipe(int fd[2]);
// 头文件:#include <unistd.h>
// 参数fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
// 返回值:若成功返回0,失败返回-1
3. 实现父子进程通信
把子进程 ls -l
命令的结果放入管道中,父进程从管道中读取这些信息,用wc -l
命令计算文件个数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fds[2];
pipe(fds);
if(fork() == 0)
{
close(1); //关闭标准输出
close(fds[0]); //关闭子进程的读
dup(fds[1]); //让子进程管道的写与标准输出建立联系
close(fds[1]);
execlp("ls", "ls", "-l", NULL);
exit(0);
}else{
close(0); //关闭标准输入
close(fds[1]); //关闭父进程的写
dup(fds[0]); //让父进程管道的读与标准输入建立联系,即把从管道里面读入的 wc -l 的结果写入到文件中
close(fds[0]);
execlp("wc", "wc", "-l", NULL);
exit(0);
}
return 0;
}
4. 管道读写规则
- 当没有数据可读时
O_NONBLOCK disable
:read
调用阻塞,即进程暂停执行,一直等到有数据来到为止。O_NONBLOCK enable
:read
调用返回-1,errno
值为EAGAIN
。
- 当管道满的时候
O_NONBLOCK disable
: write调用阻塞,直到有进程读走数据。O_NONBLOCK enable
:调用返回-1,errno
值为EAGAIN
。
如果所有管道写端对应的文件描述符被关闭,则read返回0。
如果所有管道读端对应的文件描述符被关闭,则write操作会产⽣生信SIGPIPE,进而可能导致write进程退出。
当要写入的数据量不大于
PIPE_BUF
时,linux将保证写入的原⼦子性。当要写入的数据量大于
PIPE_BUF
时,linux将不再保证写入的原子性。
2. 命名管道
1.定义
管道的应用有一个限制是只能在具有共同祖先(具有亲缘关系)的进程间通信。如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
命名管道有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
2. 匿名管道与命名管道的区别:
匿名管道由pipe函数创建并打开。命名管道由mkfifo函数创建,打开用open。
FIFO
(命名管道)与pipe
(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。
3. 创建命名管道
int mkfifo(const char *pathname, mode_t mode);
// 头文件:#include <sys/stat.h>
// 返回值:成功返回0,出错返回-1
// 参数1:mkfifo() 会依参数pathname建立特殊的FIFO文件,该文件必须不存
// 参数2:mode 表示创建出来的FIFO文件的权限。
4. 使用示例
FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。下面的例子演示了使用 FIFO 进行 IPC 的过程:
< code mkfifo_w.c>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>
int main()
{
int n, i;
int fd;
char buff[1024];
time_t tp;
printf("I am %d process\n", getpid());
if((fd = open("fifo", O_CREAT|0666)) < 0) perror("open error"), exit(1);
for(i = 0; i < 10; ++i){
time(&tp); //获取系统当前时间
n = sprintf(buff, "Process %d's time is %s", getpid(), ctime(&tp));
printf("Send message: %s", buff); // 打印
if(write(fd, buff, n+1) < 0) // 写入到FIFO中
{
perror("Write FIFO Failed");
close(fd);
exit(1);
}
sleep(1); // 休眠1秒
}
close(fd); // 关闭FIFO文件
return 0;
}
< code mkfifo_r.c >
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
int main()
{
int fd;
int len;
char buff[1024];
if(mkfifo("fifo", 0666) < 0 && errno!=EEXIST) // 创建FIFO管道
perror("Create FIFO Failed");
if((fd = open("fifo", O_RDONLY)) < 0) // 以读打开FIFO
{
perror("Open FIFO Failed");
exit(1);
}
while((len = read(fd, buff, 1024)) > 0) // 读取FIFO管道
{
printf("Read message: %s", buff);
}
close(fd); // 关闭FIFO文件
return 0;
}