4.3.2 staging中的驱动程序
Android专用驱动程序大部分放置在drivers/staging/android/目录中,这个目录是Android系统的特有目录,其中包含了特有的Kconfig和Makefile文件。
Makefile的内容如下所示:
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o obj-$(CONFIG_ANDROID_LOGGER) += logger.o obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
其中,binder和logger是两个普通的misc驱动程序,timed_output是一种Android特有的驱动程序框架;timed_gpio是基于timed_output的一个驱动程序;lowmemorykiller是一个内存管理的组件; ram_console是一个利用控制台驱动的框架。
1.Binder驱动程序
Android的Binder驱动程序为用户层程序提供了IPC(进程间通信)支持,Android整个系统的运行依赖Binder驱动。Binder提供给用户空间的接口是主设备号为10的Misc字符设备,其次设备号是动态生成的。在用户空间中,Binder设备节点为/dev/binder。
Binder驱动程序内容由binder.h和binder.c这两个文件组成。Binder设备对用户空间主要提供mmap,poll,ioctl等接口。
binder.h中的BinderDriverReturnProtocol和BinderDriverCommandProtocol两个枚举值主要在ioctl中使用,内容如下所示:
enum BinderDriverReturnProtocol { BR_ERROR = _IOR('r', 0, int), BR_OK = _IO('r', 1), /* 其他返回值 */ } enum BinderDriverCommandProtocol { BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data), BC_REPLY = _IOW('c', 1, struct binder_transaction_data), /* 其他命令 */ }
Binder驱动的使用相对复杂,同时在用户空间内需要对其调用poll接口阻塞和调用mmap映射其中内容。
在Android系统的用户空间中,Service Manager守护进程中调用Binder接口提供对整个系统的支持,Service Manager守护进程的路径为:
frameworks/base/cmds/servicemanager/
libbinder库是在Android系统中的一个对Binder驱动程序封装的库。主要内容在以下的目录中:
frameworks/base/include/binder/:Binder驱动在用户空间的封装接口;
rameworks/base/libs/binder/:Binder驱动在用户空间的封装实现。
提示:在Donut版本之前binder是libutils的一部分,Eclair版本分拆libutils中的binder部分成为一个新的库。
Binder是Android中主要使用的IPC方式,通常只需要按照模板定义相关的类即可,不需要直接调用Binder驱动程序的设备节点。
2.Logger驱动程序
Android的Logger驱动程序为用户层程序提供log支持,这个驱动作为一个工具来使用。提供给用户空间的接口是主设备号为10的Misc字符设备,其次设备号是动态生成的(3个)。
在Android系统的用户空间中,Logger有3个设备节点,均在/dev/log目录中。
/dev/log/main:主要的log
/dev/log/event:事件的log
/dev/log/radio:Modem部分的log
Logger驱动程序为用户空间提供了ioctl,read和异步write等接口。
Logger驱动程序几个ioctl的命令如下所示:
#define __LOGGERIO 0xAE #define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* log的大小 */ #define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* log可用的长度 */ #define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* 下一个入口的长度 */ #define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* 刷新log */
对于非本用户和本组,Logger驱动程序的设备节点是只可写不可读的。
在Android的用户空间,liblog库是对Logger驱动程序的封装,路径为system/core/liblog/。logcat程序调用Logger驱动,其代码的目录为是system/core/logcat/。logcat是一个可执行程序,用户取出系统log的信息,这是在系统中使用的一个辅助工具。
3.Lowmemorykiller组件
Lowmemorykiller提供了在低内存状况下的,杀死进程的功能。使用lowmemorykiller可以在用户空间设置一个内存的阀值,通过这个阀值来判断进程是不是将要被杀死。
Lowmemorykiller只包含lowmemorykiller.c一个源文件。
这个驱动程序,通过调用Linux内存管理系统的接口,注册一个shrinker。这个shrinker就是Lowmemorykiller的实现。主要内容如下所示:
static struct shrinker lowmem_shrinker = { .shrink = lowmem_shrink, .seeks = DEFAULT_SEEKS * 16 }; module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, S_IRUGO | S_IWUSR); module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, S_IRUGO | S_IWUSR); module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
由此可见,Lowmemorykiller使用了以下4个sys系统文件,在控制台中查看其内容如下所示:
# cat /sys/module/lowmemorykiller/parameters/debug_level 2 # ls /sys/module/lowmemorykiller/minfree /sys/module/lowmemorykiller/minfree: No such file or directory # cat /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144 # cat /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15 # cat /sys/module/lowmemorykiller/parameters/cost 32
在Android系统的用户空间中,这是Android管理进程的一种策略,oom_adj(Out of Memory Adjust)的数值越小,代表进程的优先级别越高。例如:“当minfree小于数值A时,结束oom_adj大于数值B的进程”。Android系统用户空间中的ActivityManagerService负责管理这个内容。
在Android中,查看一个前台的进程和一个后台的进程的oom_adj分别如下所示:
# cat /proc/298/oom_adj 0 # cat /proc/154/oom_adj 15
前台进程的oom_adj为0,后台为15。这是系统配置和管理的结果。
4.Timed Output驱动程序框架
Timed Output(定时输出)实际上是一个驱动程序的框架,用于定时发出一个输出。实际上,这种驱动程序依然是基于sys文件系统来完成的。
提示:Timed Output框架提供,需要各个设备实现的是enable和get_time这两个接口,框架通过sys文件系统提供用户空间的接口。
Timed Output驱动程序框架如图4-1所示。
图4-1 Timed Output驱动程序框架
timed output将注册/sys/class/timed_output/目录,每一个注册实现的timed output设备将在/sys/class/timed_output/目录中建立一个与设备同名的子目录,子目录中具有一个enable文件,对这个文件的读/写用于显示和控制设备。
timed_output.h中定义timed_output_dev结构体,其内容如下所示:
struct timed_output_dev { const char *name; /* 使能设备并设置定时器 */ void (*enable)(struct timed_output_dev *sdev, int timeout); /* 返回定时器剩余的时间(milliseconds为单位) */ int (*get_time)(struct timed_output_dev *sdev); /* 私有数据 */ struct device *dev; int index; int state; };
Timed Output设备的注册和注销函数如下所示:
extern int timed_output_dev_register(struct timed_output_dev *dev); extern void timed_output_dev_unregister(struct timed_output_dev *dev);
在timed_output.c的实现中,直接调用sys文件系统的方法来实现,主要的内容是show和store这两个函数,如下所示:
static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *tdev = dev_get_drvdata(dev); int remaining = tdev->get_time(tdev); return sprintf(buf, "%d\n", remaining); } static ssize_t enable_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct timed_output_dev *tdev = dev_get_drvdata(dev); int value; sscanf(buf, "%d", &value); /* 更新内容 */ tdev->enable(tdev, value); /* 使能设备 */ return size; } static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
enable文件是每个Timed Output设备中都具有的文件,写这个文件表示设置定时器时间并启动定时器,读这个文件表示查看定时器剩余的时间。
基于timed output驱动程序框架实现的驱动程序,则主要需要实现enable和get_time这两个函数指针,需要驱动程序自己去实现。
5.Timed Gpio驱动程序
Timed Gpio实际上是一个基于Timed Output驱动程序框架的驱动程序,用于定时控制GPIO。它调用timed output框架注册了一个驱动程序。在enable和get_time函数中调用gpio子系统的内容实现功能。
Timed Gpio驱动的名称在timed_gpio.h中定义,如下所示:
#define TIMED_GPIO_NAME "timed-gpio"
因此,这个驱动程序的控制方式是/sys/class/timed_output/中的enable文件。
struct timed_gpio作为这个驱动程序的私有结构体,保存GPIO的相关数据。timed_gpio.c中,调用timed output的接口,注册了这个驱动程序。
6.Ram控制台
Ram Console提供了一种可以辅助调试的内核机制。实现的方式是利用一块内存构建一个虚拟的console设备。当内核中打印信息(调用printk)的时候,调试信息将同时输出到这个console设备中。
Ram Console与用户空间的接口是/proc文件系统。在proc中使用名称为last_kmsg的文件,表示kernel最后打出的信息。
ram_console.c文件中注册的内容如下所示:
static int __init ram_console_late_init(void) { struct proc_dir_entry *entry; /* ...... 省略部分内容 */ entry = create_proc_entry("last_kmsg", S_IFREG | S_IRUGO, NULL); // 建立文件 if (!entry) { kfree(ram_console_old_log); ram_console_old_log = NULL; return 0; } entry->proc_fops = &ram_console_file_ops; entry->size = ram_console_old_log_size; return 0; }
以上代码用于在用户空间的proc文件系统中,增加了一个last_kmsg的文件,其文件的操作在ram_console_file_ops中实现。last_kmsg文件支持读取,读取的内容就是console的输出信息。