Android開發(fā)之Android和linux的區(qū)別是什么?
Android平臺是基于Linxu內(nèi)核搭建的,Linux內(nèi)核的優(yōu)勢在于大內(nèi)存管理、進程管理、基于權(quán)限的安全模型、統(tǒng)一的驅(qū)動模型、共享庫支持、代碼開源等。
Android平臺在設(shè)計過程中,針對移動終端資源有限的特點,對Linux進行了一定程度的裁剪:砍掉了原生的窗口系統(tǒng)、去除了對GNU Libc的支持(引入了更高效、針對優(yōu)化過的Bionic)、裁剪掉了一些標準Linux工具的部分特性等。
另外Android針對移動終端的特點還對Linux內(nèi)核在鬧鐘(Alarm)、Low Memory Killer、Ashmem、內(nèi)核調(diào)試(Kernel Debugger)、進程間通信(Binder)、日志(Logger)、電源管理(Power Management)等方面做了大量的優(yōu)化。
其中Low Memory Killer相對于Linux標準OOM(Out Of Memory)機制更加靈活,它可以根據(jù)需要殺死進程來釋放需要的內(nèi)存。Low Memory Killer的實現(xiàn)主要位于auroramsmmsm drivers/staging/android/lowmemorykiller.c文件中。
Ashmem為進程間提供大塊共享內(nèi)存,同時為內(nèi)核提供回收和管理這個內(nèi)存的機制。 Ashmem的實現(xiàn)位于systemcorelibcutilsashmem-dev.c文件中。
下面重點介紹進程間通信和電源管理的內(nèi)容。
1.進程間通信
在多進程環(huán)境下,應用程序和后臺服務間通常會運行在不同的進程中,彼此有著獨立的地址空間,但是因為需要相互協(xié)作,彼此間又必須進行通信和數(shù)據(jù)共享,而傳統(tǒng)的進程間通信(IPC,Internet Process Connection)卻有著進程過載和安全漏洞等方面的風險。在Android中,引入了Binder的進程間通信機制,Binder的好處在于在驅(qū)動層面就對進程間通信提供了支持、通過SMD共享內(nèi)存機制提高了進程間通信的性能、采用線程池的方式來處理進程請求、針對系統(tǒng)中的對象引入了引用計數(shù)機制和跨進程的對象引用映射機制、在進程間的同步調(diào)用。圖1顯示了Android的進程間通信過程。
圖1 Android的進程間通信過程
為了進行進程間通信,Binder采用AIDL(Android Interface Definition Lanaguage)來描述進程間的接口。
在實際的實現(xiàn)中,Binder是作為一個特殊的字符型設(shè)備來存在的,其實現(xiàn)遵循Linux設(shè)備驅(qū)動模型,相關(guān)的主要代碼位于auroramsmmsmdriversstagingandroid binder.c文件中。
在Binder驅(qū)動中,binder_thread_write()函數(shù)通過binder_transaction()函數(shù)來發(fā)送請求或返回結(jié)果,而binder_thread_read()函數(shù)用于讀取結(jié)果,Binder主要通過binder_ioctl()函數(shù)與用戶空間的進程交換數(shù)據(jù)。
Binder的私有數(shù)據(jù)結(jié)構(gòu)binder_proc則被用來記錄當前進程、進程ID、內(nèi)存映射信息、Binder的統(tǒng)計信息和線程信息等。
如果收到請求,binder_transaction()函數(shù)會通過對象的句柄找到對象所在的進程,如果句柄為空就認為對象是 context_mgr,把請求發(fā)給context_mgr所在的進程。所有的Binder對象會全部放到一個RB樹中。最后context_mgr把請求放到目標進程的事件隊列中,等待目標進程讀取。數(shù)據(jù)的解析工作放在binder_parse()中實現(xiàn)。
下面是Binder驅(qū)動中最重要的binder_ioctl()函數(shù)的實現(xiàn):
代碼1-1 binder_ioctl()函數(shù)的實現(xiàn)
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 %lxn", 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))) { //從用戶空間緩沖復制數(shù)據(jù)
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 %08lxn",
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); //傳遞數(shù)據(jù)
if (ret < 0) {
bwr.read_consumed=0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) //將數(shù)據(jù)寫回用戶空間
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); //讀取數(shù)據(jù)
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 %ldn",
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: 設(shè)置最大線程數(shù)
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret=-EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR: //設(shè)為上下文管理器
if (binder_context_mgr_node!=NULL) {
printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already setn");
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!= %dn",
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樹節(jié)點
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 exitn",
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 %dn", proc->pid, current->pid, cmd, arg, ret);
return ret;
}
2.電源管理
在目前的移動終端中,系統(tǒng)承載的功能越來越多,同時為了獲得更好的用戶體驗,GUI的設(shè)計越來越華麗,但這都不可避免地增加了系統(tǒng)的功耗,導致目前的智能移動終端普遍待機時間較短。在目前電池技術(shù)尚無法有大的突破情況下,電源管理顯得尤為重要,需要在滿足用戶需求的前提下,盡可能地減少功耗。電源管理策略是一個系統(tǒng)工程,應用程序、內(nèi)核框架、設(shè)備驅(qū)動、硬件設(shè)備都涉及其中。
對半導體器件而言,功耗分為靜態(tài)功耗、動態(tài)功耗。靜態(tài)功耗主要是指待機狀態(tài)下的泄漏電流,動態(tài)功耗才是電源管理要解決的主要問題。
Android的電源管理機制是建立在標準的Linux電源管理機制ACPI (Advanced Configuration and Power Interface)之上的,同時針對移動終端的特點采取了更積極的電源管理策略,支持休眠模式、動態(tài)電壓和調(diào)頻調(diào)節(jié)、電源管理質(zhì)量服務(PM QoS)、喚醒鎖等。
休眠模式、動態(tài)電壓和調(diào)頻調(diào)節(jié)等策略這里就不再多做介紹了。下面簡要介紹PM QoS和喚醒鎖的實現(xiàn)。
1)PM QoS
在初始化階段,Android定義的PM QoS參數(shù)有3個:cpu_dma_latency(CPU DMA延遲)、network_latency(網(wǎng)絡延遲)、 network_throughput(網(wǎng)絡吞吐量)。供驅(qū)動、子系統(tǒng)、用戶空間應用等注冊PM QoS請求。默認的參數(shù)級別有延遲、超時(Aurora中暫時不用)、吞吐量等。
在Aurora(auroramsmmsmkernelpm_qos_params.c)中, PM QoS有4個參數(shù):PM_QOS_CPU_DMA_LATENCY、PM_QOS_NETWORK_LATENCY、PM_QOS_NETWORK_ THROUGHPUT和PM_QOS_SYSTEM_BUS_FREQ等,分別針對CPU DMA延遲、網(wǎng)絡延遲、網(wǎng)絡吞吐量、系統(tǒng)總線頻率等性能指標。參數(shù)集的實現(xiàn)在pm_qos_power_init()函數(shù)中進行,使用pm_qos_init()函數(shù)在內(nèi)核里可以增加新的參數(shù)。在系統(tǒng)中,PM QoS主要用來管理CPU空閑管理、WiFi應用等。
在內(nèi)核空間,通過pm_qos_add_requirement()函數(shù)可以注冊PM QoS請求;通過pm_qos_update_requirement()函數(shù)可以更新已注冊的PM QoS請求;通過pm_qos_remove_requirement()函數(shù)可以刪除已注冊的PM QoS請求。圖2顯示了注冊PM QoS請求的過程。
圖2 注冊PM QoS請求的過程
在用戶空間,僅進程可以注冊PM QoS請求,為了注冊PM QoS請求,進程必須打開/dev/[cpu_dma_latency, network_latency, network_throughput]設(shè)備,默認的PM QoS請求名為“process_<PID>”。其中PID值在系統(tǒng)調(diào)用中獲得。
2)喚醒鎖
通過支持多種類型的喚醒鎖(wake locks),Android支持組件在電源管理方面的請求。需要注意的是,在使用喚醒鎖時需要相當小心。圖3顯示了創(chuàng)建喚醒鎖的過程。
圖3 創(chuàng)建喚醒鎖的過程
在實際的開發(fā)過程中,為了測試各應用電量消耗的情況,電量分析軟件powerTop不可或缺,它可以分析出每個具體的應用對電量的消耗情況。
Android電源管理的實現(xiàn)主要位于auroramsmmsmkernelpower目錄下,主要的文件包括earlysuspend.c、consoleearlysuspend.c、fbearlysuspend.c、wakelock.c、userwakelock.c等。
在Java層,Android封裝了一個PowerManager類來進行電源的管理。
3.驅(qū)動
驅(qū)動的實現(xiàn)與硬件平臺密切相關(guān),由于在Linux Kernel 2.6中引入了Linux設(shè)備驅(qū)動模型,Linux的驅(qū)動開發(fā)變得十分簡單。在auroramsmmsmdrivers目錄中,Qualcomm提供了非常多的硬件驅(qū)動,如BT、i2C、USB、FM、音頻、視頻等。下面簡要介紹部分驅(qū)動的情況。
●顯示驅(qū)動(Display Driver):常用基于Linux的幀緩沖( Buffer)驅(qū)動。
●照相機驅(qū)動(Camera):常用基于Linux的V4L2驅(qū)動。
●音頻驅(qū)動:常用基于ALSA(高級Linux音頻架構(gòu),Advanced Linux Sound Architecture)驅(qū)動。
●WIFI驅(qū)動:基于IEEE 802.11標準的驅(qū)動程序。Aurora支持的WIFI標準為802.11 bgn。對WAPI的支持則需要硬件平臺廠商的支持。
●Binder IPC驅(qū)動:Android的一個特殊的驅(qū)動程序,具有單獨的設(shè)備節(jié)點,實現(xiàn)進程間通信的功能。