用户工具

站点工具


circuitpython:完全用python编写的实时操作系统pyrtos

完全用python编写的实时操作系统pyRTOS

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

pyRTOS 是以 FreeRTOS 为原型的,但有一些关键的区别。最大的区别在于它使用了一个非占先的任务抢占模型,而FreeRTOS通常是使用定时器中断来强制抢占,这意味着需要用户来确保所有任务都正常运行。pyRTOS还使用了不同的命名约定,任务内置了消息传递。

除了抢先方式之外,任务调度与 FreeRTOS 中的任务调度是相同的。任务被分配了数字优先级,数字越低优先级越高,优先级最高的就绪任务被赋予CPU时间。未来可能会添加其他调度算法。

基本用法

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

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

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

有关任务和用法示例,请参见源码中的 sample.py。

任务

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

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

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

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

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

消息

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

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

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

注:

  • 目前 pyRTOS 尚不能直接在 micropythhon 上运行。
  • 从使用方式和功能看,pyRTOS 类似于 python 的异步(asyncio),当然还是有很多不同。

链接



purge    随机主题   
circuitpython/完全用python编写的实时操作系统pyrtos.txt · 最后更改: 2021/07/04 22:57 由 shaoziyang · 查看次数: 8919