1. 临界区保护

临界区是仅允许一个线程访问的共享资源。它可以是一个具体的硬件设备,也可以是一个变量、一个缓冲区。多个线程必须互斥的对他们进行访问

1.1 方法一:关闭系统调度保护临界区

禁止调度

/* 调度器商锁,上锁后不再切换到其他线程,仅响应中断 */
rt_enter_critical();
/* 临界操作 */
rt_exit_critical();

关闭中断:因为所有的线程的调度都是建立在中断的基础上的,所以当关闭中断后系统将不能再进行调度,线程自身也自然不会被其他线程抢占了

rt_base_t level;
/* 关闭中断 */
level = rt_hw_interrupt_disable();
/* 临界操作 */
rt_hw_interrupt_enable(level);

1.2 方法二:互斥特性保护临界区

信号量、互斥量

2. 信号量

嵌入式系统运行的代码主要包括线程和ISR,在它们的运行过程中,它们的运行步骤有时需要同步(按照预定的先后次序运行),有时访问的资源需要互斥(一个时刻只允许一个线程访问资源),有时也需要比本次交换数据。这些机制成为进程间通信IPC。RT-Thread中的IPC机制包括信号量、互斥量、事件、邮箱、消息队列。通过IPC,可以协调多个线程(包括ISR)默契的工作。信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。每个信号量对象都有一个信号量值和一个线程等待队列。信号量的值对应信号量对象的实例数目(资源数目),如果信号量值N,则表示有N个信号量实例(资源)可被使用。当值为0时,再请求该信号量的的线程,就会被挂起在该信号量的等待队列上。

2.1 信号量的定义

struct rt_semaphore
{
struct rt_ipc_object parent; /* IPC对象继承而来 */
rt_uint16_t value; /* 信号量的值 */
} 静态信号量:struct rt_semaphore static_sem
动态信号量:rt_sem_t dynamic_sem typedef struct rt_semaphore *rt_sem_t;

2.2 信号量的操作

  1. 初始化与脱离
静态信号量:
rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag)
rt_err_t rt_sem_detach(rt_sem_t sem) //将不用的静态信号量从系统中脱离
  1. 创建与删除
判断一下返回值是不是RT_NULL
动态信号量:
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag) //等待队列中的线程,当有资源的时候: RT_IPC_FLAG_FIFO先来先服务,先后顺序排列 RT_IPC_FLAG_PRIO按照线程优先级排列
rt_err_t rt_sem_delete(rt_sem_t sem)//释放系统资源
  1. 获取信号量
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time) //RT_WAITING_FOREVER = -1, 以系统滴答时钟为单位。即100HZ,等待10ms的倍数。如果超时则返回-RT_ETIMEOUT.切忌该函数不可在中断中调用,因为它会导致线程被挂起。只能在线程调用
rt_err_t rt_sem_trytake(rt_sem_t sem) //时间参数为0,一秒钟都不等待
  1. 释放信号量
rt_err_t rt_sem_release(rt_sem_t sem) // 既可以在线程,也可以在中断中调用。因为它不会导致线程被挂起

3. 生产者、消费者问题

两个线程,一个生产者线程和一个消费者线程,两个线程共享一个初始为空、固定大小为n的缓存区。生产者的工作是生产一段数据,只有缓冲区没满时,生产者才能把消息放入到缓冲区,否则必须等待,如此反复。只有缓冲区非空时,消费者才能从中取出数据,一次消费一段数据,否则必须等待。问题的核心是:

  1. 保证不让生产者在缓存还是满的时候仍然要向内写数据
  2. 不让消费者试图从空的缓存中取出数据

解决生产者消费者问题实际上是要解决线程间互斥关系和同步关系问题。由于缓冲区是临界资源,一个时刻只允许一个生产者放入消息,或者一个消费者从中取出消息。这里需要解决一个互斥访问的问题。同时生产者和消费者又是一个相互协作的关系,只有生产者生产之后,消费者才能消费,所以还需要解决一个同步的问题

/* 生成者线程入口 */
void producer_thread_entry(void* parameter)
{
int cnt = 0; /* 运行100次 */
while( cnt < 100)
{
/* 获取一个空位 */
rt_sem_take(&sem_empty, RT_WAITING_FOREVER); /* 修改array内容,上锁 */
rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
array[set%MAXSEM] = cnt + 1;
rt_kprintf("the producer generates a number: %d\n", array[set%MAXSEM]);
set++;
rt_sem_release(&sem_lock); /* 发布一个满位 */
rt_sem_release(&sem_full);
cnt++; /* 暂停一段时间 */
rt_thread_delay(50);
} rt_kprintf("the producer exit!\n");
} /* 消费者线程入口 */
void consumer_thread_entry(void* parameter)
{
rt_uint32_t no;
rt_uint32_t sum; /* 第n个线程,由入口参数传进来 */
no = (rt_uint32_t)parameter; sum = 0;
while(1)
{
/* 获取一个满位 */
rt_sem_take(&sem_full, RT_WAITING_FOREVER); /* 临界区,上锁进行操作 */
rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
sum += array[get%MAXSEM];
rt_kprintf("the consumer[%d] get a number: %d\n", no, array[get%MAXSEM] );
get++;
rt_sem_release(&sem_lock); /* 释放一个空位 */
rt_sem_release(&sem_empty); /* 生产者生产到100个数目,停止,消费者线程相应停止 */
if (get == 100) break; /* 暂停一小会时间 */
rt_thread_delay(10);
} rt_kprintf("the consumer[%d] sum is %d \n ", no, sum);
rt_kprintf("the consumer[%d] exit!\n");
} int semaphore_producer_consumer_init()
{
/* 初始化3个信号量 */
rt_sem_init(&sem_lock , "lock", 1, RT_IPC_FLAG_FIFO);
rt_sem_init(&sem_empty, "empty", MAXSEM, RT_IPC_FLAG_FIFO);
rt_sem_init(&sem_full , "full", 0, RT_IPC_FLAG_FIFO); /* 创建线程1 */
producer_tid = rt_thread_create("producer",
producer_thread_entry, RT_NULL, /* 线程入口是producer_thread_entry, 入口参数是RT_NULL */
THREAD_STACK_SIZE, THREAD_PRIORITY - 1, THREAD_TIMESLICE);
if (producer_tid != RT_NULL)
rt_thread_startup(producer_tid);
else
tc_stat(TC_STAT_END | TC_STAT_FAILED); /* 创建线程2 */
consumer_tid = rt_thread_create("consumer",
consumer_thread_entry, RT_NULL, /* 线程入口是consumer_thread_entry, 入口参数是RT_NULL */
THREAD_STACK_SIZE, THREAD_PRIORITY + 1, THREAD_TIMESLICE);
if (consumer_tid != RT_NULL)
rt_thread_startup(consumer_tid);
else
tc_stat(TC_STAT_END | TC_STAT_FAILED); return 0;
}

4. 互斥量

互斥量控制块是操作系统用于管理互斥量的一个数据结构。

4.1 互斥量控制块

struct rt_mutex
{
struct rt_ipc_object parent; /* IPC对象继承而来 */
rt_uint16_t value; /* 只有LOCK和UNLOCK两种值 */
rt_uint8_t original_priority; /* 上一次获得该锁的线程的优先级 */
rt_uint8_t hold; /* 该线程获取了多少次该互斥锁 */
struct rt_thread *owner; /* 当前拥有该锁的线程句柄 */
} 静态互斥量:struct rt_mutex static_mutex;
动态互斥量:rt_mutex_t dynamic_mutex;

4.2 互斥量的操作

  1. 初始化与脱离
静态互斥量
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag); //RT_IPC_FIFO RT_IPC_FLAG_PRIO
rt_err_t rt_mutex_detach(rt_mutex_t mutex);
  1. 创建与删除
动态互斥量
rt_mutex rt_mutex_create(const char *name, rt_uint8_t flag);
rt_err_t rt_mutex_delete(rt_mutex_t mutex);
  1. 获取互斥量
只能在线程中调用,且同一个线程能够take多次同一个互斥量,其成员hold+1
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time) // RT_WAITING_FOREVER = -1
  1. 释放互斥量
只能在线程中调用,不能在中断中调用。必须同一个线程获取的同一个互斥量,才能在该线程释放该互斥量
rt_err_t rt_mutex_release(rt_mutex_t mutex)

4.3 互斥量和信号量的差别

  1. 信号量可以由任何线程(以及中断)释放,它用于同步的时候就像交通灯,线程只有在获得许可的时候,才能运行,强调的是运行步骤;互斥量只能由持有它的线程释放,即只有锁上它的哪个线程,才有钥匙打开它,强调的是许可和权限
  2. 使用信号量可能导致优先级反转,互斥量可通过优先级集成的方法解决优先级反转问题

5. 线程优先级翻转

当一个高优先级线程试图通过某种互斥IPC对象机制访问共享资源时,如果该IPC对象已经被一个低优先级的线程所持有,而且这个低优先级线程运行过程中可能又被其他一些中等优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞的情况。导致高优先级的实时性得不到保证

5.1 优先级继承

在RT-Thread中,通过互斥量的优先级继承算法,可有有效解决优先级翻转问题。优先级继承是指提高某个占有某种共享资源的低优先级线程优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,从而得到更快地执行然后释放共享资源,当这个低优先级线程释放该资源时,优先级重新回到初始设定值。继承优先级的线程,避免了系统共享资源被任何中间优先级的线程抢占

优先级翻转线向提醒编程人员对共享资源进行互斥访问的代码段应尽量短。让低优先级线程尽快完成工作,释放共享资源

参考文献

  1. RT-Thread视频中心内核入门
  2. RT-Thread文档中心

本文作者: CrazyCatJack

本文链接: https://www.cnblogs.com/CrazyCatJack/p/14408842.html

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

关注博主:如果您觉得该文章对您有帮助,可以点击文章右下角推荐一下,您的支持将成为我最大的动力!


RT-Thread学习笔记2-互斥量与信号量的更多相关文章

  1. 1.linux系统基础笔记(互斥量、信号量)

    操作系统是很多人每天必须打交道的东西,因为在你打开电脑的一刹那,随着bios自检结束,你的windows系统已经开始运行了.如果问大家操作系统是什么?可能有的人会说操作系统就是windows,就是那些 ...

  2. 【C#编程基础学习笔记】6---变量的命名

    2013/7/24 技术qq交流群:JavaDream:251572072  教程下载,在线交流:创梦IT社区:www.credream.com [C#编程基础学习笔记]6---变量的命名 ----- ...

  3. linux 线程的同步 一 (互斥量和信号量)

    互斥量(Mutex) 互斥量表现互斥现象的数据结构,也被当作二元信号灯.一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源. ...

  4. C#线程学习笔记六:线程同步--信号量和互斥体

    本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/23/Mutex_And_Semaphore.html,记录一下学习过程以备后续查用.     ...

  5. 【转】【Linux】 临界区,互斥量,信号量,事件的区别

    原文地址:http://blog.itpub.net/10697500/viewspace-612045/ Linux中 四种进程或线程同步互斥的控制方法1.临界区:通过对多线程的串行化来访问公共资源 ...

  6. CPP-基础:windows api 多线程---互斥量、信号量、临界值、事件区别

    http://blog.csdn.net/wangsifu2009/article/details/6728155 四种进程或线程同步互斥的控制方法:1.临界区:通过对多线程的串行化来访问公共资源或一 ...

  7. thread学习笔记--BackgroundWorker 类

    背景: 在 WinForms 中,有时要执行耗时的操作,比如统计某个磁盘分区的文件夹或者文件数目,如果分区很大或者文件过多的话,处理不好就会造成“假死”的情况,或者报“线程间操作无效”的异常,或者在该 ...

  8. Boost Thread学习笔记三

    下面先对condition_impl进行简要分析.condition_impl在其构造函数中会创建两个Semaphore(信号量):m_gate.m_queue,及一个Mutex(互斥体,跟boost ...

  9. Boost Thread学习笔记二

    除了thread,boost种:boost::mutexboost::try_mutexboost::timed_mutexboost::recursive_mutexboost::recursive ...

随机推荐

  1. 如何使用 Vuepress

    项目结构 ├─docs │ ├─.vuepress --vuepress相关文件路径 (主要配置) │ │ ├─dist --build 打包生成路径 (自定义) │ │ ├─nav --导航条配置 ...

  2. Redis 底层数据结构设计

    10万+QPS 真的只是因为单线程和基于内存?_Howinfun的博客-CSDN博客_qps面试题 https://blog.csdn.net/Howinfun/article/details/105 ...

  3. https://stackoverflow.com/questions/3232943/update-value-of-a-nested-dictionary-of-varying-depth

    https://stackoverflow.com/questions/3232943/update-value-of-a-nested-dictionary-of-varying-depth p.p ...

  4. 进程的创建-multiprocessing

    multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情 1. 2个while循环一起执行 # ...

  5. 404 GET /nbextensions/jupyter-js-widgets/extension.js

    数据科学交流群,群号:189158789,欢迎各位对数据科学感兴趣的小伙伴的加入! 解决办法: 首先要确定你安装和配置nbextensions时有没有加--user,如果当时没加这里就不用加,否则不一 ...

  6. 三:SpringBoot-配置系统全局异常映射处理

    三:SpringBoot-配置系统全局异常映射处理 1.异常分类 1.1 业务异常 1.2 系统异常 2.自定义异常处理 2.1 自定义业务异常类 2.2 自定义异常描述对象 2.3 统一异常处理格式 ...

  7. Hadoop优势,组成的相关架构,大数据生态体系下的模式

    Hadoop优势,组成的相关架构,大数据生态体系下的模式 一.Hadoop的优势 二.Hadoop的组成 2.1 HDFS架构 2.2 Yarn架构 2.3 MapReduce架构 三.大数据生态体系 ...

  8. (10)Linux挂载详解

    1.在 Linux 看来,任何硬件设备也都是文件,它们各有自己的一套文件系统(文件目录结构). 因此产生的问题是,当在 Linux 系统中使用这些硬件设备时,只有将Linux本身的文件目录与硬件设备的 ...

  9. dedecms文章页的上下篇颠倒的问题

    dedecms的文章页底下的上下篇,如果按照时间排序的话,最新的一篇应该是最上了,但是底下还是会显示上一篇文章还有,然后下一篇文章没有了,就是颠倒了.如何修改呢. 1.修改include目录下arc. ...

  10. maven基础学习-为什么要用maven,帮助解决了什么问题,怎么解决的,希望以后学习每个知识点都可以这样问下自己

    maven基础学习 第1章 Maven介绍 1.1 什么是Maven 1.1.1 什么是Maven Maven 的正确发音是[ˈmevən],而不是"马瘟"以及其他什么瘟.Mave ...