今天带小伙伴们一起学习一下Linux中的有名管道和无名管道的使用,先大致介绍下有名管道和无名管道相关的内容,接着进行函数的学习,最后撸代码!
1 无名管道与有名管道的区别
管道分为无名管道(pipe)和有名管道(FIFO)两种。
无名管道只能用于公共祖先的两个进程间的通信,原因是自己创建的管道在别的进程中并不可见。
有名管道可用于同一系统中的任意两个进程间的通信。
1)无名管道
无名管道创建完成后,等同于操作文件。
无名管道的读端被视作一个文件,写端也被视作一个文件。
创建用pipe,操作用read、write、close。
① 无名管道通信是单向的,有固定的读端和写端。
② 数据被进程从管道读出后,管道中的数据就不存在了。
③ 进程读取空管道时,进程会阻塞。
④ 进程往满的管道写入数据时,进程会阻塞。
⑤ 管道容量最大为64KB,可以通过宏PIPE_BUFFERS进行设置。
下面补充两个无名管道的两个说明。
<1 关于公共祖先进程的说明。
管道可以用于任意两个或更多相关进程之间的通信,只要在创建子进程的系列调用之前通过一个共同的祖先进程创建管道即可。
如管道可用于一个进程和其子孙进程之间的通信。第一个进程创建管道,然后创建子进程,接着子进程再创建第一个进程的孙子进程。
管道通常用于两个兄弟进程之间的通信——它们的父进程创建了管道,并创建两个子进程。
<2 关于无名管道半双工的说明。
一般来说管道是半双工的,有些系统实现了全双工的功能,但为了方便移植尽量使用半双工方式使用管道。
半双工情况下,管道的两端在一个进程中相互连接,数据需要通过内核在管道中流动。
单个进程对管道进行读写是没有问题的,但应用比较多的是两个进程之间的通信,但此时存在竞争的问题。
如果创建两个管道,需要注意死锁的问题。如果两个进程都试图从空管道中读取数据或尝试向已满的管道中写入数据就可能会发生死锁。
此时由于半双工的原因,就需要关闭不需要的读端和写端,下图是父子进程进行通信的示意图。
图片
2)有名管道
有名管道又叫FIFO文件,它的操作与文件操作类似,需要创建、打开和关闭等操作。
创建用mkfifo,删除用unlink,读写用read、write,打开关闭用open、close。
FIFO文件的操作与普通文件的操作差异如下:
① 读取FIFO文件的进程只能以“RDONLY”方式打开FIFO文件。
② 写FIFO文件的进程只能以“WRONLY”方式打开FIFO文件。
③ FIFO文件里面的内容被读取后,就消失了。但是普通文件里面的内容读取后还存在。
2 Linux下的无名管道
1)pipe函数
pipe函数用于创建无名管道。
① 函数原型。
- int pipe(int pipefd[2])
② 头文件。
- include <unistd.h>
③ 参数。
pipefd[0]:读管道。
pipefd[1]:写管道。
④ 返回值。
成功:0。
失败:-1。
3 Linux下的有名管道
1)mkfifo函数
mkfifo函数用于创建有名管道。
① 函数原型。
- int mkfifo(const char* pathname, mode_t mode)
② 头文件。
- include <sys/type.h>
- include <sys/stat.h>
③ 参数。
pathname:要创建的fifo文件的名字,带路径。
mode:要创建的fifo文件的访问权限。
④ 返回值。
成功:0。
失败:-1。
2)unlink函数
unlink函数用于删除有名管道,其实它可以删除FIFO文件,也可以删除普通文件。
① 函数原型。
- int unlink(const char* pathname)
② 头文件。
- include <unistd.h>
③ 参数。
pathname:要创建的fifo文件的名字,带路径。
④ 返回值。
成功:0。
失败:-1。
3)open函数
open函数用于打开一个有名管道。
① 函数原型。
- int open(constchar *pathname, int flags)
- int open(constchar *pathname, int flags, mode_t mode)
② 头文件。
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
③ 参数。
pathname: 要打开的文件名(含路径)。
flags: 文件打开的标志(这里只介绍两种,更多的可以man 2 open查看)。
O_APPEND: 以追加的方式打开文件。
O_CREAT: 当打开的文件不存在的时候,创建该文件。
mode: 一定是在flags中使用了O_CREAT标志,mode记录待创建的访问权限。
④ 返回值。
成功:返回文件描述符。
失败:返回-1。
4)read函数
read函数用于读有名管道。
① 函数原型。
- ssize_t read(int fd, void *buf, size_t count)
② 头文件。
- #include <unistd.h>
③ 参数。
fd:要读取文件的文件描述符。
buf:读缓冲的起始地址(读取来的数据存到buf指向的空间)。
count:读到的字节数。
④ 返回值。
成功:读到的字节数。
失败:返回-1。
5)write函数
write函数用于写有名管道。
① 函数原型。
- ssize_t write(int fd, const void *buf, size_t count)
② 头文件。
- #include <unistd.h>
③ 参数。
fd:文件描述符。
buf:文件数据缓冲区。
count:所要写的数据字节数。
④ 返回值。
成功:写入的字节数。
失败:返回-1。
6)close函数
close函数用于关闭有名管道。
① 函数原型。
- int close(int fd)
② 头文件。
- #include <unistd.h>
③ 参数。
fd:待关闭的文件描述符。
④ 返回值。
成功:返回0。
失败:返回-1。
4 实例代码
下面用两个小程序用一下上面介绍的有名管道和无名管道。
1)无名管道的使用实例
实例代码如下,说明都在代码注释中了,图片。
PipeTest.c。
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- void main(void)
- {
- pid_t pid = 0; // 进程的pid
- int pipefd[2]; // 读写管道的文件描述符
- char buf[20]; // 数据缓冲
- pipe(pipefd); // 创建管道
- pid = fork(); // 创建子进程
- if(pid > 0) // 父进程
- {
- close(pipefd[0]); // 这是一个习惯,关闭不用的管道
- write(pipefd[1],"Test pipe!",11); // 将数据写入管道
- wait(); // 等待子进程执行完退出
- close(pipefd[1]); // 用完关掉文件
- exit(0); // 进程退出
- }
- if(pid == 0) // 子进程
- {
- close(pipefd[1]); // 这是一个习惯,关闭不用的管道
- read(pipefd[0],buf,11); // 读取管道内容,并打印
- printf("\nThe pipe's context is %s\n\n",buf); //子进程可以读到父进程写入管道的内容Test!
- close(pipefd[0]); // 关闭管道
- exit(0); // 进程退出
- }
- }
运行结果如下:
2)有名管道的使用实例
实例代码如下,说明都在代码注释中了。
WriteFIFO.c。
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- int main(void)
- {
- int fifofd; // FIFO文件描述符
- int ret;
- mkfifo("/tmp/fifo",0666); // 创建FIFO文件
- fifofd = open("/tmp/fifo",O_WRONLY); // 打开FIFO文件
- ret = write(fifofd,"Test FIFO!",11); // 写数据到FIFO文件,等待读成功才返回
- close(fifofd); // 关闭FIFO文件
- return 0;
- }
ReadFIFO.c。
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- void main(void)
- {
- int fifofd; // 管道文件描述符
- char buf[20]; // 数据缓冲
- mkfifo("/tmp/fifo",0666); // 创建FIFO文件
- fifofd = open("/tmp/fifo",O_RDONLY); // 打开FIFO文件
- read(fifofd,buf,11); // 读FIFO文件
- printf("\nThe FIFO's content is %s\n\n",buf); // 显示读到FIFO文件的内容
- unlink("/tmp/fifo"); // 删除FIFO文件
- }
运行结果如下:
先运行WriteFIFO操作,此时会阻塞,接着运行ReadFIFO,打印并解除阻塞。
本文转载自微信公众号「嵌入式杂牌军」,可以通过以下二维码关注。转载本文请联系嵌入式杂牌军公众号。