目錄
- Linux 內核基礎知識
- 進程調度
- 內存管理
- 虛擬文件系統和網絡接口
- 進程間通信
- Linux 內核編譯
- Makefile 和 Kconfig
- 內核Makefile
- 內核Kconfig
- 配置項標識的寫法
- depend 關鍵字
- select 關鍵字
- 表達式邏輯關系
- Kconfig 其他語法
- 配置文件的編譯
- Linux 內核引導方法
- Booloader 定義
- Linux 內核C編程特點
- Linux 內核編碼風格
- 零長度數組和變量數組
- 標號元素
- 可變參數宏
- 特殊屬性聲明
- type of關鍵字
- 內建函數
- do...while(0)
- Arm 處理器工作模式
Linux 內核基礎知識
Linux 內核主要由調度(SCHED
),內存管理(MM
),虛擬文件系統(VFS
),網絡接口(Net
),進程間通信(IPC
)這5個子系統組成
Linux 內核各個部分的組成關系如下圖所示:
進程調度
進程調度控制系統中多個進程對CPU
進行訪問,使得CPU能夠 微觀串行,宏觀并行的進行執行
linux
進程的狀態切換如下圖所示:
內存管理
內存管理的作用是控制多個進程安全的共享主內存區域,當CPU提供內存管理單元(MMU
)時,Linux 內存管理完成每個進程進行虛擬地址到物理內部的轉換
一般而言,Linux 進程享有4GB 的內存空間,0-3G
屬于內存空間,3-4G
屬于內核空間,如下圖所示:
虛擬文件系統和網絡接口
Linux 的虛擬文件系統(VFS
)隱藏了硬件的具體細節,為所有的設備提供了統一的接口
網絡接口提供了對各個網絡標準的存取和對網絡硬件的支持
進程間通信
Linux 支持多種進程間通信機制,包含 信號量、共享內存、管道等,這些機制可以協助多個進程,多資源的互斥訪問、進程間的同步和消息傳遞
Linux 內核編譯
內核編譯方式分為兩步 : 配置內核和編譯內核
配置內核命令
make menuconfig
//或者如果有現成的配置文件 如 lddxxxx_defconfig
make lddxxxx_defconfig
編譯內核和模塊的方式是:
make zImage
make modules
編譯完成后生成的文件:
- 未壓縮的內核鏡像文件
vmlinux
- 內核符號表文件
System.map
- arch/arm/boot 下得到壓縮鏡像文件
zImage
Makefile 和 Kconfig
內核Makefile
內核Makefile 文件的規則
obj-y = foo.o 表示將 foo 編譯并連接進內核
obj-m = foo.o 表示將 foo 以模塊化編譯 生成 ko 文件
更加通用的做法是:
obj-$(CONFIG_ISDN) = isdn.o
多文件的處理方法:
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o dir.o file.o fsync.o ialloc.o inode.o
ioctl.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
添加目錄的結構:
obj-$(CONFIG-EXT2_FS) += exte2/
內核Kconfig
內核Kconfig 項的基本格式
config xxxxx
bool “xxxxxxxxxxxxx”
default n
depend on <expr>
select xxxx
//舉例如下
config DRM_DP_AUX_CHARDEVbool "DRM DP AUX Interface"depends on DRMhelpChoose this option to enable a /dev/drm_dp_auxN node that allows toread and write values to arbitrary DPCD registers on the DP auxchannel.
- config 表示一個配置項目
bool 表示的是數據類型,其他數據類型還包括 bool、tristate、string、hex、int, tristate 和 string** 是兩種基本類型,其他類型都基于這兩種類型 - help 信息用于提示,格式如下:
help ( 或者— help — )
開始
….
結束
配置項標識的寫法
bool “DRM DP AUX Interface” 和
bool
prompt “DRM DP AUX Interface” 是一致的 prompt(英語含義:提示)
輸入提示的一般格式為:
prompt [if ]
depend 關鍵字
default [if ] // 表示配置的默認值,一個配置選項可以存在任意多個默認的值,在這種情況下,生效的第一個的值
bool “foo” if BAR
default y if BAR
和下面的等價
depends on BAR
boo “foo”
default y
select 關鍵字
config A
Select B
表示如果這個配置項被選中,那么配置項B也會被選中
示例如下:
config DRM_RADEONtristate "ATI Radeon"depends on DRM && PCI && MMUselect FW_LOADERselect DRM_KMS_HELPERselect DRM_TTMselect POWER_SUPPLYselect HWMONselect BACKLIGHT_CLASS_DEVICEselect INTERVAL_TREEhelpChoose this option if you have an ATI Radeon graphics card. Thereare both PCI and AGP versions. You don't need to choose this torun the Radeon in plain VGA mode.If M is selected, the module will be called radeon.
表達式邏輯關系
depends on 關鍵字后面的表達式之間可以有邏輯關系,列舉如下:
元素 | 說明 |
---|---|
等于 | <symbol> ‘=’ <symbol> |
不等于 | <symbol> ‘!=’ <symbol> |
賦值 | ‘(’ <expr> ‘)’ |
邏輯非 | ‘!’ <expr> |
邏輯與 | <expr> ‘&&’ <expr> |
Kconfig 其他語法
菜單結構
menu… endmenu
choices… endchoice
配置文件的編譯
配置文件在經過Linux 系統編譯后會生成一個頭文件
autoconf.h
- linux 路徑:
include/generated
- andoird 路徑:
out/target/product/xxx/obj/KERNEL_OBJ/android-5.4/xxx/include/generated
內容如下:
.....c
#define CONFIG_IP6_NF_MATCH_AH_MODULE 1
#define CONFIG_NLS_CODEPAGE_861_MODULE 1
#define CONFIG_MTD_SPI_NAND_MODULE 1
#define CONFIG_RING_BUFFER 1
#define CONFIG_HARDENED_USERCOPY_FALLBACK 1
#define CONFIG_UWB_HWA_MODULE 1
#define CONFIG_SND_SOC_WM8804_MODULE 1
#define CONFIG_NF_CONNTRACK_H323_MODULE 1
#define CONFIG_HAVE_ARCH_SECCOMP_FILTER 1
#define CONFIG_IP6_NF_SECURITY_MODULE 1
#define CONFIG_SND_PROC_FS 1
#define CONFIG_VFIO_PCI_MMAP 1
.....
generated 中的文件還包括:
元素 | 說明 |
---|---|
compile.h | 編譯主機信息 |
timeconst.h | 時間轉換的固定宏 |
uapi/linux/version.h | 內核版本信息 |
Linux 內核引導方法
- 系統上電的時候,CPU會將PC指針賦值為一個特定的地址 0xFFFF0 并執行該地址的指令,在 PC 中該地址位于 BIOS 中,保存在主板的ROM 或者 Flash 中
- BIOS 按照CMOS設置定義的啟動設備的順序來搜索處于活動狀態并且可以引導的設備,若是從硬盤啟動,BIOS會將硬盤的 MBR(主引導記錄)中的內容加載到RAM,MBR是一個512 字節的扇區,處于磁盤上的第一個扇區中(0道0柱面1扇區)。當MBR被加載到 RAM中之后,BIOS就會將控制權交給 MBR
- 主引導程序查找并且加載次引導加載程序,它在分區表中查找活動分區,將活動分區的引導記錄從這個設備讀入RAM并且啟動它
- 次引導程序加載Linux 內核和可選的初始RAM磁盤,將控制權交給linux 內核代碼
- 運行被加載的內核,并且啟動用戶空間的應用程序
Booloader 定義
-
可以在系統上電或者復位的時候以某種方法執行,
執行方法包括:被BIOS引導執行,直接 NorFlash 執行,NAND Flash 代碼被 MCU自動拷貝進入內部或者外部 RAM直接執行 -
能將U盤,磁盤,光盤,NAND/Nor Flash ROM SD卡中存儲介質,甚至串口網口中的操作系統加載到 RAM,并且將控制權交給內核源代碼執行
內核鏡像不是完全直接可以執行的代碼,而是一個壓縮過的
zImage 小內核
bzImage big 大內核
但是不是 zImage 和 bzImage 中的一切都被壓縮了,實際當中存在沒有被壓縮的部分,這部分中包含解壓縮程序,解壓縮程序會解壓映像中被壓縮的部分,zImage 和 bzImage 都是 gzip 壓縮的,在這兩個文件頭部內嵌有gzip 解壓縮代碼.
Linux 內核C編程特點
Linux 內核編碼風格
C++/windows 中習慣使用駝峰式命名法比如:
sendData
minValue
但是在Linux 中習慣使用下劃線命名方法:
send_data
min_value
Linux 社區對編碼規范的要求:
- if/for/while/switch { 不另外起一行
- if for 如果只有一行 不加{ }
- 函數的 { } 都是要另外起一行
- swicth 和 case 要對齊的
switch(suffix) {
case 'G':
break;
case 'W':
break;
Linux 代碼規范的詳細文檔位于linux 代碼中
http://androidxref.com/kernel_3.18/xref/Documentation/CodingStyle
可以使用:
scripts/checkpatch.pl 進行代碼規范的檢查
零長度數組和變量數組
struct data {
int a;
char data[0];
}
char data[0];不占用實際空間,但是通過 data[i] 可以訪問 len 之后的 第index 個地址,并沒有為data[] 分配內存 sizeof(struct data) == sizeof(int)
int n = 5;
char demo[n];
標號元素
GNUC
支持 case x…y 這樣的寫法
swicth(ch) {
case '0' ... '9':
break;
case 'a'
break;
GNUC
中,通過制定索引或者結構體成員名,允許初始化的值以任意的順序出現,指定數組索引的方法是添加 [index] = ,當然也可以用[first … last]的形式制定一個范圍
unsigned char data[MAX] = { [0...MAX-1] = 0 };
可以借助結構體成員名初始化結構體,Linux 2.6 推薦使用標準C的形式初始化結構體,就是在每個結構體成員的名稱前加上一個點
可變參數宏
標準的C支持可變參數的函數,比如printf,在GNUC 中宏也可以接受可變數目的參數
#define pr_debug(fmt,arg...) printk( fmt,##arg); )
##arg 表示的是零個或者多個參數的情況,這個參數以及參數之間的逗號構成了arg值
特殊屬性聲明
GNUC 允許聲明函數,變量類型的特殊屬性,如果要指定一個聲明得到屬性,在聲明后面添加 attribute((ATTRIBUTE))
如果存在多個屬性,句需要以逗號分隔,GNUC 支持的noreturn
format
section
aligned
packed
等十多個屬性
type of關鍵字
type of關鍵字可以定義更加通用的宏 不需要預先知道變量的類型 類似于模板的作用
#define min(x,y) ({ /
const typeof(x) _x = (x); /
const typeof(y) _y = (y); /
(void) (&_x == &_y); /
_x < _y ? _x : _y; })(void) (&_x == &_y); 的作用是檢查這兩個變量的類型是否一致。
內建函數
使用 gcc
編譯的時候,如果使用 -ansi-pedanic
編譯選項,則告訴GNC編譯器不使用GNU擴展語法
do…while(0)
使用的優點:可以在宏之中使用,使相關的語句可以正確的展開
#define safe_free§ do{ free§; p = null; }while(0) (注意不需要使用逗號)
使用的時候,可以和函數一樣的使用 safe_free§;
Arm 處理器工作模式
元素 | 說明 |
---|---|
用戶模式(Usr) | 用于正常執行程序 |
快速中斷模式(FIQ) | 用于高速數據傳輸 |
管理模式(svc) | 操作系統使用的保護模式,由系統調用執行軟中斷SWI命令觸發 |
外部中斷模式(IRQ) | 用于通常的中斷處理 |
數據訪問終止模式(abt) | 當數據或指令預取終止時進入該模式,可用于虛擬存儲以及存儲保護 |
系統模式(sys) | 運行具有特權的操作系統任務 |
未定義指令中止模式(und) | 當未定義的指令執行時進入該模式,可用于支持硬件 |
Arm的工作模式切換有兩種方法:
被動切換:在arm運行的時候產生一些異常或者中斷來自動進行模式切換
主動切換:通過軟件改變,即軟件設置寄存器來經行arm的模式切換,應為arm的工作模式都是可以通過相應寄存器的賦值來切換的
除了用戶模式 其余6種模式被稱為特權模式
特權模式中除了系統模式之外 其余5種模式稱為異常模式
Linux 只能通過系統調用或者硬件中斷完成用戶模式到特權模式的組合
特權模式可訪問任意系統資源,異常模式通常由系統異常狀態切換進來
大多數程序運行于用戶模式;進入特權模式是為了處理中斷、異常、或者訪問被保護的系統資源;