在嵌入式Linux系統開發中,通過編程控制硬件資源是至關重要的技能之一,今天跟各位小伙伴分享一篇專注于介紹如何使用C庫函數控制ELF 1開發板LED的學習筆記。
希望通過這篇學習筆記,讓屏幕前的小伙伴能深入理解Linux內核對于底層硬件資源管理的抽象機制,為進行復雜的嵌入式系統開發奠定基礎。
一、系統調用與庫函數
(一)系統調用
系統調用(System Call)是操作系統內核提供的函數,在內核態運行(Kernel Mode),是操作系統為用戶提供的一些接口。它通過軟中斷向內核態發出一個明確的請求。有一些任務需要進程跑在內核態才能執行,比如和硬件打交道。所以進程調用系統調用就能讓自己運行在內核態從而執行這些類似的任務。系統調用實現了用戶態進程和硬件設備之間的大部分接口。
常見系統調用
Open, Close, Read, Write, Ioctl,Fork,Clone,Exit,Getpid,Access,Chdir,Chmod,Stat,Brk,Mmap等,需要包含Unistd.h等頭文件。
(二)庫函數
庫函數位于系統調用的上層,扮演著封裝和抽象的角色,運行在用戶態(User Mode),旨在為程序員提供一種更為便捷的方式來調用真正實現底層功能的系統調用。這些庫函數充當了用戶態服務的供給者,其功能實現機制各異:有的庫函數可能整合并包裝了一個或多個不同的系統調用,而有的庫函數則能夠直接在用戶態提供所需服務,無需進一步調用任何系統調用。
(三)區別
系統調用通常不可替換,而庫函數通常可替換
普通的庫函數調用由函數庫或用戶自己提供,因此庫函數是可以替換的。例如,對于存儲空間分配函數malloc,如果不習慣它的操作方式,我們完全可以定義自己的malloc函數。
系統調用通常提供最小接口,而庫函數通常提供較復雜功能
例如sbrk系統調用分配一塊空間給進程,而malloc則在用戶層次對這以空間進行管理。
系統調用運行在內核空間,而庫函數運行在用戶空間
因為系統調用屬于內核,和庫函數不屬于內核。因此,如果當用戶態進程調用一個系統調用時,CPU需要將其切換到內核態,并執行一個內核函數。
內核調用都返回一個整數值,而庫函數并非一定如此
在內核中,整數或0表示系統調用成功結束,而負數表示一個出錯條件。而出錯時,內核不會將其設置在errno,而是由庫函數從系統調用返回后對其進行設置或使用。
POSIX 標準針對庫函數而不是系統調用
判斷一個系統是否與POSIX需要看它是否提供一組合適的應用程序接口,而不管其對應的函數是如何實現的。因此從移值性來講,使用庫函數的移植性較系統調用更好。
系統調用運行時間屬于系統時間,庫函數運行時間屬于用戶時間
調用系統調用開銷相對庫函數來說更大
很多庫函數本身都調用了系統調用,這得益于雙緩沖的實現,在用戶態和內核態,都應用了緩沖技術,對于文件讀寫來說,調用庫函數,可以大大減少調用系統調用的次數。而用戶進程調用系統調用需要在用戶空間和內核空間進行上下文切換,開銷較大。如此以來,庫函數的開銷也就會比直接調用系統調用小了。另外一方面,庫函數同樣會對系統調用的性能進行優化。
二、使用C庫函數控制LED
(一)實驗代碼
參考3.2.1.2 文件 I/O 的方式控制 LED的例程將使用系統調用的部分改為使用庫函數來實現。代碼如下:
#include #include #include #define LED1_BRIGHTNESS "/sys/class/leds/led1/brightness" #define LED2_BRIGHTNESS "/sys/class/leds/led2/brightness" #define LED3_BRIGHTNESS "/sys/class/leds/led3/brightness" int main() { FILE *fd1, *fd2, *fd3; fd1 = fopen(LED1_BRIGHTNESS, "w"); if(fd1 < 0) { printf("Fail to Open %s device\n", LED1_BRIGHTNESS); exit(1); } fd2 = fopen(LED2_BRIGHTNESS, "w"); if(fd2 < 0) { printf("Fail to Open %s device\n", LED2_BRIGHTNESS); exit(1); } fd3 = fopen(LED3_BRIGHTNESS, "w"); if(fd3 < 0) { printf("Fail to Open %s device\n", LED3_BRIGHTNESS); exit(1); } while(1) { fwrite("1",3,1,fd1); fflush(fd1); sleep(1); fwrite("0",1,1,fd1); fflush(fd1); fwrite("1",3,1,fd2); fflush(fd2); sleep(1); fwrite("0",1,1,fd2); fflush(fd2); fwrite("1",3,1,fd3); fflush(fd3); sleep(1); fwrite("0",1,1,fd3); fflush(fd3); } fclose(fd1); fclose(fd2); fclose(fd3); return 0; }
(二)編譯、測試將代碼編譯后拷貝到ELF 1開發板進行測試。
執行LED2,查看結果
可以看到LED按照預期循環點亮
三、總結
本次實驗使用C庫函數實現了對LED的控制,通過一個簡單的示例來感受系統調用與庫函數的區別。但是代碼中還有需要注意的地方。
代碼中調用fwrite函數寫入內容時,它可能只是把內容保存到了C庫的緩沖區,并沒有執行真正的系統調用write函數把內容寫入到設備文件,這種情況下LED燈的狀態是不會被改變的,代碼中在fwrite函數后調用了fflush要求立刻把緩沖區的內容寫入到文件,確保 執行了相應的操作。在實驗時可以嘗試把代碼中的fflush都注釋掉,這種情況下有極大的幾率是無法正常改變LED燈狀態的。
如果不考慮操作的時間開銷,其實控制硬件更推薦的做法是,每次控制LED燈都使用fopen—fwrite—fclose的流程,這樣就不需要考慮flseek、fflush的問題了,但最推薦的還是直接通過系統調用來控制硬件的方式。
-
嵌入式
+關注
關注
5086文章
19143瀏覽量
306044 -
C語言
+關注
關注
180文章
7608瀏覽量
137084 -
開發板
+關注
關注
25文章
5080瀏覽量
97678
發布評論請先 登錄
相關推薦
評論