RTT之通讯和同步扩展到进程与线程

同步的方式:要解决多个线程访问同一区域(代码或数据)的问题.

中断锁是最强大最有效的同步方法,问题在于中断锁期间不能响应任何外部事件。

rt_hw_interrupt_disable()
rt_hw_interrupt_enable()

调度锁:不能换出当前的线程,直到调度器解锁。倒是可以响应外部事件,但要考虑是否在中断中操作临界资源?若中断唤醒了更高优先级线程,也不能被执行,直到解锁。问题是不能用于中断与线程间同步及通知?因为上了调度锁后,只能一个线程在工作了,中断的同步与通知,对同一线程来说没什么意义。况且,中断也不能操作临界区变量。否则会乱套。
 

1、信号量:

工作机制:

包括两个部分:

信号量的值:

对象实例数目、资源数目。

线程等待队列:申请该信号量的线程。

可以看到信号量的相关管理操作最后都是调用了RTM_EXPORT(rt_sem_create);开头的函数?这个RTM_EXPORT是什么作用呢??

阅读系统原码会发现,几乎每个系统函数都会用RTM_EXPORT声明下,可见这个的重要性。

这其实就是RTT的动态装载实现:

用RTM_EXPORT定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,即可以将一个函数以符号的方式导出给其他模块使用。您还可以手工修改内核源代码来导出另外的函数,用于重新编译并加载新内核后的测试。

详见:rtm.h

在链接时会将一对对符号+地址构成的表存放到RTMSymTab段:在../board/linker_scripts里

个人理解,这里只是提供一个编译链接时的入口,具体如何联系上的,还是要看编译器。

信号量是如何实现周期性的检测的呢?

这个问题,其实是看使用此信号量的线程。由于线程是实现周期性的切换,那么挂接在信号量上的线程也会跟着检测!信号量(或者更大概念IPC,什么互斥量等都是差不多的操作,原理一致)其实是对线程操作的二次抽象或封装。

并且,在程序中是看不到rt_thread_delay()等操作,但在获取信号量等待时,也进行线程间的切换。如下图:获取信号量的源码:

信号量管理:

创建:rt_sem_create()分配一个semaphore对象,并初始化。标志参数决定了当信号量不可用时,多个线程等待的排队方式。

删除,rt_sem_delete()适用于动态创建。

静态:

初始化:rt_sem_init()对semaphore对象初始化

脱离:rt_sem_detach():先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中脱离,等待线程获得RT_ERROR的 返回值。

获得信号量:如果信号量的值等于0,线程将根据参数time选择直接返回或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。在time指定时间内依然得不到信号量,线程将超时返回。

无等待获取信号量:当资源为0时,直接返回RT_ETIMEOUT.

释放信号量:如字意,无等待,就把该信号量加1,有等待会唤醒等待的第一个线程。

应用场景:线程同步

1、锁:多个线程间对同一共享资源(即临界区)的访问。

2、中断与线程同步,FinSH线程同步

3、资源计数:对一个线程一次处理多个事件

2、互斥量

与信号量的不同:互斥量支持递归持有,而信号量递归持有会发生主动挂起(最终形成死锁)。

互斥量解决优先级翻转问题,优先级继承算法,提高某个占有资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,当这个低优先级线程释放该资源时,你帮优先级重新回到初始设定。

哪个操作会消除优先级翻转的问题呢???

在获取rt_mutex_take()函数中

释放和获取IPC都会进行一次线程切换操作:如互斥量释放:

互斥量管理:

创建:创建一个互斥量控制块,并初始化。

删除:所有等待此互斥量的线程都将被唤醒,等待线程获得的返回值是RT_ERROR.然后从内核对象管理器链表中删除并释放互斥量占用的内存空间。

静态:

初始化:如字意

脱离:内核先唤醒所有挂在互斥量上的线程,然后将该互斥量从内核对象管理器中脱离。

获取互斥量:没占用,线程就获得,已被当前线程占用,则该互斥量的持有计数加1,当前线程也不会挂起等待。已被其他线程占有,则当前线程在该互斥量上挂起等待,直到其他线程释放或等待时间超过指定的超时时间。

释放互斥量:只有拥有它线程才能释放它。每释放一次,持有计数减1,减到0,等待该互斥量的线程将被唤醒。若运行优先级被互斥量提升,则恢复时降到持有互斥量前的优先级。

应用场景:锁,初始化时,互斥量处于开锁状态,被线程持有立即变为闭锁状态。适用于多次持有互斥量,避免递归持有造成死锁问题。

可能会由于多线程同步而造成优先级翻转的情况。

互斥量不能用在中断服务程序中。

 

3、事件集

工作机制:线程间的同步,如其意,线程与事件存在,一对多或多对多的关系。线程等待事件同步。

RTT事件的特点:

事件只与线程相关,事件间相互独立:每个线程拥有32个事件标志,每一bit代表一个事件,若干个事件构成一个事件集。

事件仅用于同步,不提供数据传输功能。

事件无排队性,多次向线程发送同一事件(如果线程还未来得及读走),效果等同于中发送一次。

事件是发送和接收,而信号量和互斥量是释放和获取。本质差不多。需要注意的是:rt_thread_mdelay()函数,这些延时函数里,进行了线程切换:延时函数统一调用了thread.c中rt_thread_sleep()函数。

事件集管理:

创建:从对象管理器中分配事件集对象,并初始化这个对象,然后初始化父类IPC对象。

删除:唤醒所有挂起在该事件集上的线程(线程返回值是RT_ERROR),然后释放事件集对象占用的内存块。

静态:

初始化:初始化事件集对象,并加入到系统对象容器中。
脱离:唤醒所有挂在该事件集等待队列上线程,然后从内核对象管理器中脱离。

发送事件:通过参数set指定的事件标志值,遍历等待在event事件集对象上的等待线程链表,判断是否有线程的事件激活要求与当前event对象事件标志值匹配,如果有,则唤醒该线程。

接收事件:根据set参数和接收选项option来判断它要接收的事件是否发生,发生的话,根据option上是否设置有RT_EVENT_FLAG_CLEAR来决定是否重置事件的相应标志位,然后返回。没发生,把等待的set和option参数填入线程本身的结构中,然后把线程挂起在此事件上,直到满足条件或等待时间超时。

应用场景:

一定程度上可以替代信号量,一个线程或中断服务程序发送一个事件给事件对象,而后等待的线程被唤醒并对相应的事件进行处理.事件上是不可以累计的,而信号量可以.

多个事件对应一个或多个线程的场景或线程间同步.同时按照参数,可选择逻辑与或逻辑或.各个事件类型可分别发送或一起发送给事件对象,而事件对象可等待多个线程,它们仅对它感兴趣的事件进行关注.当感兴趣的事件发生时,线程会被唤醒并进行后续的处理动作.

总结如下:

 

通信:不同线程间传递消息,要解决的是共享数据缓冲区的问题

邮箱:

工作机制:

非阻塞方式的发送可以安全应用于中断服务程序中。邮箱没满,复制到邮箱,如果邮箱满,根据设置超时时间,可以等待挂起或直接返回RT_EFULL.邮件被收取后,等待线程被唤醒,继续发送。

线程从邮箱接收,邮箱空,可以选择是等待直到新邮件而唤醒,或可设置超时时间。当到设置的超时时间,邮箱依然未收到邮件时,这个线程被唤醒并返回RT_ETIMEOUT.若邮箱存在邮件,复制到接收缓存中。

邮箱管理:

创建:从对象管理器分配一个邮箱对象,分配内存空间来存放邮件,这块内存大小等于邮件大小(4个字节)与邮箱容量的乘积。接着初始化接收邮件数目和发送邮件在邮箱中的偏移量。

删除:内核先唤醒挂起在该邮箱上的所有线程(线程返回值是RT_ERROR),然后再释放邮箱使用的内存,最后删除邮箱对象。

静态:

初始化:如字意。获得用户已经申请的邮箱对象控制块。

脱离:先唤醒所有挂在邮箱上的线程,然后将邮箱对象从内核对象管理器中脱离。

发送:可以是32位任意格式的数据,如一个整数值或缓冲区指针。邮箱满时,线程或中断程序会收到RT_EFULL。

等待方式发送:如字意,非阻塞方式。

接收邮件:接收者需要指定接收邮件的邮箱句柄,并指定接收到的邮件存放位置及最多能够等待的超时时间。

 

消息队列:邮箱扩展,如串口接收不定长数据

工作机制:能够接收来自线程或中断服务例程中不固定长度的消息,并把消息缓存在自己的内存中。其他线程能够从消息队列中读取相应的消息.

当消息队列为空,挂起读取线程,当有新的消息到达时,挂起的线程将被唤醒以接收并处理消息。如下图:

消息队列管理:

创建:分配一个消息队列对象,然后给消息队列对象分配一块内存空间,组织成空闲消息链表,消息大小+消息头(用于链表连接)的大小 X 消息队列最大个数,再初始化消息队列。

删除:内核先唤醒挂起在该消息队列上的所有线程(线程返回值是RT_ERROR),然后再释放消息队列使用的内存,最后删除消息队列对象。

静态:

初始化:需要用户申请的获得消息队列对象的句柄。

脱离:还是先唤醒所有挂该消息等待队列对象上的线程,然后从内核对象管理器脱离。

发送:先从空闲消息链表上取下一个空闲消息块,把发送的消息内容复制到消息块上,然后把该消息块挂到消息队列的尾部。没有空闲的消息块,发送的线程或中断程序会收到一个错误码(RT_EFULL).

等待方式发送:如字意,设置等待时间。

发送紧急消息:与发送不同的是:从空闲消息链表上取下来的消息块不是挂到消息队列的队尾,而是挂在队首。便于接收者优先接收。

接收消息:接收者需要指定存储消息的消息队列对象句柄,并且指定一个内存缓存区,并且将接收到的内容复制到缓冲区。指定取消息的超时时间。接收消息后,消息队列上的队首消息被转移到空闲消息链表的尾部。

应用场景:

发送消息:作用与邮件同,只不过不用动态分配内存.应用于发送不定长消息的场合,包括线程与线程间消息交换,以及中断发送给线程消息(中断服务例程不可能接收消息).

同步消息:消息队列发送+信号量或邮箱确认,返回的形式。

 

信号:软中断,使消息,具有与中断处理一样的操作。信号在使用时,需要使用menuconfig来进行安装。

用做线程间异常通知、应急处理。

当一个线程收到信号后,有三种处理方式:

指定处理函数

忽略信号,不处理

保留系统默认值.

操作函数在signal.c中

信号管理:

安装:线程中安装信号是为了确定要处理哪个信号,该信号被传递给线程时,将执行何种操作。

阻塞:屏蔽后,信号不会传递,也不会引发软中断。

解除阻塞:与上相反。

发送:给设定了处理异常的线程发送信号。

等待信号:没等到信号就挂起,直到等到这个信号或等待时间超时。等到,则将指向该信号体的指针存入si.

RTT中用户可使用的信号只有SIGUSR1和SIGUSR2.

总结:邮箱和消息队列属于内核对象,可以让多个线程间可以互相传递信息.信号不在内核对象之中.

何时用同步又何时用通信呢????

  1. 基于数据为目的先后流程大概率就用通信。

  2. 基于功能实现为目的先后流程大概率就用同步。

扩展:进程与线程

根本区别:

他们之间根本区别在于 多进程中每个进程有自己的地址空间,线程则共享地址空间。

线程,相对于进程而言,是一个更加接近于执行体的概念,可以和同进程的其他线程之间直接共享数据,而且拥有自己的栈空间,拥有独立序列

因为那个根本区别,实际上只有进程间需要通信,同一进程的线程共享地址空间,没有通信的必要,但要做好同步/互斥,保护共享的全局变量。而进程间通信无论是信号,管道pipe还是共享内存都是由操作系统保证的,是系统调用.

一、进程间的通信方式

  1. 管道( pipe ):
    管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
  2. 有名管道 (namedpipe) :
    有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  3. 信号量(semophore ) :
    信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  4. 消息队列( messagequeue ) :
    消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  5. 信号 (sinal ) :
    信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
  6. 共享内存(shared memory ) :
    共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
  7. 套接字(socket ) :
    套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同设备及其间的进程通信。

二、线程间的通信方式

  1. 锁机制:包括互斥锁、条件变量、读写锁
    • 互斥锁提供了以排他方式防止数据结构被并发修改的方法。
    • 读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
    • 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
  2. 信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
    信号机制(Signal):类似进程间的信号处理

线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guangod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值