在实时操作系统里随便写代码都能硬实时吗?
这是转发宋老师写的文章,我也是刚知道,宋老师跟我一样也是养娃的人了,国庆期间,看看文章,看看升升国旗。
很久没有写技术文章了,做码农难,做养娃的码农更难,趁着娃看动画片的机会,受着王菲童鞋《我和我的祖国》歌唱精神的鼓舞,我要来说几句。
硬实时是什么?
众所周知,硬实时的概念不是越快越好,而是强调可重复的(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 里面其实也都有,我会给你一个交代。
扫码或长按关注
回复「 加群 」进入技术群聊
在实时操作系统里随便写代码都能硬实时吗?相关推荐
- ❤️小白到精英必备的100多个Python函数汇总❤️写代码都流畅多了
新手在做写代码的时候容易卡壳,尤其当接触的函数以及其他知识比较多的时候,经常会看完需求之后不知道自己该用什么方法来实现它,实现的逻辑可能你有,但怎么该用什么函数给忘了,这其实就是知识的储备不够,你记不 ...
- 程序员 笔记本 amd python_程序员写代码都用什么样的笔记本?
程序员一般喜欢用thinkpad或者Mac,因为价位等方面的因素,还是用thinkpad多点,从事不同研发方向需要的计算机的配置也不太相同,现在开发软件要求的机器配置也和十几年不太一样,那个时候嵌入式 ...
- 如何做到每天都写代码
你是否曾为项目没有进展而惆怅过?想要有完整两天时间(没有任何的放松机会)来编程也不现实,时间的零乱往往让人不知所措.还有一个重要的问题就是你上个星期写的代码,隔一个星期后再接着写,你还能记得多少? 为 ...
- 架构师究竟要不要写代码?
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! Talk is cheap, show me the c ...
- wince系统改安卓系统_什么是实时操作系统(RTOS)
在维基百科上,实时操作系统(RTOS)的定义如下: 实时操作系统(Real-time operating system, RTOS),又称即时操作系统,它会按照排序运行.管理系统资源,并为开发应用程序 ...
- python在哪里写代码比较适合-程序员面试被要求手写代码,你与顶级程序员的差别在哪?...
原标题:程序员面试被要求手写代码,你与顶级程序员的差别在哪? 前言: Python现在非常火,语法简单而且功能强大,很多同学都想学Python! 所以小的给各位看官们准备了收藏已久的视频教程分享给大家 ...
- 如何做到每天写代码?
摘要:总有一大堆事情没有做完,没有时间和精力为业余项目写代码?不要着急,看看可汗学院计算机科学院院长John Resig怎么说.本文将教你如何保证在每天都能有时间给业余项目写代码. 你是否曾为业余项目 ...
- python在哪里写代码-程序员面试被要求手写代码,你与顶级程序员的差别在哪?...
原标题:程序员面试被要求手写代码,你与顶级程序员的差别在哪? 前言: Python现在非常火,语法简单而且功能强大,很多同学都想学Python! 所以小的给各位看官们准备了收藏已久的视频教程分享给大家 ...
- Linux实时操作系统Xenomai的一小时半入门视频推荐
今天探索到了一个B站Up主工控小包总(上海翌控科技)的视频和最近的学习十分相关,感兴趣的小伙伴们可以去亲自探索一下,我在本篇文章里主要是做一些总结. 苏州诺达佳自动化NP6工控机 苏州诺达佳自动化NP ...
最新文章
- ndarray的数据类型
- python序列化模块struct_python的struct模块
- Retrofit+RxJava联网工具类
- [.NET跨平台]Jexus独立版本的便利与过程中的一些坑
- linux之vi,vim命令
- 【转】理清基本的git(github)流程
- 图像处理自学(六):图像增强算法总结
- 【优化算法】麻雀搜索优化算法(SSA)【含Matlab源码 1288期】
- MyBatis入门程序案例
- tomcat未自动解压war包原因分析
- map字符串转json格式
- 接触式IC卡和非接触式IC卡有什么区别?
- 阿里云文本反垃圾检测接口调用
- 分享java旷课版的SSM框架源码
- (纪录片)鸟瞰中国 China from Above
- ubuntu右上角没有网的问题解决
- 对WXin执行回插入代码后的回编译操作
- 等额本息贷款月付款额的推导公式
- linux 路由转发 ipv6,IPv6路由
- unity代码混淆及帧同步服务器、常用软件记录
热门文章
- find_first_of和find函数的区别
- jstl处理栏目与子栏目_芬顿氧化法废水处理工程技术规范(征求意见稿)
- get clone 出现 fatal: the remote end hung up unexpectedly5 MiB | 892.00 KiB/s 报错信息
- webgl获取鼠标形状_三模无线搭配对称手型设计,游戏致胜利器,ROG烈刃2无线鼠标...
- MySQL中外键的定义、作用、添加和删除
- Python 生成requirement 使用requirements.txt
- 数据结构:单链表操作之如何判断链表是否带环及相关操作
- ios实践之tableView顶部“弹簧”图片
- spring AOP自定义注解方式实现日志管理
- java GZIP压缩和解压