Android系統和Linux系統的對比

 

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_<PID>”。其中PID值在系統調用中獲得。

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的一個特殊的驅動程序,具有單獨的設備節點,實現進程間通信的功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章