Android中内核的结构和标准的Linux 2.6内核是基本相同的,不过Android在其基础上增加了私有内容。Android在Linux内核中增加的主要是一些驱动程序,这些驱动程主要分为两种:Android专用驱动和Android使用的设备驱动。
Android专用驱动程序不是Linux的标准驱动,它们的作用是辅助系统运行,一般不操作实际硬件。
下面将主要介绍Android专用驱动中最重要的3个驱动:
Ashmem ( Anonymous Shared Memory ),即匿名共享内存,通过内核的机制,为用户空间程序提供分配内存的机制。
Ashmem设备节点名称为/dev/ashmem,主设备号为10 ( Misc Driver ),次设备号动态生成。
Ashmem的代码路径如下:
kernel/include/linux/ashmem.h
kernel/mm/ashmem.c
Android的Logger驱动程序为用户层程序提供log的支持,这个驱动作为一个工具来使用。
Logger有3个设备节点,分别为:/dev/log/main, /dev/log/event, /dev/log/radioo
主设备号为10 (Misc Driver),次设备号动态生成。
Logger驱动的代码路径如下:
kernel/include/linux/logger.h
kernel/drivers/misc/logger.c
在用户空间logcat程序调用Logger驱动: system/core/logcat/可执行程序。
Android的Binder驱动程序为用户层程序提供IPC(进程间通信)支持,Android整个系统的运行依赖Binder驱动。
Binder设备节点名称为/dev/binder,主设备号为10 ( Misc Driver ),次设备号动态生成。
Binder的代码路径:
kernel/include/linux/binder.h
kernel/drivers/misc/binder.c
Android系统上层用户空间的libutil 工具库和ServiceManager守护进程都是调用Binder接口驱动提供对整个系统进程间通信功能的支持,它们的代码路径为:
frameworks/base/curds/servicemanager/
frameworks/base/include/utils/
frameworks/base/libs/utils/
Binder是Android中主要使用的IPC方式,通常需要按照模板定义相关的类,不需要直接调用Binde驱动程序的设备节点。
Android Power Management
一个基于标准 linux 电源管理的轻量级 Android 电源管理系统。
源码位置:
drivers/android/power.c
kernel/power/
Low Memory Killer
它在用户空间中指定了一组内存临界值,当其中某个值与进程描述中的 oom_adj 值在同一范围时,该进程将被Kill掉(在parameters/adj中指定oome_adj 的最小值)。它与标准的Linux OOM机制类似,只是实现方法不同。
源码位置:drivers/misc/lowmemorykiller.c
Android PMEM
PMEM 主要作用就是向用户空间提供连续的物理内存区域。
① 让 GPU 或 VPU 缓冲区共享 CPU 核心。
② 用于 Android service 堆。
源码位置:include/linux/android_pmem.h drivers/android/pmem.c
USB Gadget
基于标准 Linux USB gaeget 驱动框架的设备驱动。
源码位置:drivers/usb/gadet/
Ram Console
为了提供调试功能,android 允许将调试日志信息写入这个设备,它是基于 RAM 的 buffer。
源码位置: drivers/staging/android/ram_console.c
Time Device
定时控制,提供了对设备进行定时控制的功能。
源码位置:drivers/staging/android/timed_output.c(timed_gpio.c)
Android Alarm
提供一个定时器,用于把设备从睡眠状态唤醒,同时它还提供了一个即使在设备睡眠时也会运行的时钟基准。
设备节点:/dev/alarm
源码位置:drivers/trc/alarm.c
Android中常使用的设备主要有Framebuffer驱动、输入设备驱动、v412摄像头—视频驱动、OSS音频驱动、ALSA音频驱动、MTD驱动、蓝牙驱动、Wlan驱动等。
Framebuffer 驱动在 Linux 中是标准的显示设备的驱动。
它是一个字符设备,在文件系统中设备节点通常是 /dev/fbx 。 每个系统可以有多个显示设备 ,依次用 /dev/fb0、/dev/fb l等来表示。在 Android 系统中主设备号为 29 ,次设备号递增生成。
Android 对 Framebuffer 驱动的使用方式是标准的,在 / dev / graphie / 中的 Framebuffer 设备节点由 init 进程自动创建,被 libui 库调用。Android 的 GUI 系统中 , 通过调用 Framebuffer 驱动的标准接口,实现显示设备的抽象。
代码路径:include/linux/fb.h, drivers/video/fbmem.c
。
Android中的输入设备设备驱动主要包括:游戏杆(Joystick、鼠标(Mouse)和事件设备(Event)。
输入设备驱动同样也是字符设备,这个输入设备驱动程序那是相当相当的复杂。在Android内核中主要需要关注以下儿个文件:
include/linux/input.h(驱动头文件)。
driver/input/input.c(驱动核心实现,包含大量的操作接口)。
driver/input/event.c ( event机制)。
driver/input/joydev.c ( joystick驱动)。
driver/input/mousedev.c(鼠标驱动)。
上面这些都不需我们去实现,内核已经帮我们实现好了,不过在写硬件驱动时需要和Inputcore交互,所以需要用到上面这些函数中的接口,也就是说上面这些函数是透明的。
摄像头(Camera)——视频驱动驱动通常使用Video For Linux。
V4L2是V4L的升级版本,为linux下视频设备程序提供了一套接口规范。包括一套数据结构和底层V4L2驱动接口。V4L2提供了很多访问接口,可以根据具体需要选择操作方法。
注意:很少有驱动完全实现了所有的接口功能。所以在使用时需要参考驱动源码,或仔细阅读驱动提供者的使用说明。
V4L2的主设备号是81,次设备号:0~255,这些次设备号里也有好几种设备(视频设备、Radio设备、Teletext、VBI)。
include/linux/videodev.h: v4L第一版的头文件。
include/linux/videodev2.h: 定义主要的数据接日和常量。
include/media/v412-dev.h: 设备头文件,具体设备使用其中的接口注册。
driver/media/video/v412-dev.c
。 OSS(Open Sound System开放声音系统)是 linux 上最早出现的声卡驱动。OSS 由一套完整的内核驱动程序模块组成,可以为绝大多数声卡提供统一的编程接口。
OSS 是字符设备,主设备号14,主要包括下面几种设备文件:
/dev/sndstat
/dev/dsp
/dev/audio
/dev/mixer
/dev/sequencer
OSS 驱动所涉及的文件主要包括:
kernel/include/linux/soundcard.h OSS驱动的主要头文件。
kernel/include/linux/sound.h 定义 OSS 驱动的次设备号和注册函数
kernel/sound_core.c OSS核心实现部分
Flash驱动通常使用MTD ( Memory Technology Device内存技术设备)
MTD 驱动程序是 Linux 下专门为嵌入式环境开发的新一类驱动程序。Linux 下的 MTD 驱动程序接口被划分为用户模块和硬件模块:
MTD的字符设备:/dev/mtdX
,主设备号为90。
MTD的块设备:/dev/block/mtdblockX
,主设备号为13。
MTD驱动程序头文件路径:include/linux/mtd/mtd.h
。
MTD源代码路径:
drivers/mtd/mtdcore.c: MTD核心,定义MTD原始设备。
drivers/mtd/mtdchar.c: MTD字符设备。
drivers/mtd/mtdblock.c: MTD块设备。
ALSA ( Advanced Linux Sound Architecture)即高级Linux声音体系。
高级 Linux 声音体系ALSA是为音频系统提供驱动 的Linux 内核组件,以替代原先的开发声音系统 OSS 。它是一个完全开放源代码的音频驱动程序集 ,除了像 OSS 那样提供一组内核驱动程序模块之外 ,ALSA 还专门为简化应用程序的编写提供相应的函数库,与 OSS 提供的基于 ioctl 等原始编程接口相比, ALSA 函数库使用起来要更加方便一些 。
利用该函数库,开发人员可以方便、快捷地开发出自己的应用程序,细节则留给函数库进行内部处理 。所以虽然 ALSA 也提供了类似于 OSS 的系统接口,但建议应用程序开发者使用音频函数库,而不是直接调用驱动函数。
ALSA 驱动的主设备号为 116 ,次设备号由各个设备单独定义,主要的设备节点如下:
/ dev / snd / contmlCX —— 主控制 ;
/ dev / snd / pcmXXXc —— PCM 数据通道 ;
/ dev / snd / seq —— 顺序器;
/ dev / snd / timer —— 定义器。
在用户空间中 , ALSA 驱动通常配合 ALSA 库使用,库通过 ioctl 等接口调用 ALSA 驱动程序的设备节点。对于 AIJSA 驱动的调用,调用的是用户空间的 ALsA 库的接口,而不是直接调用 ALSA 驱动程序。
include/sound/asound.h: ALSA驱动的主要头文件。
include/sound/core.h: ALSA驱动核心数据结构和具体驱动的注册函数。
sound/core/sound.c
在Linux中,蓝牙设备驱动是网络设备,使用网络接口。
Android 的蓝牙协议栈使用BlueZ实现来对GAP, SDP以及RFCOMM等应用规范的支持,并获得了SIG认证。由于Bluez使用GPL授权, 所以Android 框架通过D-BUS IPC来与bluez的用户空间代码交互以避免使用未经授权的代码。
include/net/bluetooth/hci core.h
include/net/bluetooth/bluetooth.h
net/bluetooth/*
rivers/bluetooth/*
在Linux中,WLAN设备驱动是网络设备,使用网络接口。WLAN在用户空间使用标准的Socket接口进行控制。
include/net/wireless.h
net/wireless/*
drivers/net/wireless/*
驱动开发是Android开发系统中最底层的应用,属于Linux内核层的工作,因为驱动是系统和硬件之间的载体,涉及了不同硬件的应用问题,所以需要做系统移植的工作。
Android移植开发的最终目的是为了开发手机产品,从开发者的角度来看,这种类型的开发以具有硬件系统为前提,在硬件系统的基础上构建Android软件系统,这种类型的开发工作在Android系统的底层。在软件系统方面,主要工作集中在如下两个方面:
上述两个部分相互结合,共同完成了Android系统的软件移植。移植成功与否取决于驱动程序的品质和对Android硬件抽象层接口的理解程度。Android移植开发的工作由核心库、Dalvik虚拟机、硬件抽象层、Linux内核层和硬件系统协同完成,具体结构如图:
Android移植结构图
在移植过程中主要移植驱动方面的内容,具体来说,Android的移植主要分为下面的几个类型:
具体需要移植的内容如下所示:
① Display显示部分: 包括FrameBuffer驱动和Gralloc模块。
② Input用户输入部分: 包括Event驱动和EventHube
③ Codec多媒体编解码: 包括硬件Codec驱动和Codec插件,例如OpenMax o
④ 3D Accelerator ( 3D加速器)部分: 包括硬件OpenGL驱动和OpenGL插件。
⑤ Audi音频部分: 包括Audi、驱动和Audi、硬件抽象层。
⑥ Video Out视频输出部分: 包括视频显示驱动和Overlay硬件抽象层。
⑦ Camera摄像头部分: 包括Camera驱动(通常是v412)和Camera硬件抽象层。
⑧ Phone电话部分: 包括Modem驱动程序和RIL库。
⑨ GPS全球定位系统部分: 包括GPS驱动(例如串口)和GPS硬件抽象层。
⑩ Wi-Fi无线局域网部分: 包括Wlan驱动和协议以及Wi-Fi的适配层。
⑪ Blue Tooth蓝牙部分: 包括BT驱动和协议以及BT的适配层。
⑫ Sensor传感器部分: 包括Sensor驱动以及Sensor硬件抽象层。
⑬ Vibrator震动器部分: 包括Vibrator驱动和Vibrator硬件抽象层。
⑭ Light背光部分: 包括Light驱动和Light硬件抽象层。
⑮ Alarm警告器部分: 包括Alarm驱动和RTC系统以及用户空间调用。
⑯ Battery电池部分: 包括电池部分驱动和电池的硬件抽象层。
Android系统有很多组件,但并不是每一个部件都需要移植,例如浏览器引擎虽然需要下层的网络支持,但实际上并不需要直接为其移植网络接口,而是通过无线局域网或者电话系统数据连接来完成标准的网络接口,所以就不需要移植。
驱动开发的任务是,为某一个将要在Android系统上使用的硬件开发一个驱动程序。因为Android是基于Linux的,所以开发Android驱动其实就是开发Linux驱动。
对于大部分子系统来说,硬件抽象层和驱动程序都需要根据实际系统的情况来实现,例如传感器部分、音频部分、视频部分、摄像头部分和电话部分。另外也有一些子系统的硬件抽象层是标准的,只需要实现Linux内核中的驱动程序即可,例如输入部分、振动器部分、无线局域网部分和蓝牙部分等。对于有标准的硬件抽象层的系统,有的时候通常也需要做一些配置工作。
随着Android系统的更新和发展,它已经不仅仅是一个移动设备的平台,而且可以用于消费类电子和智能家电,例如从3.0以后的版本主要是针对平板电脑的,另外电子书、数字电视、机顶盒、固定电话等都逐渐使用Android系统。
在这些平台上,通常需要实现比移动设备更少的部件。一般来说,包括显示和用户输入的基本用户界面部分是需要移植的,其他部分是可选的。例如电话系统、振动器、背光、传感器等一般不需要在非移动设备系统来实现,一些固定位置设备通常不需要实现GPS系统。
Android内核是基于Linux 2.6内核的,这是一个增强内核版本,除了修改部分Bug外,还提供了用于支持Android平台的设备驱动。Android不但使用了Linux内核的基本功能,而且对Linux进行了改造,以实现更为强大的通信功能。
如果以一个原始的Linux操作系统为基础,将其改造成为一个适合于Android的系统所做的工作其实非常简单,就是增加适用于Android的驱动程序。在Android中有很多Linux系统的驱动程序,将这些驱动程序移植到新系统非常简单,具体来说有以下三个步骤。
① 编写新的源代码。
② 在KConfig配置文件中增加新内容。
③ 在Makefile中增加新内容。
在Android系统中,最常用的驱动程序有FrameBuffer驱动、Event驱动、Flash MTD驱动、WiFi驱动、蓝牙驱动和串口等驱动程序,并且还需要音频、视频、传感器等驱动和sysfs接口。所以说移植的过程就是移植上述驱动的过程,底层程序员的工作是在Linux下开发适用于Android的驱动程序,并移植到Android系统。
在Android中添加扩展驱动程序的基本步骤介绍如下:
① 在Linux内核中移植硬件驱动程序,实现系统调用接口;
② 把硬件驱动程序的调用在HAL中封装成Stub;
③ 为上层应用的服务实现本地库,由Dalvik虚拟机调用本地库来完成上层Java代码的实现;
④ 最后编写Android应用程序,提供Android应用服务和用户操作界面。
Android中的Linux内核和设备驱动结构
HAL层又被称为硬件抽象层,HAL在Android体系中有着深远的意义,因为Android是开放的而不是开源的原因就是在这一层上。因为HAL层的代码没有开源,所以Google将硬件厂商的驱动程序放在HAL层。也正是这个原因,所以Android被Linux家族删除。
HAL层(硬件抽象层)是位于操作系统内核与硬件电路之间的接口层,其目的在于将硬件抽象化。它隐藏了特定平台的硬件接口细节,为操作系统提供虚拟硬件平台,使其具有硬件无关性,这样就可以在多种平台上进行移植。
从软硬件测试的角度来看,软硬件的测试工作都可分别基于硬件抽象层来完成,从此使软硬件测试工作的并行进行成为可能。HAL层的位置结构如图所示。
HAL层结构
由上图可以看出,HAL的功能是把Android Framework< Android框架)与Linux内核隔离。这样做的目的是让Android不过度依赖Linux Kemel,从而让Android Framework开发可以在不考虑驱动程序的前提下进行。在HAL层主要包含了GPS, Vibrator, Wi-Fi, Copybit, Audio,Camera, Lights, Ril, Overlay等模块。
Android HAL层的分类,Android硬件抽象层可以分为如下六种HAL :
定义硬件抽象层接口的代码具有以下五个特点:
Android HAL层的目录结构,在Android源码中,HAL主要被保存在下面的目录中:
libhardware_legacy
: 过去的目录,采取了链接库模块观念来架构。libhardware
: 新版的目录,被调整为用I-3AL stub观念来架构。ril
: 是Radio接口层。msm7k
:和QUAL平台相关的信息。 从现在HAL层的结构可以看出,当前的HAL stub模式是一种代理(proxy)的概念,虽然stub仍以“.so”档的形式存在,但是HAL已经将“.so”档隐藏了。Stub向HAL提供了功能强大的操作函数(operation ),而runtime则从HAL获取特定模块(stub)的函数,然后再回调这些操作函数。这种以Indirect Function Call模式的架构,让HAL stub变成了一种“包含”关系,也就是说在HAL里包含了许多stub(模块)。Runtime只要说明module ID(类型)就可以取得操作函数。
在当前的HAL模式中,Android定义了HAL层结构框架,这样通过接口访问硬件时就形成了统一的调用方式。
在HAL module中主要有如下三个结构:
typedef struct hw_module_t {
/** tag必须初始化为HARDWARE_MODULE_TAG*/
uint32_t tag;
uint16_t module_api_version;
#define version_major module_api_version
uint16_t hal_api_version;
#define version_minor hal_api_version
/** 模块id */
const char *id;
/** 模块名称*/
const char *name;
/** 模块作者 */
const char *author;
/**模块方法*/
struct hw_module_methods_t* methods;
/** 模块的dso */
void* dso;
uint32_t reserved[32-7];
} hw_module_t;
typedef struct hw_module_methods_t {
/** 打开设备的函数*/
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
typedef struct hw_device_t {
/** tag必须初始化为HARDWARE_MODULE_TAG*/
uint32_t tag;
uint32_t version;
/** 设备所属的模块 */
struct hw_module_t* module;
/**留填充*/
uint32_t reserved[12];
/**关闭设备函数,对应上面hw_module_methods_t 结构体的开启函数*/
int (*close)(struct hw_device_t* device);
} hw_device_t;
HAL_MODULE_INFO_SYM是定义具体模块的结构体变量,结构体内部是一个hw_module_t结构体,这种包含关系可以理解为,具体模块继承通用模块hw_module_t结构体
#define HAL_MODULE_INFO_SYM HMI
函数hw_get_module()能够根据模块ID寻找硬件模块动态链接库的地址,然后调用load打开动态链接库从中获取硬件模块结构体地址。
执行后首先是根据固定的符号HAL_MODULE_INFO_SYM寻找到hw module t结构体,然后是在hw_moule_t中hw_module_ methods_t结构体成员函数提供的结构open打开相应的模块,并同时进行初始化操作。因为用户在调用open()时通常会传入一个指向hw device t指针的指针。这样函数open()将对模块的操作函数结构保存到结构体hw_device_t中,用户通过它可以和模块进行交互。
① Native code通过hw_get_module调用获取HAL stub:
hw_get_module (XXX_HARDWARE_MODULE_ID, (const hw_module_t**)&module)
② 通过继承hw_module_methods_t的callback来 open设备:
module->methods->open(module,XXX_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
③ 通过继承 hw_device_t的callback来控制设备(比如LED灯):
sLedDevice->set_on(sLedDevice, led);
sLedDevice->set_off(sLedDevice, led);
这一部分主要了解了Android架构里的HAL层,HAL在Android的Linux内核和应用层之间架起一架桥梁,帮助实现了底层硬件和软件的通信,主要封装了一些硬件厂商的设备驱动程序使其便于商用。