Linux驅動開發學習-02.第一個模塊HelloWorld

02.第一個模塊HelloWorld

2.1 內核劃分

2.1.1 根據系統結構劃分(5)

  • 進程管理:內核負責進程創建與銷燬,處理輸入輸出,進程間通信,共享CPU控制等
  • 內存管理:內核爲每個進程建立虛擬地址空間,內核與內存管理系統交互
  • 文件系統:內核在非結構化的硬件上建立了結構化文件系統
  • 設備控制:除處理器、內存和少見的實體外,其他任何設備控制操作由設備驅動完成
  • 網絡:報文是異步事件,在某一進程接手前必須被收集、識別和分發;系統負責在程序和網絡間遞送報文;所有路由和地址解析問題都在內核中實現
    在這裏插入圖片描述

2.1.2 根據模塊劃分

Linux優良特性之一:在系統正在運行時,仍然可以增加或去除內核的部分功能;每塊可以在運行時添加到內核的代碼即爲一個模塊;模塊並不是一個完成的可執行文件,它需要通過insmod(rmmod)動態鏈接(移除)到內核中

在這裏插入圖片描述

2.1.3 設備分類(3)

  • 字符設備:可當做字節流存取的設備(如同一個文件),驅動至少要實現openclosereadwrite系統調用;文本控制檯/dev/console和串口/dev/ttyS*是典型實例;字符設備通過文件系統結點存取,如/dev/tty1;與普通文件區別在於,字符設備只能順序存取
  • 塊設備:同字符設備,塊設備通過/dev/下的文件系統結點存取;塊設備駐有文件系統;大部分Unix系統塊設備僅能傳送2^n字節的整塊,而Linux可傳送任意字節;塊和字符設備的區別僅僅在於內核在內部管理數據的方式上,以及軟件接口上
  • 網絡接口:網絡接口負責發送和接收報文;非面向流的設備;Unix提供的對接口的存取方式仍然是分配一個名字(如eth*)但這個名字在文件系統中並沒有對應入口

除設備驅動外,不論硬件和軟件,在內核中都是模塊化的(如文件系統)

2. HelloWorld模塊

2.2.1 源文件

  • 創建hello.c
#include <linux/init.h>   
#include <linux/module.h>  

MODULE_LICENSE("Dual BSD/GPL");//告知內核自由許可證   

static int hello_init(void)   
{   
    printk(KERN_ALERT "Hello, world\n");//printk內核打印,KERN_ALERT消息優先級  
    return 0;   
}   
static void hello_exit(void)   
{   
    printk(KERN_ALERT "Goodbye, cruel world\n");   
}   

module_init(hello_init);//特別的內核宏   
module_exit(hello_exit);  
  • 創建makefile
obj-m := hello.o   
KERNELDIR := /home/fa/linux-3.4.y  
PWD := $(shell pwd)   

modules:  
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
modules_install:  
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install  
clean:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

2.2.2 模塊安裝與卸載

  • make編譯後,使用insmodmodprobermmod加載或卸載模塊
  • insmod加載模塊的代碼段和數據段到內核,加載中有類似ld的函數,它連接模塊中的符號到內核的符號表上,內核不修改模塊的磁盤文件,而是內存內的拷貝;insmod可接收許多命令行選項
  • insmod依賴kernel/module.c中定義的系統調用,函數sys_init_module分配內核內存來存放模塊(vmalloc),它接着拷貝模塊的代碼段到這塊內存區,藉助內核符號表解決模塊中的內核引用,並且調用模塊的初始化函數來啓動所有東西
  • sys_前綴的代碼對所有系統調用都是成立的
  • 模塊加載時,會有vermagic檢查,vermagic.o目標文件中包含了目標內核版本、編譯器版本,以及許多重要配置變量的設置信息,這些信息會與運行內核進行匹配,若不匹配,模塊將不會加載,可通過/var/log/message查看加載日誌
  • 模塊代碼必須要爲每個它要連接的內核版本重新編譯,模塊緊密結合到特殊內核版本的數據結構和函數原型之上

2.2.3 注意

  • 模塊沒有外部庫只能調用內核中函數,因此源文件中也不應包含通常的頭文件;大部分頭文件位於內核源碼include/linux以及include/asm目錄下,其餘部分已經關聯到特定內核子系統內
  • printk不支持浮點
  • 模塊加載到內核的過程
    在這裏插入圖片描述

2.3 帶參HelloWorld模塊

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
 
MODULE_LICENSE("Dual BSD/GPL");
static char *whom = "world";
static int howmany = 1;

//insmod hello.ko howmany=10 whom="hello"改變模塊參數
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO); 

static int hello_init(void)
{
	int i;
	for (i = 0; i < howmany; i++)
		printk(KERN_ALERT "(%d) Hello, %s\n", i, whom);
	return 0;
}
 
static void hello_exit(void)
{
	printk(KERN_ALERT "Goodbye, cruel world\n");
}
 
module_init(hello_init);
module_exit(hello_exit);
  • 參數用moudle_param宏定義來聲明,它定義在moduleparam.h中,module_param使用了3個參數,變量名、類型,以及一個權限掩碼用來做一個輔助的sysfs入口
  • 模塊參數支持類型:boolinvboolcharpintintlongshortuintulongushort
  • 數組參數使用module_param_array(name,type,num,perm);,perm是權限值,在<linux/stat.h>中定義,S_IRUGOS_IWUSR

2.4 源碼說明

2.4.1 Document目錄

  • 通過Document/kbuild目錄下文件查看模塊如何被建立,包括kbuildkconfigmakefilesmodules文件
  • 通過Document/Changes查看各種工具版本

2.4.2 與模塊相關的頭文件

  • module.h中包含了大量加載模塊需要的函數和符號的定義,init.h指定初始化和清理函數,moduleparam.h在模塊加載時傳遞參數
  • 可以在模塊中包含其他描述性定義,如MODULE_AUTHORMODULE_DESCRIPIONMODULE_VERSIONMODULE_ALIASMODULE_DEVICE_TABLE(告知用戶空間模塊支持那些設備),慣例這些聲明置於文件末尾
  • struct task_struct *current;(#include <linux/sched.h>,包含很多驅動使用的內核API的定義)
  • module_param(variable, type, perm);(#include <linux/moduleparam.h>)
  • int printk(const char * fmt, ...);(#include <linux/kernel.h>)

2.4.3 初始化&退出

  • 模塊初始化函數註冊模塊提供的任何功能
  • 初始化函數應聲明爲static,因爲它們不會在特定文件之外可見
  • __init標誌給內核暗示,給定的函數只是在初始化使用,__initdata給只在初始化時用的數據
  • __exit__exitdata
  • 使用moudle_init是強制的,它表明增加了特別的段到模塊目標代碼中,表明在哪裏找到模塊的初始化函數
  • 模塊可以註冊許多的不同設施,包括不同類型的設備、文件系統、加密轉換等;對每一個設施,有一個特定的內核函數來完成這個註冊,傳給內核註冊函數的參數常常是一些數據結構的指針,描述新設施以及要註冊的新設施的名字,數據結構常常包含模塊函數指針
static int __init initialization_function(void)
{
/* Initialization code here */
}
module_init(initialization_function);
static void __exit cleanup_function(void)
{
/* Cleanup code here */
}
module_exit(cleanup_function);

2.5 內核空間與用戶空間

  • 模塊在內核空間運行,應用程序在用戶空間運行
  • Unix下,內核在最高級操作形態運行,允許任何事情,而應用程序在最低級運行,這裏處理器控制了對硬件的直接存取以及對內存的非法存取;內核空間和用戶空間都有各自的內存映射(自己的地址空間)
  • Unix 從用戶空間轉換執行到內核空間,無論何時一個應用程序發出一個系統調用或者被硬件中斷掛起時,執行系統調用的內核代碼在進程的上下文中工作(它代表調用進程並且可以存取該進程的地址空間),即處理中斷的代碼對進程來說是異步的,不和任何特別的進程有關
  • 模塊通常的任務,一是作爲系統調用的一部分執行,二是負責中斷處理
  • Linux內核代碼包括驅動代碼,必須是可重入
    在這裏插入圖片描述

2.6 在用戶空間編寫驅動

  • 用戶空間設備驅動優點:可使用C庫,可運行常用調試器、可殺掉掛起的用戶空間驅動、用戶內存可交換、允許對設備並行存取
  • 用戶空間設備驅動缺點:無法使用中斷、只能通過內存映射/dev/mem使用DMA、存取I/O端口只能在調用iopermiopl之後、響應時間慢、最重要的設備不能在用戶空間處理

2.7 實操

  • 開發板:NanoPi-NEO-Air
  • 內核源碼版本:4.14.52 (從源碼頂層Makefile的首部可以看到源碼的版本號,開發板中使用uname -r可以看到系統運行的版本號
VERSION = 4
PATCHLEVEL = 14
SUBLEVEL = 52
  • 使用make編譯hello.c時,報錯
./arch/x86/include/asm/arch_hweight.h:55:42: error: expected ‘:’ or ‘)’ before ‘POPCNT64’
./arch/x86/include/asm/atomic64_64.h:22:22: error: request for member ‘counter$
./arch/x86/include/asm/atomic64_64.h:22:22: error: request for member ‘counter$
...

這是由於make是沒有指定編譯平臺和編譯工具鏈所致,使用make ARCH=arm CROSS_COMPILE=arm-linux-即可解決

  • 使用sudo insmod hello.ko時報錯
insmod: ERROR: could not insert module hello.ko: Unknown symbol in module

通過dmesg|tail查看日誌

[  116.880560] hello: loading out-of-tree module taints kernel.
[  116.880736] hello: Unknown symbol __gnu_mcount_nc (err 0)

百度搜索其原因,Unknown symbol __gnu_mcount_nc (err 0) ,是頂層Makefile開啓了gprof(並不知道是什麼東西),只需要把CFLAGS-pg-p參數去掉即可,即註釋掉第763

763 #CC_FLAGS_FTRACE := -pg 

再次編譯、insmod成功,通過dmesg|tail可以查看到

[  272.028920] hello: loading out-of-tree module taints kernel.
[  272.030778] Hello, world
  • 使用sudo rmmod -f hello卸載模塊時報錯
rmmod: ERROR: ../libkmod/libkmod-module.c:793 kmod_module_remove_module() could not remove 'hello': Device or resource busy
rmmod: ERROR: could not remove module hello: Device or resource busy

百度後,這種無法卸載模塊的原因有:設備號衝突(hello模塊並無設備號)、別的進程正在使用等,Linux強制卸載內核模塊(由於驅動異常導致rmmod不能卸載,該文描述的很清楚,但這裏仍舊沒找到無法卸載的原因,日後再說!

  • 調試工具
lsmod#查看已安裝的模塊
cat /proc/modules#查看設備詳細信息(模塊名字 內存大小 load次數 依賴 狀態 內存偏移位置)
dmesg#查看內核日誌(-T)
cat /proc/devices#查看設備以及設備號
modinfo hello.ko#查看模塊信息
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章