310 likes | 749 Views
Android 嵌入式系统软件开发. Android Embedded System Software Development 周 庆 国 ( zhouqg@lzu.edu.cn ) 兰 州 大 学. 第四章 Android Linux 内核. 4.1 标准的 Linux 内核 4.2 Android 系统的驱动 4.2.1 Ashmem 驱动 4.2.2 Binder 驱动 4.2.3 Log 驱动. 4.1 标准的 Linux 内核. Android 的启动流从宏观上大致可以划分为以下几个步骤:
E N D
Android嵌入式系统软件开发 Android Embedded System Software Development 周 庆 国 (zhouqg@lzu.edu.cn) 兰 州 大 学
第四章 AndroidLinux内核 • 4.1标准的Linux内核 • 4.2 Android系统的驱动 • 4.2.1 Ashmem驱动 • 4.2.2 Binder驱动 • 4.2.3 Log驱动
4.1 标准的Linux内核 Android的启动流从宏观上大致可以划分为以下几个步骤: (1)上电,并执行相关的Boot ROM代码。 (2)启动引导加载程序Boot Loader。 (3)Linux内核启动并执行。 (4)Init进程启动并执行。 (5)Zygote和Dalvik虚拟机启动并执行。 (6)System Server启动并执行。
ServiceManager进程 servicemanager、vold、netd、debuggerd、rild、zygote、mediaserver、installd、keystore、sh、qemud、adbd等都是由1号Init进程进行fork孵化出来的。可以说ServiceManager是Init创建并启动的第一个Android服务程序(shell除外,因其为传统Linux范畴)。它是Android系统中所有本地服务的管理者(大管家),当客户端应用程序要访问系统所提供的相关服务时,都将经过ServiceManager服务管理者进行查询并获得相关的服务功能。同时,这个服务访问过程中,会涉及到Android中的IPC架构,即Binder IPC通讯机制,而该IPC通讯机制本质上也是相当于一个轻量级的CORBA架构。
System Server进程 返回首页
4.2 Android系统的驱动 4.2.1 Ashmem驱动 匿名共享内存(Ashmem) ,为进程间提供大块共享内存,同时为内核提供回收和管理这个内存的机制。 源代码位于mm/ashmem.c 基于mmap系统调用,不同进程可以将同一段物理内存映射到各自的虚拟地址空间,从而实现共享。 Ashmem与mmap的区别在于Ashmem与cache shrinker关联起来,可以控制cache shrinker在适当时机回收这些共享内存。
Ashmem 通过注册Cache Shrinker回收内存 通过注册misc设备提供open,mmap等接口,mmap则通过tmpfs创建文件来分配内存,tmpfs将一块内存虚拟为一个文件,这样操作共享内存就相当于操作一个文件。 用结构体ashmem_area和ashmem_range来维护分配的内存。 ashmem_area代表共享的内存区域ashmem_range则将这段区域以页为单位分为多个range
Ashmem ashmem_area有个unpinned_list成员,挂在这个list上的range可以被回收。 ashmem_range有一个LRU链表,在cache shrink回收一个ashmem_area的某段内存时候,是根据LRU的原则来选择哪些页面优先被回收的。
Ashmem 返回首页 Ashmem驱动创建了/dev/ashmem设备文件。 进程通过open打开该文件,用ioctl命令ASHMEM_SET_NAME和ASHMEM_SET_SIZE设置共享内存块的名字和大小,并将得到的handle传给mmap,来获得共享的内存区域,其他进程可以通过将相同的handle传给mmap,获得同一块内存。 handle在进程间的传递可通过Binder来实现。
4.2.2 Binder驱动 Android Binder ,基于OpenBinder框架的一个驱动,用于提供Android平台的进程间通讯(IPC,inter-process communication)。 在实际的实现中,Binder是作为一个特殊的字符型设备而存在的,设备节点为/dev/binder,其实现遵循Linux设备驱动模型。 源码位于drivers/staging/Android/binder.c
Binder机制的组成 Android的Binder机制就是一个C/S构架,客户端和服务端直接通过Binder交互数据,打开Binder写入数据,通过Binder读取数据,这样通讯就可以完成了。
Binder机制的组成 (1)Service Manager Service Manager主要负责管理Android系统中所有的服务 (2)服务(Server) 这里的服务即上面所说的服务端,通常也是Android的系统服务 (3)客户端 这里的客户端一般是指Android系统上面的应用程序。它可以请求Server中的服务,比如Activity。 (4)服务代理 服务代理是指在客户端应用程序中生成的Server代理(proxy)。
Binder驱动 最基础的结构体binder_work Binder的类型是使用定义在binder.h头文件中的一个enum来表示 Binder被分成了5个不同的类型,但是仔细一看却是3个不同的大类,它们分别是:本地对象(BINDER_TYPE_BINDER、BINDER_TYPE_WEAK_BINDER)、远程对象的“引用”(BINDER_TYPE_HANDLE、BINDER_TYPE_WEAK_HANDLE)和文件(BINDER_TYPE_FD)。
Binder驱动 进程之间传递的数据称之为Binder对象(Binder Object),对应flat_binder_object结构体(位于binder.h文件中) 其实并没有从flat_binder_object结构体中看到Binder对象所传递的实际内容,Binder对象所传递的实际内容是通过结构体binder_transaction_data来表示的 binder_proc结构体用于保存调用Binder的各个进程或线程的信息,比如线程ID、进程ID、Binder状态信息等
Binder驱动 binder_node结构体表示一个Binder节点 binder_thread结构体用于存储每一个单独的线程的信息 binder_transaction结构体主要用来中转请求和返回结果,保存接收和要发送的进程信息 binder_init是Binder驱动的初始化函数,由设备驱动接口来调用。Android Binder设备驱动接口函数是device_initcall,与module_init和module_exit不一样。使用module_init和module_exit是为了同时兼容支持静态编译的驱动模块(buildin)和动态编译的驱动模块(module),但是Binder选择使用device_initcall的目的就是不让Binder驱动支持动态编译,而且需要在内核(Kernel)做镜像。
Binder驱动 proc_mkdir创建了一个Binder的proc文件系统的根节点(binder_ proc_dir_entry_root,/proc/binder) 如果根节点创建成功,紧接着为binder创建binder proc节点(binder_proc_dir_entry_proc,/proc/binder/proc) 然后,Binder驱动使用misc_register把自己注册为一个Misc设备,其设备节点位于/dev/binder,该节点由init进程在handle_device_fd(device_fd)函数中调用handle_device_event(&uevent)函数执行其中uevent-netlink事件在"/dev/"目录下创建。 最后,调用create_proc_read_entry创建以下只读proc文件: /proc/binder/state /proc/binder/stats /proc/binder/transactions /proc/binder/transaction_log /proc/binder/failed_transaction_log 在创建这些文件的过程中,同时也指定了操作这些文件的函数及其参数
Binder驱动 Binder设备的主设备号为10,此设备号是动态获得的,.minor被设置为动态获得设备号MISC_DYNAMIC_MINOR;.name代表设备名称。 //Binder文件操作结构体 static struct file_operations binder_fops = { .owner = THIS_MODULE, .poll = binder_poll, .unlocked_ioctl = binder_ioctl, .mmap = binder_mmap, .open = binder_open, .flush = binder_flush, .release = binder_release, };
Binder驱动 binder_open函数用于打开Binder设备文件/dev/binder binder_release函数与binder_open函数的功能相反,当Binder驱动退出时,需要使用它来释放在打开以及其他操作过程中分配的空间并清理相关的数据信息 flush操作接口将在关闭一个设备文件描述符复制时被调用 poll函数是非阻塞型IO的内核驱动实现,所有支持非阻塞IO操作的设备驱动都需要实现poll函数 该函数可以用于在threads队列中查找当前的进程信息 mmap(memory map)用于把设备内存映射到用户进程地址空间中,这样就可以像操作用户内存那样操作设备内存
Binder驱动 返回首页 Binder最核心的部分了,Binder的功能就是通过ioctl命令来实现的。Binder的ioctl命令共有7个,定义在ioctl.h 头文件中 #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t) #define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t) #define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int) #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int) #define BINDER_THREAD_EXIT _IOW('b', 8, int) #define BINDER_VERSION _IOWR('b', 9, struct binder_version)
4.2.3 Log驱动 代码:kernel/drivers/staging/android/Logger.c static int __init logger_init(void) { int ret; ret = init_log(&log_main); if (unlikely(ret)) goto out; ret = init_log(&log_events); …… }
Log驱动 Logger驱动的入口点就是Logger_init() 初始化log_main, log_events, log_radio和 log_system四个logger_log类型的结构 实现了同种设备的四个驱动,是MISC类型的驱动,通过misc_register()向系统注册,Looger也是通过minor来区分是哪一个驱动
Log驱动 static struct logger_log *get_log_from_minor(int minor) { if (log_main.misc.minor == minor) return &log_main; if (log_events.misc.minor == minor) return &log_events; if (log_radio.misc.minor == minor) return &log_radio; if (log_system.misc.minor == minor) return &log_system; return NULL; }
Logger实现的功能 返回首页 Logger就是实现了读写者,并实现同步操作。不过,Logger的读写者有些特殊,写者写操作不会被阻塞,也不会写满溢出,也就是写时只要有内容可以不停的写,超出Buffer就覆盖旧的[与应用程序具体的写操作结合来看]; 读者因为要读的内容为空就会被阻塞挂起,而一旦有内容,所有被挂起的读者都会被唤醒[与应用程序具体的读操作结合来看]。
谢谢 31 31