linux的核心模組介紹
1、options-->document operations-->C source file : *.c;*.h;*.S
x86 Asm source file : ;*.S
2、project-->new project
3、將project工程新增原始檔
4、檔案同步:project--->sync files
========================================================
二、什麼是核心模組
在linux核心中,裝置驅動程式是以模組的方式存在的,設計一個裝置驅動,首先需要設計一個module。裝置驅動是包含在module中的。
好處:
1、一個驅動程式是一個模組,各個模組之間是獨立的。
2、模組可以安裝、解除安裝,比較靈活
3、方便除錯
=============================================================
三、設計一個簡單的module
例:linux/arch/arm/mach-s5pv210/adc.c
#include <linux/module.h>
#include <linux/kernel.h>
static int __init gec210_led_init(void)
{
//根據字元裝置驅動模型:定義cdev、申請裝置號、定義初始化file_operations
//將cdev註冊到核心
printk("hello linux device driver\n");
return 0;
}
static void __exit gec210_led_exit(void)
{
//相當於入口函式的反函式
printk("good bye\n");
}
module_init(gec210_led_init);
module_exit(gec210_led_exit);
//module的描述,可有可無。#modinfo led_drv.ko
MODULE_AUTHOR("
MODULE_DESCRIPTION("S5PV210 LED driver");
MODULE_VERSION("V1.0");
MODULE_LICENSE("GPL"); //原始碼符合GPL協議
//__init--用來修飾一個初始化函式,一般只執行一次,當一個驅動程式編譯到核心的時候,當zImage啟動過程中,這個驅動會自動安裝,當核心啟動結束,__init修飾的函式所佔用的記憶體區會被釋放掉。編譯的時候,__init修飾的函式會放在初始化段。
核心的啟動:
[ 0.000000] Memory: 256MB 256MB = 512MB total
[ 0.000000] Memory: 344408k/344408k available, 179880k reserved, 0K highmem
[ 0.000000] Virtual kernel memory layout:
[ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB)
[ 0.000000] fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
[ 0.000000] DMA : 0xff000000 - 0xffe00000 ( 14 MB)
[ 0.000000] vmalloc : 0xe0800000 - 0xfc000000 ( 440 MB)
[ 0.000000] lowmem : 0xc0000000 - 0xe0000000 ( 512 MB)
[ 0.000000] modules : 0xbf000000 - 0xc0000000 ( 16 MB)
[ 0.000000] .init : 0xc0008000 - 0xc008e000 ( 536 kB) --->核心的初始化段
[ 0.000000] .text : 0xc008e000 - 0xc07c7000 (7396 kB)
[ 0.000000] .data : 0xc07c8000 - 0xc0829460 ( 390 kB)
[ 11.175934] Freeing init memory: 536K
=============================================================
四、驅動程式設計和應用設計的區別
1、應用程式有入口(main函式),沒有出口。
驅動程式有入口,有出口
入口:安裝驅動(#insmod *.ko)的時候會呼叫入口函式--->module_init()--->gec210_led_init()
出口:解除安裝驅動(#rmmod *)的時候,會調用出口函式--->module_exit()--->gec210_led_exit()
2、應用程式在裝置的時候,可以使用C的庫函式,#include <stdio.h>
驅動程式只能使用linux核心提供函式,不能使用標準C庫。stdio.h --->printf()
3、應用程式的編譯使用arm-linux-gcc,再指定庫的位置
驅動程式的編譯要使用核心原始碼包中的編譯工具,使用核心原始碼目錄下的Makefile。
(嵌入式平臺:編譯驅動,需要核心原始碼)
=============================================================
五、編譯驅動程式的Makefile
1、基於嵌入式平臺的Makefile:
obj-m += led_drv.o
KERN_DIR=/home/gec/android-kernel-samsung-dev
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules clean
1)obj-m += led_drv.o
將led_drv.c編譯成目標檔案,然後將目標檔案再編譯連結成一個獨立的module(led_drv.ko)
2)KERN_DIR=/home/gec/android-kernel-samsung-dev
定義了一個核心原始碼的路徑,編譯過程中,要去核心原始碼拿編譯工具,使用標頭檔案。
3)PWD := $(shell pwd)
指定當前目錄
4)modules:
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules
編譯的時候,去核心原碼的目錄下,找編譯工具(Makefile和kbuild)和標頭檔案(include);然後回到當前路徑下,將驅動原始檔編譯成一個module(led_drv.ko)
編譯輸出:
$ make
make -C /home/gec/linux-2.6.35.7-gec-v3.0-gt110 M=/mnt/hgfs/12-driver/01module/demo/demo1 modules
make[1]: Entering directory `/home/gec/linux-2.6.35.7-gec-v3.0-gt110'
CC [M] /mnt/hgfs/12-driver/01module/demo/demo1/led_drv.o
Building modules, stage 2.
MODPOST 1 modules
CC /mnt/hgfs/12-driver/01module/demo/demo1/led_drv.mod.o
LD [M] /mnt/hgfs/12-driver/01module/demo/demo1/led_drv.ko
make[1]: Leaving directory `/home/gec/linux-2.6.35.7-gec-v3.0-gt110'
---------------------------------------------------------------------------------------
2、ubuntu+x86平臺
obj-m += hello.o
KERN_VER = $(shell uname -r)
KERN_DIR = /lib/modules/$(KERN_VER)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules clean
=============================================================
六、編譯驅動的核心原始碼包的要求
1、linux原始碼包的版本要和目標平臺執行的linux的版本要一致
2、核心原始碼包要針對目標平臺程序配置
1)修改Makefile:ARCH ?=
CROSS_COMPILE=
2)將arch/arm/configs/smdkv210_android_defconfig拷貝給.config
3)make menuconfig
3、核心原始碼包必須要編譯完成過。
=============================================================
七、驅動使用
1、檢視ko的資訊
$ modinfo led_drv.ko
filename: led_drv.ko
license: GPL
version: V1.0
description: S5PV210 LED driver
author: [email protected]
srcversion: B4AD7101902D9417761A14B
depends:
vermagic: 2.6.35.7-GEC210 preempt mod_unload ARMv7
關鍵:vermagic(版本魔數)--->ko安裝的時候,目標環境的軟體版本和硬體版本。
提示1:如何給核心原始碼包設定本地版本-->2.6.35.7-GEC210
General setup --->
(-GEC210) Local version - append to kernel release
提示2:如何檢視硬體的版本
[[email protected] /]# uname -r
2.6.35.7-GEC210
[[email protected] /]# uname -a
Linux GEC210 2.6.35.7-GEC210 #1 PREEMPT Mon Sep 16 17:05:23 CST 2013 armv7l GNU/Linux
2、安裝驅動
# insmod led_drv.ko
[ 885.327603] hello linux device driver
3、檢視核心中的module
# lsmod
led_drv 560 0 - Live 0xbf011000
snd_soc_gec210_wm8960 3134 0 - Live 0xbf00b000
snd_soc_wm8960 19792 1 snd_soc_gec210_wm8960, Live 0xbf000000
4、解除安裝驅動
# rmmod led_drv
[ 1109.048002] good bye
=============================================================
八、printk函式
#include <linux/kernel.h>
與printf()的區別,printk()帶有優先順序
1、例:
static char banner[] __initdata = KERN_INFO \
"S5PV210 ADC driver, (c) 2010 Samsung Electronics\n";
printk(banner);
分析:
1)__initdata --->修飾的是一個初始化資料,與“__init”的作用相同
2)KERN_INFO--->printk的優先順序
----------------------------------------------------------------------------------
2、printk的優先順序
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
printk(KERN_WARNING "hello world");
printk("<4>" "hello world");
printk("<4> hello world");//錯的
------------------------------------------------------------------------------------
3、檢視系統設定的優先順序
# cat /proc/sys/kernel/printk
7 4 1 7
7--->控制檯(串列埠0,console)輸出的優先順序,只有高與這個優先順序的printk,才可以通過控制檯輸出
printk("<4>" "hello world"); --->輸出
printk("<7>" "hello world"); --->沒有輸出
4--->通過控制檯輸出的預設優先順序
printk("hello world"); --->使用預設的優先順序4
1--->寫到日誌中的最高優先順序
7--->寫道日誌中的最低優先順序
-----------------------------------------------------------------------------------
4、修改系統設定的優先順序
# echo 7 5 1 7 > /proc/sys/kernel/printk
注意:
/proc下的內容掛載的是proc檔案系統,proc檔案系統是一個虛的檔案系統,是基於記憶體的檔案系統。可以實時的反應linux核心的工作資訊。
=============================================================
九、核心符號表
1、應用場景:
一個模組A中定義一個函式,模組B使用模組A定義的函式。如何解決?
方法:
在模組A中,將該函式宣告到核心符號表中,核心符號表對linux核心來講是一個全域性的表,在任何一個模組中,都可以使用核心符號表中宣告的函式。
2、檢視核心符號表
/proc/kallsyms
3、如何將一個函式(或一個全域性變數)宣告到核心符號表
EXPORT_SYMBOL()
EXPORT_SYMBOL_GPL() --->只是符合GPL協議的module才可以使用這個函式
提示:如何讓一個module符合GPL協議????
MODULE_LICENSE("GPL"); //原始碼符合GPL協議
提示:
如何去/proc/kallsyms查詢符號add_xy
#grep -r add_xy /proc/kallsyms