用Python编写的实时操作系统:pyRTOS

MicroPython相关代码、库、工具
回复
头像
shaoziyang
帖子: 1992
注册时间: 2019年 10月 21日 13:48

用Python编写的实时操作系统:pyRTOS

#1

帖子 shaoziyang » 2021年 6月 6日 15:55

简介

pyRTOS是用Python编写的实时操作系统(RTOS)。pyRTOS的主要目标是提供一个能够在circuittpython中运行的纯Python实时操作系统,第二个目标是为希望学习使用RTOS的高级python用户提供一个教育工具。pyRTOS也应该在MicroPython中运行,甚至也可以在标准Python中使用。

pyRTOS是以FreeRTOS为原型的,但有一些关键的区别。最大的区别在于它使用了一个自愿的任务抢占模型,FreeRTOS通常通过定时器中断来强制抢占。这意味着用户有更大的责任来确保所有任务都表现良好。pyRTOS还使用不同的命名约定,任务内置了消息传递。

据我所知,除了自愿抢先之外,任务调度与FreeRTOS中的任务调度是相同的。任务被分配了数字优先级,数字越低优先级越高,最高优先级的就绪任务被赋予CPU时间,关联到当前正在运行的任务。未来可能会添加其他调度算法。

图片

基本用法

pyRTOS将功能划分为任务。任务类似于桌面操作系统中的线程,但在pyRTOS的任务中,不能迁移到其他处理器或内核,这是受到CirchtPython的限制。但是理论上,应该可以为MicroPython编写一个线程迁移的调度程序,MicroPython支持硬件多线程。

一个简单的pyRTOS程序将定义一些任务函数,将它们包装在对象中,然后使用add_task()函数向操作系统注册它们。添加所有任务后,用start()启动RTOS.

启动RTOS后,将为任务安排时间,根据优先级调度算法给任务分配CPU时间。当任务表现良好、设计为协同工作并给出正确的优先级时,操作系统将对它们进行协调,以便它们共同完成程序设计的目标。

有关任务和用法示例,请参见sample.py。
隐藏内容
你必须登入/注册才可观看隐藏内容
 
 

头像
shaoziyang
帖子: 1992
注册时间: 2019年 10月 21日 13:48

Re: 用Python编写的实时操作系统:pyRTOS

#2

帖子 shaoziyang » 2021年 6月 6日 16:17

任务

pyRTOS的任务由任务对象和包含任务代码的函数组成。任务函数只接受一个参数,即对包含该参数的任务对象。任务函数由Python创建。在第一个yield之前的任何代码都是设置代码,这个yield返回的任何内容都将被忽略。主任务循环在这个yield之后,这就是调度程序分配任务CPU时间时将执行的代码。

主任务循环通常是一个无限循环。如果任务需要终止,则应该调用返回(return),并且在返回之前执行任何必要的操作。但通常情况下,任务永远不会返回。

在pyRTOS中抢占是完全自愿的。这意味着所有任务都必须周期性地将控制权交回操作系统,否则其他任务将得不到CPU时间,任务之间无法传递消息,操作系统的其他管理功能将永远无法执行。在pyRTOS中 yield 有两个函数,其中一个是仅仅将控制权传递回操作系统,这允许操作系统重新评估任务优先级并将控制权传递给更高优先级的就绪任务,并且允许操作系统处理诸如消息传递、锁定之类的管理。Yield 应该相当频繁,但不要频繁到在操作系统中花费的时间比在任务中花费的时间还多。对于小任务,每个主循环一次就足够了。对于较大的任务,yield 应该放在重要的子部分之间。如果一个任务有一段与时间相关的代码,不要将 yield 放在可能中断时间关键流程的地方,因为不能保证 yield 会在规定的时间内恢复。

Yields 还用于进行某些阻塞 API 的调用。最常见的可能是延时。更高优先级的进程需要优先考虑,因为即使频繁的 yield 也不会给较低优先级的进程分配 CPU 时间,默认的调度程序总是给优先级最高的就绪任务分配CPU时间。低优先级任务获得时间的唯一方法是,高优先级任务在不需要CPU时间时被阻塞。通常这意味着延时,在pyRTOS中是通过超时生成器实现的。当超时生成器过期时,任务将再次就绪。在此之前,较低优先级的任务将允许分配CPU时间。任务也可以在等待消息或互斥锁定时阻塞。在未来,可能会有更多宽容的非实时调度程序。

还有一些地方的任务应该总是yield。每当传递消息时,它都会被放置在本地队列中。当任务 yield 时本地任务传出队列中的消息被传递,其它需要采取 yield 解决的地方,将在文档中注明。
 

头像
shaoziyang
帖子: 1992
注册时间: 2019年 10月 21日 13:48

Re: 用Python编写的实时操作系统:pyRTOS

#3

帖子 shaoziyang » 2021年 6月 6日 16:33

消息

消息传递机制直接构建到pyRTOS中的任务中。每个任务都有自己的传入和传出邮箱。当正在运行的任务产生时,将传递消息。这个消息传递系统相当简单。每封邮件都有一个发件人和一个收件人,消息还有一个类型,可以是pyRTOS.QUIT或用户定义的类型(参见sample.py)。用户定义的类型以128或更高的整数值开始。低于128的类型保留给pyRTOS API将来使用。消息中也可以包含消息,但这不是必需的。如果type字段足以传递必要的信息,则最好将message字段留空,以节省内存。消息字段可以包含任何内容,包括对象和列表。如果需要向新任务传递参数,一种方法是使用参数列表或元组对新创建的任务对象调用 deliver()。这将把参数添加到任务的消息队列中,允许它在初始化期间访问参数。

检查消息是任何可能接收消息的任务的关键部分。未检查的消息队列可能会累积太多消息,导致系统内存不足。如果您的任务可能接收到消息,那么在每个循环中检查消息是很重要的。还要注意不要发送太多的消息给低优先级任务而不定期阻塞高优先级任务,让任务有时间处理消息队列。如果接收消息的任务永远得不到CPU时间,这将导致内存耗尽。

消息可以通过引用目标任务对象或对象的名称来寻址。名称可以是任何类型的可比较数据,数字是效率最高的,而字符串是最可读的。对象引用寻址必须以实际存在的对象,否则操作系统将崩溃。还要注意的是,保留已终止任务的引用将阻止这些任务被垃圾收集,从而造成潜在的内存泄漏。对象引用是最快的消息寻址方法,它们在调试时可能会提供一些好处,但这取决于用户如何理解和避免相关的危害。名称寻址要安全得多,但是,发往不存任务名称的消息将以静默方式传递,从而使某些bug更难找到。此外,由于名称地址需要查找关联的对象,因此名称地址消息将消耗大量的CPU时间来传递。

sample.py中展示了几个消息传递示例。

头像
shaoziyang
帖子: 1992
注册时间: 2019年 10月 21日 13:48

Re: 用Python编写的实时操作系统:pyRTOS

#4

帖子 shaoziyang » 2021年 6月 6日 16:45

以后添加功能

互斥

我们目前有一个互斥对象(带有优先级继承),但这并不是一套完整的互斥工具。FreeRTOS有二进制信号量、计数信号量和递归互斥。这里使用了自愿抢占,所以这些优先级并不是非常高,因为任务在关键部分不能抢占,而不需要使用互斥。但仍有一些情况需要相互排斥。包括像锁定I/O执行外部硬件这样的情况,可能希望在一段时间内 yield 以允许I/O完成,而不允许其他任务在我们等待时改变这个硬件。此外,一些处理器具有向量处理和/或浮点单元,它们的速度足够慢,可以保证在等待时进行yield,而不会放弃对这些单元的独占访问。在Python的上下文中,这些内容的相关性并不清楚,但是我们肯定需要某种互斥。

在FreeRTOS中,mutex具有优先级继承机制。默认情况下,在pyRTOS中也是如此,因为阻塞条件是按任务优先级顺序检查的。二进制信号量实际上是无优先级继承的互斥体。我们如何处理基于请求顺序的锁?我想我们可以在信号量中有一个队列来跟踪谁先请求并按顺序排列优先级。这比优先级继承要昂贵得多,但也不难做到。

spinlocks 在单线程自愿抢占系统中是否相关/有用?


FreeRTOS

我们需要浏览FreeRTOS文档,看看一个功能齐全的RTOS还可以拥有什么。


大小

因为使用微控制器时,大小是一个严重的问题。代码的注释非常好,但这意味着注释占用了很大一部分空间。我们现在正在发布CircuitPython的.mpy格式,它将大小降低到5KB左右。也许我们应该在将来的版本中加入一个源代码版本,去掉注释。


说明

这需要更广泛的测试。Mutex类尚未测试。我们还需要更多的阻塞条件测试。sample.py成功使用wait_for_message()两次。timeout()也在sample.py中成功测试。

我们真正需要的是一些示例问题,包括一些实际的CircuitPython设备。当 Trinkey RP2040 问世时,将有足够的空间来安装一些 CircuitPython-RTOS示例程序。我有NeoKey Trinkey 和 Rotary Trinkey,。这两个都没有太多空间,所以它们只适用于非常简单的例子。

回复

  • 随机主题
    回复总数
    阅读次数
    最新文章