Android系统级深入开发
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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的输出信息。