一. 什么是管道

pipe: 匿名管道。

对于熟悉 linux 开发的人来说,pipe 就很熟悉了。pipe 是一种 IPC 机制,他的作用是用作有血缘进程间完成数据传递,只能从一端写入,从另外一端读出。为了解决 pipe 的弊端,linux 的大神门又引入了 mkfifo(实名管道)。这些的讲解网络上有更清晰的讲解,就不再赘述。

RT-Thread 也实现了一套 pipe,不仅有自己的接口 rt_pipe_xxx , 也对接了 posix 接口。

二. 怎么 使用管道

在使用之前先看一下 pipe 的结构体

struct rt_pipe_device
{struct rt_device parent;rt_bool_t is_named;/* ring buffer in pipe device */struct rt_ringbuffer *fifo;rt_uint16_t bufsz;rt_uint8_t readers;rt_uint8_t writers;rt_wqueue_t reader_queue;rt_wqueue_t writer_queue;struct rt_mutex lock;
};
typedef struct rt_pipe_device rt_pipe_t;

通过上面的结构体可以先尝试分析以下管道的实现原理:

is_named :确定是匿名管道,还是实名管道

fifo :通过 ringbuff 来缓存数据

readers/writes : 确定读取/写入用户的数量

reader_queue/writer_queue :通过工作队列来实现异步操作

lock :使用互斥锁来实现半双工

RT-Thread 的管道的时候,有两个使用方法:

  1. 使用 RT-ThreadAPI , 即 rt_pipe_xxx 的API

  2. 使用 posix 的管道 API

使用 RT-Thread 管道 API
  1. 创建管道

    rt_pipe_t *rt_pipe_create(const char *name, int bufsz)

    name : 创建管道的名字,设备会注册到设备管理器

    bufsz:ringbuff 缓存去的大小

    返回管道的对象

  2. 删除管道

    int rt_pipe_delete(const char *name)

    name : 管道的名字,删除函数会自动在设备管理器查找到该设备

    删除成功返回 0

  3. 打开管道

    rt_err_t rt_pipe_open (rt_device_t device, rt_uint16_t oflag)

    device : 设备对象

    oflag : 没有用到

  4. 关闭管道

    rt_err_t rt_pipe_close (rt_device_t device)

    device : 设备对象

  5. 读取管道数据

    rt_size_t rt_pipe_read (rt_device_t device, rt_off_t pos, void *buffer, rt_size_t count)

    device : 设备对象

    pos : 未使用

    buffer : 读管道数据的存储区的指针

    count : 读取数据的长度

  6. 读取管道数据

    rt_size_t rt_pipe_write (rt_device_t device, rt_off_t pos, const void *buffer, rt_size_t count)

    device : 设备对象

    pos : 未使用

    buffer : 写管道数据的存储区的指针

    count : 写取数据的长度

使用 POSIX 管道 API
  1. 创建匿名管道

    int pipe(int fildes[2])

    fildes[0] : 读文件描述符

    fildes[1] : 写文件描述符

    成功返回 0

  2. 创建实名管道

    int mkfifo(const char *path, mode_t mode)

    path : 文件名

    mode : 未使用

    成功返回 0

  3. 读写数据

    使用 read/write 接口

三. 原理分析管道

1. 创建管道
rt_pipe_t *rt_pipe_create(const char *name, int bufsz)
{rt_pipe_t *pipe;rt_device_t dev;pipe = (rt_pipe_t *)rt_malloc(sizeof(rt_pipe_t));// 申请内存if (pipe == RT_NULL) return RT_NULL;rt_memset(pipe, 0, sizeof(rt_pipe_t));pipe->is_named = RT_TRUE; /* initialize as a named pipe */rt_mutex_init(&(pipe->lock), name, RT_IPC_FLAG_FIFO);//初始化互斥锁rt_wqueue_init(&(pipe->reader_queue));//初始化读工作队列rt_wqueue_init(&(pipe->writer_queue));//初始化写工作队列RT_ASSERT(bufsz < 0xFFFF);pipe->bufsz = bufsz;//ringbuff 缓存区大小dev = &(pipe->parent);dev->type = RT_Device_Class_Pipe;// 设置设备类型dev->init        = RT_NULL;//对接 ops 接口dev->open        = rt_pipe_open;dev->read        = rt_pipe_read;dev->write       = rt_pipe_write;dev->close       = rt_pipe_close;dev->control     = rt_pipe_control;dev->rx_indicate = RT_NULL;// 不设置回调函数dev->tx_complete = RT_NULL;if (rt_device_register(&(pipe->parent), name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE) != 0) // 注册设备{rt_free(pipe);return RT_NULL;}
#ifdef RT_USING_POSIXdev->fops = (void*)&pipe_fops;
#endifreturn pipe;// 返回 pipe
}
2. 删除管道
int rt_pipe_delete(const char *name)
{int result = 0;rt_device_t device;device = rt_device_find(name);//查找设备if (device){if (device->type == RT_Device_Class_Pipe)//检查设备类型{rt_pipe_t *pipe;if (device->ref_count != 0)// 如果设备中还有任务没处理完,则不能删除{return -RT_EBUSY;}pipe = (rt_pipe_t *)device;rt_mutex_detach(&(pipe->lock));// 脱离互斥锁rt_device_unregister(device);// 取消设备注册/* close fifo ringbuffer */if (pipe->fifo) {rt_ringbuffer_destroy(pipe->fifo);// 摧毁 ringbuffpipe->fifo = RT_NULL;}rt_free(pipe);// 释放内存}else{result = -ENODEV;}}else{result = -ENODEV;}return result;
}
3. 打开管道
rt_err_t  rt_pipe_open (rt_device_t device, rt_uint16_t oflag)
{rt_pipe_t *pipe = (rt_pipe_t *)device;rt_err_t ret = RT_EOK;if (device == RT_NULL){ret = -RT_EINVAL;goto __exit;}rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);// 加锁if (pipe->fifo == RT_NULL){pipe->fifo = rt_ringbuffer_create(pipe->bufsz);//创建 ringbuffif (pipe->fifo == RT_NULL){ret = -RT_ENOMEM;}}rt_mutex_release(&(pipe->lock));// 释放锁__exit:return ret;
}
4. 关闭管道
rt_err_t  rt_pipe_close  (rt_device_t device)
{rt_pipe_t *pipe = (rt_pipe_t *)device;if (device == RT_NULL) return -RT_EINVAL;rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);// 加锁if (device->ref_count == 1){rt_ringbuffer_destroy(pipe->fifo);//摧毁 ringbuffpipe->fifo = RT_NULL;}rt_mutex_release(&(pipe->lock));//释放锁return RT_EOK;
}
5. 管道读数据
rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt_size_t count)
{uint8_t *pbuf;rt_size_t read_bytes = 0;rt_pipe_t *pipe = (rt_pipe_t *)device;if (device == RT_NULL){rt_set_errno(-EINVAL);return 0;}if (count == 0) return 0;pbuf = (uint8_t*)buffer;rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);// 加锁while (read_bytes < count){// 循环从 ringbuff 中读取数据int len = rt_ringbuffer_get(pipe->fifo, &pbuf[read_bytes], count - read_bytes);if (len <= 0) break;read_bytes += len;}rt_mutex_release(&pipe->lock);//释放锁return read_bytes;//返回读取到的数据的长度
}
6. 管道写数据
rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buffer, rt_size_t count)
{uint8_t *pbuf;rt_size_t write_bytes = 0;rt_pipe_t *pipe = (rt_pipe_t *)device;if (device == RT_NULL){rt_set_errno(-EINVAL);return 0;}if (count == 0) return 0;pbuf = (uint8_t*)buffer;rt_mutex_take(&pipe->lock, -1);//加锁while (write_bytes < count){// 往 ringbuff 写数据int len = rt_ringbuffer_put(pipe->fifo, &pbuf[write_bytes], count - write_bytes);if (len <= 0) break;write_bytes += len;}rt_mutex_release(&pipe->lock);//释放锁return write_bytes;//返回写入数据的长度
}
7. 匿名管道的实现
int pipe(int fildes[2])
{rt_pipe_t *pipe;char dname[8];// 这里应该写 RT_NAME_MAXchar dev_name[32];static int pipeno = 0;//拼接字符串,作为管道的名字rt_snprintf(dname, sizeof(dname), "pipe%d", pipeno++);pipe = rt_pipe_create(dname, PIPE_BUFSZ);// 创建管道if (pipe == RT_NULL){return -1;}// 设置为匿名管道pipe->is_named = RT_FALSE; /* unamed pipe *///拼接字符串,作为管道的名字rt_snprintf(dev_name, sizeof(dev_name), "/dev/%s", dname);//只读的方式打开文件fildes[0] = open(dev_name, O_RDONLY, 0);if (fildes[0] < 0){return -1;}//只写的方式打开文件fildes[1] = open(dev_name, O_WRONLY, 0);if (fildes[1] < 0){close(fildes[0]);return -1;}return 0;
}
8. 实名管道的实现
int mkfifo(const char *path, mode_t mode)
{rt_pipe_t *pipe;pipe = rt_pipe_create(path, PIPE_BUFSZ);// 创建管道if (pipe == RT_NULL){return -1;}return 0;
}

注意 mode 未使用。

9. POSIX 接口对接

posix 接口的实现是通过 fd 文件描述符来找到 pipe 的对象,这样就和前面一样的操作了。唯一的区别就是在 posix 接口实现里面对接了 readers/writers ,用来记录管道的使用者。

四. 总结

RT-Thread 的管道有什么特点呢?

  1. RTOS 中没有进程的概念,所以在 RTOS 中的管道用于线程间通讯
  2. RT-Thread 的管道也是半双工,半双工通过互斥锁实现
  3. 支持匿名管道和实名管道
  4. 支持 posix 标准,通过源码分析,posix 标准实现的功能更加完善

RT-Thread 隐藏的宝藏之管道相关推荐

  1. 关于RT thread系统节拍时钟的配置

    关于RT thread系统节拍时钟的配置                  -----本文基于rt-thread-3.1.3版本编写 首先,使用RTthread OS时,要配置(或者明白)它的系统节拍 ...

  2. rt thread studio使用QBOOT和片外flash实现OTA升级

    我们这里要使用单片机外部flash作为OTA的下载分区,外部flash硬件连接关系 PB3-->SPI3_CLK PB4-->SPI3_MISO PB5-->SPI3_MOSI PE ...

  3. rt thread 使用FAL遇到fal_init() undefined reference

    rt thread FAL 0.5版,之前有没有不知道,遇到一个坑. 在main.cpp里面已经 #include <fal.h> fal_init() 编译报错,说 fal_init() ...

  4. RT Thread Free Modbus移植问题整理

    RT Thread Free Modbus移植问题整理 问题描述: 在读写寄存器中,写数据正常,只能读1个寄存器的值,多个值会异常. 在移植过程中发现串口(或RS485)数据接收长度异常. 一.环境描 ...

  5. Yeelink平台使用——远程控制 RT Thread + LwIP+ STM32

    1.前言     [2014年4月重写该博文]     经过若干时间的努力终于搞定了STM32+LwIP和yeelink平台的数据互通,在学习的过程中大部分时间花在以太网协议栈学习上,但是在RT Th ...

  6. RT Thread根据开发板制作BSP方法

    之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...

  7. RT Thread之 Uart2 操作

    官网连接:https://docs.rt-thread.org/#/rt-thread-version/rt-thread-standard/programming-manual/device/uar ...

  8. 基于rt thread smart构建EtherCAT主站

    我把源码开源到到了gitee,https://gitee.com/rathon/rt-thread-smart-soem 有兴趣的去可以下载下来跑一下 软件工程推荐用vscode 打开.rt thre ...

  9. RT Thread利用STM32CUBEMX和RT Thread studio来创建模板工程

    (1)RT Thread利用STM32CUBEMX来创建模板工程 1.参考文档: 基于 CubeMX 移植 RT-Thread Nano:RT-Thread 文档中心 注意:串口2必须使能异步模式(启 ...

  10. rt thread系统下添加wiznet软件包后,不插网线CPU利用率100%问题

    rt thread系统下添加wiznet软件包后如果不插网线的话其他任务运行很卡,使用ps命令发现优先级低的任务很多都超时了 rt thread线程错误码 添加了一个可以查看CPU利用率的软件包CPU ...

最新文章

  1. 结营答辩!28天!Datawhale助力公益AI一起学,点亮最暖寒假!
  2. dedeCMS修改文章更新发布时间问题
  3. 转载:识别圆环的一种思路
  4. content type 介绍
  5. NYOJ 745 蚂蚁的难题(二)
  6. 文档生成器 Xcode与Appledoc
  7. asp.netcore3.0 使用 DbProviderFactories 连接数据库
  8. Matlab快速入门
  9. [AGC031E] Snuke the Phantom Thief(网络流)
  10. LsLoader——通用移动端Web App离线化方案
  11. java读取src xml文件路径_Java获取路径方法相对路径读取xml文件方法
  12. Visio画图--我的形状
  13. git 命令行(一)-版本回退
  14. JavaWeb知识点:Http协议
  15. excel锁定单元格不能修改_Excel如何锁定部分区域不被编辑,1分钟就学会
  16. 月薪过万的前端工程师的自我修养
  17. 如何取消Word文档的只读模式或者限制编辑
  18. 什么是索引覆盖?什么是索引下推?
  19. 螺旋模型的优点与缺点
  20. 开启子进程的两种方式,孤儿进程与僵尸进程,守护进程,互斥锁,IPC机制,生产者与消费者模型...

热门文章

  1. C#读取Excel表
  2. rowspan动态设置导致的table错位问题
  3. 删除iPhone邮箱smtp服务器,教你iPhone邮件怎么添加删除附件及iPhone特殊代码分享...
  4. 我们和他们,究竟谁是傻X? | 华尔街黑历史(一)
  5. 钱诚11.12黄金原油行情走势分析伦敦金美盘投资操作建议
  6. android surfaceview学习(一)
  7. 故事也许不只是故事,内中蕴含着大…
  8. SpringCloud Nacos入门教程
  9. 高仿QQ音乐之旋转专辑图片的实现—仿留声机效果
  10. Mysql 5.6版本二进制安装