天天看點

MicroPython原理淺析

簡介

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。具體流程可參考如下圖一。

MicroPython原理淺析

​ 圖一

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二進制如下:

MicroPython原理淺析

紅色部分就是Top檔案功能可執行代碼,mpy_tool.py工具會提取該功能可執行代碼生成 frozen mpy的C語言檔案,與該Top.py對應的的ByteStream數組如下:

MicroPython原理淺析

通過比較,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接口。

MicroPython原理淺析
MicroPython原理淺析

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

點選檢視原文

繼續閱讀