1. 程式人生 > >Linux驅動----1、最簡單的驅動hello.ko

Linux驅動----1、最簡單的驅動hello.ko

    
    驅動程式應該處理如何使硬體可用、而將怎樣使用硬體留給上層應用程式。裝置分類:字元裝置、塊裝置、網路裝置。
    字元裝置驅動程式至少實現open、close、read、write系統呼叫。字元裝置可以通過檔案系統節點來訪問。這些裝置檔案和普通檔案之間的唯一差別在於普通檔案的訪問可以前後移動訪問位置,而大多字元裝置是一個只能順序訪問的資料通道。
    塊裝置上能容納檔案系統,一個檔案系統決定了如何在塊裝置上組織資料,以及表示目錄和檔案形成的樹。檔案系統不是裝置驅動程式,它沒有實際物理裝置同這種資訊組織方式相關聯。它只是個軟體驅動程式,將低層資料結構對映到高層資料結構。
    模組能呼叫的函式僅僅是由核心匯出的那些函式,而不存在任何可連線的庫函式。Linux核心程式碼必須是可重入的,它必須能同時執行在多個上下文。

hello.c原始檔

  • module.h包含可裝載模組需要的大量符號和函式定義
  • init.h指定初始化和清除函式
  • MODULE_LICENSE巨集告訴核心,該模組採用的協議
  • printk可能不會將資訊列印在終端上,可用dmesg命令檢視(dmsg | tail -5只讀最後5行)
  • module_init該巨集在模組目的碼中增加一個特殊的段,用於說明核心初始化函式所在的位置。
#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");
static int
hello_init(void) { printk(KERN_ALERT "Hello, world\n"); return 0; } static void hello_exit(void) { printk(KERN_ALERT "Goodbye, world\n"); } module_init(hello_init); module_exit(hello_exit);

編譯生成hello.ko模組

方法一、Makefile+命令列
Makefile

obj-m := hello.o

命令列

  • make modules 忽略中間引數是編譯模組的意思
  • -C $(KDIR)指明跳轉到核心原始碼目錄下讀取那裡的Makefile
  • M=$(PWD) 表明然後返回到當前目錄繼續讀入、執行當前的Makefile
make -C /usr/src/linux-headers-4.13.0-16-generic M=`pwd` modules
//這裡4.13.0-16-generic=uname -r

方法二、完整的Makefile

obj-m := hello.o
    KERNELBUILD := /lib/modules/$(shell uname -r)/build
defaule:
    make -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
    rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers 

    make會在Makefile中的第一個不是以.開頭的目標作為預設的目標執行。KERNELRELEASE定義在核心Makefile中,所以執行else後面的KERNELDIE, PWD賦值,執行default:-C指明跳轉到核心原始碼目錄下讀取那裡的Makefile;M=$(PWD) 表明然後返回到當前目錄繼續讀入、執行當前的Makefile。當從核心原始碼目錄返回時,KERNELRELEASE已被定義,**kbuild也被啟動去解析kbuild語法的語句,make將繼續讀取else之前的內容。**else之前的內容為kbuild語法的語句,指明模組原始碼中各檔案的依賴關係,以及要生成的目標模組名

# Comment/uncomment the following line to disable/enable debugging
#DEBUG = y

# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O -g # "-O" is needed to expand inlines
else
  DEBFLAGS = -O2
endif

EXTRA_CFLAGS += $(DEBFLAGS) -I$(LDDINCDIR)

ifneq ($(KERNELRELEASE),)
# call from kernel build system

obj-m   := hello.o

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINCDIR=$(PWD)/../include modules

endif



clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

//make depend = make .depend = make dep
depend .depend dep:
    $(CC) $(EXTRA_CFLAGS) -M *.c > .depend


ifeq (.depend,$(wildcard .depend))
include .depend
endif

安裝、解除安裝模組

  • sudo insmod hello.ko安裝之後,呼叫dmesg可以看到hello,world
  • 也可以通過tail /var/log/kern.log檢視
  • sudo rmmod hello 解除安裝後,呼叫dmesg可以看到goodbye,world
  • lsmod 通過讀取/proc/modules虛擬檔案獲得已安裝模組
  • 以裝載的模組可以在/sys/module下找到

核心編譯的注意事項

  1. 核心使用非常小的棧,可能只有4096B(一頁),自己編寫的驅動函式必須和整個核心空間共享這個一棧。因此需要大的結構,使用動態分配kmalloc。
  2. “_ ”雙下劃綫字首的函式通常是介面的底層元件。 _ init標記表示,模組裝載之後將扔掉初始化函式。 _ _exit標記表示該程式碼用於模組解除安裝,如果模組直接編譯進核心或不允許解除安裝,這該函式被丟棄。
  3. 使用者空間的驅動程式可以實現為一個伺服器程序,其任務是代替核心作為硬體控制的唯一代理,客戶應用程式可連線到該伺服器並和裝置執行實際通訊。