原文地址:https://developer.mozilla.org/en-US/docs/Archive/B2G_OS/Architecture
1、本文参照FireFox官网与外网上相对较少的资料,对B2G系统的系统架构做相关研究记录!
2、本文会侧重记录B2G OS自身的架构实现,并涉及到其与Android之间的联系。
3、本文是对B2G OS平台架构的高级概述,介绍了关键概念,并解释了其组件在基本级别上的交互作用。
4、某些外文部分的引用,并不会将外网文章进行翻译整理,怕个人理解有误导致歧义,将保留原文。
B2G:Boot to Gecko的缩写。
Boot to Gecko:用于B2G操作系统的工程代号。因为它在项目有正式名称之前已经使用了很长时间。所以经常看到这个术语用于代表B2G操作系统。
B2G OS:B2G操作系统是一种由Mozilla社区提供的智能手机操作系统。
Gaia:B2G系统的用户界面。B2G系统启动后,手机屏幕上所绘制的所有内容都属于Gaia的一部分。也就是说在B2G系统中,用户所见到的几乎所有的UI界面都是基于Gaia实现的。Gaia实现了锁屏、主屏、拨号键盘、短信应用、相机应用......。Gaia是完全由HTML、CSS和JAVASCRIPT实现的,Open Web APIs(由Gecko实现)是它与操作系统(即:系统内核)之间通讯的唯一接口。 Gaia 可以完美的运行于B2G之上,除此之外,由于它是基于标准的Web API实现的,因此可以在其他的操作和浏览器上工作,第三方应用也可以与 Gaia一起被安装。
Gecko:B2G的应用运行时环境(application runtime)。 站在比较高的角度来看,Gecko实现了HTML、CSS和JS的开放标准并且使这些接口在所有Gecko支持的系统中运行良好。这就是说Gecko包含了: 网络栈、 图形栈、布局引擎、 JS虚拟机以及端口层。
Gonk: Gonk是B2G OS平台的底层操作系统,由Linux内核(基于Android开放源码项目(AOSP)和用户空间硬件抽象层(HAL)组成。内核和一些用户空间库是常见的开源项目:Linux、libusb、bluez等等。HAL的其他部分也与AOSP共享:GPS、摄像头和其他部分。可以说Gonk是一个非常简单的Linux发行版。Gonk是Gecko的移植目标。由于B2G操作系统项目对Gonk有完全的控制,所以我们可以将接口暴露给不能在其他操作系统上公开的Gecko。例如,Gecko可以直接访问Gonk上的全电话栈和显示帧缓冲区,但是在其他操作系统上没有这种访问权限。
Jank:这一术语通常用于移动应用领域,指的是在应用中缓慢/低效的代码操作的效果,它会阻止用户界面的更新,导致它变得延迟或反应迟钝。Gaia工程师会使用各种优化技术来避免这种情况的发生。
B2G OS消除了操作系统和应用程序层之间的本机API层。这种集成设计减少了平台的开销,并简化了安全性,同时又不牺牲性能或丰富的用户智能手机体验。
1.Gaia是该设备的核心web应用,以及用户界面层,所有这些都是用HTML5、CSS和JavaScript编写的,有许多暴露的api,允许UI代码与手机硬件和Gecko功能交互。
2.Gecko是B2G操作系统的web引擎和表示层,它通过充当web内容和底层设备之间的接口来连接硬件和HTML。Gecko提供了一个HTML5解析和呈现引擎,通过安全的web APIs、一个全面的安全框架、更新管理和其他核心服务来对硬件功能进行编程访问。
3.Gonk是B2G操作系统堆栈中的内核级组件,它充当了Gecko和底层硬件之间的接口。Gonk控制底层硬件,并将硬件功能公开给在Gecko中实现的Web APIs。Gonk可以被看作是“黑盒”,它可以在后台执行所有复杂的、详细的工作,通过在硬件级别上执行请求来控制移动设备。
4.移动设备是运行B2G操作系统的移动电话硬件。原始设备制造商(原始设备制造商)负责提供移动设备。
本节描述B2G OS设备启动的流程和所涉及的部分。一般的系统启动流是从内核空间的引导加载程序,到本地代码中的init,到B2G,然后是用户空间中的Gecko,最后是系统应用,窗口管理器,然后是Gecko中的主屏幕应用。
下图是其它app如何在系统上层运行的:
当一个B2G操作系统启动时,执行从主引导加载程序开始。主操作系统的加载过程以一种典型的方式(逐级引导)进行。最后,执行被传递给Linux内核。
关于引导过程有几点值得注意:
1.引导加载程序(bootloader)通常显示在设备启动时看到的第一个闪屏;这里通常是供应商标识。
2.引导加载程序用于支持为设备刷入image,不同的设备可能会使用不同的刷机协议(fastboot协议、odin协议)。
3.By the end of the bootstrapping process, the modem image is usually loaded and running on the modem processor. How this happens is highly device-specific and may be proprietary
Gonk中的linux内核一定程度上接近upstream(The Linux community) linux,但是,AOSP(Android Open Source Project)做了少量的修改,这些修改并不在upstream linux之中。供应商也在按照他们自己的计划修改linux内核和 upstream。不过,总的来说,Gonk中linux的内核与仓库中的还是非常接近的。
Linux的启动过程在互联网上有大量详细的文档介绍,因此这里就不再包含。
Linux内核将会启动设备并运行必要的进程;它将执行定义在init.rc中的进程,并启动定义在init.b2g.rc中的关键进程(如:b2g、rild。b2g是FireFox OS的基础进程,其包含Gecko。rild是与电话相关的过程,可能由不同的芯片组专有。)
在最后,如同一般的类UNIX操作系统一样,会启动一个用户空间进程init。一旦init进程启动,Linux内核就会掌控来自应用空间的系统调用、中断请求、硬件请求。大多硬件特性会通过 sysfs 暴露给用户空间。比如,下面是一段Gecko中的 代码 用以读取电池状态:
1
2
3
4
5
6
|
FILE
*capacityFile =
fopen
(
"/sys/class/power_supply/battery/capacity"
,
"r"
);
double
capacity = dom::battery::kDefaultLevel * 100;
if
(capacityFile) {
fscanf
(capacityFile,
"%lf"
, &capacity);
fclose
(capacityFile);
}
|
3.3 关于init进程的补充
在Gonk中,init进程挂载必要的文件系统,孵化系统服务并且在服务启动后充当服务管理器。这与其他类Unix系统中的init进程非常相似。它通过解释一些脚本(如:init.rc)来实现控制启动各种系统服务。init.rc会根据具体设备的不同而有所区别,B2G OS的init.rc和Android 的init.rc并无太大区别,只是各自启动各子系统所需要的进程、服务。
最关键的是,B2G的init.rc启动了b2g进程,该进程是B2G OS的核心。
下面代码段显示了其在init.rc→init.b2g.rc中的启动命令:
1
2
3
|
service b2g /
system
/bin/b2g.sh
class
main
onrestart restart media
|
下面要深入了解B2G操作系统的各个组件是如何组合在一起并相互作用的。该图显示了B2G OS中的主要用户空间过程。
b2g进程是最主要的系统进程,它具有很高的权限,它可以访问大多数硬件设备。b2g
communicates with the modem, draws to the display framebuffer, and talks to GPS, cameras, and other hardware features. Internally, b2g
runs the Gecko layer (implemented by libxul.so
). See Gecko(后面章节会介绍) for details on how the Gecko layer works, and how b2g
communicates with it.(这段怕有歧义,所以不翻译)
b2g进程可能会会孵化一些低权限的内容进程(content processes)。Web应用和其他一些网页内容就是在这些进程中被加载的。这些进程通过IPDL(一个消息传递系统)与主Gecko服务进程通信。
The b2g
process runs libxul, which references b2g/app/b2g.js
to get default preferences. From the preferences it will open the described HTML file b2g/chrome/content/shell.html
, which is compiled within the omni.ja
file. shell.html
includes b2g/chrome/content/shell.js
file, which triggers the Gaia system
app.
rild是到modem processor的接口,rild是 Radio Interface Layer (RIL)的守护进程。It's a proprietary piece of code that's implemented by the hardware vendor to talk to their modem hardware.。rild 允许客户端连接到它所绑定的UNIX-domain socket。在init.rc中rild是通过下面启动的:
1
2
|
service ril-daemon /
system
/bin/rild
socket rild stream 660 root radio
|
4.3 rilproxy
在B2G中,rild客户端是rilproxy进程,它仅仅在rild和b2g之间充当了一个静默转发代理(dumb forwarding proxy)。This proxy is needed as an implementation detail; suffice it to say, it is indeed necessary. The rilproxy
code can be found on GitHub.
mediaserver
process 用以控制视频、音频播放。Gecko通过RPC(Android Remote Procedure Call)机制与它进行交互。Gecko支持的一些媒体(OGG Vorbis audio, OGG Theora video, and WebM video)由Gecko负责解码并直接发送到mediaserver,其他媒体文件由libstagefright解码,它能访问专有解码器和硬件解码器。
(mediaserver流程是B2G操作系统的“临时”组件,它是用来帮助最初的开发工作的,但是它最终会消失。然而,这种情况很可能不会在B2G OS 2.0出现之前就发生。)
netd进程用来配置网络接口。
wpa_supplicant是通过端点连接到WiFi的标准UNIX-sytle 守护进程。
dbus-daemon 用以充当D-Bus(https://www.freedesktop.org/wiki/Software/dbus/),它是B2G OS用以蓝牙通信的。
Gecko是B2G操作系统的web引擎和表示层,它通过充当web内容和底层设备之间的接口来连接硬件和HTML。Gecko提供了一个HTML5解析和呈现引擎,通过安全的web APIs、一个全面的安全框架、更新管理和其他核心服务来对硬件功能进行编程访问。
一个应用程序app由一系列相关的HTML5 web内容组成。为了构建运行在B2G操作系统移动设备上的web应用程序,开发人员只需组装、打包和分发这些web内容。在运行时,这个web内容在web浏览器中被解释、编译和呈现。想了解更多关于应用的信息,请参见应用中心(App Center)。
该目录主要包含了大部分B2G OS相关的功能。
b2g/chrome/content:
包含运行在system app上的Javascript文件。
b2g/chrome/content/shell.html:
Gaia的进入点–system app的HTML文件,shell.html调用了setting.js和shell.js 。
1
2
|
|
其中,setings.js包含了系统默认的配置参数。
b2g/chrome/content/shell.js:
shell.js是Gaia system app启动的第一个脚本。
shell.js载入了所有必需模块,注册key listener,定义了与Gaia通信的sendCustomEvent和sendChromeEvent
,提供了webapp安装助手:indexedDB quota, RemoteDebugger, keyboard helper, and screenshot tool。
但是,shell.js最主要的功能是加载了Gaia system app,然后接管所有针对Gaia system app相关的系统管理工作。
1
2
3
4
|
let systemAppFrame =
document.createElementNS(
'http://www.w3.org/1999/xhtml'
,
'html:iframe'
);
...
container.appendChild(systemAppFrame);
|
b2g/app/b2g.js:
此脚本包含了预定义设置,比如:config in browser, and the same as Gaia's pref.js 。这些设置可通过Setting app进行修改,也可以直接在Gaia的build script中修改Gaia的user.js文件。
新的API实现(post-b2g)会放置在dom/文件夹中。旧的APIs会被放置在dom/base中(比如:Navigator.cpp)。
dom/apps
.jsm
will be loaded — .js
API implementations such as webapp.js
install, getSelf
, etc.
dom/apps/PermissionsTable.jsm
All permissions are defined in PermissionsTable.jsm
WebIDL is the language used to define web APIs. For supported attributes, read WebIDL_bindings.
This directory contains files related to the gonk port layer.
module/libpref/src/init/all.js:Contains all config files.
/system/b2g/ omni.ja and omni.js:Contains the pack of styles for resources in the device.
Gecko中的大部分动作都是由输入事件触发的,这些事件包括:按下按钮、触摸触摸屏等。These events enter Gecko through the Gonk implementation of nsIAppShell
, a Gecko interface that is used to represent the primary entrance points for a Gecko application; that is, the input device driver calls methods on the nsAppShell
object that represents the Gecko subsystem in order to send events to the user interface.
举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
void
GeckoInputDispatcher::notifyKey(nsecs_t eventTime,
int32_t deviceId,
int32_t source,
uint32_t policyFlags,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
nsecs_t downTime) {
UserInputData data;
data.timeMs = nanosecsToMillisecs(eventTime);
data.type = UserInputData::KEY_DATA;
data.action = action;
data.flags = flags;
data.metaState = metaState;
data.key.keyCode = keyCode;
data.key.scanCode = scanCode;
{
MutexAutoLock lock(mQueueLock);
mEventQueue.push(data);
}
gAppShell->NotifyNativeEvent();
}
|
这些输入事件起源于标准的linux输入事件系统(the standard linux input_event system),B2G在它的基础上加了层轻量级的抽象处理层(light abstraction layer);这提供了一些很好的特性,比如事件过滤。你可以在widget/gonk/libui/EventHub.cpp文件中的EventHub::getEvents()方法看到,input events的创建过程。
一旦事件由Gecko接收到,它们将会被nsAppShell派发到DOM:
1
2
3
4
5
6
7
8
9
10
11
|
static
nsEventStatus sendKeyEventWithMsg(uint32_t keyCode,
uint32_t msg,
uint64_t timeMs,
uint32_t flags) {
nsKeyEvent event(
true
, msg, NULL);
event.keyCode = keyCode;
event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_MOBILE;
event.
time
= timeMs;
event.flags |= flags;
return
nsWindow::DispatchInputEvent(event);
}
|
从这里开始,事件或者被Gecko内部消耗(consumed),或者以DOM事件( DOM events)的形式被分发到Web应用。
在非常底层的地方,Gecko使用OpenGL ES 2.0进行绘制,绘制到一个包含硬件帧缓冲的叫做图形上下文(glcontext )的地方。它的实现靠的是Gonk中的nsWindow通过如下一段代码:
1
2
|
gNativeWindow =
new
android::FramebufferNativeWindow();
sGLContext = GLContextProvider::CreateForWindow(
this
);
|
其中的FramebufferNativeWindow类是直接取自Android的;可查看FramebufferNativeWindow.cpp。它使用gralloc API通过图像驱动以从framebuffer设备中的图像缓存映射进内存。
Gecko使用它的图层系统将内容进行组合,绘制显示到屏幕上。
The details of how Gecko handles the rendering of web content is outside the scope of this document.
(注意:请不要混淆Gecko的硬件抽象层与Gonk的硬件抽象层。)
"HAL"位于Gecko的移植层( porting layers)。它被低层通过多平台访问系统接口。hal为Gecko中的更高层次提供了一个跨平台的C++ API,这些API实在hal内部实现的,而实现的本身是与平台相关的。hal没有被直接暴露给Gecko中除C++代码之外的任何东西,换句话说,hal只能被Gecko中的C++代码所访问。
从上往下看,当用户请求使用一个手机功能(比如拨打一个号码,访问一个本地的wifi网络,或者通过蓝牙连接)时,B2G操作系统技术栈中的所有层都被涉及到了。Gaia层中的应用程序和Web中的内容通过Web API调用(从HTML5函数中调用)来提交访问底层设备的请求,这些是在Gecko中实现的。然后Gecko将请求提交给Gonk。来自Gecko的单个请求可以触发由Gonk在移动电话中发起和管理的一系列复杂的操作。
以Vibration API(震动的API)为例。Gecko HAL对它的API定义在 hal/Hal.h 中,本质上(为了清晰起见,简化方法签名),有这个函数:
1
|
void
Vibrate(
const
nsTArray
|
这是由Gecko代码调用的函数,根据指定的模式开始设备的振动;一个对应的函数可以用来取消任何正在进行的振动。在Gonk 代码中对应的方法在hal/gonk/GonkHal.cpp中:
1
2
3
4
|
void
Vibrate(
const
nsTArray
EnsureVibratorThreadInitialized();
sVibratorRunnable->Vibrate(pattern);
}
|
将震动请求发送给另外的线程的代码在VibratorRunnable::Run()中。这个线程的主循环如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
while
(!mShuttingDown) {
if
(mIndex < mPattern.Length()) {
uint32_t duration = mPattern[mIndex];
if
(mIndex % 2 == 0) {
vibrator_on(duration);
}
mIndex++;
mMonitor.Wait(PR_MillisecondsToInterval(duration));
}
else
{
mMonitor.Wait();
}
}
|
vibrator_on()是打开振动器电机的Gonk HAL API。在内部,这个函数通过读写驱动设备文件来实现。