高效的嵌入式系统功能开发
在嵌入式Linux系统开发过程中,开发人员常常使用脚本语言来实现一些系统功能,例如python/Shell等,以提高一些系统配置、控制、监测方面开发的效率。基于脚本的系统功能开发,是可行的;主要因为与业务相关的应用相比,系统相关的功能,没有高并发、低延迟等实时性的要求。而且一些系统服务在运行过程中,其对系统做出的调整,常常不是瞬时的;例如压缩一个日志文件、操作某个网口的网络配置等,都需要持续一段时间(才能完成),这些功能的开发与脚本的功能特点符合得比较好。因此,使用高级的、抽象的脚本语言来完成嵌入式系统的开发,是开发效率和运行稳定性的最优的选择。
然而,对于python脚本而言,其对系统配置要求高,可能不适合一些系统资源受限的嵌入式设备;而Shell脚本语言表达能力比较低,不适合完成一些比较复杂的功能模块。这样就需要一个折中的脚本语言,即要兼顾资源受限的嵌入式设备,又要具备细腻精确的表达能力;Lua脚本编程语言很好地填补这一空缺。实际上,Lua脚本语言太过成功,以致于国产的一些单片机提供了几乎纯Lua版本的系统SDK,例如合宙的LuatOS。
可执行多任务的单线程脚本
某些情况下,对系统的一些监视、运维功能,可以使用一段简洁的Lua脚本来完成。但作为系统服务的一个,嵌入式系统开发人员并不能随意地增加或减少系统服务。因此需要有一种简单地机制,在现存的某个服务中增加一段函数,用于周期性地执行某个操作,就显得很重要了。
很幸运,Lua编程语言内置了协程,这是一种可以让多个任务同时运行的任务管理机制。本文主要介绍了笔者对Lua协程的封装模块,方便嵌入式系统开发人员随意在一个Lua脚本中增减必要的功能。因Lua有多个版本,笔者对协程的封装可以兼容各个版本(包括luajit等)。相关代码可从笔者的github仓库下载:
sh
git clone https://github.com/jaqchen/ftask.git
多任务示例代码
该Lua模块并不是由纯Lua实现,它包含了一个用于获取系统时间的C语言模块ftime,可以通过以下命令编译:
sh
cd ftask
make EXTRA_CFLAGS=-I/usr/include/lua5.4 # 使用`lua5.4`解析器
为了对笔者提供的简单协程的使用加以说明,笔者在仓库中提供了一个简单的示例,test.lua,运行的结果如下:
# lua5.4 test.lua
Task 1 started with argument: Hello World
Task 2 started with type: table
Task 1 running happily! 7
Task 2 running happily! 1
Task 1 running happily! 8
Task 1 running happily! 9
Task 2 running happily! 2
Task 1 running happily! 10
Task 2 running happily! 3
Task 1 running happily! 11
INFO: restarting Lua thread at 1
Task 2 running happily! 4
Task 1 started with argument: Hello World
Task 1 running happily! 7
Task 2 running happily! 5
Warning, destory Lua thread: 2
Task 1 running happily! 8
^Clua5.4: ./ftask.lua:129: interrupted!
下面,结合该示例代码,简要地说明使用方法:
lua
#!/usr/bin/lua
local ftask = require "ftask"
local cfmt = string.format
local task1 = ftask.newtasks()
local function task_1(arg1)
io.stdout:write(cfmt("Task 1 started with argument: %s\n", arg1))
local cnt = 6
while cnt < 11 do
cnt = cnt + 1
task1:mdelay(1000)
io.stdout:write(cfmt("Task 1 running happily! %d\n", cnt))
end
return nil
end
local function task_2(arg2)
io.stdout:write(cfmt("Task 2 started with type: %s\n", type(arg2)))
local cnt = 0
while cnt < 5 do
cnt = cnt + 1
task1:mdelay(1500)
io.stdout:write(cfmt("Task 2 running happily! %d\n", cnt))
end
return nil
end
task1:add(task_1, "Hello World", true)
task1:add(task_2, {[1] = 1, [2] = 2}, false)
while true do
task1:loop(60000, nil, nil)
end
其中,需要强调的是,task1:add方法,其第一个参数为某个Lua方法(或函数)入口。函数可以不返回,也可以返回。第二个参数为该方法的参数,只能有一个。第三个参数为布尔类型,指示该方法如果返回后,是否重新运行。
此外,task1:mdelay(XXX)封装了Lua协程的切换操作。它会让当前的协程在XXX毫秒之后再次运行。最后,值得注意的是task1:loop方法,它的第二个参数可以是一个Lua函数,该函数需要休眠 调用时指定的时间,例如阻塞在某个套接字的读过程中(这样就不会调用ftime.so模块同的mdelay方法),进一步利用好服务的空闲时间。