RT-Thread 操作系统 之 线程间同步 IO设备模型
- 一、线程间同步
- 1.1、信号量
- 1.1.1、信号量结构体
- 1.1.2、信号量的使用和管理
- 1.1.3、信号量同步例程
- 1.2、互斥量
- 1.2.1、互斥量的使用和管理
- 1.3、事件集
- 1.3.1、事件集使用和管理方法
- 1.3.2、事件集三个线程同步实例
- 二、IO设备模型
- 2.1、IO设备类型
- 2.2、创建和注册IO设备
- 2.3、访问IO设备
一、线程间同步
多个执行单元(线程、中断)同时执行临界区,操作临界资源,会导致竟态产生,为了解决这种竟态问题,RT-Thread OS提供了如下几种同步互斥机制:
☐ 信号量
☐ 互斥量
☐ 事件集
1.1、信号量
信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。
每个信号量对象都有一个信号量值和一个线程等待队列,信号量的值对应了信号量对象的实例数目、资源数目,假如信号量值为 5,则表示共有 5 个信号量实例(资源)可以被使用,当信号量实例数目为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上,等待可用的信号量实例(资源)
1.1.1、信号量结构体
struct rt_semaphore
{
struct rt_ipc_object parent; /**< inherit from ipc_object 继承自ipc_object类*/
rt_uint16_t value; /**< value of semaphore. */
rt_uint16_t reserved; /**< reserved field 预留*/
};
typedef struct rt_semaphore *rt_sem_t;
1.1.2、信号量的使用和管理
对一个信号量的操作包含:创建 / 初始化信号量、获取信号量、释放信号量、删除 / 脱离信号量。
☐ 创建和删除信号量
系统不再使用信号量时,可通过删除信号量以释放系统资源,适用于动态创建的信号量。
调用这个函数时,系统将删除这个信号量。如果删除该信号量时,有线程正在等待该信号量,那么删除操作会先唤醒等待在该信号量上的线程(等待线程的返回值是-RT_ERROR),然后再释放信号量的内存资源。
/**
* This function will delete a semaphore object and release the memory
*
* @param sem the semaphore object
*
* @return the error code
*
* @see rt_sem_detach
*/
rt_err_t rt_sem_delete(rt_sem_t sem)
☐ 初始化和脱离信号量
/**
* This function will initialize a semaphore and put it under control of
* resource management.
*
* @param sem the semaphore object
* @param name the name of semaphore
* @param value the initial value of semaphore
* @param flag the flag of semaphore
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_sem_init(rt_sem_t sem,
const char *name,
rt_uint32_t value,
rt_uint8_t flag)
脱离信号量就是让信号量对象从内核对象管理器中脱离,适用于静态初始化的信号量
使用该函数后,内核先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中脱离。原来挂起在信号量上的等待线程将获得 - RT_ERROR 的返回值。
/**
* This function will detach a semaphore from resource management
*
* @param sem the semaphore object
*
* @return the operation status, RT_EOK on successful
*
* @see rt_sem_delete
*/
rt_err_t rt_sem_detach(rt_sem_t sem)
☐ 获取信号量
线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1,如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请该信号量的线程将根据time参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数time指定的时间内依然得不到信号量,线程将超时返回,返回值是 - RT_ETIMEOUT。
☐ 释放信号量
释放信号量可以唤醒挂起在该信号量上的线程
/**
* This function will release a semaphore, if there are threads suspended on
* semaphore, it will be waked up.
*
* @param sem the semaphore object
*
* @return the error code
*/
rt_err_t rt_sem_release(rt_sem_t sem)
1.1.3、信号量同步例程
1.2、互斥量
互斥量体现的是排他性,也是解决多线程同时操作临界区临界资源导致的竟态的一种方法。(类似于特殊的信号量——二值信号量)
区别:信号量可由不同线程释放,互斥量只能由同一线程进行释放。
1.2.1、互斥量的使用和管理
互斥量的操作包含:创建 / 初始化互斥量、获取互斥量、释放互斥量、删除 / 脱离互斥量
☐ 创建和删除
不再使用互斥量时,通过删除互斥量以释放系统资源,适用于动态创建的互斥量
当删除一个互斥量时,所有等待此互斥量的线程都将被唤醒,等待线程获得的返回值是
- RT_ERROR
/**
* This function will delete a mutex object and release the memory
*
* @param mutex the mutex object
*
* @return the error code
*
* @see rt_mutex_detach
*/
rt_err_t rt_mutex_delete(rt_mutex_t mutex)
☐ 初始化和脱离互斥量
/**
* This function will initialize a mutex and put it under control of resource
* management.
*
* @param mutex the mutex object
* @param name the name of mutex
* @param flag the flag of mutex
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
使用该函数接口后,内核先唤醒所有挂在该互斥量上的线程(线程的返回值是
-RT_ERROR) ,然后系统将该互斥量从内核对象管理器中脱离。
/**
* This function will detach a mutex from resource management
*
* @param mutex the mutex object
*
* @return the operation status, RT_EOK on successful
*
* @see rt_mutex_delete
*/
rt_err_t rt_mutex_detach(rt_mutex_t mutex)
☐ 获取互斥量
/**
* This function will take a mutex, if the mutex is unavailable, the
* thread shall wait for a specified time.
*
* @param mutex the mutex object
* @param time the waiting time
*
* @return the error code
*/
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)
☐ 释放互斥量
/**
* This function will release a mutex, if there are threads suspended on mutex,
* it will be waked up.
*
* @param mutex the mutex object
*
* @return the error code
*/
rt_err_t rt_mutex_release(rt_mutex_t mutex)
1.3、事件集
事件集也是线程间同步的机制之一,一个事件集可以包含多个事件,利用事件集可以完成一对多,多对多的线程间同步。
一个线程和多个事件的关系可设置为:
其中任意一个事件唤醒 线程,或几个事件都到达后唤醒线程,多个事件集合可以用一个32bit无符号整型变量来表示,变量的每一位代表一个事件,线程通过"逻辑与"或"逻辑或"将一个或多个事件关联起来,形成事件组合。
RT-Thread 定义的事件集有以下特点:
☐ 事件只与线程相关,事件间相互独立
☐ 事件仅用于同步,不提供数据传输功能
☐ 事件无排队性,即多次向线程发送同一事件(如果线程还未来得及读走),其效果等同于只发送一次
1.3.1、事件集使用和管理方法
对一个事件集的操作包含:创建/初始化事件集、发送事件、接收事件、删除/脱离事件集。
☐ 创建和删除
/*
* event structure
*/
struct rt_event
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint32_t set; /**< event set */
};
typedef struct rt_event *rt_event_t;
/**
* This function will create an event object from system resource
*
* @param name the name of event
* @param flag the flag of event RT_IPC_FLAG_FIFO RT_IPC_FLAG_PRIO
*
* @return the created event, RT_NULL on error happen
*/
rt_event_t rt_event_create(const char *name, rt_uint8_t flag)
/**
* This function will delete an event object and release the memory
*
* @param event the event object
*
* @return the error code
*/
rt_err_t rt_event_delete(rt_event_t event)
☐ 初始化和脱离
/**
* This function will initialize an event and put it under control of resource
* management.
*
* @param event the event object
* @param name the name of event
* @param flag the flag of event
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag)
/**
* This function will detach an event object from resource management
*
* @param event the event object
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_event_detach(rt_event_t event)
☐ 发送事件
/**
* This function will send an event to the event object, if there are threads
* suspended on event object, it will be waked up.
*
* @param event the event object
* @param set the event set
*
* @return the error code
*/
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)
☐ 接收事件
/**
* This function will receive an event from event object, if the event is
* unavailable, the thread shall wait for a specified time.
*
* @param event the fast event object
* @param set the interested event set
* @param option the receive option, either RT_EVENT_FLAG_AND or
* RT_EVENT_FLAG_OR should be set. RT_EVENT_FLAG_CLEAR
* @param timeout the waiting time RT_WAITING_FOREVER RT_WAITING_NO
* @param recved the received event, if you don't care, RT_NULL can be set.
*
* @return the error code
*/
rt_err_t rt_event_recv(rt_event_t event,
rt_uint32_t set,
rt_uint8_t option,
rt_int32_t timeout,
rt_uint32_t *recved)
1.3.2、事件集三个线程同步实例
见源码
二、IO设备模型
RT-Thread 提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。
☐ 应用程序通过 I/O 设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层 I/O 硬件设备进行交互。
☐ I/O 设备管理层实现了对设备驱动程序的封装
☐ 设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。
☐ 设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。
简单设备的注册不经过设备驱动框架层,直接将设备注册到I/O设备管理器中
☐ 设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过rt_device_register()接口注册到 I/O 设备管理器中
☐ 应用程序通过 rt_device_find()接口查找到设备,然后使用 I/O 设备管理接口来访问硬件,如下图所示:
对于一些复杂设备,需要使用到对应的设备驱动框架层,进行注册,如:看门狗定时器
☐ 看门狗设备驱动程序根据看门狗设备模型定义,创建出具备硬件访问能力的看门狗设备实例,并将该看门狗设备通过 rt_hw_watchdog_register()接口注册到看门狗设备驱动框架中
☐ 看门狗设备驱动框架通过 rt_device_register()接口将看门狗设备注册到 I/O 设备管理器中
☐ 应用程序通过 I/O 设备管理接口来访问看门狗设备硬件
2.1、IO设备类型
☐ RT-Thread 支持多种 I/O 设备类型,主要设备类型如下所示
RT_Device_Class_Char = 0, /**< character device */ RT_Device_Class_Block, /**< block device */ RT_Device_Class_NetIf, /**< net interface */ RT_Device_Class_MTD, /**< memory device */ RT_Device_Class_CAN, /**< CAN device */ RT_Device_Class_RTC, /**< RTC device */ RT_Device_Class_Sound, /**< Sound device */ RT_Device_Class_Graphic, /**< Graphic device */ RT_Device_Class_I2CBUS, /**< I2C bus device */ RT_Device_Class_USBDevice, /**< USB slave device */ RT_Device_Class_USBHost, /**< USB host bus */ RT_Device_Class_SPIBUS, /**< SPI bus device */ RT_Device_Class_SPIDevice, /**< SPI device */ RT_Device_Class_SDIO, /**< SDIO bus device */ RT_Device_Class_Timer, /**< Timer device */ RT_Device_Class_Miscellaneous, /**< misc device */ RT_Device_Class_Sensor, /**< Sensor device */ RT_Device_Class_Touch, /**< Touch device */ RT_Device_Class_Unknown /**< unknown device */
2.2、创建和注册IO设备
☐ 驱动层负责创建设备实例,并注册到 I/O 设备管理器中
当一个动态创建的设备不再需要使用时可以通过如下函数来销毁
☐设备被创建后,需要实现它访问硬件的操作方法
☐ 设备被创建后,需要注册到 I/O 设备管理器中,应用程序才能够访问
/**
* This function registers a device driver with specified name.
*
* @param dev the pointer of device driver structure
* @param name the device driver's name
* @param flags the capabilities flag of device 设备模式标志
*
* @return the error code, RT_EOK on initialization successfully.
*/
rt_err_t rt_device_register(rt_device_t dev,
const char *name,
rt_uint16_t flags)
#define RT_DEVICE_FLAG_RDONLY 0x001 /*只读*/
#define RT_DEVICE_FLAG_WRONLY 0x002 /*只写*/
#define RT_DEVICE_FLAG_RDWR 0x003 /*读写*/
#define RT_DEVICE_FLAG_REMOVABLE 0x004 /*可移除*/
#define RT_DEVICE_FLAG_STANDALONE 0x008 /*独立*/
#define RT_DEVICE_FLAG_SUSPENDED 0x020 /*挂起*/
#define RT_DEVICE_FLAG_STREAM 0x040 /*流模式*/
#define RT_DEVICE_FLAG_INT_RX 0x100 /*中断接收*/
#define RT_DEVICE_FLAG_DMA_RX 0x200 /*DMA接收*/
#define RT_DEVICE_FLAG_INT_TX 0x400 /*中断发送*/
#define RT_DEVICE_FLAG_DMA_TX 0x800 /* DMA发送*/
设备注销后的,设备将从设备管理器中移除,也就不能再通过设备查找搜索到该设备。注销设备不会释放设备控制块占用的内存
/**
* This function removes a previously registered device driver
*
* @param dev the pointer of device driver structure
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_device_unregister(rt_device_t dev)
2.3、访问IO设备
应用程序通过 I/O 设备管理接口来访问硬件设备,当设备驱动实现后,应用程序就可以访问该硬件,I/O 设备管理接口与 I/O 设备的操作方法的映射关系下图所示
☐ 查找设备
/**
* This function finds a device driver by specified name.
*
* @param name the device driver's name
*
* @return the registered device driver on successful, or RT_NULL on failure.
*/
rt_device_t rt_device_find(const char *name)
☐ 初始化设备
☐ 打开和关闭设备
注:RT_DEVICE_FLAG_STREAM:流模式用于向串口终端输出字符串:当输出的字符是 “\n”(对应 16 进制值为 0x0A)时,自动在前面输出一个 “\r”(对应 16 进制值为 0x0D)做分行。
流模式 RT_DEVICE_FLAG_STREAM 可以和接收发送模式参数使用或 “|” 运算符一起使用
☐ 控制设备
☐ 读写设备
/**
* This function will read some data from a device.
*
* @param dev the pointer of device driver structure
* @param pos the position of reading
* @param buffer the data buffer to save read data
* @param size the size of buffer
*
* @return the actually read size on successful, otherwise negative returned.
*
* @note since 0.4.0, the unit of size/pos is a block for block device.
*/
rt_size_t rt_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
/**
* This function will write some data to a device.
*
* @param dev the pointer of device driver structure
* @param pos the position of written
* @param buffer the data buffer to be written to device
* @param size the size of buffer
*
* @return the actually written size on successful, otherwise negative returned.
*
* @note since 0.4.0, the unit of size/pos is a block for block device.
*/
rt_size_t rt_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
☐ 数据收发回调,当硬件设备收到数据时,可以通过如下函数回调另一个函数来设置数据接收指示,通知上层应用线程有数据到达
/**
* This function will set the reception indication callback function.
* This callback function
* is invoked when this device receives data.
*
* @param dev the pointer of device driver structure
* @param rx_ind the indication callback function
*
* @return RT_EOK
*/
rt_err_t
rt_device_set_rx_indicate(rt_device_t dev,
rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size))
/**
* This function will set the indication callback function when device has
* written data to physical hardware.
*
* @param dev the pointer of device driver structure
* @param tx_done the indication callback function
*
* @return RT_EOK
*/
rt_err_t
rt_device_set_tx_complete(rt_device_t dev,
rt_err_t (*tx_done)(rt_device_t dev, void *buffer))