Android平台是基于Linxu内核搭建的,Linux内核的优势在于大内存管理、进程管理、基于权限的安全模型、统一的驱动模型、共享库支持、代码开源等。
Android平台在设计过程中,针对移动终端资源有限的特点,对Linux进行了一定程度的裁剪:砍掉了原生的窗口系统、去除了对GNU Libc的支持(引入了更高效、针对嵌入式优化过的Bionic)、裁剪掉了一些标准Linux工具的部分特性等。
另外Android针对移动终端的特点还对Linux内核在闹钟(Alarm)、Low Memory Killer、Ashmem、内核调试(Kernel Debugger)、进程间通信(Binder)、日志(Logger)、电源管理(Power Management)等方面做了大量的优化。
其中Low Memory Killer相对于Linux标准OOM(Out Of Memory)机制更加灵活,它可以根据需要杀死进程来释放需要的内存。Low Memory Killer的实现主要位于aurora\msm\msm drivers/staging/android/lowmemorykiller.c文件中。
Ashmem为进程间提供大块共享内存,同时为内核提供回收和管理这个内存的机制。 Ashmem的实现位于system\core\libcutils\ashmem-dev.c文件中。
下面重点介绍进程间通信和电源管理的内容。
1.进程间通信
在多进程环境下,应用程序和后台服务间通常会运行在不同的进程中,彼此有着独立的地址空间,但是因为需要相互协作,彼此间又必须进行通信和数据共享,而传统的进程间通信(IPC,Internet Process Connection)却有着进程过载和安全漏洞等方面的风险。在Android中,引入了Binder的进程间通信机制,Binder的好处在于在驱动层面就对进程间通信提供了支持、通过SMD共享内存机制提高了进程间通信的性能、采用线程池的方式来处理进程请求、针对系统中的对象引入了引用计数机制和跨进程的对象引用映射机制、在进程间的同步调用。图1显示了Android的进程间通信过程。
图1 Android的进程间通信过程
为了进行进程间通信,Binder采用AIDL(Android Interface Definition Lanaguage)来描述进程间的接口。
在实际的实现中,Binder是作为一个特殊的字符型设备来存在的,其实现遵循Linux设备驱动模型,相关的主要代码位于aurora\msm\msm\drivers\staging\android\ binder.c文件中。
在Binder驱动中,binder_thread_write()函数通过binder_transaction()函数来发送请求或返回结果,而binder_thread_read()函数用于读取结果,Binder主要通过binder_ioctl()函数与用户空间的进程交换数据。
Binder的私有数据结构binder_proc则被用来记录当前进程、进程ID、内存映射信息、Binder的统计信息和线程信息等。
如果收到请求,binder_transaction()函数会通过对象的句柄找到对象所在的进程,如果句柄为空就认为对象是 context_mgr,把请求发给context_mgr所在的进程。所有的Binder对象会全部放到一个RB树中。最后context_mgr把请求放到目标进程的事件队列中,等待目标进程读取。数据的解析工作放在binder_parse()中实现。
下面是Binder驱动中最重要的binder_ioctl()函数的实现:
代码1-1 binder_ioctl()函数的实现
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc=filp->private_data;
struct binder_thread *thread;
unsigned int size=_IOC_SIZE(cmd);
void __user *ubuf=(void __user *)arg;
/*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
return ret;
mutex_lock(&binder_lock);
thread=binder_get_thread(proc); //获取一个Binder线程
if (thread==NULL) {
ret=-ENOMEM;
goto err;
}
switch (cmd) {
case BINDER_WRITE_READ: {
struct binder_write_read bwr;
if (size!=sizeof(struct binder_write_read)) {
ret=-EINVAL;
goto err;
}
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { //从用户空间缓冲复制数据
ret=-EFAULT;
goto err;
}
if (binder_debug_mask & BINDER_DEBUG_READ_WRITE)
printk(KERN_INFO "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
proc->pid, thread->pid, bwr.write_size, bwr.write_buffer, bwr.read_size, bwr.read_buffer);
if (bwr.write_size > 0) {
ret=binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed); //传递数据
if (ret < 0) {
bwr.read_consumed=0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) //将数据写回用户空间
ret=-EFAULT;
goto err;
}
}
if (bwr.read_size>0) {
ret=binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); //读取数据
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait); //唤醒挂起的线程
if (ret<0) {
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto err;
}
}
if (binder_debug_mask & BINDER_DEBUG_READ_WRITE)
printk(KERN_INFO "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
proc->pid, thread->pid, bwr.write_consumed, bwr.write_size, bwr.read_consumed, bwr.read_size);
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret=-EFAULT;
goto err;
}
break;
}
case BINDER_SET_MAX_THREADS: 设置最大线程数
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret=-EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR: //设为上下文管理器
if (binder_context_mgr_node!=NULL) {
printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
ret=-EBUSY;
goto err;
}
if (binder_context_mgr_uid!=-1) {
if (binder_context_mgr_uid!=current->cred->euid) {
printk(KERN_ERR "binder:BINDER_SET_"
"CONTEXT_MGR bad uid %d!= %d\n",
current->cred->euid,
binder_context_mgr_uid);
ret=-EPERM;
goto err;
}
} else
binder_context_mgr_uid=current->cred->euid;
binder_context_mgr_node=binder_new_node(proc, NULL, NULL);//新的RB树节点
if (binder_context_mgr_node==NULL) {
ret=-ENOMEM;
goto err;
}
binder_context_mgr_node->local_weak_refs++;
binder_context_mgr_node->local_strong_refs++;
binder_context_mgr_node->has_strong_ref = 1;
binder_context_mgr_node->has_weak_ref = 1;
break;
case BINDER_THREAD_EXIT: //销毁消除
if (binder_debug_mask & BINDER_DEBUG_THREADS)
printk(KERN_INFO "binder: %d:%d exit\n",
proc->pid, thread->pid);
binder_free_thread(proc, thread); //释放线程
thread=NULL;
break;
case BINDER_VERSION: //获取Binder版本信息
if (size!=sizeof(struct binder_version)) {
ret=-EINVAL;
goto err;
}
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->protocol_version)) {
ret=-EINVAL;
goto err;
}
break;
default:
ret=-EINVAL;
goto err;
}
ret=0;
err:
if (thread)
thread->looper&=~BINDER_LOOPER_STATE_NEED_RETURN;
mutex_unlock(&binder_lock);
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret !=-ERESTARTSYS)
printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
return ret;
}
2.电源管理
在目前的移动终端中,系统承载的功能越来越多,同时为了获得更好的用户体验,GUI的设计越来越华丽,但这都不可避免地增加了系统的功耗,导致目前的智能移动终端普遍待机时间较短。在目前电池技术尚无法有大的突破情况下,电源管理显得尤为重要,需要在满足用户需求的前提下,尽可能地减少功耗。电源管理策略是一个系统工程,应用程序、内核框架、设备驱动、硬件设备都涉及其中。
对半导体器件而言,功耗分为静态功耗、动态功耗。静态功耗主要是指待机状态下的泄漏电流,动态功耗才是电源管理要解决的主要问题。
Android的电源管理机制是建立在标准的Linux电源管理机制ACPI (Advanced Configuration and Power Interface)之上的,同时针对移动终端的特点采取了更积极的电源管理策略,支持休眠模式、动态电压和调频调节、电源管理质量服务(PM QoS)、唤醒锁等。
休眠模式、动态电压和调频调节等策略这里就不再多做介绍了。下面简要介绍PM QoS和唤醒锁的实现。
1)PM QoS
在初始化阶段,Android定义的PM QoS参数有3个:cpu_dma_latency(CPU DMA延迟)、network_latency(网络延迟)、 network_throughput(网络吞吐量)。供驱动、子系统、用户空间应用等注册PM QoS请求。默认的参数级别有延迟、超时(Aurora中暂时不用)、吞吐量等。
在Aurora(aurora\msm\msm\kernel\pm_qos_params.c)中, PM QoS有4个参数:PM_QOS_CPU_DMA_LATENCY、PM_QOS_NETWORK_LATENCY、PM_QOS_NETWORK_ THROUGHPUT和PM_QOS_SYSTEM_BUS_FREQ等,分别针对CPU DMA延迟、网络延迟、网络吞吐量、系统总线频率等性能指标。参数集的实现在pm_qos_power_init()函数中进行,使用pm_qos_init()函数在内核里可以增加新的参数。在嵌入式系统中,PM QoS主要用来管理CPU空闲管理、WiFi应用等。
在内核空间,通过pm_qos_add_requirement()函数可以注册PM QoS请求;通过pm_qos_update_requirement()函数可以更新已注册的PM QoS请求;通过pm_qos_remove_requirement()函数可以删除已注册的PM QoS请求。图2显示了注册PM QoS请求的过程。
图2 注册PM QoS请求的过程
在用户空间,仅进程可以注册PM QoS请求,为了注册PM QoS请求,进程必须打开/dev/[cpu_dma_latency, network_latency, network_throughput]设备,默认的PM QoS请求名为“process_
2)唤醒锁
通过支持多种类型的唤醒锁(wake locks),Android支持组件在电源管理方面的请求。需要注意的是,在使用唤醒锁时需要相当小心。图3显示了创建唤醒锁的过程。
图3 创建唤醒锁的过程
在实际的开发过程中,为了测试各应用电量消耗的情况,电量分析软件powerTop不可或缺,它可以分析出每个具体的应用对电量的消耗情况。
Android电源管理的实现主要位于aurora\msm\msm\kernel\power目录下,主要的文件包括earlysuspend.c、consoleearlysuspend.c、fbearlysuspend.c、wakelock.c、userwakelock.c等。
在Java层,Android封装了一个PowerManager类来进行电源的管理。
3.驱动
驱动的实现与硬件平台密切相关,由于在Linux Kernel 2.6中引入了Linux设备驱动模型,Linux的驱动开发变得十分简单。在aurora\msm\msm\drivers目录中,Qualcomm提供了非常多的硬件驱动,如BT、i2C、USB、FM、音频、视频等。下面简要介绍部分驱动的情况。
● 显示驱动(Display Driver):常用基于Linux的帧缓冲(Frame Buffer)驱动。
● 照相机驱动(Camera):常用基于Linux的V4L2驱动。
● 音频驱动:常用基于ALSA(高级Linux音频架构,Advanced Linux Sound Architecture)驱动。
● WIFI驱动:基于IEEE 802.11标准的驱动程序。Aurora支持的WIFI标准为802.11 b\g\n。对WAPI的支持则需要硬件平台厂商的支持。
● Binder IPC驱动:Android的一个特殊的驱动程序,具有单独的设备节点,实现进程间通信的功能。