Android HAL层模块的加载过程
一、概述HAL层是Android系统架构里介于Linux内核和系统运行库层之间的一个层,个人认为这个层存在的目的主要是为了避免GPL协议所带来的开源问题。Android系统会统一加载硬件抽象层模块,负责加载硬件抽象层模块的函数是hw_get_module函数。二、hw_get_module1、hw_get_module原型在Android硬件抽象层中,负责加载硬件抽象层模块的函数是hw_get_m
一、概述
HAL层是Android系统架构里介于Linux内核和系统运行库层之间的一个层,个人认为这个层存在的目的主要是为了避免GPL协议所带来的开源问题。Android系统会统一加载硬件抽象层模块,负责加载硬件抽象层模块的函数是hw_get_module函数。
二、hw_get_module
1、hw_get_module原型
在Android硬件抽象层中,负责加载硬件抽象层模块的函数是hw_get_module,当调用者需要加载这些模块时,只要指定它们的ID值就可以了。它的原型如下:
1 /**
2 * Get the module info associated with a module by id.
3 * @return: 0 == success, <0 == error and *pHmi == NULL
4 */
5 int hw_get_module(const char *id, const struct hw_module_t **module)
{
return hw_get_module_by_class(id,NULL,module);
}
它有id和module两个参数。
id:输入参数,表示要加载的硬件抽象层模块ID;
module:输出参数,如果加载成功,则它指向一个自定义的硬件抽象层模块结构体。
返回值:是一个整数,如果等于0,则表示加载成功;如果小于0,则表示加载失败。
2、hw_get_module函数的实现
该函数在Android源码位置如下:
hardware/libhardware/hardware.c
1
具体实现如下(可跳过不看):
01 /** Base path of the hal modules */
02 #define HAL_LIBRARY_PATH1 "/system/lib/hw"
03 #define HAL_LIBR ARY_PATH2 "/vendor/lib/hw"
04
05 static const char *variant_keys[] = {
06 "ro.hardware", /* This goes first so that it can pick up a different
07 file on the emulator. */
08 "ro.product.board",
09 "ro.board.platform",
10 "ro.arch"
11 };
12
13 static const int HAL_VARIANT_KEYS_COUNT =
14 (sizeof(variant_keys)/sizeof(variant_keys[0]));
15
16 int hw_get_module(const ch ar *id, const struct hw_module_t **module)
17 {
18 int status;
19 int i;
20 const struct hw_module_t *hmi = NULL;
21 char prop[PATH_MAX];
22 char path[PATH_MAX];
23
24 /*
25 * Here we rely on the fact that calling dlopen multiple times on
26 * the same .so will simply increment a refcount (and not load
27 * a new copy of the library).
28 * We also assume that dlopen() is thread-safe.
29 */
30
31 /* Loop through the configuration variants looking for a module */
32 for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
33 if (i < HAL_VARIANT_KEYS_COUNT) {
34 if (property_get(variant_keys[i], prop, NULL) == 0) {
35 continue;
36 }
37
38 snprintf(path, sizeof(path), "%s/%s.%s.so",
39 HAL_LIBRARY_PATH1, id, prop);
40 if (access(path, R_OK) == 0) break;
41
42 snprintf(path, sizeof(path), "%s/%s.%s.so",
43 HAL_LIBRARY_PATH2, id, prop);
44 if (access(path, R_OK) == 0) break;
45 } else {
46 snprintf(path, sizeof(path), "%s/%s.default.so",
47 HAL_LIBRARY_PATH1, id);
48 if (access(path, R_OK) == 0) break;
49 }
50 }
51
52 status = -ENOENT;
53 if (i < HAL_VARIANT_KEYS_COUNT+1) {
54 /* load the module, if this f ails, we're doomed, and we should not try
55 * to load a different variant. */
56 status = load(id, path, module);
57 }
58
59 return status;
60 }
数组variant_keys:它用来组装要加载的硬件抽象层模块的文件名称。
常量HAL_VARIANT_KEYS_COUNT:表示数组variant_keys的大小。
宏HAL_LIBRARY_PATH1和HAL_LIBRARY_PATH2:用来定义要加载的硬件抽象层模块文件所在的目录。(编译好的模块文件位于out/target/product/generic/system/lib/hw目录中,而这个目录经过打包后,就对应于设备上的/system/lib/hw目录。)
宏HAL_LIBRARY_PATH2所定义的目录/vendor/lib/hw,用来保存设备厂商所提供的硬件抽象层模块接口文件。
函数第32行到第50行的for循环根据数组variant_keys在HAL_LIBRARY_PATH1和HAL_LIBRARY_PATH2目录中检查对应的硬件抽象层模块文件是否存在,如果存在,则结束for循环;第56行调用load函数来执行加载硬件抽象层模块的操作。
三、加载的具体步骤
以加载硬件抽象层模块freg为例,来分析硬件抽象层模块的加载过程。
Step1:(传参数给hw_get_module)调用hw_get_module函数加载硬件抽象层模块freg,传入的参数id的值为FREG_HARDWARE_MODULE_ID。
Step2:(获取模块路径)在上面的for循环中,首先找到通过property_get函数获得的系统属性“ro.hardware”的值。在Android模拟器中,这个属性的值定义为“goldfish”,于是通过第38和39两行的snprintf函数,就得到变量path的值为“/system/lib/hw /freg.goldfish.so”。
Step3:(判断模块是否存在)第40行调用access函数判断文件/system/lib/hw/freg.goldfish.so 是否存在,如果存在,就跳出循环;否则,再通过第42行到第44行的代码来判断文件/vendor/lib/hw /freg.goldfish.so是否存在,如果存在,那么也会跳出循环,因为要加载的硬件抽象层模块文件已经找到了。如果这两个文件都不存在,那么按照相同的方法来依次查找数组variant_keys中其他元素所对应的硬件抽象层模块文件是否存在。如果数组variant_keys中的所有元素对应的硬件抽象层模块文件都不存在,那么第46行到第48行的代码就会在“/system/lib/hw”目录中检查是否存在一个freg.default.so文件。如果也不存在,那么硬件抽象层模块freg的加载就失败了。
Step4:(加载模块,得到模块句柄值)找到了硬件抽象层模块文件之后,第56行就调用load函数来执行硬件抽象层模块的加载操作,它的实现如下所示。hardware/libhardware/hardware.c
hardware/libhardware/hardware.c
(可以跳过该代码,直接看下面的解释)
01 static int load(const char *id,
02 const char *path,
03 const struct hw_module_t **pHmi)
04 {
05 int status;
06 void *handle;
07 struct hw_module_t *hmi;
08
09 /*
10 * load the symbols resolving undefined symbols before
11 * dlopen returns. Since RTLD_GLOBAL is not or'd in with
12 * RTLD_NOW the external symbols will not be global
13 */
14 handl e = dlopen(path, RTLD_NOW);//打开动态链接库
15 if (handle == NULL) {
16 char const *err_str = dlerror();
17 LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
18 status = -EINVAL;
19 goto done;
20 }
21
22 /* 获得结构体hal_module_info的地址 */
23 const char *sym = HAL_MODULE_INFO_SYM_AS_STR;// 被定义为了“ HMI ”
24 hmi = (struct hw_module_t *)dlsym(handle, sym);// 查找“ HMI ”这个导出符号,并获取其地址
25 if (hmi == NULL) {
26 LOGE("load: couldn't find symbol %s", sym);
27 status = -EINVAL;
28 goto done;
29 }
30
31 /* 检查ID是否匹配 */
32 if (strcmp(id, hmi->id) != 0) {
33 LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
34 status = -EINVAL;
35 goto done;
36 }
37
38 hmi->dso = handle;
39
40 /* 找到模块 */
41 status = 0;
42
43 done:
44 if (status != 0) {
45 hmi = NULL;
46 if (handle != NULL) {
47 dlclose(handle);
48 handle = NULL;
49 }
50 } else {
51 LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
52 id, path, *pHmi, handle);
53 }
54
55 *pHmi = hmi;//模块句柄值handle保存在hw_module_t结构体指针hmi的成员变量dso中,返回给调用者。
56
57 return status;//0表示成功
58 }
前面提到,硬件抽象层模块文件实际上是一个动态链接库文件,即so文件。因此,第14行调用dlopen函数将它加载到内存中。加载完成这个动态链接库文件之后,第24行就调用dlsym函数来获得里面名称为HAL_MODULE_INFO_SYM_AS_STR的符号。这个HAL_MODULE_INFO_SYM_AS_STR符号指向的是一个自定义的硬件抽象层模块结构体,也就是获取了对应的硬件抽象层模块的所有信息。AL_MODULE_INFO_SYM_AS_STR是一个宏,它的值定义为“HMI”。根据硬件抽象层模块的编写规范,每一个硬件抽象层模块都必须包含一个名称为“HMI”的符号,而且这个符号的第一个成员变量的类型必须定义为hw_module_t,因此,第24行可以安全地将模块中的HMI符号转换为一个hw_module_t结构体指针。
获得了这个hw_module_t结构体指针之后,第32行调用strcmp函数来验证加载得到的硬件抽象层模块ID是否与所要求加载的硬件抽象层模块ID一致。如果不一致,就说明出错了,函数返回一个错误值-EINVAL。最后,第38行将成功加载后得到的模块句柄值handle保存在hw_module_t结构体指针hmi的成员变量dso中,然后将它返回给调用者。
至此,HAL层的一个模块加载完毕。
四、小结
硬件抽象层加载一个模块无非就是:
调用者传目标模块的ID参数到加载函数 。
获取目标模块的存储路径。
检查该路径下是否有该模块的.so文件。
加载模块至内存。
将句柄返回给调用者。
剧终~
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)