第12章工程中的 Linux设备驱动之Android设备驱动

12.8 Android设备驱动

    Android 设备驱动与 Linux 一样,因为 Android 本身基于 Linux 内核,但 Android 对内核引入如下主要补丁。

1.binder IPC 系统

    binder 机制是 Android 提供的一种进程间通信方法,使一个进程可以(以类似远程过程调用的形式)调用另一个进程所提供的功能。MSM8953开发板已经将其代码移植到drivers/staging/android/binder.h、drivers/staging/android/binder.c下面,从代码清单 12.25 可知,是一种典型的以 miscdevice形式实现的字符设备,而且提供了一些/proc 结点。本质上,binder 用户空间的程序绝大多数情况下在底层是调用了 binder 驱动的 ioctl()函数。

    static const struct file_operations binder_fops = {
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .compat_ioctl = binder_ioctl,
    .mmap = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_release,
    };

    struct binder_device {
    struct hlist_node hlist;
    struct miscdevice miscdev;
    struct binder_context context;

    };

static int __init binder_init(void)
{
int ret;
char *device_name, *device_names;
struct binder_device *device;
struct hlist_node *tmp;

atomic_set(&binder_transaction_log.cur, ~0U);
atomic_set(&binder_transaction_log_failed.cur, ~0U);
binder_deferred_workqueue = create_singlethread_workqueue("binder");
if (!binder_deferred_workqueue)
return -ENOMEM;

binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
if (binder_debugfs_dir_entry_root)
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);

if (binder_debugfs_dir_entry_root) {
debugfs_create_file("state",
    S_IRUGO,
    binder_debugfs_dir_entry_root,
    NULL,
    &binder_state_fops);
debugfs_create_file("stats",
    S_IRUGO,
    binder_debugfs_dir_entry_root,
    NULL,
    &binder_stats_fops);
debugfs_create_file("transactions",
    S_IRUGO,
    binder_debugfs_dir_entry_root,
    NULL,
    &binder_transactions_fops);
debugfs_create_file("transaction_log",
    S_IRUGO,
    binder_debugfs_dir_entry_root,
    &binder_transaction_log,
    &binder_transaction_log_fops);
debugfs_create_file("failed_transaction_log",
    S_IRUGO,
    binder_debugfs_dir_entry_root,
    &binder_transaction_log_failed,
    &binder_transaction_log_fops);
}

/*
* Copy the module_parameter string, because we don't want to
* tokenize it in-place.
*/
device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
if (!device_names) {
ret = -ENOMEM;
goto err_alloc_device_names_failed;
}
strcpy(device_names, binder_devices_param);

while ((device_name = strsep(&device_names, ","))) {
ret = init_binder_device(device_name);
if (ret)
goto err_init_binder_device_failed;
}

return ret;

err_init_binder_device_failed:
hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
misc_deregister(&device->miscdev);
hlist_del(&device->hlist);
kfree(device);
}
err_alloc_device_names_failed:
debugfs_remove_recursive(binder_debugfs_dir_entry_root);
destroy_workqueue(binder_deferred_workqueue);

return ret;

}

总结:

    Android 中的 binder 通信基于 Service/Client 模型,所有需要 IBinder 通信的进程都必须创建一个 IBinder 接口。Android 虚拟机启动之前系统会先启动 Service Manager 进程,Service Manager打开 binder 驱动,并通知 binder 驱动程序这个进程将作为 System Service Manager,然后该进程将进入一个循环,等待处理来自其他进程的数据。

    在用户程序方面,Service 端创建一个 System Service 后,通过 defaultServiceManager()可以得到远程 ServiceManager 的接口,通过这个接口可以调用 addService()函数将新的 SystemService 添加到 Service Manager 进程中。对于 Client 端,则可以通过 getService()获取到需要连接的目的 Service 的 IBinder 对象。对用户程序,获得这个对象后就可以通过 binder 驱动访问 Service 对象中的方法。

    Client 与 Service 在不同的进程中,通过这种方式实现了类似线程间的迁移的通信方式,对用户程序,当调用 Service 返回的 IBinder 接口后,访问 Service 中的方法就如同调用自己的函数。两个进程间通信就好像是一个进程进入另一个进程执行代码然后带着执行的结果返回。

2.ashemem 匿名共享内存机制

    ashmem 是 Android 新增的一种内存分配/共享机制,MSM8953开发板将其代码移植到

drivers/staging/android/ashmem.c中。从代码清单 12.26 可知,ashemem也是一种典型的以 miscdevice 形式实现的字符设备。

代码清单 12.26 Android ashmem 驱动

static const struct file_operations ashmem_fops = {
    .owner = THIS_MODULE,
    .open = ashmem_open,
    .release = ashmem_release,
    .read = ashmem_read,
    .llseek = ashmem_llseek,
    .mmap = ashmem_mmap,
    .unlocked_ioctl = ashmem_ioctl,
    #ifdef CONFIG_COMPAT
    .compat_ioctl = compat_ashmem_ioctl,
    #endif
};

static struct miscdevice ashmem_misc = {
    .minor = MISC_DYNAMIC_MINOR, /* 动态分配次设备号 */
    .name = "ashmem",
    .fops = &ashmem_fops,
};

static int __init ashmem_init(void)
{
int ret;

        /* 创建Slab高速缓存 */

ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",
  sizeof(struct ashmem_area),
  0, 0, NULL);
if (unlikely(!ashmem_area_cachep)) {
pr_err("failed to create slab cache\n");
return -ENOMEM;
}

ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",
  sizeof(struct ashmem_range),
  0, 0, NULL);
if (unlikely(!ashmem_range_cachep)) {
pr_err("failed to create slab cache\n");
return -ENOMEM;
}

ret = misc_register(&ashmem_misc); /* 注册混杂设备 */
if ( unlikely(ret)) {
pr_err("failed to register misc device!\n");
return ret;
}

register_shrinker(&ashmem_shrinker);

pr_info("initialized\n");

return 0;
}

分析:

    在 dev 目录下对应的设备是/dev/ashmem,相比于传统的内存分配机制,如 malloc、匿名/命名 mmap,其好处是提供了辅助内核内存回收算法的 pin/unpin 机制。

ashmem 的典型用法是先打开设备文件,然后做 mmap 映射。

(1)先打开设备文件

include/uapi/linux/ashmem.h(可以在用户空间使用)

#define __ASHMEMIOC             0x77

#define ASHMEM_SET_NAME         _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
#define ASHMEM_GET_NAME         _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
#define ASHMEM_SET_SIZE         _IOW(__ASHMEMIOC, 3, size_t)
#define ASHMEM_GET_SIZE         _IO(__ASHMEMIOC, 4)
#define ASHMEM_SET_PROT_MASK    _IOW(__ASHMEMIOC, 5, unsigned long)
#define ASHMEM_GET_PROT_MASK    _IO(__ASHMEMIOC, 6)
#define ASHMEM_PIN              _IOW(__ASHMEMIOC, 7, struct ashmem_pin)
#define ASHMEM_UNPIN            _IOW(__ASHMEMIOC, 8, struct ashmem_pin)
#define ASHMEM_GET_PIN_STATUS   _IO(__ASHMEMIOC, 9)
#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)

fd = open("/dev/ashmem", O_RDWR);
ioctl(fd, ASHMEM_SET_NAME, region_name);

ioctl(fd, ASHMEM_SET_SIZE, region_size);

(2)应用程序一般会调用 mmap 来把 ashmem 分配的空间映射到进程空间:

mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);

    应用程序还可以通过 ioctl()来 pin(ASHMEM_PIN)和 unpin(ASHMEM_UNPIN)某一段映射的空间,以提示内核的 page cache 算法可以把哪些页面回收,这是一般 mmap 做不到的。

    通过ASHMEM_GET_PIN_STATUS 这个 IOCTL 可以查询 pin 的状态。

3.Android 电源管理

    Android 电源管理针对标准 Linux 内核的电源管理进行了一些优化,这部分代码MSM8953开发板已经移植到了 kernel/power/目录。


4.Android Low Memory Killer

    Linux 内核本身提供了 OOM(Out Of Memory)机制, OOM可以在系统内存不够的情况下主动杀死进程腾出内存。不过 Android 的 Low Memory Killer 相对于 Linux 标准 OOM 机制更加灵活, Android 的 Low Memory Killer  可 以 根 据 需 要 杀 死 进 程 来 释 放 内 存 。MSM8953开发板将这部分代码移植到

drivers/staging/android/lowmemorykiller.c文件。

5.Android RAM console 和 log 设备

    为了辅助调试,Android 增加了用于将内核打印消息保存起来的 RAM console和用户应用程序可以写入、读取 log 信息的 logger 设备驱动。MSM开发板将这部分代码放置到了

drivers/staging/android/ram_console.c和drivers/staging/android/logger.c。

RAM console 通过 register_console()被注册,而 logger 又是一个典型的 miscdevice。

6.Android alarm、timed_gpio 等。

    就 Android 系统本身而言,在其上编写设备驱动没有什么神秘的,基本完全按照 Linux 内核本身的框架进行。而 Android 自身引入的这些补丁,曾经有部分进入过 Linux mainline 的drivers/staging 目录,尔后由于缺少维护的原因,被 Greg KH 移除。


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80678608
今日推荐