国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

LuatOS框架的使用(上)

合宙LuatOS ? 來源:合宙LuatOS ? 作者:合宙LuatOS ? 2026-01-27 19:38 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

在資源受限的物聯(lián)網(wǎng)終端設(shè)備中,如何實(shí)現(xiàn)快速開發(fā)與穩(wěn)定運(yùn)行是關(guān)鍵挑戰(zhàn)。LuatOS框架通過將Lua語言與底層硬件抽象層深度融合,提供了一套簡(jiǎn)潔高效的開發(fā)范式。本文將圍繞LuatOS框架的使用展開,從環(huán)境搭建、模塊調(diào)用到任務(wù)調(diào)度,全面解析其開發(fā)流程與最佳實(shí)踐。

本篇文章主要講LuatOS 框架;LuatOS 框架是整個(gè) LuatOS 開發(fā)中最基礎(chǔ)也是最核心的內(nèi)容,無論使用 LuatOS 開發(fā)什么功能,都會(huì)用到它;

LuatOS 框架主要包含以下幾部分:

1、LuatOS 軟件的構(gòu)成

2、LuatOS 開發(fā)環(huán)境的說明

3、LuatOS 用戶腳本程序的運(yùn)行機(jī)制

4、LuatOS 的 sys 核心庫中任務(wù)、消息、定時(shí)器和調(diào)度器功能的使用

一、初步認(rèn)識(shí) LuatOS 開發(fā)

現(xiàn)在我們開始正式進(jìn)入 LuatOS 開發(fā)的世界。

1.1 Lua 語言介紹

用戶基于 LuatOS 進(jìn)行軟件二次開發(fā),使用的編程語言為 Lua 語言,Lua 語言相對(duì)來說還是比較簡(jiǎn)單的,大家只要有其他編程語言的開發(fā)經(jīng)驗(yàn),就不用在 Lua 語言學(xué)習(xí)上花費(fèi)太多時(shí)間,花個(gè)一兩天大概看一遍就夠了;后續(xù)可以邊看 LuatOS demo,邊學(xué)習(xí) Lua 語言,這樣學(xué)習(xí)效率更高。

1.2 LuatOS 軟件的構(gòu)成

1.2.1 LuatOS 軟件架構(gòu)

wKgZO2l4Q-GAVeINAAHY2lJJFtM876.png

LuatOS 軟件的總體架構(gòu)參考上圖,從二次開發(fā)的角度來看,主要理解幾個(gè)概念:內(nèi)核固件,標(biāo)準(zhǔn)庫,核心庫,擴(kuò)展庫,demo,project,用戶開發(fā)的 LuatOS 項(xiàng)目應(yīng)用軟件;

接下來我結(jié)合架構(gòu)圖逐一分析下這幾個(gè)概念(為了演示方便,在這里我打開兩個(gè)瀏覽器窗口,左邊是框架圖,右邊是文字介紹)。

1.2.2 LuatOS 內(nèi)核固件

1、內(nèi)核固件(又叫固件,底層固件,或者 core),我們會(huì)針對(duì)每種硬件編譯好并且對(duì)外發(fā)布內(nèi)核固件

2、內(nèi)核固件文件以 soc 做為后綴,例如:

(1) LuatOS-SoC_V2008_Air8000_1.soc 表示Air8000 硬件的V2008版本的1 號(hào)LuatOS 固件文件;

(2) LuatOS-SoC_V2008_Air780EHM.soc 表示Air780EHM 硬件的V2008版本的 LuatOS 固件文件;

(3) LuatOS-SoC_V1004_Air8101.soc 表示Air8101 硬件的V1004版本的 LuatOS 固件文件;

3、內(nèi)核固件包含主芯片系統(tǒng)平臺(tái)層(這里有一個(gè)嵌入式操作系統(tǒng),目前比較流行的是 FreeRTOS,接下來本文用到的嵌入式操作系統(tǒng)都會(huì)以 FreeRTOS 為例來說明),LuatOS 適配層,Lua 虛擬機(jī),Lua 標(biāo)準(zhǔn)庫,LuatOS 核心庫幾部分,除了主芯片系統(tǒng)平臺(tái)層的源碼沒有開放外,其余四部分的源碼全部開放;

4、內(nèi)核固件中和用戶軟件二次開發(fā)息息相關(guān)的兩部分是 Lua 標(biāo)準(zhǔn)庫和 LuatOS 核心庫,接下來我們看下這兩部分內(nèi)容;

1.2.3 Lua 標(biāo)準(zhǔn)庫

Lua 標(biāo)準(zhǔn)庫是 Lua 語言內(nèi)置的一組核心功能模塊,它們?yōu)?Lua 提供了基礎(chǔ)編程能力。標(biāo)準(zhǔn)庫的設(shè)計(jì)遵循 Lua 的“小而精”哲學(xué),僅包含最必要的功能。

Lua 標(biāo)準(zhǔn)庫已經(jīng)編譯到了 LuatOS 內(nèi)核固件中,用戶無法修改,可以直接使用。

主要包含以下幾種庫

基礎(chǔ)庫(Basic Library):支持 collectgarbage(垃圾回收)、_G(全局變量表)、ipairs(迭代數(shù)組元素)、pairs(迭代鍵值對(duì))、tostring(轉(zhuǎn)換為字符串)、tonumber(轉(zhuǎn)換為數(shù)字)、type(獲取類型名)等功能函數(shù);

協(xié)程庫(Coroutine Library):支持 coroutine.create(創(chuàng)建協(xié)程)、coroutine.yield(掛起協(xié)程)、coroutine.resume(恢復(fù)協(xié)程)、coroutine.status(獲取協(xié)程狀態(tài))、coroutine.running(獲取正在運(yùn)行的協(xié)程)等功能函數(shù);

字符串處理庫(String Library):支持 string.len(獲取字符串長(zhǎng)度)、string.match(字符串模式匹配)、string.byte(獲取字符的 ascii 碼)、string.char(獲取 ascii 碼對(duì)應(yīng)的字符)等功能函數(shù);

表處理庫(Table Library):支持 table.insert(插入元素)、table.remove(移除元素)、table.concat(將表中的元素連接為一個(gè)字符串)、table.unpack(解包表中的元素為多個(gè)返回值)等功能函數(shù);

此外還有數(shù)學(xué)庫,輸入輸出庫,操作系統(tǒng)庫,調(diào)試庫等功能模塊,在這里我就不逐一列舉了;

LuatOS 中使用的 Lua 是 5.3 版本

1.2.4 LuatOS 核心庫

LuatOS 核心庫是 專為嵌入式設(shè)備設(shè)計(jì)的 Lua 運(yùn)行時(shí)擴(kuò)展庫,針對(duì)物聯(lián)網(wǎng)(IoT)和資源受限環(huán)境(如 MCU)進(jìn)行了深度優(yōu)化和功能擴(kuò)展。

LuatOS 核心庫已經(jīng)編譯到了 LuatOS 內(nèi)核固件中,用戶無法修改,可以直接使用。

目前支持 74 個(gè)核心庫,核心庫中的所有功能模塊,我們?cè)诤罄m(xù)文章中都會(huì)逐一講解;

在核心庫中有一個(gè) sys 庫,在本章中會(huì)重點(diǎn)介紹。

1.2.5 LuatOS 擴(kuò)展庫

LuatOS 擴(kuò)展庫是 在核心庫基礎(chǔ)上針對(duì)特定場(chǎng)景或硬件功能提供的附加功能模塊,用于增強(qiáng) LuatOS 在物聯(lián)網(wǎng)(IoT)和嵌入式系統(tǒng)中的能力。

LuatOS 擴(kuò)展庫是用 Lua 語言實(shí)現(xiàn)的功能模塊

用戶開發(fā)項(xiàng)目軟件時(shí),需要主動(dòng)加載擴(kuò)展庫文件,才能使用。

目前支持 19 個(gè)擴(kuò)展庫

擴(kuò)展庫中的部分功能模塊,我們?cè)诤罄m(xù)文章中都會(huì)進(jìn)行講解。

1.2.6 LuatOS demo

LuatOS demo 是在 Lua 標(biāo)準(zhǔn)庫、LuatOS 核心庫、LuatOS 擴(kuò)展庫的基礎(chǔ)上,我們針對(duì)獨(dú)立的應(yīng)用場(chǎng)景,開發(fā)的示例代碼合集。

展示了 Lua 標(biāo)準(zhǔn)庫、LuatOS 核心庫、LuatOS 擴(kuò)展庫的實(shí)際應(yīng)用方法,可以幫助開發(fā)者快速驗(yàn)證硬件功能、理解各種庫的 API 使用場(chǎng)景,開發(fā)者可以參考這些 demo 快速開發(fā)自己的項(xiàng)目。

1.2.7 LuaOS project

LuatOS project 是基于功能相對(duì)完整的硬件(例如整機(jī)開發(fā)板,硬件上集成了模組或者工業(yè)引擎,lcd,tp,攝像頭,485 接口,矢量字庫芯片,tf 卡,以太網(wǎng)等),開發(fā)的完整項(xiàng)目軟件。開發(fā)者可以參考這種硬件和項(xiàng)目軟件,更加快速地開發(fā)自己的項(xiàng)目。

1.2.8 用戶開發(fā)的 LuaOS 項(xiàng)目應(yīng)用軟件

用戶開發(fā)的 LuatOS 項(xiàng)目應(yīng)用軟件,是指開發(fā)者基于自己的實(shí)際項(xiàng)目需求(可以是演示 demo 需求,也可以是真實(shí)的項(xiàng)目需求),使用 Lua 腳本語言,調(diào)用 Lua 標(biāo)準(zhǔn)庫、LuatOS 核心庫、LuatOS 擴(kuò)展庫編寫的項(xiàng)目應(yīng)用腳本代碼。

用戶開發(fā)的 LuatOS 項(xiàng)目應(yīng)用軟件,我們開發(fā)的 LuatOS demo, LuatOS project,這三種軟件都屬于 LuatOS 項(xiàng)目應(yīng)用軟件,區(qū)別在于 demo 和 project 是我們開發(fā)的,另外一種是我們的用戶開發(fā)的。

1.3 LuatOS 項(xiàng)目應(yīng)用軟件(hello_luatos)開發(fā)調(diào)試過程

了解了 LuatOS 軟件構(gòu)成之后,我們接下來以一個(gè) hello_luatos 項(xiàng)目為例,先總體看下用戶如何開發(fā)調(diào)試自己的項(xiàng)目應(yīng)用軟件。

再來回顧一下 LuatOS 軟件架構(gòu)圖:

用戶開發(fā)的項(xiàng)目應(yīng)用軟件,位于架構(gòu)圖中的最上層(也就是黃色背景的這一層);

開發(fā)用戶項(xiàng)目應(yīng)用軟件時(shí),需要調(diào)用 Lua 標(biāo)準(zhǔn)庫、LuatOS 核心庫、LuatOS 擴(kuò)展庫來實(shí)現(xiàn)。

hello_luatos 項(xiàng)目應(yīng)用軟件的需求為:每隔一秒鐘通過日志輸出一次 Hello, LuatOS。

1.3.1 根據(jù)項(xiàng)目需求編寫項(xiàng)目應(yīng)用軟件代碼

開發(fā)項(xiàng)目軟件使用的編程語言為 Lua 腳本語言,編寫的腳本文件后綴為.lua;

代碼編寫工具推薦使用 Visual Studio Code;

hello_luatos 的項(xiàng)目軟件代碼我已經(jīng)編寫好了,源碼已經(jīng)提交到:https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/luatos_framework/hello_luatos

打開這個(gè)路徑,可以看到,一共包含如下三個(gè)文件:

wKgZPGl4WkuAXLyRAAAQBJww2vw844.png

在這里我們先簡(jiǎn)單的看下這三個(gè)文件的核心內(nèi)容,詳細(xì)內(nèi)容在后續(xù)章節(jié)會(huì)細(xì)細(xì)講解。

1、main.lua:這個(gè)文件的作用大家可以看文件頭注釋,是一個(gè)入口文件,類似于 C 語言的 main 函數(shù),LuatOS 項(xiàng)目軟件代碼是從 main.lua 開始執(zhí)行的,所以 main.lua 文件必須存在,并且文件名也必須是 main.lua

wKgZO2l4WiuAO034AAAqNPba9Rs898.png

在 main.lua 中還有一行代碼,如下圖所示,加載 hello_luatos 應(yīng)用功能模塊,也就是說執(zhí)行到這行代碼時(shí),就會(huì)加載并且運(yùn)行 hello_luatos.lua 這個(gè)文件;和 main.lua 不同的是,hello_luatos.lua 的文件名可以根據(jù)自己的應(yīng)用功能模塊含義自定義(只要定義的文件名和 Lua 標(biāo)準(zhǔn)庫、LuatOS 核心庫、LuatOS 擴(kuò)展庫不要重復(fù)就行),文件名修改后,在 main.lua 中 require 新的文件名即可;例如文件名修改為 hello_air8000.lua,在 main.lua 中 require "hello_air8000"即可。

wKgZPGl4WomAW-pyAAAIJORqHNo599.png

2、hello_luatos.lua:還是先看下圖中的這個(gè)文件頭注釋,重點(diǎn)看下選中的幾行文字描述:

wKgZO2l4WsWANbB3AAA74f1NahA476.png

為什么單獨(dú)創(chuàng)建一個(gè) hello_luatos.lua 去實(shí)現(xiàn)每隔一秒打印一次Hello, LuatOS的業(yè)務(wù)功能呢? 這個(gè)是為了應(yīng)用功能設(shè)計(jì)模塊化,不同功能模塊之間解耦,邏輯清晰,代碼閱讀成本低;這個(gè)是一個(gè)很好的設(shè)計(jì)思想,希望大家以后開發(fā)項(xiàng)目軟件都要遵循。

3、readme.md:這個(gè)文件是對(duì)當(dāng)前 demo 項(xiàng)目軟件的使用說明,從功能概述、硬件環(huán)境、軟件環(huán)境,操作步驟幾方面說明了當(dāng)前這個(gè) demo 項(xiàng)目如何使用。

wKgZO2l4WyaAT1kUAADWSlY9OG4295.png

1.3.2 LuatOS 軟件的運(yùn)行載體

開發(fā)好 hello_luatos 的項(xiàng)目應(yīng)用軟件代碼后,這個(gè)項(xiàng)目完整的 LuatOS 軟件也就完成了;

完整的 LuatOS 軟件 = LuatOS 內(nèi)核固件 + LuatOS 擴(kuò)展庫 + hello_luatos 的項(xiàng)目應(yīng)用軟件;

完整的 LuatOS 軟件需要一個(gè)運(yùn)行載體,才能看到軟件的運(yùn)行效果;

目前提供了兩類運(yùn)行載體:

1、硬件核心板或者開發(fā)板:例如 Air8000 的核心板和開發(fā)板,Air780EHM/EHV/EGH/EPM 的核心板和開發(fā)板,Air8101 的核心板和開發(fā)板; 本篇中我們將使用 Air8000 的核心板來運(yùn)行 LuatOS 軟件進(jìn)行演示;

2、PC 模擬:直接可以在電腦上使用 LuatOS 模擬器來運(yùn)行,使用模擬器運(yùn)行有一些限制,模擬器支持的功能并不完整,例如和外設(shè)有關(guān)的功能,lcd,字庫等可能并不支持,但是一些純軟件的功能,例如 LuatOS 運(yùn)行框架,網(wǎng)絡(luò)應(yīng)用等還是可以模擬運(yùn)行的。所以本講的 LuatOS 運(yùn)行框架,我們也會(huì)使用 PC 模擬器來進(jìn)行演示,這樣可以節(jié)省硬件載體燒錄軟件所花費(fèi)的時(shí)間。

1.3.3 燒錄 完整的 hello_luatos 項(xiàng)目 LuatOS 軟件 到 Air8000 核心板 運(yùn)行

我們先來看下,完整的 hello_luatos 項(xiàng)目的 LuatOS 軟件第一種運(yùn)行載體(以 Air8000 核心板為例)上的運(yùn)行效果。

要將完整的 LuatOS 軟件燒錄到 Air8000 核心板中,并且觀察軟件的運(yùn)行效果,需要用到 Luatools 下載調(diào)試工具;

在這里我就不詳細(xì)說明 Luatools 的用法了,只重點(diǎn)說明一下燒錄 hello_luatos 這個(gè)完整的項(xiàng)目軟件過程中涉及到的一些關(guān)鍵操作:

(1)首先我們要準(zhǔn)備好一塊 Air8000 核心板、一根帶有數(shù)據(jù)傳輸功能的 type-c 接口的 usb 數(shù)據(jù)線、一個(gè) WINDOWS10 以及以上版本操作系統(tǒng)的電腦;

(2)Air8000 核心板正面的供電/充電撥動(dòng)開關(guān) 撥到供電一端,背面的USB ON/USB OFF撥動(dòng)開關(guān) 撥到 USB ON 一端;

(3)type-c 接口的 usb 數(shù)據(jù)線連接 Air8000 核心板和電腦;

(4)打開 Luatools,勾選 4G 模塊 USB 打印,點(diǎn)擊項(xiàng)目管理測(cè)試,創(chuàng)建一個(gè)項(xiàng)目,選擇好 LuatOS 內(nèi)核固件,hello_luatos 的應(yīng)用軟件腳本代碼,勾選 USB BOOT 下載,然后點(diǎn)擊 下載底層和腳本 按鈕;

wKgZPGl4XEaAIUYQAAB7U_VsqMU825.pngwKgZO2l4XGOAMhvVAABqwfWp8Oo465.png


此時(shí)如果 Air8000 核心板已經(jīng)處于開機(jī)運(yùn)行狀態(tài),則等一段時(shí)間,會(huì)自動(dòng)開始燒錄軟件; 如果處于開機(jī)運(yùn)行狀態(tài)下,沒有自動(dòng)燒錄軟件,則用手按住核心板正面的下載按鈕不放開,然后再按一下核心板的復(fù)位按鈕就可以開始燒錄軟件; 如果沒有處于開機(jī)運(yùn)行狀態(tài),則用手按住核心板正面的下載按鈕不放開,然后再長(zhǎng)按開機(jī)按鈕 2 秒,就可以開始燒錄軟件; 出現(xiàn)如下圖所示的下載完成狀態(tài)就表示燒錄成功:

wKgZPGl4XI-AR61LAAAhZXNmixI697.png

下載成功后,Air8000 核心板自動(dòng)開機(jī)運(yùn)行,在 Luatools 主界面的日志窗口就可以看到運(yùn)行日志,我們可以看到大約每隔 1 秒鐘,日志輸出一次 Hello, LuatOS

wKgZO2l4XLaAN-N1AAA8WXHJ1qg574.png

下面我來實(shí)際演示這個(gè)過程。

1.3.4 使用 PC 模擬器 運(yùn)行 完整的 hello_luatos 項(xiàng)目 LuatOS 軟件

打開 Luatools,點(diǎn)擊 賬戶-> 打開資源下載 的菜單

wKgZO2l4XOmAc8o1AAAeicw2Uw0745.png

會(huì)彈出 Luatools 資源管理窗口

wKgZO2l4XQiACEHrAAB7QQ8sW6E116.png

勾選公共資源->LuatOS 的 PC 模擬器->VXXXX 版本下的默認(rèn)資源,然后點(diǎn)擊右上角的開始下載(非刷機(jī))按鈕;

下載成功后,點(diǎn)擊右上角的打開本地資源目錄按鈕,resource 目錄下的LuatOS_PC就是 LuatOS 的 PC 模擬器,找到最新版本的壓縮包,解壓縮之后,打開解壓縮后的目錄,有如下文件:

wKgZO2l4XVSAFbM3AAANOOFA66w059.png

其中 luatos-pc.exe 就是模擬器的可執(zhí)行文件,luatos.bat 就是運(yùn)行模擬器的批處理文件。

在這個(gè)目錄下,創(chuàng)建一個(gè) cmd 命令行窗口的快捷方式,如下圖所示:

wKgZPGl4XXaAErqRAAAQ4rtJ9VE681.png

雙擊 cmd 命令行窗口,然后輸入下面一行命令,運(yùn)行 luatos 批處理文件,同時(shí)輸入要運(yùn)行的 luatos 項(xiàng)目配置文件

luatos --llt=H:Luatoolsprojectluatos_framework_hello_luatos_Air8000.ini

然后按回車鍵,就可以運(yùn)行 hello_luatos 項(xiàng)目軟件;

這行命令的前半部分固定為luatos --llt=,表示要加載 Luatools 創(chuàng)建的項(xiàng)目配置文件,根據(jù)配置文件找到所有的應(yīng)用腳本文件來運(yùn)行;

這行命令的后半部分為使用 Luatools 創(chuàng)建的 hello_luatos 項(xiàng)目軟件的配置文件的絕對(duì)路徑,根據(jù)你自己的實(shí)際情況進(jìn)行填寫;配置文件都存儲(chǔ)在 Luatools 目錄下的 project 目錄;

模擬器運(yùn)行后的效果如下圖所示:

wKgZO2l4Xa-ALFcRAACv2LmPwXY979.png

我們可以看到:每隔 1 秒鐘,日志輸出一次 Hello, LuatOS

下面我來實(shí)際演示下這個(gè)過程。

1.4 LuatOS 項(xiàng)目應(yīng)用軟件(hello_luatos)的運(yùn)行邏輯詳解

在使用 Air8000 核心板和模擬器實(shí)際運(yùn)行 hello_luatos 項(xiàng)目軟件之后,大家對(duì)這個(gè)項(xiàng)目實(shí)現(xiàn)的功能應(yīng)該是比較清楚了;

接下來,我用 Visual Studio Code,打開 hello_luatos 的項(xiàng)目應(yīng)用腳本 main.lua 和 hello_luatos.lua,逐行分析應(yīng)用邏輯的運(yùn)行過程。

在分析腳本代碼之前,先來看看簡(jiǎn)化后的下面這張圖,這張圖說明了 LuatOS 項(xiàng)目應(yīng)用軟件的總體運(yùn)行過程

wKgZO2l4Xe6AAa-RAAH46s7p-l8055.png

從這張圖可以看出,在 LuatOS 內(nèi)核固件中有一個(gè) FreeRTOS,F(xiàn)reeRTOS 運(yùn)行起來之后,創(chuàng)建了很多任務(wù),有軟件定時(shí)器任務(wù),TCP/IP 協(xié)議棧任務(wù),文件系統(tǒng)任務(wù),Lua 虛擬機(jī)任務(wù)等。

其中 Lua 虛擬機(jī)任務(wù)和 LuatOS 項(xiàng)目的應(yīng)用軟件關(guān)系最為密切;

Lua 虛擬機(jī)任務(wù)運(yùn)行起來之后,經(jīng)過必要的初始化動(dòng)作,就會(huì)去尋找 main.lua 腳本文件,找到之后,從 main.lua 的第一行代碼開始解析執(zhí)行,main.lua 會(huì)執(zhí)行必要的初始化動(dòng)作并且加載運(yùn)行其他的 Lua 腳本應(yīng)用功能模塊,main.lua 的最后一行代碼為 sys.run(),sys.run()是一個(gè) while true 的循環(huán)函數(shù),實(shí)際上也是 Lua 虛擬機(jī)任務(wù)的處理函數(shù);

在這個(gè) while 循環(huán)里面,不斷的分發(fā)處理各種消息,調(diào)度 LuatOS 項(xiàng)目應(yīng)用軟件的正常運(yùn)行。

在理解了 LuatOS 項(xiàng)目應(yīng)用軟件的基本運(yùn)行邏輯之后,接下來,我們一起來看下 hello_luatos 的項(xiàng)目應(yīng)用腳本 main.lua 和 hello_luatos.lua 的運(yùn)行過程。

hello_luatos 的應(yīng)用邏輯比較簡(jiǎn)單,并且代碼中的注釋也比較詳細(xì),我們就邊看代碼邊講解。

二、全面認(rèn)識(shí) LuatOS 運(yùn)行框架如何使用

我們?cè)谏弦恍」?jié)中分析了 hello_luatos 的應(yīng)用腳本代碼,雖然 hello_luatos 這個(gè)項(xiàng)目很小,但是我們已經(jīng)接觸到了:

1、LuatOS 中的兩個(gè)核心概念:任務(wù)和定時(shí)器;

2、LuatOS 的調(diào)度器:sys.run()函數(shù)

3、LuatOS 的一個(gè)核心庫 sys

從本章開始,我們一起系統(tǒng)性地學(xué)習(xí) LuatOS 運(yùn)行框架如何使用,主要包含以下幾項(xiàng)內(nèi)容:

1、LuatOS 的三個(gè)核心概念:任務(wù)(task),消息(message),定時(shí)器(timer);

2、LuatOS 的一個(gè)調(diào)度器:sys.run()函數(shù)

3、LuatOS 的一個(gè)核心庫:sys 核心庫

4、基于一個(gè)相對(duì)完整的 LuatOS 項(xiàng)目,分析本節(jié)課學(xué)習(xí)到的核心概念,調(diào)度器和核心庫知識(shí),系統(tǒng)理解本節(jié)課的知識(shí)在實(shí)際項(xiàng)目中的應(yīng)用方法;

2.1 LuatOS 的任務(wù)(task)

2.1.1 基本概念

2.1.1.1 FreeRTOS task 和 LuatOS task

先來看一下這張圖,和上一張LuatOS項(xiàng)目應(yīng)用軟件的總體運(yùn)行過程圖相比,新增了一段說明文字以及幾個(gè) LuatOS task 示意圖:

在 LuatOS 項(xiàng)目應(yīng)用軟件腳本運(yùn)行過程中,只要代碼可以被執(zhí)行到,就可以調(diào)用 sys.taskInit 和 sys.taskInitEx 兩個(gè)核心庫的 API,創(chuàng)建 LuatOS 的 task。

wKgZO2l4XsmADuBYAAJitibt_5I213.png

從這張圖中我們可以看到,有兩種任務(wù):

第一種是 LuatOS 內(nèi)核固件中的任務(wù),也就是 FreeRTOS 創(chuàng)建的任務(wù);這種任務(wù)我們把它命名為 FreeRTOS task;

第二種是 LuatOS 項(xiàng)目應(yīng)用腳本中的任務(wù),也就是用戶腳本代碼中調(diào)用 sys.taskInit 和 sys.taskInitEx 兩個(gè) API 創(chuàng)建的任務(wù),這種任務(wù)我們把它命名為 LuatOS task;嚴(yán)格意義上說,LuatOS task 并不是真正的 task,而是利用了 Lua 中的協(xié)程(coroutine)概念,來等價(jià)實(shí)現(xiàn)的一種 task 效果,因?yàn)?task 的受眾面比協(xié)程的受眾面要廣的很多,所以我們?cè)?LuatOS 中,將協(xié)程(coroutine)包裝成了 task 的概念,這樣更容易理解和使用;關(guān)于協(xié)程(coroutine)的知識(shí)不屬于本課程講解的范疇,大家有興趣的可以借助網(wǎng)絡(luò)資源自行學(xué)習(xí)。

在這里我使用一個(gè)形象的比喻,爭(zhēng)取可以讓大家更加直觀的理解這兩種任務(wù)的聯(lián)系和區(qū)別:

1、有一個(gè)森林,這個(gè)森林就是 FreeRTOS;

2、森林里生長(zhǎng)了很多棵大樹,每一棵大樹都是 FreeRTOS 創(chuàng)建的一個(gè)任務(wù),就是 FreeRTOS task;

3、這些大樹分成了兩種:

一種是長(zhǎng)出了樹干,樹干上還有很多樹枝,這一種大樹只有一棵,就是 Lua 虛擬機(jī)任務(wù)

一種是只長(zhǎng)了光禿禿的樹干,樹干上沒有樹枝,這種大樹有很多,除 Lua 虛擬機(jī)之外,其余所有任務(wù)都是這種大樹

4、Lua 虛擬機(jī)任務(wù)這棵大樹的樹干上長(zhǎng)出的所有樹枝,就是一個(gè)個(gè)的 LuatOS task

根據(jù)以上描述畫一張簡(jiǎn)圖如下:

wKgZPGl4X2aAdoXQAAKass2YwnM676.png


第一次接觸 LuatOS 開發(fā)的用戶,對(duì) LuatOS task 的功能特性可能會(huì)有誤區(qū),所以在這里我們先對(duì)比下 FreeRTOS task 和 LuatOS task 的一些重要的功能特性區(qū)別;

LuatOS 內(nèi)核固件中的任務(wù)(FreeRTOS task)和 LuatOS 項(xiàng)目應(yīng)用腳本中的任務(wù)(LuatOS task)的重要區(qū)別如下:

wKgZPGl4X9yAEzgpAAEQqt6tflY955.png

通過上面這個(gè)表格中的文字描述,可能理解的不是很直觀,接下來舉兩個(gè)例子,來實(shí)際說明一下 LuatOS task 的特性。

2.1.1.2 LuatOS 的多任務(wù)調(diào)度機(jī)制

第一個(gè)例子用來說明 LuatOS task 的協(xié)作式的任務(wù)調(diào)度機(jī)制;

核心代碼片段如下,我們首先分析下這段代碼的業(yè)務(wù)邏輯

wKgZO2l4YCmAc1klAATRPpDLcNs136.png

我們使用 PC 模擬器來實(shí)際運(yùn)行一下這個(gè)例子:

我已經(jīng)在 Luatools 工具上創(chuàng)建了一個(gè) luatos_framework_luatos_task_Air8000 項(xiàng)目,并且已經(jīng)把應(yīng)用腳本添加到這個(gè)項(xiàng)目下,為了節(jié)省時(shí)間,使用模擬器來運(yùn)行一下這個(gè)例子來看看實(shí)際的效果:

打開 cmd 命令行窗口,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運(yùn)行日志如下

wKgZPGl4a2KATo3lAASae401H7o631.png

下面我們結(jié)合代碼來分析一下關(guān)鍵步驟的運(yùn)行日志;

通過以上代碼和日志的分析,可以知道,在這個(gè)程序中有 task1 和 task2 兩個(gè) LuatOS task;

當(dāng) task1 內(nèi)部的代碼運(yùn)行到 sys.wait(500)時(shí),task1 會(huì)掛起自己,task1 掛起之后,下一個(gè)恢復(fù)運(yùn)行的 task 就是 task2,為什么呢?因?yàn)?task2 掛起時(shí)長(zhǎng)是 300ms,task1 的掛起時(shí)長(zhǎng)是 500ms,task1 要等 500ms 之后才能恢復(fù)運(yùn)行,task2 最長(zhǎng)只需要等待 300ms 就可以恢復(fù)運(yùn)行,所以接下來肯定是 task2 先恢復(fù)運(yùn)行;

當(dāng) task2 內(nèi)部的代碼運(yùn)行到 sys.wait(300)時(shí),task2 會(huì)掛起自己,task2 掛起之后,下一個(gè)恢復(fù)運(yùn)行的 task 有可能是 task1,也有可能是 task2,為什么呢?因?yàn)?task2 掛起時(shí)長(zhǎng)是 300ms,task1 的掛起時(shí)長(zhǎng)是 500ms;

如果 task1 之前已經(jīng)被掛起的時(shí)長(zhǎng)超過了 200ms,接下來不到 300ms,task1 就應(yīng)該重新恢復(fù)運(yùn)行,這種情況下,因?yàn)?task2 在 300ms 之后就能恢復(fù)運(yùn)行,所以 task1 就應(yīng)該先恢復(fù)運(yùn)行;

如果 task1 之前已經(jīng)被掛起的時(shí)長(zhǎng)小于 200ms,接下來超過 300ms,task1 才能重新恢復(fù)運(yùn)行,這種情況下,因?yàn)?task2 在 300ms 之后就能恢復(fù)運(yùn)行,所以 task2 就應(yīng)該先恢復(fù)運(yùn)行;

從這個(gè)例子,我們可以看出,task1 和 task2 都會(huì)通過 sys.wait(timeout)函數(shù)將自己掛起來實(shí)現(xiàn)不同 task 之間的任務(wù)調(diào)度;

如果我們簡(jiǎn)單地方修改一下代碼,如下圖所示,注釋掉黃色背景的一行代碼-- sys.wait(500) :

wKgZO2l4ao-AZukdAATMIzfiD-0725.png

會(huì)發(fā)生什么事情呢?在模擬器上實(shí)際運(yùn)行一下看看:

wKgZO2l4axmAblCZAAlWG33KogU154.png

可以看到,task1 一直在運(yùn)行,task2 永遠(yuǎn)得不到運(yùn)行;

因?yàn)?task 沒有優(yōu)先級(jí)的概念,task1 首先運(yùn)行,并且 task1 沒有掛起自己,所以 task1 就會(huì)一直運(yùn)行,task2 就沒機(jī)會(huì)運(yùn)行;

2.1.1.3 LuatOS 的多任務(wù)訪問共享資源方式

第二個(gè)例子用來說明 LuatOS task 的多任務(wù)如何使用共享資源;

核心代碼片段如下,我們首先分析下這段代碼的業(yè)務(wù)邏輯

wKgZPGl4bAuAKqp_AAXMYhqqQfg003.png

我們使用 PC 模擬器來實(shí)際運(yùn)行一下這個(gè)例子:

我已經(jīng)在 Luatools 工具上創(chuàng)建了一個(gè) luatos_framework_luatos_task_Air8000 項(xiàng)目,并且已經(jīng)把應(yīng)用腳本添加到這個(gè)項(xiàng)目下,為了節(jié)省時(shí)間,使用模擬器來運(yùn)行一下這個(gè)例子來看看實(shí)際的效果:

打開 cmd 命令行窗口,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運(yùn)行日志如下

wKgZPGl4bsmAeqn1AAbWo-miTSg057.png

可以看到:

1、task1 中 for 循環(huán)前后,全局共享變量的值增加了 100;

2、task2 中 for 循環(huán)前后,全局共享變量的值增加了 100;

非常有規(guī)律,說明:

1、task1 在 for 循環(huán)執(zhí)行過程中,沒有被其他 task(這個(gè) demo 中就是 task2)打斷;

2、task2 在 for 循環(huán)執(zhí)行過程中,沒有被其他 task(這個(gè) demo 中就是 task1)打斷;

3、如果被打斷了,循環(huán)前后的值就不會(huì)增加 100,要比 100 要大;

如果我們簡(jiǎn)單地修改一下代碼,如下圖所示,打開黃色背景的兩行代碼 sys.wait(5) :

wKgZO2l4byCAXLmbAANV25ZHCZU343.png

會(huì)發(fā)生什么事情呢?在模擬器上實(shí)際運(yùn)行一下看看

wKgZO2l4b2KAQpreAALI2QQFGsE004.png

可以看到:

無論是 task1 還是 task2,都不是增加了 100

1、task1 中 for 循環(huán)前后,全局共享變量的值增加了 200;

2、task2 中 for 循環(huán)前后,全局共享變量的值增加了 199;

說明:

1、task1 在 for 循環(huán)執(zhí)行過程中,task2 插入執(zhí)行了;

2、task2 在 for 循環(huán)執(zhí)行過程中,task1 插入執(zhí)行了;

看到這里,就出現(xiàn)了共享資源沖突的問題,但這個(gè)沖突完全是我們自己寫 LuatOS 腳本代碼故意讓他沖突的;

task1 和 task2 在各自的 for 循環(huán)執(zhí)行了 sys.wait(5)語句,執(zhí)行這個(gè)語句,就是把自己掛起,讓其他 task 運(yùn)行;

只要我們?cè)诰幋a時(shí),在一個(gè) task 內(nèi)部操作共享資源過程中,不要主動(dòng)掛起這個(gè) task,就不會(huì)存在共享資源競(jìng)爭(zhēng)和沖突的問題;

講到這里,大家對(duì) LuatOS task 的概念應(yīng)該有了一個(gè)基本的認(rèn)識(shí),接下來我們?cè)敿?xì)看下 LuatOS task 如何使用。

注意事項(xiàng):

若無特別指定是 FreeRTOS task 還是 LuatOS task,后續(xù)本文中的任務(wù)(或者 task)都是指 LuatOS task;

2.1.2 作用

我們首先想一個(gè)問題,為什么要有 task 這個(gè)概念,task 有什么用呢?

2.1.2.1 編程設(shè)計(jì)更簡(jiǎn)單

帶著這個(gè)問題,我先來回顧一下 LuatOS 的最初的一次版本演變過程;

在 LuatOS 誕生的前一兩年,應(yīng)該是 2012 年到 2013 年,具體的時(shí)間記不清楚了;

當(dāng)時(shí)在 LuatOS 中是沒有 task 這個(gè)概念的,沒有 task 的話,寫代碼以及邏輯跳轉(zhuǎn)太繁瑣了;

例如有個(gè)簡(jiǎn)單的指示燈閃爍功能需求:一個(gè)指示燈亮 500 毫秒,然后滅 500 毫秒,一直這樣循環(huán);

如果沒有 task,核心代碼片段如下:

wKgZO2l4b-2AEVdBAAF6D5pjUys298.png


在這段代碼中,需要通過定時(shí)器不斷地進(jìn)行異步處理,根據(jù)異步處理邏輯代碼運(yùn)行會(huì)不斷地發(fā)生跳轉(zhuǎn),設(shè)計(jì)或者閱讀代碼時(shí),用戶的思路也要不斷地跳來跳去,使用起來比較繁瑣;

后來 LuatOS 支持了 task,這個(gè)功能需求,使用 task 來實(shí)現(xiàn),核心代碼片段如下:

wKgZO2l4cCuAJAfeAAFQ2_j551A561.png


在一個(gè) task 的處理函數(shù)中線性的直來直去的控制指示燈閃爍,比在非 task 中繞來繞去的控制指示燈閃爍,思路更清晰,邏輯也更簡(jiǎn)單。

通過剛才的這個(gè)例子,我們基本可以明白,LuatOS 最初設(shè)計(jì) task 時(shí),很重要的一個(gè)原因就是為了讓用戶編程設(shè)計(jì)更簡(jiǎn)單。

2.1.2.2 其他作用

到后來,隨著 LuatOS task 的應(yīng)用越來越廣泛,LuatOS task 在以下幾方面所起的作用也越來越明顯:

1、實(shí)現(xiàn)多任務(wù)協(xié)作式的并發(fā)執(zhí)行;

2、更簡(jiǎn)捷地支持了模塊化解耦設(shè)計(jì);

3、更簡(jiǎn)捷的處理異步事件,可以很方便地將異步處理邏輯封裝成同步處理邏輯;

這些作用,在接下來的內(nèi)容中都會(huì)講解到,我們繼續(xù)往下看;

2.1.3 創(chuàng)建

2.1.3.1 創(chuàng)建 API

怎么創(chuàng)建一個(gè) task,在 sys 核心庫中提供了兩個(gè) api:

sys.taskInit(task_func, ...)

sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)

這兩個(gè) api 分別創(chuàng)建兩種 task,首先我們給這兩種 task 起個(gè)名字,一種叫基礎(chǔ) task,一種叫高級(jí) task;

sys.taskInit(task_func, ...)創(chuàng)建的 task 是基礎(chǔ) task;

sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)創(chuàng)建的 task 是高級(jí) task;

從設(shè)計(jì)原理的角度來看,基礎(chǔ) task 和高級(jí) task 的區(qū)別是:

(1) 所有的基礎(chǔ) task 共享一個(gè)全局消息隊(duì)列;

(2) 每個(gè)高級(jí) task 都有自己獨(dú)立的消息隊(duì)列,同時(shí)又能使用全局消息隊(duì)列;

從用戶使用的角度來看,基礎(chǔ) task 和高級(jí) task 的區(qū)別是:

(1) 基礎(chǔ) task 如果阻塞功能使用不當(dāng),可能會(huì)丟失自己應(yīng)該處理的消息;

(2) 高級(jí) task 如果阻塞功能使用不當(dāng),不會(huì)丟失自己應(yīng)該處理的消息;

雖然從設(shè)計(jì)原理來看,高級(jí) task 比基礎(chǔ) task 使用起來不容易犯錯(cuò);

但是由于基礎(chǔ) task 使用起來簡(jiǎn)潔,基礎(chǔ) task 還是需要掌握,一旦掌握之后,也不容易犯錯(cuò);

接下來我們先看下這兩個(gè) api 的說明,結(jié)合說明再運(yùn)行一些實(shí)際的例子,來理解如何創(chuàng)建 task;

sys.taskInit(task_func, ...)

功能:創(chuàng)建并且啟動(dòng)運(yùn)行一個(gè)基礎(chǔ) task;這里表述的是基礎(chǔ) task,既然有基礎(chǔ) task,肯定還會(huì)有另外一種 task,在這里我們先不展開說明,后續(xù)的小節(jié)內(nèi)容會(huì)講到。

注意事項(xiàng):

1、可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);關(guān)于這一點(diǎn),我們?cè)谶@里先不展開講,等到下一小節(jié)再展開;

2、在 LuatOS 中,對(duì)創(chuàng)建的 task 數(shù)量沒有特別限制,只要 ram 夠用,可以一直創(chuàng)建;不同的硬件,用戶可用 RAM 資源也不同,例如:

(1) Air8000 系列的用戶可用 RAM 為 4MB

(2) Air780 系列中 Air780EPM 用戶可用 RAM 為 1MB,Air780EHM/EHV/EGH 用戶可用 RAM 為 4MB

(3) Air8101 系列的用戶可用 RAM 為 2MB

(4) PC 模擬器用戶可用 RAM 為 2MB

下面這個(gè)例子用來說明如何查看用戶可用 ram 信息;

我們首先分析下這段代碼的業(yè)務(wù)邏輯

wKgZPGl4cZeAPrVVAATDJqVyxbw569.png

我們?cè)谀M器上實(shí)際運(yùn)行一下看看,輸入命令luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運(yùn)行日志如下

wKgZPGl4cfyAaDxQAAMW3Z5KfI4057.png

通過上圖的這份日志,可以看出,Lua虛擬機(jī)中的用戶可用ram信息:

- 第一個(gè)數(shù)值為分配給Lua虛擬機(jī)的總ram為2097144字節(jié),將近2MB字節(jié);這個(gè)值在運(yùn)行過程中一直保持不變;

- 第二個(gè)數(shù)值為L(zhǎng)uatOS應(yīng)用程序運(yùn)行過程中實(shí)時(shí)使用的ram,根據(jù)業(yè)務(wù)邏輯會(huì)發(fā)生動(dòng)態(tài)變化,我們演示的這個(gè)demo業(yè)務(wù)邏輯比較簡(jiǎn)單,所有變化幅度比較小,一直在35KB左右波動(dòng);如果業(yè)務(wù)邏輯復(fù)雜,波動(dòng)幅度就會(huì)比較大;

- 第三個(gè)數(shù)值為L(zhǎng)uatOS應(yīng)用程序運(yùn)行過程中歷史最高使用的ram,我們演示的這個(gè)demo中,為37040字節(jié),即歷史最高水位; 平時(shí)開發(fā)過程中,大家可以加上這段代碼,實(shí)時(shí)觀察下第二個(gè)數(shù)值和第三個(gè)數(shù)值,如果這兩個(gè)數(shù)值頻繁的接近第一個(gè)數(shù)值,那就說明你的程序占用的內(nèi)存就比較多了,此時(shí)就需要重點(diǎn)分析解決問題,否則很容易就會(huì)造成內(nèi)存不足而導(dǎo)致重啟;(一般來說,不會(huì)出現(xiàn)這樣的問題;關(guān)于如何分析解決內(nèi)存使用接近上限的問題,后續(xù)會(huì)有文章專門來講,今天在這里就不說了)

知道了怎么查看用戶可用ram信息后,我們?cè)賮砜匆粋€(gè)問題,每創(chuàng)建一個(gè)基礎(chǔ)task需要占用多少ram資源? 下面這個(gè)例子用來說明如何分析一個(gè)基礎(chǔ)task占用的ram資源; 這個(gè)例子的核心代碼片段如下,我們首先分析下這段代碼的業(yè)務(wù)邏輯

wKgZPGl4cvWAL0RKAAWmZyTokIY562.png

在模擬器上運(yùn)行下面一段代碼實(shí)際看下日志

wKgZO2l4czCAHSQUAAEKULVgH4E457.png

從日志可以看出: led task創(chuàng)建前,Lua ram使用35296字節(jié),led task創(chuàng)建后,Lua ram使用36112字節(jié),led這個(gè)task創(chuàng)建并且運(yùn)行需要816字節(jié)的ram; 因?yàn)閘ed task的邏輯很簡(jiǎn)單,所以占用的ram較小,如果創(chuàng)建并且運(yùn)行一個(gè)邏輯復(fù)雜的task,占用的ram就會(huì)變大; 因?yàn)閠ask的ram消耗主要包括兩部分: - 一部分是創(chuàng)建開銷,這個(gè)相對(duì)較小,每個(gè)task的創(chuàng)建開銷基本一樣; - 一部分是運(yùn)行開銷,這個(gè)和task內(nèi)部創(chuàng)建的所有局部變量、表、字符串等都有關(guān)系,不同task的消耗不一樣,差別就比較大;

剛才這個(gè)例子演示的task很簡(jiǎn)單,創(chuàng)建并且運(yùn)行消耗了816字節(jié)的ram; 大家可能會(huì)有疑問,這個(gè)demo只有這個(gè)task是我自己寫的,僅消耗了816字節(jié)的ram,為什么日志中打印的總消耗ram是36112字節(jié)呢?剩余的35KB左右的ram被誰用去了? Lua虛擬機(jī)初始化運(yùn)行時(shí),會(huì)創(chuàng)建Lua狀態(tài)機(jī),加載標(biāo)準(zhǔn)庫/核心庫中的函數(shù)名和常量,加載運(yùn)行main.lua以及main.lua中require的其余l(xiāng)ua文件,這些都需要消耗ram,可以說,這些都是LuatOS應(yīng)用程序運(yùn)行的基本開銷,不同的LuatOS應(yīng)用項(xiàng)目,這個(gè)基本開銷也不一樣,主要取決于初始化過程中用戶main.lua以及其他應(yīng)用腳本的復(fù)雜度;

了解了用戶可用ram以及每個(gè)基礎(chǔ)task大概占用的ram之后,我們?cè)倩氐皆嫉膯栴}:在LuatOS中,對(duì)創(chuàng)建的task數(shù)量沒有特別限制,只要ram夠用,可以一直創(chuàng)建。 帶著這個(gè)問題,我們?cè)倩氐絼偛诺娜罩窘貓D,以剛才的簡(jiǎn)單demo為例,初始化之后,創(chuàng)建用戶的第一個(gè)task之前,剩余的可用ram是2097144 - 35296 = 2061848字節(jié),假設(shè)創(chuàng)建并且運(yùn)行每個(gè)task需要消耗816字節(jié),則應(yīng)該可以創(chuàng)建 2053120/816 = 2526個(gè)task。

下面這個(gè)例子用來說明可以創(chuàng)建多少個(gè)task; 這個(gè)例子的核心代碼片段如下,我們首先分析下這段代碼的業(yè)務(wù)邏輯

wKgZO2l4c42ABbFuAAKv15QnnfU066.png

在模擬器上實(shí)際運(yùn)行一下看看,輸入命令luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運(yùn)行日志如下

wKgZPGl4c9KAV5r1AAIK39BE2fQ967.png

可以看到,創(chuàng)建了2366個(gè)task之后,就提示memory不足,報(bào)錯(cuò)了; 和計(jì)算的理論值2526個(gè)有一定誤差,這個(gè)在可以接受的范圍內(nèi),一方面是因?yàn)榇嬖趦?nèi)存碎片(上圖的日志還有2097144-2042032=55112字節(jié)的ram無法使用,如果這部分ram可以利用,還能再多創(chuàng)建55112/816=67個(gè)task),另一方面可能還會(huì)存在一些我們沒考慮到的其他一些地方需要消耗比較少的ram; 至此,大家應(yīng)該理解了在LuatOS中,對(duì)創(chuàng)建的task數(shù)量沒有特別限制,只要ram夠用,可以一直創(chuàng)建所表達(dá)的意思。

參數(shù)

task_func

wKgZPGl4dDiAZuVKAAJha8vrujo338.png


下面這個(gè)例子用來說明_task_func 參數(shù)的一種典型的錯(cuò)誤使用方式_;

核心代碼片段如下,我們首先分析下這段代碼的業(yè)務(wù)邏輯

wKgZPGl4dGeAAMF0AAJb-aIT7HA192.png


在模擬器上實(shí)際運(yùn)行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運(yùn)行日志如下

wKgZO2l4dKuAOD1ZAABPcCs3yu4822.png

黃色背景提示出現(xiàn)的錯(cuò)誤是指:創(chuàng)建 task 的時(shí)候,傳入的第一個(gè)參數(shù)期望是 function 類型,但是實(shí)際卻傳入了 nil 類型;

為什么會(huì)出現(xiàn)這個(gè)錯(cuò)誤呢,和 Lua 語言的解析執(zhí)行順序有關(guān);

Lua 解析器是按照從上到下的順序解析執(zhí)行代碼的,首先執(zhí)行第 1 行的sys.taskInit(led_task_func),執(zhí)行到這里發(fā)現(xiàn)led_task_func變量不存在,所以就報(bào)錯(cuò);因?yàn)榇藭r(shí)還沒有執(zhí)行過第 3 行;犯的錯(cuò)誤是:先使用后定義。

怎么解決這個(gè)問題呢?有兩種方式:

一種方式是還是在同一個(gè) lua 文件中,參考以下代碼,調(diào)整一下 led_task_func 函數(shù)定義和使用的順序,先定義,再使用就沒問題

wKgZO2l4dOiAK1ufAADGmUK4J20013.png

另一種方式是以模塊化的方式創(chuàng)建多個(gè) lua 文件,函數(shù)定義和聲明放到一個(gè)單獨(dú)的 lua 庫文件中,函數(shù)使用放到另一個(gè) lua 應(yīng)用文件中,在應(yīng)用文件中,加載庫文件,就可以使用庫文件的函數(shù);這種方式我們?cè)谶@里就不介紹了,后續(xù)有完整的項(xiàng)目 demo,大家可以參考;

這兩種解決方式,本質(zhì)上,采用的都是:先定義后使用的思路。

...

參數(shù)含義:task的處理函數(shù)攜帶的可變參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項(xiàng):是Lua語言的一種語法,表示可變參數(shù),參數(shù)數(shù)量可以是0個(gè),1個(gè),2個(gè),...,多個(gè);

參數(shù)示例:

不傳入,或者bool類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內(nèi)容都行;

下面這個(gè)例子用來說明_可變參數(shù)...的使用方式_;

核心代碼片段如下,我們首先分析下這段代碼的業(yè)務(wù)邏輯

wKgZO2l4eciAcsuaAANyASFg17o347.png


在模擬器上實(shí)際運(yùn)行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運(yùn)行日志如下

wKgZPGl4efiAeV0RAADaEH-tQlo465.png

從日志可以看出,sys.taskInit(led_task_func, "arg1", 3, nil, true, led_task_func) 的可變參數(shù),"arg1", 3, nil, true, led_task_func 按照順序傳遞給了任務(wù)處理函數(shù) led_task_func;

返回值

local task_object = sys.taskInit(task_func, ...)

有一個(gè)返回值 task_object

task_object

含義說明:創(chuàng)建的task對(duì)象;如果為thread類型,表示創(chuàng)建成功;如果為nil類型,表示創(chuàng)建失敗;

數(shù)據(jù)類型:thread或者nil;

取值范圍:無特別限制;

注意事項(xiàng):

雖然這個(gè)函數(shù)有返回值,但是這個(gè)返回值在整個(gè)LuatOS系統(tǒng)中,沒有應(yīng)用場(chǎng)景;

所以不用深入了解這個(gè)返回值的用途;

sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)

功能

創(chuàng)建并且啟動(dòng)運(yùn)行一個(gè)高級(jí) task;剛才我們看過的 api,sys.taskInit 是創(chuàng)建一個(gè)基礎(chǔ) task;在這里,sys.taskInitEx 是創(chuàng)建一個(gè)高級(jí) task,看到這里,我們接觸到了兩種 task:基礎(chǔ) task 和高級(jí) task,等我們把這個(gè) api 看完,再總結(jié)下基礎(chǔ) task 和高級(jí) task 的區(qū)別;

sys.taskInitEx這個(gè) api 和sys.taskInit這個(gè) api 的區(qū)別主要體現(xiàn)在task_name, non_targeted_msg_cbfunc這兩個(gè)參數(shù)上,其余內(nèi)容完全一樣;

所以我們接下來我們重點(diǎn)看一下這兩個(gè)參數(shù),其余內(nèi)容就簡(jiǎn)單的過一遍;

注意事項(xiàng)

1、可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);關(guān)于這一點(diǎn),我們?cè)谶@里先不展開講,等到下一小節(jié)再展開;

2、在 LuatOS 中,對(duì)創(chuàng)建的 task 數(shù)量沒有特別限制,只要 ram 夠用,可以一直創(chuàng)建;這一點(diǎn)在介紹 sys.taskInit 時(shí)已經(jīng)講過,內(nèi)容完全一樣,這里就不重復(fù)介紹了;

參數(shù)

task_func

wKgZPGl4esaAAYFMAANUqNCMKOA650.png

task_name

參數(shù)含義:task的名稱;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項(xiàng):

在一個(gè)的LuatOS項(xiàng)目中,創(chuàng)建的所有高級(jí)task的task_name不能重復(fù);

目前在核心庫中沒有檢查是否重復(fù),需要用戶自行保證;

參數(shù)示例:"LED_TASK"、"GPIO_TASK"等任意自定義的字符串;

task_name 這個(gè)參數(shù),字面意思來看,表示 task 的名稱;實(shí)際上,在 sys 核心庫內(nèi),會(huì)根據(jù)這個(gè) task_name 創(chuàng)建一個(gè)資源表,資源表中有一個(gè)消息隊(duì)列,這個(gè)消息隊(duì)列就可以看做是這個(gè)高級(jí) task 專有的消息隊(duì)列;所以說,當(dāng)一個(gè)高級(jí) task 有了自己的消息隊(duì)列,誰都可以發(fā)送定向消息到這個(gè) task 的消息隊(duì)列中,提高了消息處理的效率;

我們?cè)倏匆幌聞?chuàng)建基礎(chǔ) task 的 api,sys.taskInit(task_func, ...),沒有 task_name,在 sys 核心庫內(nèi),不會(huì)為基礎(chǔ) task 創(chuàng)建獨(dú)立的消息隊(duì)列。雖然如此,但是會(huì)創(chuàng)建一個(gè)全局的消息隊(duì)列,所有的基礎(chǔ) task 和高級(jí) task 都會(huì)共享使用這一個(gè)全局消息隊(duì)列。

現(xiàn)在大家對(duì)基礎(chǔ) task,高級(jí) task,獨(dú)立消息隊(duì)列,全局消息隊(duì)列先有一個(gè)基本的認(rèn)識(shí),后續(xù)我們還會(huì)通過示例以及文字描述來做進(jìn)一步總結(jié)。

non_targeted_msg_cbfunc

wKgZPGl4fIOABbRiAAY9k-d19es344.png

這個(gè)參數(shù)的意思是:當(dāng)一個(gè)高級(jí) task 在讀取自己的獨(dú)立消息隊(duì)列中的某一種消息時(shí),發(fā)現(xiàn)讀出來的消息不是自己期望的消息,則直接把這個(gè)消息丟給_non_targeted_msg_cbfunc_處理。

下面這個(gè)例子用來說明如何使用;

這個(gè)例子的核心代碼片段如下,我們首先分析下這段代碼的業(yè)務(wù)邏輯

wKgZPGl4fMCASSb8AA4VEdyqwlM560.png

我們?cè)谀M器上實(shí)際運(yùn)行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運(yùn)行日志如下:

wKgZPGl4fPiAWjUpAAFvnbuk938166.png


紅色背景的日志為非目標(biāo)消息回調(diào)函數(shù)的運(yùn)行邏輯;

綠色背景的日志為目標(biāo)消息的運(yùn)行邏輯;

從日志可以看出,在高級(jí) task 內(nèi)部使用 sys.waitMsg 等待目標(biāo)消息(消息名為"MQTT_EVENT")時(shí),如果收到了非目標(biāo)消息,都給非目標(biāo)消息回調(diào)函數(shù) mqtt_client_main_cbfunc 去處理了。

參數(shù)含義:task的處理函數(shù)攜帶的可變參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項(xiàng):...是Lua語言的一種語法,表示可變參數(shù),參數(shù)數(shù)量可以是0個(gè),1個(gè),2個(gè),...,多個(gè);

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內(nèi)容都行;

返回值

local task_object = sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)

有一個(gè)返回值 task_object

task_object

含義說明:表示創(chuàng)建的task對(duì)象;如果為thread類型,表示創(chuàng)建成功;如果為nil類型,表示創(chuàng)建失敗;

數(shù)據(jù)類型:thread或者nil;

取值范圍:無特別限制;

注意事項(xiàng):雖然這個(gè)函數(shù)有返回值,但是這個(gè)返回值在整個(gè)LuatOS系統(tǒng)中,沒有應(yīng)用場(chǎng)景,所以不用深入了解這個(gè)返回值的用途;

基礎(chǔ) task 和高級(jí) task 的區(qū)別

現(xiàn)在我們回顧一下剛才講的兩個(gè) api,一個(gè)是創(chuàng)建基礎(chǔ) task,一個(gè)是創(chuàng)建高級(jí) task;

在這里我們對(duì)基礎(chǔ) task 和高級(jí) task 先做一個(gè)簡(jiǎn)單的總結(jié):

task分為基礎(chǔ)task和高級(jí)task兩種;

從設(shè)計(jì)原理的角度來看,基礎(chǔ)task和高級(jí)task的區(qū)別是:

(1) 所有的基礎(chǔ)task共享一個(gè)全局消息隊(duì)列;

(2) 每個(gè)高級(jí)task都有自己獨(dú)立的消息隊(duì)列,同時(shí)又能使用全局消息隊(duì)列;

從用戶使用的角度來看,基礎(chǔ)task和高級(jí)task的區(qū)別是:

(1) 基礎(chǔ)task如果阻塞功能使用不當(dāng),可能會(huì)丟失自己應(yīng)該處理的消息;

(2) 高級(jí)task如果阻塞功能使用不當(dāng),不會(huì)丟失自己應(yīng)該處理的消息;

雖然從設(shè)計(jì)原理來看,高級(jí)task比基礎(chǔ)task使用起來不容易犯錯(cuò);

但是由于基礎(chǔ)task使用起來簡(jiǎn)潔,基礎(chǔ)task還是需要掌握,一旦掌握之后,也不容易犯錯(cuò);

sys核心庫提供的task管理功能有以下幾種:

(1) 基礎(chǔ)task的創(chuàng)建和啟動(dòng)運(yùn)行:sys.taskInit(task_func, ...)

(2) 高級(jí)task的創(chuàng)建和啟動(dòng)運(yùn)行:sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)


在這里,大家對(duì)這兩種 task 的區(qū)別有一個(gè)基本的認(rèn)識(shí)就行,在后續(xù)消息章節(jié),我們還會(huì)進(jìn)一步深入講解;

2.1.3.2 什么時(shí)間創(chuàng)建

理解了怎么創(chuàng)建 task 之后,我們來看下一個(gè)問題;

在什么時(shí)間點(diǎn)創(chuàng)建 task,在代碼的什么位置創(chuàng)建 task?

task 的創(chuàng)建時(shí)間點(diǎn)非常靈活,例如:

1、每個(gè)應(yīng)用功能模塊初始化時(shí),可以創(chuàng)建 task;

2、檢測(cè)到某個(gè)事件發(fā)生時(shí),例如 4G 模組檢測(cè)到插入了一張 sim 卡時(shí),可以創(chuàng)建 task;

3、在一個(gè) task 的處理函數(shù)中運(yùn)行時(shí),可以創(chuàng)建另外一個(gè) task;

4、......等等各種場(chǎng)景

總結(jié)下來只有一句話:只要寫的一段代碼能被執(zhí)行到,在這段代碼中就可以使用 api 創(chuàng)建 task。

那是不是意味著,在項(xiàng)目應(yīng)用軟件代碼的任何位置都能創(chuàng)建 task,也并不是,只有一個(gè)例外;

在 main.lua 中 sys.run()之后不能寫代碼創(chuàng)建 task;

為什么有這個(gè)限制,我們?cè)?1.4 章節(jié),基于 hello_luatos 講解 LuatOS 應(yīng)用軟件的運(yùn)行邏輯時(shí)已經(jīng)講到,我們?cè)俑鶕?jù)下面這張圖以及 hello_luatos 代碼來簡(jiǎn)單的回顧一下:

wKgZO2l4f06ADxONAAJeFHBoR54665.png

2.1.3.3 應(yīng)用腳本代碼運(yùn)行位置

剛才在學(xué)習(xí) sys.taskInit 和 sys.taskInitEx 這兩個(gè) api 時(shí),有以下兩段話:

只要寫的一段代碼能被執(zhí)行到,在這段代碼中就可以使用這兩個(gè) api 創(chuàng)建 task;

回調(diào)函數(shù)是在 task 之外的業(yè)務(wù)邏輯中被執(zhí)行的; 在回調(diào)函數(shù)內(nèi)部無法使用 sys.wait(timeout)、sys.waitUntil(msg, timeout)、sys.waitMsg(task_name, msg, timeout)等必須用在 task 中的函數(shù);

從這兩段話中,引出一個(gè)問題:在 LuatOS 應(yīng)用腳本開發(fā)過程中,我們所編寫的應(yīng)用腳本代碼,存在兩種業(yè)務(wù)運(yùn)行的邏輯環(huán)境:

1、一種是在 task 的任務(wù)處理函數(shù)內(nèi)部的業(yè)務(wù)邏輯環(huán)境中運(yùn)行,我們簡(jiǎn)稱為:在 task 內(nèi)部運(yùn)行;

2、一種是在 task 的任務(wù)處理函數(shù)外部的業(yè)務(wù)邏輯環(huán)境中運(yùn)行,我們簡(jiǎn)稱為:在 task 外部運(yùn)行;

怎么理解這兩種業(yè)務(wù)邏輯運(yùn)行環(huán)境?我們看下面這張圖

看右邊生長(zhǎng)出分支的這棵大樹,這棵大樹就是 FreeRTOS 創(chuàng)建的 Lua 虛擬機(jī) task,是一個(gè) FreeRTOS task;

在這個(gè) Lua 虛擬機(jī) FreeRTOS task 上,這棵大樹再分為兩部分:

1、樹干部分:樹干部分運(yùn)行的業(yè)務(wù)邏輯環(huán)境就是 LuatOS task 外部運(yùn)行環(huán)境;

2、樹枝部分:每個(gè)樹枝都是一個(gè)獨(dú)立的 LuatOS task,樹枝部分運(yùn)行的業(yè)務(wù)邏輯環(huán)境就是 LuatOS task 內(nèi)部運(yùn)行環(huán)境;

在這里,大家只要知道有 task 內(nèi)部運(yùn)行和 task 外部運(yùn)行兩種環(huán)境即可,后續(xù)我們講解其他功能時(shí),會(huì)用到這兩種概念,并且也會(huì)結(jié)合示例來說明二者的差別;

wKgZO2l4f8-AAF4MAALlCDYTNzg628.png

2.1.4 狀態(tài)

task 被創(chuàng)建之后,就會(huì)按照代碼中設(shè)計(jì)的業(yè)務(wù)邏輯來來運(yùn)行,task 的狀態(tài)有三種:

運(yùn)行狀態(tài),阻塞狀態(tài),死亡狀態(tài);

我們先來看下一個(gè) task 的狀態(tài)機(jī)

wKgZO2l4gAGAHEeFAAFEO_T2PvE958.png

1、調(diào)用 sys.taskInit 或者 sys.taskInitEx 兩個(gè) api 后,創(chuàng)建的 task 就會(huì)進(jìn)入運(yùn)行狀態(tài);

2、當(dāng)發(fā)生以下兩種事件中的任意一種,運(yùn)行狀態(tài)切換為死亡狀態(tài),一旦進(jìn)入死亡狀態(tài),這個(gè) task 也就結(jié)束了

(1) task 的任務(wù)處理函數(shù),按照正常的業(yè)務(wù)邏輯已經(jīng)運(yùn)行結(jié)束,正常退出函數(shù);也就是狀態(tài)機(jī)中的 normal exit

(2) task 的任務(wù)處理函數(shù),在運(yùn)行過程中,出現(xiàn)了異常,例如對(duì)非 number 類型的變量進(jìn)行數(shù)據(jù)運(yùn)算,把一個(gè) number 類型當(dāng)做 table 來用等等等等,一旦出現(xiàn)類似的異常, 任務(wù)處理函數(shù)就會(huì)異常退出;也就是狀態(tài)機(jī)中的 abnormal exit

3、當(dāng)發(fā)生以下三種事件中的任意一種,運(yùn)行狀態(tài)切換為阻塞狀態(tài),一旦進(jìn)入阻塞狀態(tài),就是 task 任務(wù)處理函數(shù)中的代碼把自己給掛起了

(1) 任務(wù)處理函數(shù)中執(zhí)行到 sys.wait(timeout),表示在這里阻塞等待 timeout 毫秒的時(shí)間;

(2) 任務(wù)處理函數(shù)中執(zhí)行到 sys.waitUntil(msg, timeout),表示在這里阻塞等待全局的 msg 消息,或者阻塞等待 timeout 毫秒的時(shí)間;

(3) 任務(wù)處理函數(shù)中執(zhí)行到 sys.waitMsg(task_name, msg, timeout),表示在這里阻塞等待定向發(fā)給 task_name 的 msg 消息,或者阻塞等待 timeout 毫秒的時(shí)間;

4、當(dāng)發(fā)生以下三種事件中的任意一種,阻塞狀態(tài)切換為運(yùn)行狀態(tài)

(1) 任務(wù)處理函數(shù)中執(zhí)行到 sys.wait(timeout),sys.waitUntil(msg, timeout),sys.waitMsg(task_name, msg, timeout)創(chuàng)建的定時(shí)器超時(shí)時(shí)間到達(dá),此時(shí)會(huì)產(chǎn)生一個(gè)定時(shí)器到達(dá)事件

(2) 任意代碼處執(zhí)行 sys.publish(msg, ...),發(fā)布了全局 msg 消息,在等待這個(gè)全局 msg 消息的所有處于阻塞狀態(tài)的 task 都能依次接收到此消息,并且從阻塞狀態(tài)切換為運(yùn)行狀態(tài)繼續(xù)運(yùn)行

(3) 任意代碼處執(zhí)行 sys.sendMsg(task_name, msg, arg2, arg3, arg4),向名稱為 task_name 的高級(jí) task 發(fā)送了定向 msg 消息,在等待這個(gè)定向 msg 消息的并且處于阻塞狀態(tài)的 task 可以接收到此消息,并且從阻塞狀態(tài)切換為運(yùn)行狀態(tài)繼續(xù)運(yùn)行

通過以上的 task 狀態(tài)機(jī)介紹,我們可以看出,運(yùn)行狀態(tài)和阻塞狀態(tài)之間的切換最為復(fù)雜;

這兩種狀態(tài)之間的切換,用到定時(shí)器和消息兩種事件,接下來我們來重點(diǎn)看下消息和定時(shí)器兩個(gè)概念。

2.2 LuatOS的消息(message)

2.2.1 基本概念

LuatOS 的消息機(jī)制是LuatOS task協(xié)作和事件驅(qū)動(dòng)編程的核心部分,消息機(jī)制包括消息的訂閱、發(fā)布/發(fā)送、接收處理;

消息需要存儲(chǔ)到消息隊(duì)列中,消息隊(duì)列中的消息遵循先進(jìn)先出(FIFO)原則;

根據(jù)消息隊(duì)列的創(chuàng)建位置,把LuatOS 的消息隊(duì)列分為兩種: 內(nèi)核消息隊(duì)列和應(yīng)用消息隊(duì)列;

內(nèi)核消息隊(duì)列是內(nèi)核固件中創(chuàng)建的,應(yīng)用消息隊(duì)列是擴(kuò)展庫腳本和項(xiàng)目應(yīng)用腳本中創(chuàng)建的;

wKgZO2l4gPuAUHRwAAJY7QiAPZ4457.png

根據(jù)消息存儲(chǔ)使用的消息隊(duì)列類型以及消息發(fā)送方的不同,對(duì)消息做以下劃分:

wKgZO2l4gTuALjtJAAUMA0xy3IU558.png

2.2.2 內(nèi)核消息

看下面這張圖,黃色背景的就是內(nèi)核消息隊(duì)列;

FreeRTOS創(chuàng)建的每一個(gè)task都有一個(gè)消息隊(duì)列,這種消息隊(duì)列就叫內(nèi)核消息隊(duì)列;

內(nèi)核消息隊(duì)列是FreeRTOS直接管理的,存儲(chǔ)每個(gè)FreeRTOS task的內(nèi)核消息的隊(duì)列;

內(nèi)核消息的發(fā)送,由內(nèi)核固件自行處理,不開放給LuatOS用戶使用;

wKgZO2l4gXiAOU0WAALpcxok11M910.pngwKgZPGl4gaCAXdvyAAH6RaZfFB4517.png

從內(nèi)核消息的接收處理方式來劃分,內(nèi)核消息可以分為定時(shí)器消息和非定時(shí)器消息兩大類

2.2.2.1 非定時(shí)器消息

在LuatOS的應(yīng)用腳本程序中,針對(duì)某一種消息或者某一類消息,注冊(cè)回調(diào)函數(shù),當(dāng)內(nèi)核固件中的驅(qū)動(dòng)程序或者FreeRTOS的task給FreeRTOS的Lua虛擬機(jī)task發(fā)送消息后,在sys.run()調(diào)度器中會(huì)讀取內(nèi)核消息隊(duì)列中的消息,讀到消息后,直接執(zhí)行注冊(cè)的回調(diào)函數(shù);在這里我舉兩個(gè)例子,同時(shí)結(jié)合上面的兩張圖說明一下這個(gè)過程:

第一個(gè)例子是串口接收數(shù)據(jù)處理,核心代碼如下:

wKgZPGl4geKAF5jCAAII-mw9N6A896.png

第二個(gè)例子是mqtt客戶端異步消息處理,核心代碼如下:

wKgZPGl4gg6AA019AAN7UyfvDJw237.png

除了上面列舉的這兩種非定時(shí)器消息的uart,mqtt內(nèi)核消息處理之外,LuatOS應(yīng)用腳本開發(fā)中,還會(huì)用到很多種類似的內(nèi)核消息處理邏輯,例如socket異步消息處理,http異步消息處理等等,這些內(nèi)核消息的處理邏輯都是完全一樣,只要注冊(cè)一個(gè)回調(diào)處理數(shù)據(jù)即可,后續(xù)我們講解具體的功能模塊時(shí),會(huì)詳細(xì)講解;

2.2.2.2 定時(shí)器消息

第二類內(nèi)核消息是定時(shí)器消息,和第一類的內(nèi)核消息相比,定時(shí)器消息不僅僅可以通過注冊(cè)回調(diào)函數(shù)來處理,還能控制task的阻塞和運(yùn)行,所以定時(shí)器消息功能更加強(qiáng)大,使用起來更加靈活,定時(shí)器內(nèi)容在后續(xù)章節(jié)會(huì)重點(diǎn)講解,在這里就暫時(shí)跳過了;

2.2.3 應(yīng)用消息

應(yīng)用消息隊(duì)列由Lua虛擬機(jī)內(nèi)部管理,應(yīng)用消息隊(duì)列中的消息訂閱,發(fā)布/發(fā)送,和接收處理,LuatOS用戶可以直接控制;

應(yīng)用消息隊(duì)列中的消息,和LuatOS應(yīng)用腳本程序的關(guān)系最為密切,本章節(jié),我們將重點(diǎn)學(xué)習(xí)應(yīng)用消息隊(duì)列中的消息如何使用;

應(yīng)用消息隊(duì)列又可以進(jìn)一步劃分為全局消息隊(duì)列和定向消息隊(duì)列:

全局消息隊(duì)列中存儲(chǔ)的全局消息有系統(tǒng)全局消息用戶全局消息

定向消息隊(duì)列中存儲(chǔ)的定向消息有系統(tǒng)定向消息用戶定向消息

2.2.3.1 系統(tǒng)全局消息

系統(tǒng)全局消息是內(nèi)核固件的C代碼中調(diào)用sys.publish接口發(fā)布的消息,例如"IP_READY";

LuatOS用戶在腳本程序中可以使用sys.subscribe和sys.waitUntil訂閱和接收處理;

這部分消息牽涉到的功能模塊比較多,,有個(gè)簡(jiǎn)單的認(rèn)識(shí)就行,這些系統(tǒng)全局消息,在后續(xù)的LuatOS課程中,每講到一個(gè)主題,都會(huì)講解對(duì)應(yīng)的系統(tǒng)全局消息;

在本章節(jié)就不細(xì)講了。

2.2.3.2 系統(tǒng)定向消息

系統(tǒng)定向消息是內(nèi)核固件的C代碼中調(diào)用sys.sendMsg接口發(fā)布的消息,例如socket.EVENT;

LuatOS用戶在腳本程序中可以使用sys.waitMsg接收處理;

但是由于這部分內(nèi)容和內(nèi)核固件的耦合比較大,一般來說都是LuatOS的擴(kuò)展庫去使用,目前只有在socket功能有關(guān)的擴(kuò)展庫中使用過系統(tǒng)定向消息,用戶編寫的Lua應(yīng)用腳本程序中很少用到,所以在這里也不細(xì)講了;

在后續(xù)的LuatOS課程中,每講到一個(gè)主題,如果有對(duì)應(yīng)的系統(tǒng)定向消息,我們?cè)僦鹨恢v解;

接下來我們重點(diǎn)看下用戶全局消息和用戶定向消息:

從消息接收處理方的角度來劃分,消息可以分為全局消息和定向消息;

全局消息是指消息可以被任意訂閱方接收處理;

定向消息是指消息只能被指定的task接收處理;

sys核心庫提供的全局消息管理功能有以下幾種:

(1) 全局消息發(fā)布:sys.publish(msg, ...)

(2) 全局消息訂閱:sys.subscribe(msg, msg_cbfunc)

(3) 全局消息取消訂閱:sys.unsubscribe(msg, msg_cbfunc)

(4) 全局消息阻塞等待(只能在task中使用):sys.waitUntil(msg, timeout)

sys核心庫提供的定向消息管理功能有以下幾種:

(1) 定向消息發(fā)布:sys.sendMsg(task_name, msg, arg2, arg3, arg4)

(2) 定向消息阻塞等待(只能在task中使用):sys.waitMsg(task_name, msg, timeout)

(3) 定向消息清除:sys.cleanMsg(task_name)


2.2.3.3 用戶全局消息

用戶全局消息處理的完整周期

用戶全局消息處理的完整周期包括以下幾部分:

wKgZO2l4g5-ATsOqAAJ-7JWDRTM032.pngwKgZPGl4g7OAKFbcAAH7Mo2bLQU890.png

1、消息訂閱:有兩個(gè)api可以訂閱全局消息

sys.subscribe(msg, cbfunc):為全局消息msg訂閱一個(gè)回調(diào)函數(shù)cbfunc;相當(dāng)于在全局消息訂閱表中增加一項(xiàng)[msg] = {cbfunc = true}

sys.waitUntil(msg, timeout):為全局消息msg訂閱一個(gè)task,這個(gè)api只能在task任務(wù)處理函數(shù)的業(yè)務(wù)邏輯中使用;相當(dāng)于在全局消息訂閱表中增加一項(xiàng)[msg] = {task = true}

2、消息發(fā)布:有一個(gè)api可以發(fā)布全局消息

sys.publish(msg, ...):發(fā)布一條全局消息;相當(dāng)于在全局消息隊(duì)列中的隊(duì)尾位置增加一項(xiàng){msg, ...}

3、消息調(diào)度處理:有一個(gè)api對(duì)全局消息進(jìn)行調(diào)度處理

sys.run():讀取全局消息并且分發(fā)給訂閱者去處理;

從全局消息隊(duì)列中取出第一條消息{msg1, ...}

根據(jù)消息名msg1,到全局消息訂閱表中找msg1的訂閱者

如果訂閱者是回調(diào)函數(shù)bfunc1,則執(zhí)行cbfunc1,執(zhí)行完之后,并不會(huì)自動(dòng)從全局消息訂閱表中刪除msg1對(duì)應(yīng)的cbfunc1 = true標(biāo)記,只有腳本程序中主動(dòng)調(diào)用sys.unsubscribe(msg1, cbfunc1),才會(huì)刪除這個(gè)標(biāo)記;

如果訂閱者是一個(gè)task1,task1處于阻塞狀態(tài),則讓task1退出阻塞狀態(tài),繼續(xù)運(yùn)行;然后自動(dòng)從全局消息訂閱表中刪除msg1對(duì)應(yīng)的task1 = true標(biāo)記

在全局消息表中處理完msg1的所有訂閱者;

從全局消息隊(duì)列中刪除{msg1, ...}這條消息

4、刪除訂閱者:訂閱者有兩種,刪除的方式各不相同

回調(diào)函數(shù)類型的訂閱者,在消息調(diào)度處理過程中,不會(huì)自動(dòng)刪除;只有腳本程序中主動(dòng)調(diào)用sys.unsubscribe(msg, cbfunc),才會(huì)刪除

task類型的訂閱者,在消息調(diào)度處理過程中,處理完之后,會(huì)自動(dòng)刪除;

5、刪除消息:sys.run()消息調(diào)度處理過程中,自動(dòng)刪除;

有一個(gè)重要注意事項(xiàng):

全局消息必須先訂閱,然后再發(fā)布,這樣才能保證發(fā)布的消息可以被訂閱者處理;怎么理解呢?我們?cè)倏聪律厦孢@張圖,在sys.sun()調(diào)度時(shí),首先從全局消息隊(duì)列中取出隊(duì)首的一條消息,然后從全局消息訂閱表中找到這條消息對(duì)應(yīng)的所有訂閱者進(jìn)行處理,處理完之后,就刪除了這條消息,所以此時(shí)如果訂閱者還沒有準(zhǔn)備好,則無法處理消息;

了解了用戶全局消息的完整生命周期后,接下來我們:

1、先看下在整個(gè)生命周期中用到的幾個(gè)sys核心庫的api

2、寫一個(gè)完整的demo示例來實(shí)際運(yùn)行演示一下如何使用;

sys.publish(msg, ...)

功能

發(fā)布一個(gè)全局消息;

注意事項(xiàng)

可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);

sys.publish(msg, ...)是全局消息的生產(chǎn)者,全局消息有生產(chǎn)就會(huì)有消費(fèi),不然消息就沒有存在的意義了;

有兩個(gè)接口可以注冊(cè)全局消息的消費(fèi)者:

1、一個(gè)是sys.subscribe(msg, msg_cbfunc)中注冊(cè)的msg_cbfunc消息回調(diào)函數(shù);

2、一個(gè)是sys.waitUntil(msg, timeout)所在的task;

所以全局消息的生產(chǎn)者和消費(fèi)者的使用組合,有以下兩種:

1、sys.publish(msg, ...) 和 sys.subscribe(msg, msg_cbfunc)

在sys.publish(msg, ...)之前,必須使用sys.subscribe(msg, msg_cbfunc)注冊(cè)消息回調(diào)函數(shù);

這樣才能保證發(fā)布的msg消息可以被msg_cbfunc消息回調(diào)函數(shù)處理;

2、sys.publish(msg, ...) 和 sys.waitUntil(msg, timeout)

在sys.publish(msg, ...)之前,必須保證task正在sys.waitUntil(msg, timeout)代碼處,處于阻塞等待狀態(tài);

這樣才能保證發(fā)布的msg消息可以被task處理;

參數(shù)

msg

參數(shù)含義:消息的名稱;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;

...

參數(shù)含義:消息攜帶的可變參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項(xiàng):...是Lua語言的一種語法,表示可變參數(shù),參數(shù)數(shù)量可以是0個(gè),1個(gè),2個(gè),...,多個(gè);

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內(nèi)容都行;

返回值

nil

示例

wKgZO2l4hMiAPfgoAABrk40xdbw489.png

sys.subscribe(msg, msg_cbfunc)

功能

訂閱一個(gè)全局消息的回調(diào)函數(shù);

注意事項(xiàng)

可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);

sys.publish(msg, ...) 和 sys.subscribe(msg, msg_cbfunc)配合使用時(shí):

在sys.publish(msg, ...)之前,必須使用sys.subscribe(msg, msg_cbfunc)注冊(cè)消息回調(diào)函數(shù);

這樣才能保證發(fā)布的msg消息可以被msg_cbfunc消息回調(diào)函數(shù)處理;

同一個(gè)全局消息msg,可以多次調(diào)用sys.subscribe(msg, msg_cbfunc)訂閱多個(gè)不同的回調(diào)函數(shù);

參數(shù)

msg

參數(shù)含義:全局消息的名稱,和sys.publish(msg, ...)中的msg保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;


msg_cbfunc

wKgZO2l4hb-AFspFAAVJJsZGhwY248.png

返回值

nil

示例

wKgZO2l4hquAZPMYAAHND7TnlkU437.png

sys.unsubscribe(msg, msg_cbfunc)

功能

取消訂閱一個(gè)全局消息的回調(diào)函數(shù);

注意事項(xiàng)

可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);

參數(shù)

msg

參數(shù)含義:全局消息的名稱,和sys.subscribe(msg, msg_cbfunc)中的msg保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;

msg_cbfunc

wKgZO2l4h1yADlm0AATpNo_PMoQ837.png

返回值

nil

示例

wKgZPGl4h5CAeIG2AAJub8Wlxk8864.png

sys.waitUntil(msg, timeout)

功能

在task中阻塞等待一個(gè)全局消息;

注意事項(xiàng)

只能在基礎(chǔ)task和高級(jí)task處理函數(shù)的業(yè)務(wù)邏輯中使用此函數(shù);

sys.publish(msg, ...) 和 sys.waitUntil(msg, timeout)配合使用時(shí):

在sys.publish(msg, ...)之前,必須保證task正在sys.waitUntil(msg, timeout)代碼處,處于阻塞等待狀態(tài);

這樣才能保證發(fā)布的msg消息可以被task處理;

同一個(gè)全局消息msg,可以被多個(gè)正在sys.waitUntil(msg, timeout)代碼處阻塞等待的task處理;

參數(shù)

msg


參數(shù)含義:全局消息的名稱,和sys.publish(msg, ...)中的msg保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;


timeout

參數(shù)含義:阻塞等待全局消息msg的超時(shí)時(shí)長(zhǎng),單位毫秒;

數(shù)據(jù)類型:number或者nil;

取值范圍:

大于等于1,小于等于0x7FFFFFFF,之間的所有正整數(shù);最大時(shí)長(zhǎng)0x7FFFFFFF毫秒 ≈ 596小時(shí) ≈ 24.85天;

如果為nil,表示一直阻塞等待全局消息,不會(huì)超時(shí);

是否必選:可選傳入此參數(shù);

注意事項(xiàng):

此處的超時(shí)機(jī)制基于軟件定時(shí)器實(shí)現(xiàn),受系統(tǒng)負(fù)載、任務(wù)數(shù)量、消息堆積、網(wǎng)絡(luò)中斷優(yōu)先級(jí)最高等多因素影響,無法實(shí)現(xiàn)高精度

尤其是幾個(gè)毫秒級(jí)別,幾十毫秒級(jí)別,幾百毫秒級(jí)別的超時(shí)時(shí)長(zhǎng),誤差都比較大,秒級(jí)別以上的超時(shí)時(shí)長(zhǎng)誤差較小;

例如:設(shè)置的1毫秒,可能要等幾十毫秒;設(shè)置的幾十毫秒,可能要等上百毫秒;設(shè)置的幾百毫秒,可能要等幾百幾十毫秒;

可以簡(jiǎn)單的認(rèn)為會(huì)延遲幾十毫秒左右,以此來評(píng)估超時(shí)時(shí)長(zhǎng)精度是否可以滿足業(yè)務(wù)需求;

調(diào)用本接口時(shí),如果傳入了timeout參數(shù),則sys核心庫內(nèi)部會(huì)自動(dòng)創(chuàng)建并且運(yùn)行一個(gè)軟件定時(shí)器,超時(shí)時(shí)長(zhǎng)到達(dá)后,會(huì)自動(dòng)刪除這個(gè)定時(shí)器;

LuatOS應(yīng)用程序中可用的軟件定時(shí)器總數(shù)量為64個(gè);

注意控制自已應(yīng)用程序中的同時(shí)運(yùn)行的軟件定時(shí)器總數(shù)量不要超過64個(gè),否則創(chuàng)建新的定時(shí)器會(huì)返回失敗;

參數(shù)示例:50、1000、60000等;


返回值

local result, arg1, arg2, arg3, argN = sys.waitUntil(msg, timeout)

有數(shù)量不固定的返回值:

第一個(gè)返回值為result

剩余的返回值arg1, arg2, arg3, argN,表示可變數(shù)量的返回值,只有當(dāng)?shù)谝粋€(gè)返回值result為true時(shí),這些可變數(shù)量的返回值才有意義,和sys.publish(msg, ...)中...表示的可變參數(shù)一一對(duì)應(yīng)

result

含義說明:阻塞等待的結(jié)果;true表示收到了msg消息,false表示超時(shí)沒有收到msg消息;

數(shù)據(jù)類型:boolean;

取值范圍:true或者false;

注意事項(xiàng):暫無;


arg1, arg2, arg3, argN

含義說明:

當(dāng)result為true時(shí),arg1, arg2, arg3, argN表示sys.publish(msg, ...)中...可變參數(shù),從前到后一一對(duì)應(yīng);

當(dāng)result為false時(shí),arg1, arg2, arg3, argN全部都為nil,沒有任何意義;

數(shù)據(jù)類型:

當(dāng)result為true時(shí),arg1, arg2, arg3, argN的數(shù)據(jù)類型和sys.publish(msg, ...)中...可變參數(shù)的數(shù)據(jù)類型,從前到后一一對(duì)應(yīng);

當(dāng)result為false時(shí),arg1, arg2, arg3, argN全部都為nil類型;

取值范圍:無特別限制;

注意事項(xiàng):暫無;


正確示例

wKgZPGl4iHiAUZNrAAK5Yh0Kg0A008.png

錯(cuò)誤示例

wKgZO2l4iOeAcazJAAU1eaQ4BHQ479.png


用戶全局消息處理的代碼示例

在了解了全局消息的幾個(gè)api之后,我們?cè)倏聪聢D回顧一下全局消息處理的完整周期

wKgZPGl4iSqAPBeEAAJVyg56vnM642.png

下面這個(gè)例子用來說明用戶全局消息的完整處理過程;

這個(gè)例子的完整代碼鏈接:global_msg_receiver1.luaglobal_msg_receiver2.luaglobal_msg_sender.lua

因?yàn)槭侨齻€(gè)文件,并且代碼比較多,所以在這里我就不粘貼代碼到這里了,直接打開vscode來分析代碼的業(yè)務(wù)邏輯;

我們?cè)谀M器上實(shí)際運(yùn)行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運(yùn)行15秒鐘的日志如下,為了方便分析,我把日志做了處理,每個(gè)訂閱者訂閱同一類型的消息日志匯總到了一起:

init_subscribe_cbfunc訂閱者

init_subscribe_cbfunc訂閱的task內(nèi)部發(fā)布的消息,運(yùn)行正常,符合預(yù)期,每秒接收到一次task內(nèi)部發(fā)布的消息;

因?yàn)?0秒鐘之后,取消了訂閱,所以只收到前10條消息

wKgZPGl4icmAG1aKAAE8geQZICQ141.png

init_subscribe_cbfunc訂閱的timer發(fā)布的消息,運(yùn)行正常,符合預(yù)期,每秒接收到一次task內(nèi)部發(fā)布的消息;

因?yàn)?0秒鐘之后,取消了訂閱,所以只收到前10條消息

wKgZPGl4ie-AEa4aAAEo3E25Wg4744.png

delay_subscribe_cbfunc訂閱者

delay_subscribe_cbfunc訂閱的task內(nèi)部發(fā)布的消息,我們可以看到是從第6條消息開始接收處理,前面的5條丟失了;這是因?yàn)閟ys.timerStart(sys.subscribe, 5000, "SEND_DATA_REQ", delay_subscribe_cbfunc)這行代碼,是開機(jī)之后延遲5秒,然后才訂閱了"SEND_DATA_REQ"回調(diào)函數(shù)的delay_subscribe_cbfunc,所以前5秒的消息都無法接收處理;這段運(yùn)行日志就可以驗(yàn)證本文前面描述的這段話:

在sys.publish(msg, ...)之前,必須使用sys.subscribe(msg, msg_cbfunc)注冊(cè)消息回調(diào)函數(shù);這樣才能保證發(fā)布的msg消息可以被msg_cbfunc消息回調(diào)函數(shù)處理;再簡(jiǎn)單點(diǎn)來說,訂閱要在發(fā)布前,就像郵局定報(bào)紙一樣,你只有了訂閱服務(wù),在郵局發(fā)報(bào)紙的時(shí)候才不會(huì)把你漏掉;

wKgZO2l4ipGAXazVAACxUGa3Lvo444.png

同理,delay_subscribe_cbfunc訂閱的timer內(nèi)部發(fā)布的消息,表現(xiàn)也是一樣

wKgZO2l4iraAWXbUAACveS1UH0c482.png

task(success_wait_until_base_task_func)訂閱者

task任務(wù)處理函數(shù)success_wait_until_base_task_func中通過sys.waitUntil("SEND_DATA_REQ")訂閱的task內(nèi)部發(fā)布的消息,運(yùn)行正常,符合預(yù)期,每秒接收到一次task內(nèi)部發(fā)布的消息,一共收到15條消息;

wKgZO2l4ivWAQ928AAIzlFNL-6I915.png

task任務(wù)處理函數(shù)success_wait_until_base_task_func中通過sys.waitUntil("SEND_DATA_REQ")訂閱的timer發(fā)布的消息,運(yùn)行正常,符合預(yù)期,每秒接收到一次task內(nèi)部發(fā)布的消息,一共收到15條消息;

wKgZPGl4ix-AVRHpAAI--cQerSQ309.png

task(lost_wait_until_base_task_func)訂閱者

task任務(wù)處理函數(shù)lost_wait_until_base_task_func中通過sys.waitUntil("SEND_DATA_REQ")訂閱的task內(nèi)部發(fā)布的消息,運(yùn)行異常,一共收到0條消息;15條消息全部丟失

為什么task內(nèi)部發(fā)布的15條消息,task(lost_wait_until_base_task_func)全部丟失,我們添加一些日志來分析一下,如下兩部分代碼塊的黃色背景為新增的日志打印代碼

wKgZO2l4i0-AbMQGAAUrXu7Kb7E008.png

再來看一下運(yùn)行日志

wKgZPGl4i62AQ-JCAAu8fkhRtlg722.png

在這段日志中:

lost_wait_until_base_task_func before wait 3000,表示接下來lost_wait_until_base_task_func的task阻塞等待3秒鐘,此狀態(tài)下,其他地方發(fā)布的"SEND_DATA_REQ"無法處理,會(huì)丟失;global_sender_msg_task_func這個(gè)task所發(fā)布的消息"SEND_DATA_REQ",都是在lost_wait_until_base_task_func before wait 3000之后發(fā)布的,所以消息全部丟失;

lost_wait_until_base_task_func before wait SEND_DATA_REQ,表示接下來lost_wait_until_base_task_func的task阻塞等待消息"SEND_DATA_REQ",此狀態(tài)下,其他地方發(fā)布的"SEND_DATA_REQ"可以被處理;在lost_wait_until_base_task_func before wait SEND_DATA_REQ之后,都是global_sender_msg_timer_cbfun這個(gè)定時(shí)器回調(diào)函數(shù)在發(fā)布"SEND_DATA_REQ",所以這個(gè)時(shí)間點(diǎn)的"SEND_DATA_REQ"都可以被處理;

task任務(wù)處理函數(shù)lost_wait_until_base_task_func中通過sys.waitUntil("SEND_DATA_REQ")訂閱的定時(shí)器發(fā)布的消息,運(yùn)行異常,一共收到4條消息;11條消息全部丟失

wKgZPGl4i-yAD74nAACcGd_Q74A230.png


這段運(yùn)行日志就可以驗(yàn)證本文前面描述的這段話:

在sys.publish(msg, ...)之前,必須保證task正在sys.waitUntil(msg, timeout)代碼處,處于阻塞等待狀態(tài);

這樣才能保證發(fā)布的msg消息可以被task處理;

我們?cè)倏聪逻@個(gè)訂閱者task的任務(wù)處理函數(shù)代碼,在如下代碼的第4行和第5行,都會(huì)使task阻塞,其中第4行會(huì)阻塞3秒,這3秒內(nèi),無論是task發(fā)布的消息還是timer發(fā)布的消息,都無法接收處理,直接丟失;3秒后,在第5行阻塞等待"SEND_DATA_REQ"消息,等到一條消息后,重新又阻塞3秒,在本demo中的表現(xiàn)就是每隔3秒接收到一次task或者timer發(fā)布的消息;

wKgZPGl4jBuAT2jFAAFyVw-rBgM284.png

如何避免這種問題呢?

在接收消息的task任務(wù)處理函數(shù)業(yè)務(wù)邏輯中,最好只有sys.waitUntil,并且也只有一處sys.waitUntil阻塞當(dāng)前task;上面這個(gè)代碼塊,只要把第4行去掉就沒有問題了;

2.2.3.4 用戶定向消息

用戶定向消息處理的完整周期

簡(jiǎn)化后用戶定向消息處理的完整周期包括以下幾部分:

wKgZO2l4jFaADhZPAAP1TiESXFw913.pngwKgZPGl4jG-AetcJAAH1SJqu7yE038.png

消息發(fā)送:有一個(gè)api可以發(fā)送定向消息

sys.sendMsg(task_name, msg, arg2, arg3, arg4):發(fā)送一條定向消息給task_name;相當(dāng)于在定向消息隊(duì)列中的隊(duì)尾位置增加一項(xiàng){msg, arg2, arg3, arg4}

消息接收:有一個(gè)api可以接收定向消息

sys.waitMsg(task_name, msg, timeout):讀取task_name的一條定向消息,如果定向消息隊(duì)列中有指定的msg消息,則讀出處理;如果有其他非指定的消息,給非目標(biāo)消息回調(diào)函數(shù)處理;如果沒有消息,則阻塞等待

消息調(diào)度處理:有一個(gè)api對(duì)定向消息進(jìn)行調(diào)度處理

如果發(fā)送定向消息后,接收者高級(jí)task處于阻塞狀態(tài),則sys.run()調(diào)度器會(huì)控制處于阻塞狀態(tài)的task,退出阻塞狀態(tài),讀取定向消息進(jìn)行處理;

刪除接收者:在消息調(diào)度處理過程中,sys.waitMsg(task_name, msg, timeout)讀出指定的msg消息,處理完之后,會(huì)自動(dòng)刪除接收者,只有等到下次運(yùn)行到sys.waitMsg(task_name, msg, timeout)時(shí)才會(huì)創(chuàng)建新的接收者;

刪除消息:sys.run()消息調(diào)度處理過程中,sys.waitMsg(task_name, msg, timeout)讀出指定的msg消息,處理完消息之后會(huì)自動(dòng)刪除;

上一小節(jié)我們討論了用戶全局消息,還記得用戶全局消息處理有兩個(gè)注意事項(xiàng):

1、在sys.publish(msg, ...)之前,必須使用sys.subscribe(msg, msg_cbfunc)注冊(cè)消息回調(diào)函數(shù);這樣才能保證發(fā)布的msg消息可以被msg_cbfunc消息回調(diào)函數(shù)處理;

2、在sys.publish(msg, ...)之前,必須保證task正在sys.waitUntil(msg, timeout)代碼處,處于阻塞等待狀態(tài);這樣才能保證發(fā)布的msg消息可以被task處理;

現(xiàn)在我們討論的用戶定向消息已經(jīng)不存在這個(gè)問題,消息發(fā)布和消息處理沒有嚴(yán)格的時(shí)序要求,可以先發(fā)送定向消息,消息會(huì)存儲(chǔ)到定向消息隊(duì)列中,只有當(dāng)調(diào)用sys.waitMsg(task_name, msg, timeout)接口主動(dòng)去讀取定向消息時(shí),才會(huì)從定向消息隊(duì)列中讀出消息進(jìn)行處理,所以不存在消息丟失的問題;但是如果使用不當(dāng),可能會(huì)出現(xiàn)消息處理延遲的問題;

了解了用戶定向消息的完整生命周期后,接下來我們:

1、先看下在整個(gè)生命周期中用到的幾個(gè)sys核心庫的api

2、寫一個(gè)完整的demo示例來實(shí)際運(yùn)行演示一下如何使用;

sys.sendMsg(task_name, msg, arg2, arg3, arg4)

功能

向名稱為task_name的task發(fā)布一個(gè)定向消息;

注意事項(xiàng)

可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);

sys.sendMsg(task_name, msg, arg2, arg3, arg4)是定向消息的生產(chǎn)者,定向消息有生產(chǎn)就會(huì)有消費(fèi),不然消息就沒有存在的意義了;

sys.waitMsg(task_name, msg, timeout)所在的task是定向消息的消費(fèi)者;

sys.sendMsg(task_name, msg, arg2, arg3, arg4) 和 sys.waitMsg(task_name, msg, timeout)配合使用;

在sys.sendMsg(task_name, msg, arg2, arg3, arg4)之前,需要保證名稱為task_name的task已經(jīng)被創(chuàng)建,否則定向消息也會(huì)丟失;

參數(shù)

task_name

參數(shù)含義:task的名稱,和sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)中的task_name保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:"LED_TASK"、"GPIO_TASK"等任意自定義的字符串;


msg

參數(shù)含義:消息的名稱;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;


arg2

參數(shù)含義:msg消息攜帶的第一個(gè)參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內(nèi)容都行;


arg3

參數(shù)含義:msg消息攜帶的第二個(gè)參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內(nèi)容都行;


arg4

參數(shù)含義:msg消息攜帶的第三個(gè)參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內(nèi)容都行;


返回值

local result = sys.sendMsg(task_name, msg, arg2, arg3, arg4)

result

含義說明:定向消息發(fā)布結(jié)果,成功返回true,失敗返回false;

數(shù)據(jù)類型:boolean;

取值范圍:true或者false;

注意事項(xiàng):暫無;


示例

wKgZO2l4j0uAL2euAACOd9SUZ00875.png

sys.waitMsg(task_name, msg, timeout)

功能

在task中阻塞等待名稱為task_name的task的定向消息;

注意事項(xiàng)

只能在高級(jí)task處理函數(shù)的業(yè)務(wù)邏輯中使用此函數(shù);

sys.sendMsg(task_name, msg, arg2, arg3, arg4)是定向消息的生產(chǎn)者,定向消息有生產(chǎn)就會(huì)有消費(fèi),不然消息就沒有存在的意義了;

sys.waitMsg(task_name, msg, timeout)所在的task是定向消息的消費(fèi)者;

sys.sendMsg(task_name, msg, arg2, arg3, arg4) 和 sys.waitMsg(task_name, msg, timeout)配合使用;

在sys.sendMsg(task_name, msg, arg2, arg3, arg4)之前,需要保證名稱為task_name的task已經(jīng)被創(chuàng)建,否則定向消息也會(huì)丟失;

參數(shù)

task_name

參數(shù)含義:task的名稱,和sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)中的task_name保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:"LED_TASK"、"GPIO_TASK"等任意自定義的字符串;


msg

參數(shù)含義:消息的名稱,和sys.sendMsg(task_name, msg, arg2, arg3, arg4)中的msg保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項(xiàng):暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;


timeout

參數(shù)含義:阻塞等待定向消息msg的超時(shí)時(shí)長(zhǎng),單位毫秒,nil表示一直阻塞等待;

數(shù)據(jù)類型:number或者nil;

取值范圍:

大于等于1,小于等于0x7FFFFFFF,之間的所有正整數(shù);最大時(shí)長(zhǎng)0x7FFFFFFF毫秒 ≈ 596小時(shí) ≈ 24.85天;

如果為nil,表示一直阻塞等待全局消息,不會(huì)超時(shí);

是否必選:可選傳入此參數(shù);

注意事項(xiàng):

此處的超時(shí)機(jī)制基于軟件定時(shí)器實(shí)現(xiàn),受系統(tǒng)負(fù)載、任務(wù)數(shù)量、消息堆積、網(wǎng)絡(luò)中斷優(yōu)先級(jí)最高等多因素影響,無法實(shí)現(xiàn)高精度;

尤其是幾個(gè)毫秒級(jí)別,幾十毫秒級(jí)別,幾百毫秒級(jí)別的超時(shí)時(shí)長(zhǎng),誤差都比較大,秒級(jí)別以上的超時(shí)時(shí)長(zhǎng)誤差較小;

例如:設(shè)置的1毫秒,可能要等幾十毫秒;設(shè)置的幾十毫秒,可能要等上百毫秒;設(shè)置的幾百毫秒,可能要等幾百幾十毫秒;

可以簡(jiǎn)單的認(rèn)為會(huì)延遲幾十毫秒左右,以此來評(píng)估超時(shí)時(shí)長(zhǎng)精度是否可以滿足業(yè)務(wù)需求;

調(diào)用本接口時(shí),如果傳入了timeout參數(shù),則sys核心庫內(nèi)部會(huì)自動(dòng)創(chuàng)建并且運(yùn)行一個(gè)軟件定時(shí)器,超時(shí)時(shí)長(zhǎng)到達(dá)后,會(huì)自動(dòng)刪除這個(gè)定時(shí)器;

LuatOS應(yīng)用程序中可用的軟件定時(shí)器總數(shù)量為64個(gè);

注意控制自已應(yīng)用程序中的同時(shí)運(yùn)行的軟件定時(shí)器總數(shù)量不要超過64個(gè),否則創(chuàng)建新的定時(shí)器會(huì)返回失敗;

參數(shù)示例:50、1000、60000等;


返回值

local message = sys.waitMsg(task_name, msg, timeout)

有一個(gè)返回值為message

message

含義說明:

阻塞等待的結(jié)果;table類型表示接收到msg消息,false表示超時(shí)沒有收到msg消息;

當(dāng)接收到msg消息時(shí),message[1],message[2],message[3],message[4]和sys.sendMsg(task_name, msg, arg2, arg3, arg4)中的 msg, arg2, arg3, arg4 一一對(duì)應(yīng);

當(dāng)超時(shí)沒有收到msg消息時(shí),message為false;

數(shù)據(jù)類型:table或者boolean;

取值范圍:無特別限制;

注意事項(xiàng):暫無;


示例

wKgZPGl4kCKAZRHbAAPz6rlPN8w945.png

用戶定向消息處理的代碼示例

在了解了定向消息的兩個(gè)api之后,我們?cè)倏聪聢D回顧一下定向消息處理的完整周期

wKgZO2l4kIaAMrk4AAJPv_zc334783.png

下面這個(gè)例子用來說明用戶定向消息的完整處理過程;

這個(gè)例子的完整代碼鏈接:tgted_msg_receiver.luatargeted_msg_sender.lua

核心代碼片段如下,我們首先分析下這兩段代碼的業(yè)務(wù)邏輯

wKgZPGl4kUGANjYqAAtFCMrTnJ8067.png

我們?cè)谀M器上實(shí)際運(yùn)行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運(yùn)行15秒鐘的日志如下,為了方便分析,我把日志做了處理,每個(gè)消息接收者的日志匯總到了一起:

normal_wait_msg_task接收者

nromal_wait_msg_task的任務(wù)處理函數(shù)中,調(diào)用sysplus.waitMsg("nromal_wait_msg_task", "SEND_DATA_REQ")接收消息,在15秒的時(shí)間內(nèi),一共收到15條消息,運(yùn)行正常,符合預(yù)期,每秒接收到一次消息;

wKgZPGl4kXyAGyfhAALC9rV6F48731.png

delay_wait_msg_task接收者

delay_wait_msg_task的任務(wù)處理函數(shù)中,調(diào)用sysplus.waitMsg("nromal_wait_msg_task", "SEND_DATA_REQ")接收消息,在15秒的時(shí)間內(nèi),一共收到5條消息,和代碼設(shè)計(jì)相符,每延時(shí)3秒接收到一次消息;

但是我們?cè)僮屑?xì)觀察下收到消息,消息的計(jì)數(shù)參數(shù)依次為1,2,3,4,5,說明雖然延時(shí)收到消息,但是消息并沒有出現(xiàn)丟失;

這一點(diǎn)兒和上一小節(jié)講的全局消息的處理完全不同,全局消息在這種情況下會(huì)丟失消息;

wKgZO2l4kaKACU2rAADx08tBc3Y335.png

2.2.3.5 用戶定向消息 vs 用戶全局消息,如何選擇?

在了解了用戶定向消息和用戶全局消息之后,大家可能會(huì)有一個(gè)疑問:我平時(shí)開發(fā)程序時(shí),怎么判斷應(yīng)該使用定向消息還是全局消息呢?一般來說,建議大家按照以下幾步來做選擇:

1、首先確認(rèn)你的程序中有沒有使用高級(jí)task,因?yàn)橛行?yīng)用功能模塊是必須要使用高級(jí)task的,例如創(chuàng)建一個(gè)socket時(shí),要使用高級(jí)task;如果使用了高級(jí)task,那就只能使用用戶定向消息了;如果沒有使用高級(jí)task,繼續(xù)向下判斷;

如果你對(duì)用戶全局消息的處理過程以及其中容易出現(xiàn)的消息丟失問題,非常清楚,知道如何規(guī)避這種消息丟失問題,則可以使用全局消息,因?yàn)槿窒⑹褂闷饋砗?jiǎn)單;

如果你對(duì)用戶全局消息的處理過程以及其中容易出現(xiàn)的問題都不是很清楚,這種情況下,建議使用定向消息和高級(jí)task,因?yàn)檫@種方式下,只要在發(fā)送定向消息前,高級(jí)task已經(jīng)創(chuàng)建,則消息不會(huì)丟失;

由于篇幅過長(zhǎng),更多精彩內(nèi)容,請(qǐng)看下篇~


審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 物聯(lián)網(wǎng)
    +關(guān)注

    關(guān)注

    2945

    文章

    47818

    瀏覽量

    414798
  • LuatOS
    +關(guān)注

    關(guān)注

    0

    文章

    156

    瀏覽量

    2692
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    LuatOS 框架的嵌入式系統(tǒng)架構(gòu)設(shè)計(jì)原理

    API 接口,使開發(fā)者能用高級(jí)語言快速實(shí)現(xiàn)底層控制。這一設(shè)計(jì)顯著提升了開發(fā)效率,同時(shí)保持了良好的運(yùn)行性能。 一、LuatOS框架中的概念 ? 在LuatOS中,有三個(gè)核心概念和一個(gè)調(diào)度器: 1、三個(gè)核心概念:任務(wù)(task),
    的頭像 發(fā)表于 02-03 15:57 ?97次閱讀
    <b class='flag-5'>LuatOS</b> <b class='flag-5'>框架</b>的嵌入式系統(tǒng)架構(gòu)設(shè)計(jì)原理

    LuatOS 系統(tǒng)框架的模塊化設(shè)計(jì)原理

    裁剪功能,適應(yīng)不同規(guī)模的嵌入式項(xiàng)目需求。 一、LuatOS框架中的概念 ? 在LuatOS中,有三個(gè)核心概念和一個(gè)調(diào)度器: 1、三個(gè)核心概念:任務(wù)(task),消息(message),定時(shí)器(timer
    的頭像 發(fā)表于 02-03 15:56 ?115次閱讀
    <b class='flag-5'>LuatOS</b> 系統(tǒng)<b class='flag-5'>框架</b>的模塊化設(shè)計(jì)原理

    基于LuatOS的MQTT物聯(lián)網(wǎng)通信全解

    在構(gòu)建物聯(lián)網(wǎng)終端設(shè)備時(shí),通信協(xié)議的選擇直接決定系統(tǒng)的穩(wěn)定性與擴(kuò)展性。LuatOS通過內(nèi)置MQTT客戶端支持,使開發(fā)者能以極少代碼實(shí)現(xiàn)設(shè)備云。本文將從協(xié)議原理到代碼實(shí)現(xiàn),全面解析基于LuatOS
    的頭像 發(fā)表于 01-29 19:42 ?170次閱讀
    基于<b class='flag-5'>LuatOS</b>的MQTT物聯(lián)網(wǎng)通信全解

    輕松掌握——LuatOS socket基礎(chǔ)知識(shí)和應(yīng)用開發(fā)

    socket課程主要包含以下幾個(gè)部分: 1、TCP/IP總體介紹; 2、LuatOS的?4G/WiFi/以太網(wǎng)?三種網(wǎng)絡(luò)環(huán)
    的頭像 發(fā)表于 01-28 20:07 ?169次閱讀
    輕松掌握——<b class='flag-5'>LuatOS</b>  socket基礎(chǔ)知識(shí)和應(yīng)用開發(fā)

    解鎖:LuatOS框架的使用(下篇)

    接上一篇 2.3 LuatOS 的定時(shí)器(timer) 對(duì)于 LuatOS 應(yīng)用程序來說,定時(shí)器本質(zhì)也算是一種特殊的消息,因?yàn)槎〞r(shí)器太常用了,所以把他單獨(dú)拎出來,單獨(dú)的一個(gè)章節(jié)進(jìn)行講解
    的頭像 發(fā)表于 01-28 13:18 ?148次閱讀
    解鎖:<b class='flag-5'>LuatOS</b><b class='flag-5'>框架</b>的使用(下篇)

    LuatOS-Air腳本移植到LuatOS版本注意事項(xiàng)

    一、lua版本不一樣 LuatOS-Air使用的是lua5.1版本,本身不支持位移運(yùn)算符。 LuatOS使用的是lua5.3版本,取消了module(..., package.seeall)這種形式
    的頭像 發(fā)表于 01-17 14:48 ?1135次閱讀
    <b class='flag-5'>LuatOS</b>-Air腳本移植到<b class='flag-5'>LuatOS</b>版本注意事項(xiàng)

    LuatOS-Air轉(zhuǎn)LuatOS常見故障排查手冊(cè)

    當(dāng)LuatOS-Air腳本在LuatOS環(huán)境中運(yùn)行失敗,問題往往集中在幾個(gè)關(guān)鍵模塊:任務(wù)調(diào)度、外設(shè)驅(qū)動(dòng)、網(wǎng)絡(luò)配置和固件版本匹配。本文以故障排查的邏輯為主線,列出常見報(bào)錯(cuò)現(xiàn)象、可能原因及快速修復(fù)
    的頭像 發(fā)表于 01-13 19:20 ?132次閱讀
    <b class='flag-5'>LuatOS</b>-Air轉(zhuǎn)<b class='flag-5'>LuatOS</b>常見故障排查手冊(cè)

    警惕兼容性陷阱:LuatOS-Air腳本在LuatOS中的運(yùn)行異常分析

    即使語法正確的LuatOS-Air腳本,在LuatOS環(huán)境中也可能出現(xiàn)“靜默失敗”——程序無報(bào)錯(cuò)但功能未執(zhí)行。這類問題多與系統(tǒng)事件循環(huán)、模塊加載時(shí)機(jī)或硬件抽象層調(diào)用方式有關(guān)。本文通過多個(gè)真實(shí)
    的頭像 發(fā)表于 01-13 19:20 ?151次閱讀
    警惕兼容性陷阱:<b class='flag-5'>LuatOS</b>-Air腳本在<b class='flag-5'>LuatOS</b>中的運(yùn)行異常分析

    掌握LuatOS系統(tǒng)消息:新手也能看懂的列表詳解

    視角出發(fā),用通俗語言解析其工作原理與配置方法。此處列舉了LuatOS框架中自帶的系統(tǒng)消息列表。 ? 一、sys ? 文檔鏈接:https://docs.openluat.com/osapi/core
    的頭像 發(fā)表于 01-13 18:12 ?118次閱讀
    掌握<b class='flag-5'>LuatOS</b>系統(tǒng)消息:新手也能看懂的列表詳解

    教程來啦!LuatOS中的消息通信機(jī)制詳解及其應(yīng)用場(chǎng)景

    在資源受限的嵌入式環(huán)境中,LuatOS采用消息機(jī)制實(shí)現(xiàn)模塊間解耦與高效通信。通過預(yù)定義消息名稱(如“new_msg”),開發(fā)者可輕松構(gòu)建響應(yīng)式程序結(jié)構(gòu)。接下來我們將深入剖析其實(shí)現(xiàn)原理與典型使用方法
    的頭像 發(fā)表于 09-26 18:59 ?424次閱讀
    教程來啦!<b class='flag-5'>LuatOS</b>中的消息通信機(jī)制詳解及其應(yīng)用場(chǎng)景

    LuatOS腳本開發(fā)入門:嵌入式運(yùn)行框架全解析!

    想搞懂LuatOS如何運(yùn)行Lua腳本?本文深入剖析其嵌入式運(yùn)行框架,涵蓋虛擬機(jī)加載、任務(wù)協(xié)程、系統(tǒng)初始化等關(guān)鍵環(huán)節(jié),適合初學(xué)者。 一、LuatOS 編程起步 1.1 底層固件怎么啟動(dòng) Luat
    的頭像 發(fā)表于 09-26 17:45 ?476次閱讀
    <b class='flag-5'>LuatOS</b>腳本開發(fā)入門:嵌入式運(yùn)行<b class='flag-5'>框架</b>全解析!

    嵌入式開發(fā)新選擇:LuatOS腳本框架入門教程

    LuatOS正成為嵌入式開發(fā)的新趨勢(shì)!本教程帶你從基礎(chǔ)入手,全面了解其基于Lua的腳本開發(fā)模式與輕量級(jí)運(yùn)行框架。 一、LuatOS 編程起步 1.1 底層固件怎么啟動(dòng) LuatOS
    的頭像 發(fā)表于 09-26 17:34 ?581次閱讀
    嵌入式開發(fā)新選擇:<b class='flag-5'>LuatOS</b>腳本<b class='flag-5'>框架</b>入門教程

    Task任務(wù):LuatOS實(shí)現(xiàn)“任務(wù)級(jí)并發(fā)”的核心引擎

    Task任務(wù)通過其強(qiáng)大的并發(fā)處理能力,使LuatOS能夠在單線程環(huán)境中模擬多線程執(zhí)行,通過協(xié)程的掛起與恢復(fù)機(jī)制,實(shí)現(xiàn)任務(wù)級(jí)的并行操作,顯著提升系統(tǒng)效能。 sys核心庫是LuatOS運(yùn)行框架庫,也是
    的頭像 發(fā)表于 08-28 13:49 ?507次閱讀
    Task任務(wù):<b class='flag-5'>LuatOS</b>實(shí)現(xiàn)“任務(wù)級(jí)并發(fā)”的核心引擎

    揭秘LuatOS Task:多任務(wù)管理的“智能中樞”

    Task任務(wù)作為LuatOS的核心組成部分,通過智能化的任務(wù)管理機(jī)制,實(shí)現(xiàn)任務(wù)的創(chuàng)建、調(diào)度與協(xié)同運(yùn)行,讓復(fù)雜應(yīng)用得以高效并行處理,滿足實(shí)時(shí)場(chǎng)景下的嚴(yán)苛需求。 sys核心庫是LuatOS運(yùn)行框架
    的頭像 發(fā)表于 08-28 13:48 ?634次閱讀
    揭秘<b class='flag-5'>LuatOS</b> Task:多任務(wù)管理的“智能中樞”

    解碼LuatOS:短信功能的底層運(yùn)作機(jī)制

    短信功能在LuatOS中的運(yùn)行并非表面所見那么簡(jiǎn)單。本文將深入系統(tǒng)底層,解碼其通信協(xié)議、數(shù)據(jù)處理與系統(tǒng)交互,呈現(xiàn)完整的運(yùn)作圖譜。 我們這期主要拆解airsms.lua文件,講清楚,短信功能
    的頭像 發(fā)表于 06-27 18:05 ?634次閱讀
    解碼<b class='flag-5'>LuatOS</b>:短信功能的底層運(yùn)作機(jī)制