这里介绍一下mpy的技术。
https://github.com/micropython
GitHub官网
https://github.com/micropython/micropython.git
Mpy的Github地址
下载
导入模块时,MicroPython将代码编译为字节码,然后由MicroPython虚拟机(VM)执行字节码。字节码存储在RAM中。编译器本身需要RAM,但其在编译完成后才可用。
若已导入多个模块,则在没有足够的RAM来运行编译器时,会出现这种情况。在这种情况下,导入语句将引发内存异常。
若模块在导入时实例化全局对象,则将在导入时占用RAM,编译器就无法在随后的导入中使用该RAM。通常, 最好避免导入时运行的代码;更好的方法是在所有模块被导入后都有由应用程序运行的初始化代码。这一方法将编译器可用的RAM最大化。
若RAM仍不足够编译所有模块,一种解决方案是预编译模块。MicroPython有一个交叉编译器, 可将Python模块编译为字节码(参见mpy-cross目录中的README)。生成的字节码文件的扩展名为.mpy。此文件可能被复制到文件系统,并以常规方式导入。或者,某些或所有模块可实现为冻结字节码:在大多数平台上,这样可以节省更多的RAM,因为字节码直接从闪存运行而没有存储在RAM中的。
当正在运行的程序实例化对象时,将从一个固定大小的池中分配必要的RAM,这个池被称为堆。当对象超出范围 (换言之:已不可用于代码)时,冗余对象即为”垃圾”。”垃圾回收”(GC)的进程回收该内存,并将其返回到空闲堆。这个过程自动进行,但可通过发出 gc.collect()
来直接调用。
全局模块在此
一个新类型的定义
对象基址和模块的符号表
所有的python全局模块从:
STATIC const mp_rom_map_elem_t mp_builtin_module_table[]中
查找,这张查找表位于py/objmodule.c。
函数
模块的实现。
这些全局模块的价值在于:
扩展mp功能
为添加新的模块提供参考
以下来自一个博客,写的不错:
https://www.limfx.pro/ReadArticle/1115/micropython-yun-hang-yuan-li-yi-ji-ru-he-tong-guocyu-yan-kuo-zhan-micropython-module
Micropython技术是依赖Byte Code的执行,在编译阶段就将py文件先转换成mpy文件,在通过mpy-tool.py生成Byte Code,Byte Code在执行时会依赖Virtual Machine入口表,找到对应的Module入口,最终找到对应的Funcion binary code执行。其中所有的Function都通过Dictionary的形式存储,而每一个Dictionary都有自己的QSTR,Micropython有buildin的QSTR和用户扩展的QSTR。具体流程可参考如下图。
每次编译前makemodulesdefs.py
, makeqstrdefs.py
, makeqstrdata.py
脚本会收集和生成所有的QSTR,以QDEF(MP_QSTR_sizeof, (const byte*)"\x49\x73\x06" "sizeof")
的形式写入genhdr/qstrdefs.generated.h
文件。其中\x49\x73
是字符串哈希值,\x06
是字符串长度。MicroPython通过哈希值和长度进行字符串比较从而尽可能地保证性能。
qstrdefs.generated.h
主要在qstr.h
和qstr.c
中被include。
/* qstr.h */
enum {
MP_QSTRnumber_of, // no underscore so it can't clash with any of the above
};
typedef struct _qstr_pool_t {
struct _qstr_pool_t *prev;
size_t total_prev_len;
size_t alloc;
size_t len;
const byte *qstrs[];
} qstr_pool_t;
/* qstr.c */
const qstr_pool_t mp_qstr_const_pool = {
NULL, // no previous pool
0, // no previous pool
MICROPY_ALLOC_QSTR_ENTRIES_INIT,
MP_QSTRnumber_of, // corresponds to number of strings in array just below
{
#ifndef NO_QSTR
#define QDEF(id, str) str,
#include "genhdr/qstrdefs.generated.h"
#undef QDEF
#endif
},
};
不同文件里QDEF的意义是不同的,qstr.h
里是取了前半部分也就是MP_QSTR_xx
形式的操作符,加入到enum中作为index,qstr.c
中则提取了由哈希值长度以及实际字符串组成的字符串,即MicroPython字节码,将其加入到qstr_pool这个数据结构中。qstr_pool中都是预定义的qstr,包括MicroPython内置qstr和用户通过c语言定义的qstr。这个pool称为interned pool。
mpy-cross将py文件编译成mpy文件,mpy文件就是由字节码组成,字节码输入MicroPython虚拟机后就会查找qstr pool执行对应的函数。
我们通过C语言扩展MicroPython时也需要将关键字注册到qstr pool中。
通过C语言扩展API的代码在这里实现
py Python 解释器相关的抽象实现的代码,包含运行时等等。
mpy-cross MicroPython 编译器,处理 Python 代码回 ByteCode 机器码。
ports 对应平台移植配置文件
tests 框架测试脚本
docs 配置到 Sphinx 的文档网站
extmod 一些不需要在 Core 中的抽象 C 接口代码。
tools 各类脚本辅助工具,例如 Pyboard.py 可以通信控制 MicroPython 。
examples Python 示例代码。
lib 给 port 用的各自平台的 SDK 依赖库,可能在里面,也可能在外面,并不重要。
drivers 通过软实现的硬件驱动,基于 py 的架构使用标准 C 实现的 Python 模块(C + Python),和芯片自己提供的 SDK 略微不同,有较大的兼容性。
目录
ports/bare-arm/——ARM MCU的MicroPython的最低版本。主要用于控制代码大小。
ports/teensy/——运行在Teensy3.1上的MicroPython的一个版本(初步的,但可以使用)。
ports/pic16bit/——用于16-bitPIC微控制器的MicroPython版本。
ports/cc3200/——运行在TI的cc3200上的MicroPython版本。
ports/esp8266/——运行在Espressif的esp8266 SoC上的MicroPython版本。
——在32个微端口上运行的python/Espressif版本。
ports/nrf/——运行在北欧nRF51和nRF52微型计算机上的MicroPython版本。
extmod/--用C实现的附加(non-core)模块。
tools/--各种工具,包括pyboard.py模块。
examples/——几个示例Python脚本。
这个目录是大量的驱动。