RT-Thread 隐藏的宝藏之管道
一. 什么是管道
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
的管道的时候,有两个使用方法:
使用
RT-Thread
的API
, 即rt_pipe_xxx
的API使用
posix
的管道API
使用 RT-Thread
管道 API
创建管道
rt_pipe_t *rt_pipe_create(const char *name, int bufsz)
name : 创建管道的名字,设备会注册到设备管理器
bufsz:
ringbuff
缓存去的大小返回管道的对象
删除管道
int rt_pipe_delete(const char *name)
name : 管道的名字,删除函数会自动在设备管理器查找到该设备
删除成功返回 0
打开管道
rt_err_t rt_pipe_open (rt_device_t device, rt_uint16_t oflag)
device : 设备对象
oflag : 没有用到
关闭管道
rt_err_t rt_pipe_close (rt_device_t device)
device : 设备对象
读取管道数据
rt_size_t rt_pipe_read (rt_device_t device, rt_off_t pos, void *buffer, rt_size_t count)
device : 设备对象
pos : 未使用
buffer : 读管道数据的存储区的指针
count : 读取数据的长度
读取管道数据
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
创建匿名管道
int pipe(int fildes[2])
fildes[0] : 读文件描述符
fildes[1] : 写文件描述符
成功返回 0
创建实名管道
int mkfifo(const char *path, mode_t mode)
path : 文件名
mode : 未使用
成功返回 0
读写数据
使用
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
的管道有什么特点呢?
RTOS
中没有进程的概念,所以在RTOS
中的管道用于线程间通讯RT-Thread
的管道也是半双工,半双工通过互斥锁实现- 支持匿名管道和实名管道
- 支持
posix
标准,通过源码分析,posix
标准实现的功能更加完善
RT-Thread 隐藏的宝藏之管道相关推荐
- 关于RT thread系统节拍时钟的配置
关于RT thread系统节拍时钟的配置 -----本文基于rt-thread-3.1.3版本编写 首先,使用RTthread OS时,要配置(或者明白)它的系统节拍 ...
- rt thread studio使用QBOOT和片外flash实现OTA升级
我们这里要使用单片机外部flash作为OTA的下载分区,外部flash硬件连接关系 PB3-->SPI3_CLK PB4-->SPI3_MISO PB5-->SPI3_MOSI PE ...
- rt thread 使用FAL遇到fal_init() undefined reference
rt thread FAL 0.5版,之前有没有不知道,遇到一个坑. 在main.cpp里面已经 #include <fal.h> fal_init() 编译报错,说 fal_init() ...
- RT Thread Free Modbus移植问题整理
RT Thread Free Modbus移植问题整理 问题描述: 在读写寄存器中,写数据正常,只能读1个寄存器的值,多个值会异常. 在移植过程中发现串口(或RS485)数据接收长度异常. 一.环境描 ...
- Yeelink平台使用——远程控制 RT Thread + LwIP+ STM32
1.前言 [2014年4月重写该博文] 经过若干时间的努力终于搞定了STM32+LwIP和yeelink平台的数据互通,在学习的过程中大部分时间花在以太网协议栈学习上,但是在RT Th ...
- RT Thread根据开发板制作BSP方法
之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...
- RT Thread之 Uart2 操作
官网连接:https://docs.rt-thread.org/#/rt-thread-version/rt-thread-standard/programming-manual/device/uar ...
- 基于rt thread smart构建EtherCAT主站
我把源码开源到到了gitee,https://gitee.com/rathon/rt-thread-smart-soem 有兴趣的去可以下载下来跑一下 软件工程推荐用vscode 打开.rt thre ...
- RT Thread利用STM32CUBEMX和RT Thread studio来创建模板工程
(1)RT Thread利用STM32CUBEMX来创建模板工程 1.参考文档: 基于 CubeMX 移植 RT-Thread Nano:RT-Thread 文档中心 注意:串口2必须使能异步模式(启 ...
- rt thread系统下添加wiznet软件包后,不插网线CPU利用率100%问题
rt thread系统下添加wiznet软件包后如果不插网线的话其他任务运行很卡,使用ps命令发现优先级低的任务很多都超时了 rt thread线程错误码 添加了一个可以查看CPU利用率的软件包CPU ...
最新文章
- 结营答辩!28天!Datawhale助力公益AI一起学,点亮最暖寒假!
- dedeCMS修改文章更新发布时间问题
- 转载:识别圆环的一种思路
- content type 介绍
- NYOJ 745 蚂蚁的难题(二)
- 文档生成器 Xcode与Appledoc
- asp.netcore3.0 使用 DbProviderFactories 连接数据库
- Matlab快速入门
- [AGC031E] Snuke the Phantom Thief(网络流)
- LsLoader——通用移动端Web App离线化方案
- java读取src xml文件路径_Java获取路径方法相对路径读取xml文件方法
- Visio画图--我的形状
- git 命令行(一)-版本回退
- JavaWeb知识点:Http协议
- excel锁定单元格不能修改_Excel如何锁定部分区域不被编辑,1分钟就学会
- 月薪过万的前端工程师的自我修养
- 如何取消Word文档的只读模式或者限制编辑
- 什么是索引覆盖?什么是索引下推?
- 螺旋模型的优点与缺点
- 开启子进程的两种方式,孤儿进程与僵尸进程,守护进程,互斥锁,IPC机制,生产者与消费者模型...