本章将简要介绍Emscripten环境下与运行时相关的部分知识,包括消息循环、文件系统、内存管理等内容。
-
main函数与生命周期
生成本地代码时,作为C/C++程序的入口函数,通常main()
函数意味着程序的整个生命周期,程序随main()
函数返回的返回而退出;而在Emscripten下,情况有所不同:
main()
函数退出后,Emscripten运行时核心(Module
)依然可用!而且在之前的章节,很多例子里面甚至根本都没有main()
函数,由此可见:对Emscripten来说,main()
函数既不必须,运行时生命周期亦不由其控制。
如果希望在main()
函数返回后注销Emscripten运行时,可以在编译时添加-s NO_EXIT_RUNTIME=0
选项,例如:
emcc main.cc -s NO_EXIT_RUNTIME=0 -o main.js
-
消息循环
除了一次性执行立即退出的程序外,大多数C/C++程序都存在类似下列伪代码的消息循环:
int main() {
while(1) {
msg_loop();
}
return 0;
}
但网页中的JavaScript脚本是单线程运行的,一个带有消息循环的C/C++程序如果不加处理,直接使用Emscripten编译后导入网页中运行,消息循环不退出,会阻塞页面程序的运行,导致DOM无法更新,整个页面失去响应。为此Emscripten提供了一组函数用于消息循环的模拟及调度执行。
emscripten_set_main_loop()
函数声明:
void emscripten_set_main_loop(em_callback_func func, int fps, int simulate_infinite_loop)
参数:
func
:消息处理回调函数。fps
:消息循环的执行帧率。如果该参数小等于0,则使用页面的requestAnimationFrame
机制调用消息处理函数,该机制可以确保页面刷新率与显示器刷新率对齐,对于需要执行图形渲染任务的程序,使用该机制可以得到平滑的渲染速度。simulate_infinite_loop
:是否模拟“无限循环”,用法后续介绍。
返回值:
- 无
先来看一个简单的例子:
//msg_loop.cc
#include <emscripten.h>
#include <stdio.h>
void msg_loop() {
static int count = 0;
if (count % 60 == 0) {
printf("count:%d\n", count);
}
count++;
}
int main() {
printf("main() start\n");
emscripten_set_main_loop(msg_loop, 0, 1);
printf("main() end\n");
return 0;
}
编译后导入页面,控制台输出如下:
注意控制台输出了“main() start”,但是没有输出“main() end”!这是因为调用emscripten_set_main_loop
时,simulate_infinite_loop
参数设为了1。
若调用emscripten_set_main_loop
时,simulate_infinite_loop
参数设为了0,控制台将输出如下:
无论simulate_infinite_loop
参数是否为1,消息处理函数都会按照设定的帧率无限执行,区别仅在于,当其为1时:
emscripten_set_main_loop
后续代码不执行。main()
函数栈未销毁。
simulate_infinite_loop
参数为1时,emscripten_set_main_loop()
函数会抛出SimulateInfiniteLoop
异常,JavaScript中的胶水代码截获该异常终止后续代码执行。