在讀者學習本章之前,最好擁有部分裸機程序基礎(如點亮LED、arm-linux-gcc、中斷等),初學者可以查看:ARM裸機加強版
本章使用的平台為TINY4412,移植等過程課查看之前的隨筆:TINY4412:移植uboot、內核和掛接網絡文件系統
本系列參考了宋寶華老師的《Linux設備驅動開發詳解:基於最新的Linux 4.0內核》以及韋東山老師的嵌入式驅動教程,在此向兩位老師表示衷心感謝
一、系統調用過程
Linux將存儲器和外設分為3個基礎大類:
1. 字符設備:如LED、按鍵
2. 塊設備:如磁盤
3. 網絡設備
對於用戶而言,它們都需要使用文件系統的操作接口open()、read()、write()和close()進行訪問
對於驅動而言,我們需要完成的是操作接口函數對應的函數
那么這兩個的對應和調用關系是什么樣的呢?
首先,接口函數會通過SWI實現從用戶模式變換到管理模式
SWI指令格式如下:
cond:執行指令的條件
immed_24:24位整數,用於區分用戶的不同操作,從而執行不同內核函數
讀者到這里可能會有一個疑問,為什么要通過SWI進入管理模式呢?
類比於Windows,有的時候我們會使用管理員身份運行程序,這樣的程序就會有更高的權利
操作系統之所以這么做,是為應用程序的運行創建良好的環境,保障每個程序都可以最大化利用硬件資源,防止非法程序破壞其它應用程序執行環境
為達目的,操作系統會將硬件的操作權限交給內核程序來管理,用戶程序不能隨意使用硬件,使用硬件(對硬件寄存器進行讀寫)時要先向操作系統發出請求,操作系統內核幫助用戶程序實現其操作,操作系統通過一組稱為系統調用的(System Call)的接口呈現給用戶,系統調用把應用程序的請求傳給內核,調用相應的內核函數完成所需的處理,將處理結果返回給應用程序
內核中的系統調用定義在arch/arm/kernel/calls.S
/* 0 */ CALL(sys_restart_syscall) CALL(sys_exit) CALL(sys_fork_wrapper) CALL(sys_read) CALL(sys_write) /* 5 */ CALL(sys_open) CALL(sys_close) ... /* 375 */ CALL(sys_setns) CALL(sys_process_vm_readv) CALL(sys_process_vm_writev)
其中CALL的定義如下,為代碼段的偏移地址,可以看到sys_open()的偏移是5
#define CALL(x) .long x
那么內核是怎么知道調用哪個函數呢?
在EABI模式下,系統調用通過傳入的寄存器r7的值來判斷調用哪個函數
mov r7, #num @ num對應系統調用的某個函數 swi 0x0 @ 設置多少號軟中斷
原來的系統調用方式是這樣:
swi (#num | 0x900000) @ 0x900000是個magic值
這兩個選擇是由宏CONFIG_OABI_COMPAT(原來的調用方式)和CONFIG_AEABI(EABI)控制的
對於原來的調用方式,內核給出的處理是給它建立一個單獨的system call table,叫sys_oabi_call_table,這樣,兼容方式下就會有兩個system call table,以原來的調用方式的系統調用會執行old_syscall_table表中的函數指針,EABI方式的系統調用會用sys_call_table中的函數指針
總結起來就是,通過SWI指令和寄存器傳入參數,以查表方式找到對應的系統調用函數
下圖為內核中設備驅動的層次關系:
之后,如果我們想要控制LED的亮滅,由於LED屬於字符驅設備驅動,因此程序就會依次通過應用程序open() -> SWI -> 系統調用sys_open() -> VFS -> 驅動程序open() -> 硬件操作
下一章 2、點亮LED