學習筆記1-深入理解Android內核設計思想

4.1 計算機體系結構(Computer Architecture)

4.1.1 馮·諾依曼結構

兩個深遠影響的觀點:

  • 採用二進制,拋棄十進制

  • 程序存儲(stored-program)

馮·諾依曼結構.png

4.1.2 哈佛結構

對馮諾依曼結構的改進與完善,區別在指令與數據並不保存在同一個存儲器。

這意味着:

  • 指令與數據可以有不同的的數據寬度;

  • 執行速度更快。

哈佛結構.png

計算機結構的基本元素:

  • 中央處理器(CPU)

  • 內存儲器

  • I/O設備

4.2 什麼是操作系統

定義:

計算機操作系統是負責管理系統硬件,併爲上層應用提供穩定編程接口和人機交互界面的軟件集合。

最核心的工作:硬件管理與抽象。

肩負兩大重任:

  • 面向下層:管理硬件(CPU、內存、Flash、各種IO設備)

  • 面向上層:一方面,爲用戶提供可用的人機交互界面;另一方面,負責爲第三方程序的研發提供便捷、可靠、高效的API。

操作系統的難點:進程和內存管理、硬件驅動的支持等,這正是Linux的長處所在。

4.3 進程間通信的經典實現

進程間的通信(Inter-process communication,IPC)是指運行在不同進程(不論是否在同一臺機器)中的若干線程間的數據交換。

實現方式:消息傳遞、管道、文件共享、操作系統提供的公共信息機制等等。

4.3.1 共享內存(Shared Memory)

一種常用的IPC機制,優勢:共享內存區域,減少數據的複製操作,速度快。

實現步驟:

  • 創建內存共享區

  • 映射內存共享區

  • 訪問內存共享區

  • 進程間通信

  • 撤銷內存映射區

  • 刪除內存共享區

4.3.2 管道(Pipe)

一種常見的IPC方式

  • 分立管道的兩邊,進行數據的傳輸通信

  • 管道是單向的

  • 一根管道同時具有“讀取”端(read end)和“寫入”端(write end)

  • 管道有容量限制

4.3.3 UNIX Domain Socket(UDS)

專門針對單機內的進程間通信,有時稱爲IPC Socket。

Network Socket是以TCP/IP協議棧爲基礎,UDS因爲是本地內的“安全可靠操作”,實現機制上並不依賴於這些協議。

典型流程:

UNIX Domain Socket的通信流程.png

UDS的基本流程與傳統Socket一致,只是在參數上有區分:

  • 服務器端監聽IPC請求;

  • 客戶端發起IPC申請;

  • 雙方成功建立起IPC連接;

  • 客戶端向服務端發送數據,證明IPC通信是有效的

4.3.4 Remote Procedure Calls(RPC)

涉及的通信雙方通常運行於兩臺不同的機器中。

RPC通信圖釋.png

4.4 同步機制的經典實現

同步:如果多個進程間存在時序關係,需要協同工作以完成一項任務

互斥:如果多個進程並不滿足協同的條件,而只是因爲共享具有排他性的資源時所產生的關係。

4.4.1 信號量(Semaphore)

涉及的元素:信號量(S)、PV原語操作(有時稱wait()、signal())。

S:共享資源的可用數量

P: 減少S的計數(進入共享區的操作)

V:增加S的計數(退出共享區的操作)

PV操作圖.png

4.4.2 Mutex

是Mutual Exclusion的縮寫,即互斥體

4.4.3 管程(Monitor)

一種控制更爲簡單的同步手段。

可以被多個進程/線程安全訪問的對象(object)或模塊(module)。

具備安全性、互斥性、共享性。

4.4.4 Linux Futex

核心優勢是Fast。

4.4.5 同步範例

生產者與消費者問題:

兩個進程共享一塊大小爲N的緩衝區,其中一個進程負責填充數據(生產者),另一個進程負責讀取數據(消費者)。

問題的核心有兩點:

  • 當緩衝區滿時,禁止生產者繼續添加數據,直到消費者讀取了部分數據;

  • 當緩衝區空時,消費者應等待對方繼續生產後再執行操作。

解決方式:信用量,用到3個Semaphore,功能如下:

  • S_emptyCount: 用於生產者獲取可用的的緩衝空間大小,初始N

  • S_fillCount: 用於消費者獲取可用的數據大小,初始爲0

  • S_mutex: 用於操作緩衝區,初始爲1

生產者的執行步驟:

  • 循環開始;

  • Produce_item;

  • P(S_emptyCount)

  • P(S_mutex)

  • Put_item_to_buffer

  • V(S_mutex)

  • V(S_fillCount)

  • 繼續循環

消費者的執行步驟:

  • 循環開始

  • P(S_fillCount)

  • P(S_mutex)

  • Read_item_from_buffer

  • V(S_mutex)

  • V(S_emptyCount)

  • Consume

  • 繼續循環

4.5 Android中的同步機制

4.5.1 進程間同步 - Mutex

頭文件:frameworks/native/include/utils/Mutex.h

既可以處理進程內同步、又可以解決進程間同步。

4.5.2 條件判斷 - Condition

頭文件:frameworks/native/include/utils/Condition.h

核心思想:判斷“條件是否已經滿足”,如果滿足則馬上返回,繼續執行未完成的動作,否則就進行休眠等待,直到條件滿足時有人喚醒它。

4.5.3 “柵欄、障礙” - Barrier

頭文件:frameworks/native/services/surfaceflinger/Barrier.h

Barrier是填充了“具體條件”的Condition,專門爲SurfaceFlinger而設計。

通常被用於對某線程是否初始化完成進行判斷,這種場景具有不可逆性。

4.5.4 加解鎖的自動化操作 - Autolock

在Mutex類內部的嵌套類Autolock,實現加、解鎖的自動化操作。

當Auto構造時,會主動調用內部成員變量mLock的lock()方法獲取一個鎖。

而析構時正好相反,調用它的unlock()方法釋放鎖。

4.5.5 讀寫鎖 - ReaderWriterMutex

基礎仍是mutex,特點是 允許有多個對象共享Read鎖,但同時卻只能有唯一一個對象擁有Write鎖。

4.6 操作系統內存管理基礎

內存管理是操作系統的重點和難點

內存管理重點理解幾個核心:虛擬內存、內存分配與回收、內存保存。

4.6.1 虛擬內存(Virtual Memory)

虛擬內存:爲大體積程序的運行提供了可能。

基本思想:

  • 將外存儲器的部分空間作爲內存的擴展

  • 當內存資源不足時,系統將按照一定算法自動挑選優先級低的數據塊,並把它們存儲到硬盤中。

  • 後續如果需要用到硬盤中的這些數據塊,系統將產生“缺頁”指令,然後把它們交換回內存中。

  • 這些操作是由操作系統內部內核自動完成,對上層應用“完全透明”。

涉及3種不同的地址空間:

1、邏輯地址

是程序編譯後所產生的地址,Segment Selector(段選擇子, 16bit) + Offset (偏移值,32bit)

2、線性地址

是邏輯地址經過分段機制轉換後形成的

3、物理地址

是指機器真實的物理內存所能表示的地址空間範圍,64KB內存的物理地址範圍是 0x0000 - 0xFFFF

地址空間的轉換過程簡圖.png

4.6.2 內存保護(Memory Protection)

4.6.3 內存分配與回收

分Native層(C/C++)、Java層

4.6.4 進程間通信 - mmap

IPC方式: 通過映射同一塊物理內存來共享內存,減少數據複製次數,提高效率。

mmap可以將某個設備或者文件映射到應用進程的內存空間中,這樣訪問這塊內存就相當於對設備/文件進行讀寫,而不需要通過read()、write()。

4.6.5 寫時拷貝技術(Copy on Write)

基本思想:多個對象在起始時共享某些資源(如代碼段、數據段),直到某個對象需要修改該資源才擁有自己的一份拷貝。

4.7 Android中的Low Memory Killer

Linux底層內核的內存監控機制:OOMKiller,核心思想:按照優先級順序,從低到高逐步殺掉進程,回收內存。

優先級的設定策略需綜合以下幾個因素:

  • 進程消耗的內存

  • 進程佔用的CPU時間

  • oom_adj(OOM權重)

Android擴展出自己的內存監控體系,Linux的“內存殺手”要等到系統資源“瀕臨絕境”的情況下才產生效果,而Android則實現了“不同梯級”的Killer(Low Memory Killer(LMK))。

LMK的源碼在內核工程的driver/staging/android/Lowmemorykiller.c中。

4.8 Android匿名共享內存(Anonymous Shared Memory)

Anonymous Shared Memory(簡稱Ashmem)是Android特有的內存共享機制,可以將制定的物理內存分別映射到各個進程自己的虛擬地址空間中,從而便捷地實現進程間的內存共享。

應用實例:基於ashmem設備來實現跨進程內存共享,如MemoryDealer。

匿名共享內存涉及設備驅動、Binder原理等一系列技術,比較難理解,等學習相關基礎知識,再來攻克。

4.9 JNI

JNI(Java Native Interface)是一種允許運行於JVM的Java程序去調用(反向亦然)本地代碼的編程框架。

有3種情況需要用到JNI:

  • 應用程序需要一些平臺相關的feature的支持,而Java無法滿足

  • 兼容以前的用其他語言書寫的代碼庫

  • 應用程序的某些關鍵操作對運行速度要求較高。

JNI涉及以下兩方面:

  • Java Code -> Native Code

  • Native Code -> Java Code

4.9.1 Java函數的本地實現

創建一個可供Java代碼調用的本地函數步驟:

  • 將需要本地實現的Java方法加上native聲明;

  • 使用javac命令編譯Java類

  • 使用javah生成.h頭文件

  • 在本地代碼中實現native方法

  • 編譯上述的本地方法,生成動態鏈接庫

  • 在Java類中加載這一動態鏈接庫

  • Java代碼中其他地方可以正常調用這個native方法

4.9.2 本地代碼訪問JVM

上節內容的“逆向”操作,本地層(C/C++)訪問JVM空間(Java)。

4.10 Java中的反射機制

4.11 學習Android系統的兩條線索

主線:操作系統的體系結構、硬件組成

輔線:在主線的基礎上,以Android系統的5層框架爲輔,逐一解析各層框架中的重要元素,或拾級而上,或深入淺出,直到問題的最根源處。

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