MicroPython原理浅析

简介: MicroPython包含了诸如交互式提示,任意精度整数,关闭,列表解析,生成器,异常处理等高级功能。 足够精简,适合运行在只有256k的代码空间和16k的RAM的芯片上。

简介

MicroPython是 Python 3 语言 的精简高效实现 ,包括Python标准库的一小部分,经过优化可在微控制器和受限环境中运行。

MicroPython包含了诸如交互式提示,任意精度整数,关闭,列表解析,生成器,异常处理等高级功能。 足够精简,适合运行在只有256k的代码空间和16k的RAM的芯片上。

MicroPython旨在尽可能与普通Python兼容,让您轻松将代码从桌面传输到微控制器或嵌入式系统。

应用展望

金融智能卡COS系统利用JavaCard虚拟机实现了APPLET的跨平台应用。但是JavaCard标准规范由Oracle公司定制,具有一定的License费用。国内COS厂商每年都需要花费几千万元来获取JavaCard标准的使用权。为了减少这笔开销,COS厂商开始寻找新的技术来取代JavaCard虚拟机,比如Native方案等,但这虽然可以在一定的程度上解决了虚拟机的作用,但对于跨平台的应用实现还是无法彻底解决。

物联网应用碎片化和安全性问题一直是阻碍其发展的主要原因,Micropython虚拟机技术理论上可以完全替代JAVACARD虚拟机,但缺少实用场景,并且其安全性也需要进一步证明论证。但从技术本身上看,Micropython虚拟机的确为物联网的应用开发提供了一种可能,如果有实际产品出现在市场上,经过物联网应用的进一步孵化,有可能会成为物联网领域的"JavaCard"。

平头哥作为物联网AIoT设备的基础设施提供者,于2015年就针对物联网领域推出MCU级别的TEE安全方案,我们认为Micropython技术应用在TEE里,有希望成为物联网安全应用技术的主要组成部分。基于Micropython虚拟机自身的移植性,很快速便捷的在平头哥基于CK802T安全处理器的Hobbit1-2上运行起来。基于物联网产品功能单一的特性,我们可以完全去除Micropython技术的一些无用特性,使之资源消耗达到最小,符合资源受限的物联网设备。

工作原理

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。具体流程可参考如下图一。

image.png
​ 图一

QSTR的定义

QDEF(MP_QSTR___main__, (const byte*)"\x8e\x13\x08" "__main__")

其中x8ex13是dbj2 hash算法计算出来,x08是QSTR "main"的长度

def dbj2_hash(qstr, bytes_hash):
    hash = 5381
    for b in qstr:
        hash = (hash * 33) ^ b
    # Make sure that valid hash is never zero, zero means "hash not com    puted"
    return (hash & ((1 << (8 * bytes_hash)) - 1)) or 1

通过dbj2 hash值和子串长度作为一个QSTR的ID。

  • Build in QSTR

Micropython支持常用的build in的QSTR,包括基本整形类型和运算符等。比如

QDEF(MP_QSTR_ptr32, (const byte*)"\xb2\xca\x05" "ptr32")
QDEF(MP_QSTR_s32i, (const byte*)"\x3e\x34\x04" "s32i")
QDEF(MP_QSTR_float, (const byte*)"\x35\x44\x05" "float")
QDEF(MP_QSTR_add, (const byte*)"\x44\x32\x03" "add")
QDEF(MP_QSTR_sub, (const byte*)"\x21\x8d\x03" "sub")
QDEF(MP_QSTR_xor, (const byte*)"\x20\x93\x03" "xor")
QDEF(MP_QSTR_max, (const byte*)"\xb1\x43\x03" "max")
QDEF(MP_QSTR_min, (const byte*)"\xaf\x42\x03" "min")
QDEF(MP_QSTR___eq__, (const byte*)"\x71\x3e\x06" "__eq__")
QDEF(MP_QSTR___ge__, (const byte*)"\xa7\x46\x06" "__ge__")
QDEF(MP_QSTR___gt__, (const byte*)"\xb6\x82\x06" "__gt__")
  • Extend QSTR

除了常用的Build in QSTR外,Micropython还支持扩展的QSTR,从而添加用户自己的应用功能。比如

 // 添加mbedtls udp socket接口
QDEF(MP_QSTR_uselect, (const byte*)"\x58\x8e\x07" "uselect")
QDEF(MP_QSTR_usocket, (const byte*)"\x75\x00\x07" "usocket")
QDEF(MP_QSTR_ussl, (const byte*)"\x1c\xf2\x04" "ussl")
// 添加http socket服务    
QDEF(MP_QSTR_uwebsocket, (const byte*)"\xe5\x33\x0a" "uwebsocket")
// 添加密码学算法服务
QDEF(MP_QSTR_ucryptolib, (const byte*)"\x34\xda\x0a" "ucryptolib")
// 添加随机数服务
QDEF(MP_QSTR_urandom, (const byte*)"\xab\xae\x07" "urandom")
// 添加Json包服务
QDEF(MP_QSTR_ujson, (const byte*)"\xe8\x30\x05" "ujson")

QSTR的生成

无论是Build in QSTR还是Extend QSTR, 都是通过makemodulesdefs.py, makeqstrdefs.py, makeqstrdata.py脚本生成,存放在qstrdefs.generated.h文件里。用户自行扩展的QSTR是否添加成功,可以通过在QSTR AUTO GENERATOR后通过查看该文件。

ByteStream的生成

Python源文件首先经过mpy_cross.elf工具转成mpy文件,例如Top.py转成的Top.mpy二进制如下:

image.png

红色部分就是Top文件功能可执行代码,mpy_tool.py工具会提取该功能可执行代码生成 frozen mpy的C语言文件,与该Top.py对应的的ByteStream数组如下:

image.png

通过比较,fun_data_Top__lt_module_gt__process_apdu的数组内容实际上就是Top.py文件的功能可执行代码。利用mpy_tool.py工具可以将Python语言脚本转换成C语言数组,而该数组内容实际上就是该Python文件的功能可执行代码,也称为ByteStream。

一旦Python文件调用另一个python文件,mpy_tool.py工具的做法是通过叫做mp_raw_code_t的数据接口将不同的python文件调用关系连接起来,例如如下mp_raw_code_t的数组将Top.py和Applet_AID1234567890.py文件连接起来,其源于Top.py文件调用了Applet_AID1234567890.py文件里的process_apdu接口。

image.png
image.png

Virutal Machine Entry Table

  • Byte Code定义
Name Value
MP_BC_LOAD_CONST_FALSE 0x10
MP_BC_LOAD_CONST_NONE 0x11
MP_BC_LOAD_CONST_TRUE 0x12
MP_BC_LOAD_CONST_SMALL_INT 0x14
MP_BC_LOAD_CONST_STRING 0x16
MP_BC_LOAD_CONST_OBJ 0x17
MP_BC_LOAD_NAME 0x1b
MP_BC_LOAD_ATTR 0x1d
MP_BC_STORE_NAME 0x24
MP_BC_STORE_ATTR 0x26
MP_BC_POP_JUMP_IF_TRUE 0x36
MP_BC_POP_JUMP_IF_FALSE 0x37
MP_BC_BUILD_MAP 0x53
MP_BC_BUILD_SET 9x56
MP_BC_YIELD_VALUE 0x5d
MP_BC_YIELD_FROM 0x5e
MP_BC_MAKE_FUNCTION 0x60
MP_BC_MAKE_FUNCTION_DEFARGS 0x61
MP_BC_CALL_FUNCTION 0x64
MP_BC_CALL_FUNCTION_VAR_KW 0x65
MP_BC_CALL_METHOD 0x66
MP_BC_CALL_METHOD_VAR_KW 0x67
MP_BC_IMPORT_NAME 0x68
MP_BC_IMPORT_FROM 0x69
MP_BC_IMPORT_STAR 0x6a
MP_BC_LOAD_CONST_SMALL_INT_MULTI 0x70
MP_BC_LOAD_FAST_MULTI 0xb0
MP_BC_UNARY_OP_MULTI 0xd0
MP_BC_BINARY_OP_MULTI 0xd7

上述表格定义了部分Byte Code,这些Byte Code在mpy_tool.py工具里同样定义了一套。用于将Python文件转换成Byte Stream数组,在执行Byte Stream时,Micropython会解析其中的Byte Code, 执行相应的Byte Code处理服务函数。在执行Byte Code处理服务函数里,根据QSTR在字典数据结构里的查找到对应的C语言接口,从而实现真正的python执行脚本功能。

MicroPython SDK

SDK里的主要部件如下:

  • py/ – the core Python implementation, including compiler, runtime, and
  1. library.
  • mpy-cross/ – the MicroPython cross-compiler which is used to turn scripts
  1. precompiled bytecode.
  • ports/unix/ – a version of MicroPython that runs on Unix.
  • ports/csky/ – a version of MicroPython that runs on the PyBoard and similar
    Hobbit1-2 boards.
  • tests/ – test framework and test scripts.
  • docs/ – user documentation in Sphinx reStructuredText format.
  • extmod/ – additional (non-core) modules implemented in C.
  • tools/ – various tools, including the pyboard.py module.
  • examples/ – a few example Python scripts.

mpy_cross tool

Micropython的移植首先需要编译生成MicroPython cross-compiler工具,该工具可以将Python 源文件预编译成mpy文件。

$ cd mpy-cross
$ make

如何创建一个Python对象和方法

该实例创建了了一个名为example的python对象,同时也支持add_ints的一个方法。

  • Step 1

创建一个example.c的文件, 包含python系统头
文件。

#include "py/obj.h"
#include "py/runtime.h"
#include "py/builtin.h"
  • Step 2

定义add_ints 方法。

 STATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {
    // Extract the ints from the micropython input objects
    int a = mp_obj_get_int(a_obj);
    int b = mp_obj_get_int(b_obj);

    // Calculate the addition and convert to MicroPython object.
    return mp_obj_new_int(a + b);
}
  • Step 3

定义example 对add_ints方法的引用。

STATIC MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);
  • Step 4

定义example对象的属性,利用key/value方式作为example对象的入口查找方式。

 STATIC const mp_rom_map_elem_t example_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example) },
    { MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },
};
STATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
  • Step 5

定义example对象。

  const mp_obj_module_t example_user_cmodule = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t*)&example_module_globals,
};
  • Step 6

注册example对象到MicroPython对象库里。

 MP_REGISTER_MODULE(MP_QSTR_example, example_user_cmodule, MODULE_EXAMPLE_ENABLED);
  • Step 7

写python脚本对example对象的访问

 import example
print(example.add_ints(1, 3))

总结

Micrypython为嵌入式系统的跨平台应用开发提供了一种可能,特别是资源受限的物联网设备。利用其跨平台的特性,能够快速的移植应用到嵌入式平台上。和Javacard相比,其安全性没有得到证明,像Byte Code的定义都没有形成标准规范,对Global Platform的SE规范没有支持,这些都需要进一步的完善。

原文作者:cui_632
点击查看原文

相关文章
|
6月前
|
存储 安全 Linux
从零开始学习DPDK:掌握这些常用库函数就够了(上)
从零开始学习DPDK:掌握这些常用库函数就够了
|
6月前
|
存储 缓存 算法
从零开始学习DPDK:掌握这些常用库函数就够了(下)
从零开始学习DPDK:掌握这些常用库函数就够了
|
7月前
|
传感器 C语言 芯片
「入门指南」轻松学习嵌入式 GPIO:从原理到应用一步到位
「入门指南」轻松学习嵌入式 GPIO:从原理到应用一步到位
|
8月前
|
芯片 Python Windows
MicroPython 玩转硬件系列1:环境搭建
MicroPython 玩转硬件系列1:环境搭建
|
传感器 物联网 项目管理
【毕设参考】“摸鱼”神器,用 ESP32 + HaaS Python DIY一款全功能鱼缸 控温、过滤、换水一键执行
【毕设参考】“摸鱼”神器,用 ESP32 + HaaS Python DIY一款全功能鱼缸 控温、过滤、换水一键执行
180 0
|
Ubuntu Linux 程序员
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十一)驱动进化之路:设备树的引入及简明教程(中)
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十一)驱动进化之路:设备树的引入及简明教程
671 0
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十一)驱动进化之路:设备树的引入及简明教程(中)
|
Linux 芯片
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十一)驱动进化之路:设备树的引入及简明教程(下)
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十一)驱动进化之路:设备树的引入及简明教程
190 0
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十一)驱动进化之路:设备树的引入及简明教程(下)
|
Linux 芯片
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十一)驱动进化之路:设备树的引入及简明教程(上)
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十一)驱动进化之路:设备树的引入及简明教程
342 0
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十一)驱动进化之路:设备树的引入及简明教程(上)
|
存储 缓存 API
iOS网络编程之一——iOS网络框架简介
iOS网络编程之一——iOS网络框架简介
175 0
iOS网络编程之一——iOS网络框架简介
|
Linux
全志A33 linux led驱动编程(附实测参考代码)
开发平台 * 芯灵思SinlinxA33开发板 实验原理 在芯灵思开发板上,没有led灯模块,只能通过引脚电平观察: 这里我选择LS-INT引脚。 全志A33一共有10组IO口,每组IO有9个相关功能控制器,LS-INT属于PB7,相关寄存器如图 本次实验只用到这两个寄存器,在程序中命名为gpio_con,gpio_dat ,设置为输出引脚。
1638 0