rt-thread 优化系列(四)信号对 ipc 的影响

描述

前言

信号 signal,并不是线程间同步的信号量 semaphore。后者是线程间同步机制的一种,而前者是线程间异步通信的一种。
官方文档里对其解释是:“信号(又称为软中断信号),在软件层次上是对中断机制的一种模拟,在原理上,一个线程收到一个信号与处理器收到一个中断请求可以说是类似的。”

信号本质是**软中断**,用来通知线程发生了异步事件,**用做线程之间的异常通知、应急处理**。一个**线程不必通过任何操作来等待信号的到达**,事实上,**线程也不知道信号到底什么时候到达**。线程之间可以互相通过调用 `rt_thread_kill` 发送信号。

以上画线部分是我特意要大家注意的,我们要看待中断回调函数那样,看待信号回调函数**被执行的实机**,但不需要过分担忧的是回调函数**执行时间**,因为**终究信号回调函数还是在线程上下文被执行的**。

从官方文档可以清楚了解到,使用信号很简单,安装信号、解除信号掩码、发送信号、处理信号等几个过程。

更多关于信号的原理详见官方文档 [信号]( https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/ipc2/ipc2?id=%e4%bf%a1%e5%8f%b7 )

一个示例引起的血案

官方原版示例笔者就不贴出来了,直接拷贝到自己的项目完美运行。但是,笔者经过如下修改,发现一点儿疑问。

/* 线程 1 的入口函数 */
static void thread1_entry(void *parameter)
{
...
while (cnt < 10)
   {
       ...
       tick = rt_tick_get();
       rt_thread_mdelay(1000);
       tick = rt_tick_get();
   }
   ...
}

把延时时间增长,前后添加测时。多次运行发现 tick 值改变只有 300 (`rt_thread_mdelay(300)`)。这说明了线程响应 signal 后,处理了信号回调函数之后放弃了之前的延时!那么问题来了,应用层想要的延时时间不足,应用层知道吗?答案是,*不知道!*

rt-thread 中阻塞函数列表

前一段时间在文章 rt-thread 那些你必须知道的几类 api 里总结了 *禁止在中断中调用*、*必须在任务调度器运行以后才能使用*、*不能用在线程自己身上*的几类 api。

可能还缺一种:哪些 api 会引起线程调度,使得当前线程放弃 cpu 使用权——所有调用 `rt_schedule` 的函数都属于这类。这里边又分三种情况,一种是时间片耗尽让出 cpu 使用权;一种是释放资源或者信号让出 cpu 使用权;还有一种是等待资源而被动放弃 cpu。最后这种情况,是有目地的,往往希望有资源可用了之后从阻塞中恢复继续运行,如果线程从阻塞中恢复运行但同时没有资源可用是不是就乌龙了?以下的关注重点也是这类函数。

所有第三类引起线程调度的函数和上面的 `rt_thread_mdelay` 一样,在 signal 面前可能遇到一样的遭遇。大体上,分这么几类:

  • 延时函数
  • 线程间同步机制函数
  • 线程间通信机制部分函数(signal除外)
  • posix 下的 select poll 等接口(可能使用了线程间同步和通信机制)

这几类在遇到 signal 之后行为分别是什么样的?

被阻塞函数遇到 signal 后什么反应?

延时函数遇到 signal

这个前面已经经过测试的了,它会退出阻塞提前结束延时,但是应用层并不知道是达到延时时间还是有信号。

线程间同步通信机制函数遇到 signal

  • `rt_sem_take` 线程 error 非 RT_EOK (包括 RT_EINTR)直接返回线程错误状态
   /* do schedule */
   rt_schedule();
   if (thread->error != RT_EOK)
   {
       return thread->error;
   }
  • `rt_mutex_take` 考虑到了 signal 的影响,返回继续阻塞等待 `time` 时间。这是 ipc 里唯一例外的一个。
               /* do schedule */
               rt_schedule();

               if (thread->error != RT_EOK)
               {
#ifdef RT_USING_SIGNALS
                   /* interrupt by signal, try it again */
                   if (thread->error == -RT_EINTR) goto __again;
#endif /* RT_USING_SIGNALS */

其它,其余的  ipc 都和 `rt_sem_take` 一样。

完成量遇到 signal

`rt_completion_wait` 返回线程错误状态。

           /* do schedule */
           rt_schedule();
           /* thread is waked up */
           result = thread->error;

           level = rt_hw_interrupt_disable();
       }
   }
   ...

   return result;

select poll 等接口与 signal

因为文件描述符对应的设备不尽相同,设备底层实现 `poll` 的方式可能也千差万别,但是他们大概率是使用上面的线程间同步和通信机制了。

`poll` 实现过程调用个超时等待函数 `poll_wait_timeout` ,它也没有区分超时和信号两种情况。

       rt_schedule();
       level = rt_hw_interrupt_disable();
   }

   ret = !pt->triggered;
   rt_hw_interrupt_enable(level);

   return ret;

我们发现,`rt_sem_take` 结束了阻塞,并可能返回了 `RT_EINTR` ,而 `rt_mutex_take` 继续了循环阻塞。

等待资源而被动放弃 cpu 时怎么应对 signal 才合适?

现做以下约定,等待资源而被动放弃 cpu 的线程在此约定下,当有 signal 的时候会提前结束阻塞,返回应用层,应用层可以根据线程错误状态区别处理。

1. 复位线程错误状态为 `RT_EOK` 。
2. 调用 `rt_schedule` 进行线程调度,线程被阻塞挂起。
3. 从 `rt_schedule` 恢复唤醒,有一定手段通知到应用层(返回线程错误状态),应用层可以区分出是因为资源可用还是因为信号。

哪些 api 做到了以上这几点呢?

```
rt_completion_wait
rt_sem_take
rt_event_recv
rt_mb_send_wait
rt_mb_recv
rt_mq_send_wait
rt_mq_recv
rt_data_queue_push
rt_data_queue_pop

rt_mp_alloc

哪些 api 没有做到以上几点?

```
rt_mutex_take
rt_thread_sleep
rt_thread_delay
rt_thread_delay_until
rt_thread_mdelay
rt_wqueue_wait

笔者曾经在 gitee 上提交过一个 [issue]( https://gitee.com/rtthread/rt-thread/issues/I44JNS ) ,当时笔者隐隐中认为 ipc 中的不一致行为总有些隐患,感觉所有的阻塞等待都应该处理一下意外唤醒后的超时等待。却没意识到有什么意外情况可以让这些函数从阻塞等待中提前退出。通过研究 signal 实现原理的过程中发现,这种意外情况还有存在的,只是担忧的问题重点变了,不是处理阻塞等待剩余时间,而是在 signal 的影响下通知应用层的问题。

解决方案

有了上面的梳理,下面的修改方向就有了,改动范围也确定了。

  • 几个延时函数返回 `thread->error` 代替目前的 `RT_EOK` ;
  • `rt_mutex_take` 去掉 `goto __again` 也返回 `thread->error` ;
  • `rt_wqueue_wait` 返回 `thread->error` 代替目前的 `RT_EOK` 。
  • `poll` 目前返回值是 >= 0 的,返回 0 可能是超时,也可能是被信号中断了。暂时不发表修改意见。

结束语

以上搜索不一定完整完全,但应该包括了大部分受到影响的函数。如果看客有发现其它的 api 有不符合上述约定行为的,请留言告知,谢谢!

本人能力有限,文中难免有错误。望各位同仁不吝赐教。

相关文章

rt-thread 优化系列(0) SysTick 优化分析

rt-thread 优化系列(一) 之 过多关中断

rt-thread 优化系列(二) 之 同步和消息关中断分析

rt-thread优化系列(三)软定时器的定时漂移问题分析


  审核编辑:汤梓红
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分