- 精通Linux內核:智能設備開發核心技術
- 姜亞華
- 5972字
- 2020-06-04 15:15:25
第1章 基于Linux內核的操作系統
Linux內核第一版發布于1991年,如今最新版本已經到了5.x。最初它僅僅像是一只五臟俱全的小麻雀,發展到現在浩瀚如海,代碼量也已經超過了千萬行;最初基于Intel x86的PC,如今囊括了x86和ARM等主流平臺在內的幾十個平臺;現在已經有CentOS、Debian、Fedora、openSUSE、Ubuntu、Red Hat Enterprise Linux和Android等基于Linux的操作系統。Android的崛起,更是將Linux帶到了大眾面前,如今不僅僅影響著數以萬計的程序員,連人民大眾的生活也與它息息相關。
1.1 處理器、平臺和操作系統
處理器,也就是我們常說的中央處理器(Central Processing Unit, CPU),它是一臺計算機的運算中心和控制中心,簡單地說,它負責解釋執行指令,并處理數據。
但是僅僅一個處理器是無法運行的,必須有一套與處理器配合的硬件,它們與處理器一起構成一個平臺。不同的處理器要求的硬件是不同的,比如x86處理器,一般配套的有南橋、北橋芯片和存儲BIOS的芯片等。
操作系統(Operating System, OS)是管理計算機硬件和軟件資源的程序,是運行在平臺上最基本的軟件,其他軟件以它為基礎。
處理器運行操作系統用于管理應用程序的執行,應用程序通過操作系統使用平臺的硬件,達到預期的運行效果,滿足用戶的需求。
內核是操作系統最基本的部分,也是操作系統與硬件關系最密切的一部分,控制系統對硬件的訪問。同樣,Linux內核是它的操作系統的基礎,包含了內存管理、文件系統、進程管理和設備驅動等核心模塊。一個基于Linux內核的操作系統,一般應該包含以下幾個部分。
(1)BootLoader:比如GRUB和SYSLINUX,它們負責將內核加載進內存,在系統上電或者BIOS初始化完成后執行加載。
(2)init程序:負責啟動系統的服務和操作系統的核心程序。
(3)必要的軟件庫:比如加載elf文件的ld-linux.so、支持C程序的庫,再比如GNU C Library(簡稱glibc)、Android的Bionic。
(4)必要的命令和工具:比如shell命令和GNU coreutils等。coreutils是GNU下的一個軟件包,提供ls等常用的命令。
1.2 以安卓為例剖析操作系統
Android最初是由Andy Rubin開發的,后被谷歌收購,如今已經風靡全球。它基于Linux內核,主要用于移動設備領域,起初應用于智能手機和平板電腦,隨后擴展到智能電視、車載系統和可穿戴設備。
Android的成功不僅僅源于操作系統本身,還包括一系列配套的工具、文檔和全球數以萬計的開發者,成百上千萬的應用,如今已經形成了一個完整且龐大的生態。
從事Android的開發者主要分為以下三個領域。
(1)應用開發者:利用系統的接口(SDK)開發運行在移動設備上的應用,主要使用Java、Kotlin和C++等高級編程語言。
(2)Framework和類庫開發者:負責構建某個模塊的架構,定義、維護接口,主要使用Java、C++和C語言。
(3)驅動和內核開發者:包括設備驅動開發和平臺驗證等工作,主要使用C和C++語言,多任職于設備公司和器件廠商。
1.2.1 安卓的整體架構
與開發者的劃分對應,Android操作系統的架構也是層次分明的,如圖1-1所示。

圖1-1 Android操作系統架構圖
顯然,從應用層開始,到Linux內核,都是上一層依賴于下一層。
Linux內核是整個架構的基礎,它負責管理內存、電源、文件系統和進程等核心模塊,提供系統的設備驅動。
硬件抽象層可以保護芯片廠商的知識產權,避開Linux的GPL協議。由于內核是開源的,部分芯片廠商保密算法的代碼,可以以二進制文件的形式,提供給手機等設備制造公司集成在硬件抽象層。
它的另一個主要作用是屏蔽硬件的差異。Linux內核與設備驅動的聯系緊密,可以根據系統的要求將數據和控制標準化。比如加速度傳感器,它們的驅動報告值的單位由芯片和驅動共同決定,不同的傳感器可能有差異,但Android系統要求的單位是統一的(比如m/s2),否則應用將不得不處理硬件差異,硬件抽象層負責將得到的數據轉化為Android要求的數值。
需要說明的是,從應用層到硬件抽象層都屬于用戶空間,硬件抽象層并不屬于內核,它與內核雖然邏輯上聯系緊密,但也不能直接調用內核的函數,一般調用類庫提供的接口實現,比如Bionic。圖1-1所示的Android操作系統的架構圖描述的只是邏輯關系,并不是函數調用關系,從函數調用關系角度看,libc(Bionic的一部分)與內核的關系更加緊密。
Framework和類庫是Android的核心,也是Android與其他以Linux內核為基礎的操作系統最大的區別。它規定并實現系統的標準、接口,構建Android的框架,比如數據存儲、顯示、服務和應用程序管理等。
應用層與最終用戶關系距離最近,利用Framework的接口,接收設備驅動的數據,根據用戶的指令,將控制傳遞至設備驅動。
以上是Android系統整體架構,針對某一個模塊,比如Sensor(傳感器),系統運行時的架構如圖1-2所示。

圖1-2 Sensor架構圖
圖中箭頭表示控制和數據傳遞關系,控制由APP向下傳遞,數據則由下向上。
圖中涉及三個進程,APP1、APP2和Service,APP和Service之間傳遞控制和數據都需要通過進程通信實現,Service將控制傳遞至HAL,最終到設備驅動,HAL得到驅動產生的數據,報告至Service,由Service分發給APP1和APP2。
從函數調用角度來看,Framework、Service和HAL都可能調用library,最終進入內核,Framework可能需要通過內核打開、關閉文件或與其他進程通信等,但它并不會直接訪問設備驅動。僅保留Service和HAL的唯一訪問設備驅動的通路,有助于設備的單一控制。如果APP進程直接控制驅動,會造成混亂。
需要說明的是,并不是每一個模塊都需要HAL,比如Touch(觸摸屏),Touch不需要APP控制,屏幕亮起即工作,關屏則關閉,Touch驅動的數據單位就是點坐標,也不需要轉化,所以它不需要HAL。
不僅僅是Sensor,任何一個模塊,只要它與某個硬件有關,哪怕只是申請內存,都繞不開內核。下面從操作系統的幾個核心功能看看Android與內核的關系。
進程管理:進程這個概念本身是由內核實現的,還包括進程調度、進程通信等。
Android利用Pthread等實現了它的進程、線程,是對內核系統調用的封裝。另外,Android還引入Binder通信,Binder也不是僅僅依靠用戶空間就可以完成的,它也是需要驅動的,目前Linux內核的代碼已經包含了Binder的驅動代碼。
內存管理:內存、內存映射和內存分配、回收等內核都已實現。
Android也實現了特有的ION內存管理機制,可以使用它在用戶空間申請大段連續的物理內存。與Binder一樣,ION的驅動代碼也已經存在于最新的內核中了。
文件系統:內核實現了文件系統的框架和常用的文件系統。
文件系統不僅關乎數據存儲,進程、內存和設備等都離不開它。對Linux而言,幾乎是“一切皆文件”,Android延續了這種理念。
用戶界面:Android開發了一套控件(View)供應用開發人員使用,背后有一套完整的顯示架構支持它們,包括Framework中的Window Manager和Library中的Surface Manager等。除此之外,Android還開發了虛擬機(Dalvik),運行Java開發的應用程序。
設備驅動:設備驅動由內核提供,需要隨系統啟動而運行的設備驅動,一般在內核啟動過程中加載,某些在特定情況下才會使用的設備驅動(比如Wifi),在設備使用時被加載,前者一般被編譯進內核,后者以ko文件的形式存在。
如果把整個Android比作一個房子,內核就是地基,加上Android架構,就成了一個毛坯房,再加上應用開發者開發的應用才是一個可以拎包入住的房子。如此看來,一部新手機算是精裝交付了。
1.2.2 Linux內核的核心作用
Linux內核與操作系統是底層基礎和上層建筑的關系,內核提供了基本功能和概念,操作系統在此基礎上根據自身的定位和特色實現自身的架構,提供接口和工具來支持應用開發,由應用體現其價值。
開機后,內核由bootloader加載進入內存執行,它是創建系統的第一個進程,初始化時鐘、內存、中斷等核心功能,然后執行init程序。init程序是基于Linux內核的操作系統,在用戶空間的起點,它啟動核心服務,掛載文件系統,更改文件權限,由后續的服務一步步初始化整個操作系統。
內核實現了內存管理、文件系統、進程管理和網絡管理等核心模塊,將用戶空間封裝內核提供的系統調用作為類庫,供其他部分使用。
內核并不是最底層的,下面還有硬件層,即內核驅動硬件、系統的數據來源于硬件、最終的結果也要靠硬件去體現,內核是系統與硬件的橋梁。
內存管理、文件系統和進程管理是本書討論的重點,接下來的章節中讀者可以看到它們實現的細節。
1.3 內核整體架構
內核的代碼比較復雜,一方面是代碼量大,另一方面是模塊間交叉,一個問題往往關聯多個模塊。好在內核維護的過程中,代碼的結構一直比較清晰,本節將對內核的整體架構進行介紹。
1.3.1 內核代碼的目錄結構
內核源代碼中一級子目錄如下。
Documentation目錄:存放說明文檔,沒有代碼。內核中一些復雜或者專業的模塊會有幫助文檔,涉及它們的背景和總結等,讀者困惑的時候可以查看該目錄下是否有相關說明。
arch目錄:arch是architecture的簡稱,包含了與體系結構相關的代碼,下面的每一個子目錄都表示內核支持的一種體系結構,比如x86和ARM等。系統中有些特性的實現與具體的體系結構相關,比如內存頁表和進程上下文等,這部分代碼基本都會存放在此,其他目錄存放的代碼多是共性的。
kernel目錄:內核的核心部分,包含進程調度、中斷處理和時鐘等模塊的核心代碼,它們與體系結構相關的代碼存放在arch/xxx/kernel下。
drivers目錄:設備驅動代碼集中存放于此,體量龐大,隨著內核代碼更新不斷引入新的硬件驅動。
mm目錄:mm是memory management的縮寫,包含內存管理相關的代碼,這部分代碼與體系結構無關,與體系結構相關的代碼存放在arch/xxx/mm下。
fs目錄:fs是file system的縮寫,包含文件系統的代碼,涉及文件系統架構(VFS)和系統支持的各種文件系統,一個子目錄至少對應一種文件系統,比如proc子目錄對應proc文件系統。
ipc目錄:ipc是inter process communication的縮寫,包含了消息隊列、共享內存和信號量等進程通信方式的實現。
block目錄:包含塊設備管理的代碼,塊設備與字符設備對應。前者支持隨機訪問,SD卡和硬盤等都是塊設備;后者只能順序訪問,鍵盤和串口等都是字符設備。
lib目錄:包含公用的函數庫,比如紅黑樹和字符串操作等。通過上節的分析可以知道,內核處在C語言庫(glibc等)的下一層。實際上,glibc是由封裝內核的系統調用實現的,所以在內核中編程不能使用它們,應該使用的是內核提供的庫(不限于lib目錄)。新手可能會有類似“為什么不能使用C標準庫的printf在內核中打印調試信息”的問題,這就是原因。
init目錄:包含內核初始化相關的代碼,其中的main.c定義了內核啟動的入口start_kernel函數。
firmware目錄:包含運行在芯片內的固件,固件也是一種軟件,只不過由芯片執行,而不是CPU。
scripts目錄:包含輔助內核配置的腳本,比如運行make menuconfig命令配置內核時,由它們提供支持。
剩下的目錄中,net、crypto、certs、security、tools和virt等分別與網絡、加密、證書(certificates)、安全、工具和虛擬化等相關。
鑒于篇幅,本書不討論網絡相關的話題,主要涉及從arch到init這些目錄。
1.3.2 內核的核心模塊及關聯
內核中的模塊較多,而且關系錯綜復雜,整體結構如圖1-3所示。

圖1-3 內核模塊關系圖
首先是幾個相對獨立的模塊,比如中斷(異常)、時鐘和內核同步等,它們依賴于硬件和具體的體系結構,對其他模塊依賴較小。它們也都屬于基礎模塊,用于為其他模塊提供支持。
中斷模塊不僅在設備驅動中頻繁使用,內存管理和進程調度等也需要它的支持,內存管理需要缺頁中斷,進程調度則需要時鐘中斷和處理器間中斷等。
時鐘設備一方面是系統計算時間的基礎,另一方面提供時鐘中斷,與大多數模塊都有關聯。
內核同步模塊貫穿整個內核,如果沒有同步機制,錯綜復雜的執行流訪問臨界區域就會失去保護,系統瞬間癱瘓。
內存管理、文件管理和進程管理算得上是內核中除了網絡外最復雜的三個模塊,也是本書討論的重點。說它們最復雜與drivers目錄體量龐大并不矛盾:drivers目錄包含了成千上萬個設備的驅動,單算一個驅動其實并不復雜。
內存管理模塊涉及內存尋址、映射、虛擬內存和物理內存空間的管理、緩存和異常等。
文件系統的重要性從“一切皆文件”這句話就可以體現出來,硬件的控制、數據的傳遞和存儲,幾乎都與文件有關。
進程管理模塊涉及進程的實現、創建、退出和進程通信等,進程本身是管理資源的載體,管理的資源包括內存、文件、I/O設備等,所以它與內存管理和文件系統等模塊都有著緊密的關系。本書先介紹內核管理和文件系統,最后介紹進程管理,希望讀者能夠對進程有更清晰地認識。
至于設備驅動模塊,從功能角度來看,每一個設備驅動都是一個小型的系統,電源管理、內存申請、釋放、控制、數據,復雜一些的還會涉及進程調度等,它與前面幾個模塊都有著密切關系。所以對內核函數的熟悉程度,很大程度上決定了一個驅動工程師的效率;對其他模塊的理解程度,很大程度上決定了他的高度。
1.4 實例分析
在本書中,每一章結尾都會添加兩個小節,其內容有些是與章節內容相關的實例,有些是知識點總結和心得體會,統一命名為“實例分析”。它們以討論邏輯和方法為目的,多數并不局限于某一領域,但為了方便理解,本書依舊會將在某一個具體領域內討論它們,在其他領域內的使用也是類似的。
1.4.1 系統響應“點擊智能手機觸摸屏”的過程
曾幾何時,我一直有個疑問:當移動鼠標的時候,屏幕上的光標隨之移動,是如何實現的?
智能手機時代來臨,這個疑問就變成了:點擊或者滑動手機屏幕,屏幕隨之改變,這是如何實現的?
這些看似簡單的動作,實際上需要硬件和操作系統的多個部分協作完成。
首先是觸摸屏芯片,點擊觸摸屏引起信號變化,芯片監測到信號后,計算出當前觸摸的位置(坐標),然后通過中斷模塊通知CPU“有事報告”。
CPU得到中斷后,根據中斷信息,調用觸摸屏驅動的處理函數。驅動接下來讀取觸摸的坐標,通過input子系統報告給操作系統。
操作系統經過一系列“輾轉騰挪”,將點的坐標信息傳遞給應用,過程中可以涉及進程通信、IO多路復用等。
應用得到點坐標后,根據點的位置和當前的界面布局做出判斷,決定下一步需要進行的操作,比如,點落在空白區域被忽略、落在按鈕上觸發按鈕操作、落在App圖標上啟動應用等。
這是由下到上的過程,接下來應用還需要根據相應的操作刷新屏幕,假設它希望改變某個圖標的顏色,通過函數調用將該請求傳遞至操作系統,操作系統通過計算得到新屏幕中的各圖層,又經過一系列“輾轉騰挪”,屏幕的驅動得到了刷新界面的指令,獲取數據刷新界面,這是一個由上到下的過程。
1.4.2 智能手機的傳感器游戲
智能手機有很多傳感器游戲(應用),比如激流快艇、微信搖一搖、統計每天走動步數的App等,它們都是使用手機傳感器的數據作為計算的依據。
有了觸摸屏的例子后,理解傳感器游戲就水到渠成了,它們的原理是一樣的,只不過將觸摸屏芯片換成了傳感器芯片。
但是App需要的傳感器是不同的,比如,激流快艇游戲,需要的是陀螺儀的數據;統計步數的App,需要的是加速度傳感器的數據。這些傳感器的精確度和采樣頻率對用戶體驗的影響較大,比如陀螺儀,高端手機必備,低端手機上可能并沒有安裝,這種情況下的陀螺儀數據是通過其他傳感器的數據計算得到的,精確度不足,采樣頻率也達不到要求,操作會有明顯的滯后和偏差。
從流程角度看,傳感器與觸摸屏大同小異,同樣是由芯片開始,經過操作系統的傳遞,數據到達應用,然后計算并刷新屏幕。不同的地方在于,系統可能會根據策略對數據做一定的處理,比如在低端手機上沒有陀螺儀的情況下,根據加速度和磁力傳感器的數據計算得到陀螺儀的數據。
換作另外一個桌面操作系統,可能就不需要做類似的處理,因為它并不執著于得到陀螺儀的數據。觸摸屏的例子也是如此,如果它不需要支持觸控操作,也不需要實現中間的“輾轉騰挪”。內核是操作系統的基礎,是它們的通用部分,操作系統根據自身的定位決定它需要在此基礎上實現的功能,定制它的特有算法和流程。
- Linux設備驅動開發詳解:基于最新的Linux4.0內核
- Windows Server 2019 Cookbook
- WindowsServer2012Hyper-V虛擬化部署與管理指南
- VMware Horizon View 6 Desktop Virtualization Cookbook
- 精通Linux內核開發
- Windows Phone 7.5 Data Cookbook
- 高性能Linux服務器構建實戰:運維監控、性能調優與集群應用
- Installing and Configuring Windows 10:70-698 Exam Guide
- 嵌入式操作系統(Linux篇)(微課版)
- 嵌入式實時操作系統:RT-Thread設計與實現
- 直播系統開發:基于Nginx與Nginx-rtmp-module
- Windows Server 2012網絡操作系統項目教程(第4版)
- Distributed Computing with Go
- INSTANT Galleria Howto
- 分布式高可用架構之道