嵌入式系统之linux系统编程---17 管道通信(无名管道、有名管道)

1、一些说明

有名管道与无名管道的区别是文件系统里面有没有文件名。

在这里插入图片描述
在应用层中有“进程a”和“进程b”两个进程,这两个进程在应用层里面是不能直接交流的,它们必须通过内核才能进行通讯;利用管道,进程a往管道里面写数据,进程b从管道中去读数据,这样就实现了进程a和进程b之间的通讯。

2、无名管道

无名管道只能实现有亲缘关系间的进程通信,比如说父子进程。像上面提到的进程a与进程b,它们之间无亲缘关系,就不能使用无名管道了。
(1)pipe函数
作用:创建管道

头文件
#include<unistd.h>
函数原型
int pipe(int pipefd[2])
参数 pipefd[2]:这个参数是得到的是文件描述符,而不是传进去的文件描述符
在“管道”中,有两个对应的文件描述符,一个是读端,一个是写端。
读:fd[0]     写:fd[1]

(2)无名管道使用步骤:
a. 调用pipe()创建无名管道;
b.fork()创建子进程,一个进程读,使用 read(),一个进程写,使用 write()。
(3)无名管道测试示例
在编写无名管道测试代码时,存在一个疑问:在fork函数之前还是fork函数之后调用pipe函数来创建管道呢?还是说在这两个位置都可以嘞?
无名管道只能实现有亲缘关系间的进程的通信,因为它要保证对同一个管道进行操作。如何保证呢?在fork函数之前创建pipe函数,就可以保证是在同一个管道里了。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(void)
{
        //定义buf变量。然后让子进程从管道中读数据,让父进程往管道中写数据
        char buf[32] = {0};
        pid_t pid;
        // 定义一个变量来保存文件描述符 ,且数量为2个
        // 因为有一个读端,一个写端,所以数量为 2 个
        int fd[2];
       //调用pipe函数来创建一个管道
        pipe(fd); 
        //打印读端和写端的文件描述符
        printf("fd[0] is %d\n", fd[0]);
        printf("fd[2] is %d\n", fd[1]);
        // 创建进程 
        pid = fork();
        if (pid < 0)
                {
                         printf("error\n");
                }
        if (pid > 0)
                {
                //让父进程往管道里写数据,调用write函数
                //父进程只用到了写端,故一开始便关闭读端,写完之后再关闭写端
                         int status;
                        close(fd[0]);
                        write(fd[1], "hello", 5);
                        close(fd[1]);
                        //在父进程中调用wait函数,会令其阻塞在那里
                        wait(&status);
                        exit(0);
                }
         if (pid == 0)
                {
                //在子进程中调用read函数,读到的数据放入buf中,读32个
                   //在子进程中,只用到了读端,故暂时关闭写端
                        close(fd[1]);
                        read(fd[0], buf, 32);
                        printf("buf is %s\n", buf);
                     //读完之后,将读端也关闭
                        close(fd[0]);
                        exit(0);
                }
               return 0;
}

在该程序中,如果父进程不写入数据,如下代码所示:

 if (pid > 0)
                {
                         int status;
                        wait(&status);
                        exit(0);
                }

则运行之后会发现子进程不会 printf(“buf is %s\n”, buf)打印该打印的东西,为啥呢?因为父进程不往管道里面写东西,父进程阻塞了(管道里面没有东西,阻塞了),需要在父进程中调用write函数,让父进程往管道里写数据。

把程序写完整之后,在ubuntu上的编译结果如下:
在这里插入图片描述
观察ubuntu界面的编译运行结果,可以看到文件描述符的返回值是3和4,为什么是3和4呢?因为文件描述符0,1,2代表的是标准输入、标准输出、标准出错,所以是从3开始的。
此外,有一点需要注意,这个管道是创建在内存里面的,假如进程已经结束了,那么这个空间就会得到释放,管道也就不存在了。管道里面的东西也是读完就没有啦,读完就删除了。

3、有名管道

有名管道可以实现没有任何关系的进程之间的通信。
因为有名管道是在文件系统里面存在这样一个文件名,它是一个特殊的文件类型,是管道类型。只需要知道这个文件所在的路径,就可以进行没有任何亲缘关系的进程间的通信。在使用有名管道时,第一步是先在文件系统里面创建这样一个文件名,创建这个文件名不能使用open函数或者creat函数来完成,因为open函数这一类函数只能创建普通文件,而不能创建特殊文件。那么这一类的特殊文件(管道类型文件)如何创建呢?这里使用mkfifo函数,该函数位于man手册的第三页。
(1)mkfifo函数
该函数的作用是:创建一个FIFO这样的特殊文件,如果创建成功的话,返回值为0;创建失败,返回值为-1.

头文件:
#include<sys/types.h>
#include<sys/stat.h>
函数:
int mkfifo(const char *pathname, const char *pathname)
其中,const char *pathname指的是“创建的FIFO文件的文件名”
const char *pathname指的是“权限”,该权限与umask有关

存在这样的函数,那么就存在这么一个命令,在ubuntu界面输入“man 1 mkfifo”,会出现“mkfifo”命令,该命令的功能是创建FIFO,使用方法是“mkfifo + FIFO文件名”
即:在文件系统中创建一个FIFO文件,有两种方法:一是直接使用命令创建;二是在代码中调用mkfifo函数
(2)有名管道测试示例

  • a、使用命令创建fifo
    在这里插入图片描述
    使用ls -al命令查看其权限,可以看到fifo文件的类型为p,意为管道类型;可以看到它的大小为0,意思是该管道类型的文件只有inode号,并不占磁盘的空间(块设备文件也不占用磁盘空间)

  • b、使用 mkfifo()函数创建有名管道的示例

    第一步:读数据
    从有名管道里面读数据,同样是从命令行里面传进去它要打开的文件,以“只读”的方式打开,打开之后,每隔一秒种,就将fifo文件里面的数据读出来,放到buf里面,然后打印buf的内容。
    读数据的代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buf[32] = {0};
int fd;
if (argc < 2)
{
printf("Usage:%s <fifo name> \n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDONLY);
while (1)
{
sleep(1);
read(fd, buf, 32);
printf("buf is %s\n", buf);
memset(buf, 0, sizeof(buf));
}
close(fd);
return 0;
}

在ubuntu界面编译该读文件的代码并运行,结果如下:
在这里插入图片描述
观察该“读数据”的代码的编译运行结果可以看到,阻塞啦,并没有打印buf的内容,说明不论是有名管道还是无名管道都有阻塞的属性,当管道里面没有数据时就会阻塞在那里
接下来,重新打开一个终端,编译写入数据的代码,代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
        int ret;
        char buf[32] = {0};
        int fd;
        if (argc < 2)
        {
                printf("Usage:%s <fifo name> \n", argv[0]);
                return -1;
        }
        if (access(argv[1], F_OK) == 1)
        {
                ret = mkfifo(argv[1], 0666);
                if (ret == -1)
                {
                        printf("mkfifo is error \n");
                        return -2;
                }
        printf("mkfifo is ok \n");
        }
        fd = open(argv[1], O_WRONLY);
        while (1)
        {
                sleep(1);
                write(fd, "hello", 5);
        }
        //每隔一秒钟就打印hello
        close(fd);
        return 0;
}

编译fifo_write文件并执行,让它往fifo里面写数据,再执行fifo_read 看是否可以执行成功?
运行结果如下,可以看到可执行文件fifo_read每隔一秒钟就显示 buf is hello。
在这里插入图片描述
将fifo_write进程kill掉,发现fifo_read还在打印,只是内容为空。(如下图所示)此时在fifo_read所在的终端里将该进程kill掉就终止运行啦
在这里插入图片描述

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值