这是转发宋老师写的文章,我也是刚知道,宋老师跟我一样也是养娃的人了,国庆期间,看看文章,看看升升国旗。

很久没有写技术文章了,做码农难,做养娃的码农更难,趁着娃看动画片的机会,受着王菲童鞋《我和我的祖国》歌唱精神的鼓舞,我要来说几句。

硬实时是什么?

众所周知,硬实时的概念不是越快越好,而是强调可重复的(repeatable)、决定性的时间期限内给予响应(deterministic response time)。所以它的本质点是可预期,实时系统的计算正确性不仅取决于计算的逻辑正确性,还取决于产生结果的时间。比如,汽车碰撞后,必须在X时间内弹开安全气囊,你弹开晚了,人已经挂了。

当然最恶劣场景下的延迟,可能作为一个时间参数,来评估RTOS本身的性能指标。比如你改造了Linux,实现了中断或高优先级任务的100us以内的确定性延迟;但是RT-Thread,可能不改造,就可以到达us以内的延迟。那么,你延迟要求只需要200us以内的,你选改造后的Linux也没有什么毛病,但是如果你要的就是us级,那么对不起,Linux不是你的菜

众所周知,RT-Thread、FreeRTOS、VxWorks这样的操作系统是硬实时的;Linux这样的操作系统是提供软实时能力的,针对的 miss 掉截止期限也死不了人的那种应用,比如看电影。

那么,这个时候我们诞生了一个疑问,是不是在RTOS里面随便写代码都能满足硬实时,而在Linux里面无论怎么写代码都满足不了硬实时?我认为这2个问题的答案都是否定的。

Linux为什么不硬实时?

我们首先看一下,Linux为什么不能提供硬实时能力。我们认为Linux主要有如下问题(你站在硬实时的角度看它是问题,你换个角度看,它就反而是正确的地方)。

1. spinlock是一个随处可见被内核、驱动使用的API

Linux内核和驱动程序员钟爱spinlock到了如痴如狂的程度,可以说不睡眠、时间较短的critical section场景,都会第一时间想到spinlock。平生不识自旋锁,就称英雄也枉然。自旋锁的优越性在于,在2个人(这2个人可能是线程与线程、中断与线程、中断与中断等)竞争一个锁的时候,避免失败的那一方切换上下文context,所以与其上下文切换,不如原地等。但是自旋锁本身也引起了副作用,它引起了持有锁的CPU核的抢占调度的禁止。内核自旋锁的实现,更多的是核间自旋转而核内是通过禁止抢占来实现临界区保护的。

假设T1, T2, T3, T4运行在一个核上面,当T1拿到spinlock后,这个核上的抢占调度被禁止,如果在T1持有spinlock的时间内,T2是一个高优先级的实时任务,尽管T2被唤醒,它也不可能立即打断T1的执行,必须等待T1释放spinlock。由于T1究竟会持有 spinlock 多久做xxxx,这个鬼都不知道,所以T2究竟要等多久,也未可知,这显然破坏了决定性的时延

2. Linux的中断执行时间可能过长且不可嵌套

众所周知,早期的Linux版本有个标记叫IRQF_DISABLED,标记本中断在执行的时候,其他所有中断都被禁止进入;而后Linux内核实际去掉了这个申请flags,其实就是都是IRQF_DISABLED了,总体可认为Linux内核不支持中断的嵌套。

int request_irq(unsigned int irq, irq_handler_t handler,    unsigned long irqflags, const char *devname, void *dev_id);

中断在执行的时候,所有的中断都进不来,这个设计本身简化了内核,但是对于硬实时的打击是致命的,前面的中断不执行完成,优先级再高的中断也得给我等着。

比如中断1在执行的过程中,来了中断2,而中断2对应的事情是必须要决定性时延的,由于IRQ1的中断服务程序也是码农写的,我们无法确定这个中断服务程序要执行多久。这显然让高优先级中断2的进入延迟不再具备可预期性。

3.软中断(softirq)是一个比进程上下文优先级更高的上下文

我们设想一个场景,哪怕Linux解决了问题2,就是Linux的中断变地可嵌套,高优先级的中断可以打断低优先级的中断,并且高优先级的中断2唤醒了一个用户写的实时线程。

IRQ2唤醒了实时任务T1,但是T1必须等待IRQ1唤起的软中断(也包括使用软中断上下文的tasklet等)被执行完,T1才能投入执行。IRQ1唤起的softirq的代码是码农写的,这个码农写多久,鬼都不知道,这显然破坏了实时任务T1得以调度执行的确定性时延。

4. 内核里面会屏蔽中断的API如local_irq_disable、spin_lock_irqsave等

前面笔者已经反复强调过,在驱动程序里面调用local_irq_disable()通常都是一个bug,因为它无法修复另外一个核上运行的线程、中断服务程序与本核线程之间的竞争。尽管在单核处理器里面调用这个API是通常安全的,但是我们哪怕是在单核编程,都要假装自己是多核的样子,这个是在Linux里面写代码跨平台的最基本常识。

相信绝大多数童鞋都不会傻到写驱动的时候再去调local_irq_disable这样的API。但是spin_lock_irqsave这样的API在内核的使用可以说太常见了,它其实是适用于一个经典的场景,就是中断服务程序与线程之间有竞态的情况。作为一个内核程序员,相信如下的经典用法你已经熟悉地不能再熟悉,满满地都是套路:

它把T1、T2、T3、T4、IRQ1、IRQ2这6者之间的竞争消灭于无形。T1如果持有了spin_lock_irqsave,本核上的T2、IRQ1显然进不来,CPU1上面的T3、T4、IRQ2想访问T1访问的临界资源必须spin。IRQ1如果持有了spin_lock, CPU1上面的T3、T4、IRQ2想访问IRQ1访问的临界资源必须spin。

那么,问题又来了,spin_lock_irqsave既屏蔽了抢占,又屏蔽了中断,这会导致中断和实时任务的确定性时延造成不可预期的破坏。因为spin_lock_irqsave 和 spin_lock_irqrestore是码农写的,鬼都不知道它要多久。

当然,历史上,粗犷的大内核锁(Big Kernel Lock,BKL)也是一个问题。由于晶晶姑娘不喜欢内核粗犷的一面,BKL在如今的内核里面已经烟消云散。

在Linux的世界里,这些锁当然都没有一个锁牛逼,就是RCU,尤其是面对这个世界符合阿姆达尔定律(Amdahl's law)定律的情况下,我们既要保证临界资源访问的被保护,又要尽一切可能地让多个线程同时狂奔。关于RCU的细节,谢神医已经有多篇文章论述。

Linux的世界大概是这样的:中断、软中断、线程(包括ksoftirqd线程)。我们都清楚地知道,软中断大量陷入的情况下,内核会将后续的软中断投入ksoftirqd内核线程执行,所以软中断还有一个可能的执行时机是在内核线程里面。

5. Linux用户空间内存的lazy分配机制与交换swap

对于喜欢在RTOS写程序的童鞋来说,Linux的世界一时半会难以理解,但是对于写Linux的童鞋来说,绝大多数的RTOS简直就是在裸奔。

我们都知道,在Linux里面,用户空间的内存都执行lazy的分配机制。比如你malloc一个内存

char *p = malloc(1024*1024);

这个时候Linux忽悠你说拿到了内存并且p获得了地址,但是实际的拿到却是在你写的时候,以page fault缺页中断的形式获得的。比如你写p[0]=1就拿到了第一页,你写p[4096]就拿到了第2页。这个lazy的分配机制,也同样适用于栈、代码段等。

你是一个实时的线程,你被唤醒得以执行,你执行的时候,发现你访问的临时变量还没有获得内核,你的代码段可能还特马在硬盘里,请问你实时个什么鬼?你执行到函数b的时候,去访问d[1000],结果发现这个栈的这页内存还要通过page fault来通过内核buddy去申请,你的确定性延迟还如何满足?

main()
{   …   a();
}
a()
{   …   b();
}
b()
{   int d[1024];    d[1000]=100;   c();
}

当然,已经进入内存的东西,也由于内核的swap机制,会与磁盘进行交换。

绝大多数的RTOS都没有这个“问题”,这也恰恰是他们不够“牛逼”的地方。对于手机、电脑这种富应用的系统而言,你不能用资源已经被确定性分配的思维模式来思考。

Linux preempt-rt如何解决这些问题?

前段时间,这篇文章刷屏了:《

到今天为止,ARCH_SUPPORTS_RT谁他么都不是真:

barry@barryUbuntu:~/develop/linux$ git grep ARCH_SUPPORTS_RT

arch/Kconfig:config ARCH_SUPPORTS_RT

kernel/Kconfig.preempt: depends on EXPERT && ARCH_SUPPORTS_RT

所以,你要真地在mainline见到PREEMPT_RT开花结果,还必须活地更久一点。

当提到 preempt-rt 补丁的时候,我必须强调一点,Linux不是一个裸奔的操作系统。Linux 的应用都是在用户空间写的一个个进程、线程。所以相对于其他 RTOS 可能更加强调高优先级中断的确定性时延(RTOS不太特别强调机制与策略分离的概念,整个东西编译在一起的话,在中断里面放策略也未尝不可),在Linux时间里,用户空间高优先级的 RT 线程的确定性调度时延就显得更加critical(因为Linux内核里面你不能裸奔地把用户策略的东西放进内核,内核提供的是一些操作接口而已,简单来说,你要做的事情是一个应用,而应用是个用户空间的东西)。

风在吼,马在叫,娃儿在咆哮。今天就谈到这里,明天接着谈。我相信你还有很多的疑惑,比如很多童鞋说,你刚才提到的Linux的一些硬实时的毛病,在 RTOS 里面其实也都有,我会给你一个交代。

扫码或长按关注

回复「 加群 」进入技术群聊

在实时操作系统里随便写代码都能硬实时吗?相关推荐

  1. ❤️小白到精英必备的100多个Python函数汇总❤️写代码都流畅多了

    新手在做写代码的时候容易卡壳,尤其当接触的函数以及其他知识比较多的时候,经常会看完需求之后不知道自己该用什么方法来实现它,实现的逻辑可能你有,但怎么该用什么函数给忘了,这其实就是知识的储备不够,你记不 ...

  2. 程序员 笔记本 amd python_程序员写代码都用什么样的笔记本?

    程序员一般喜欢用thinkpad或者Mac,因为价位等方面的因素,还是用thinkpad多点,从事不同研发方向需要的计算机的配置也不太相同,现在开发软件要求的机器配置也和十几年不太一样,那个时候嵌入式 ...

  3. 如何做到每天都写代码

    你是否曾为项目没有进展而惆怅过?想要有完整两天时间(没有任何的放松机会)来编程也不现实,时间的零乱往往让人不知所措.还有一个重要的问题就是你上个星期写的代码,隔一个星期后再接着写,你还能记得多少? 为 ...

  4. 架构师究竟要不要写代码?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! Talk is cheap, show me the c ...

  5. wince系统改安卓系统_什么是实时操作系统(RTOS)

    在维基百科上,实时操作系统(RTOS)的定义如下: 实时操作系统(Real-time operating system, RTOS),又称即时操作系统,它会按照排序运行.管理系统资源,并为开发应用程序 ...

  6. python在哪里写代码比较适合-程序员面试被要求手写代码,你与顶级程序员的差别在哪?...

    原标题:程序员面试被要求手写代码,你与顶级程序员的差别在哪? 前言: Python现在非常火,语法简单而且功能强大,很多同学都想学Python! 所以小的给各位看官们准备了收藏已久的视频教程分享给大家 ...

  7. 如何做到每天写代码?

    摘要:总有一大堆事情没有做完,没有时间和精力为业余项目写代码?不要着急,看看可汗学院计算机科学院院长John Resig怎么说.本文将教你如何保证在每天都能有时间给业余项目写代码. 你是否曾为业余项目 ...

  8. python在哪里写代码-程序员面试被要求手写代码,你与顶级程序员的差别在哪?...

    原标题:程序员面试被要求手写代码,你与顶级程序员的差别在哪? 前言: Python现在非常火,语法简单而且功能强大,很多同学都想学Python! 所以小的给各位看官们准备了收藏已久的视频教程分享给大家 ...

  9. Linux实时操作系统Xenomai的一小时半入门视频推荐

    今天探索到了一个B站Up主工控小包总(上海翌控科技)的视频和最近的学习十分相关,感兴趣的小伙伴们可以去亲自探索一下,我在本篇文章里主要是做一些总结. 苏州诺达佳自动化NP6工控机 苏州诺达佳自动化NP ...

最新文章

  1. ndarray的数据类型
  2. python序列化模块struct_python的struct模块
  3. Retrofit+RxJava联网工具类
  4. [.NET跨平台]Jexus独立版本的便利与过程中的一些坑
  5. linux之vi,vim命令
  6. 【转】理清基本的git(github)流程
  7. 图像处理自学(六):图像增强算法总结
  8. 【优化算法】麻雀搜索优化算法(SSA)【含Matlab源码 1288期】
  9. MyBatis入门程序案例
  10. tomcat未自动解压war包原因分析
  11. map字符串转json格式
  12. 接触式IC卡和非接触式IC卡有什么区别?
  13. 阿里云文本反垃圾检测接口调用
  14. 分享java旷课版的SSM框架源码
  15. (纪录片)鸟瞰中国 China from Above
  16. ubuntu右上角没有网的问题解决
  17. 对WXin执行回插入代码后的回编译操作
  18. 等额本息贷款月付款额的推导公式
  19. linux 路由转发 ipv6,IPv6路由
  20. unity代码混淆及帧同步服务器、常用软件记录

热门文章

  1. find_first_of和find函数的区别
  2. jstl处理栏目与子栏目_芬顿氧化法废水处理工程技术规范(征求意见稿)
  3. get clone 出现 fatal: the remote end hung up unexpectedly5 MiB | 892.00 KiB/s 报错信息
  4. webgl获取鼠标形状_三模无线搭配对称手型设计,游戏致胜利器,ROG烈刃2无线鼠标...
  5. MySQL中外键的定义、作用、添加和删除
  6. Python 生成requirement 使用requirements.txt
  7. 数据结构:单链表操作之如何判断链表是否带环及相关操作
  8. ios实践之tableView顶部“弹簧”图片
  9. spring AOP自定义注解方式实现日志管理
  10. java GZIP压缩和解压