linux驱动:编写最简单的内核模块helloWorld并移植到ARM板

环境:主机-Ubuntu 16.04,开发板-友善之臂tiny4412开发板,内核版本linux-3.5

参考《Linux设备驱动开发详解 基于最新的Linux 4.0内核》(宋宝华 编著)

 

一、简介

一个Linux内核模块主要由以下几部分组成:

1、模块加载函数

当通过insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。

2、模块卸载函数

当通过rmmod命令卸载模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。

3、模块许可证

许可证(LINCESE)声明描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染(Kernel Tainted)的警告。在Linux内核模块领域,可接受的LICENSE包括”GPL“、"GPL v2"、"GPL and additional rights" ...等。

4、模块参数(可选)

模块参数是模块被加载的时候可以传递给它的值,它本身对应模块内部的全局变量。

5、模块导出符号(可选)

内核模块可以导出的符号(symbol,对应于函数或变量),若导出,其他模块则可以使用本模块的变量或函数。

6、模块作者等信息声明(可选)

 

 

二、模块实现

代码:

#include 
#include 

int __init hello_init(void)
{
	printk("Hello World enter ++\n");
	return 0;
}

void __exit hello_exit(void)
{
	printk("Hello world exit --\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_AUTHOR("https://blog.csdn.net/qq_30155503");
MODULE_LICENSE("GPL v2");

1、加载:Linux内核模块加载函数一般以"__init"标识声明,如“int __init hello_init(void)”函数;通过module_init()函数指定加载函数,如“module_init(hello_init);”,指定加载函数为hello_init();当通过insmod或modprobe命令加载内核模块时,hello_init()将被自动执行;

2、卸载:Linux内核模块加载函数一般以"__exit"标识声明,如“void __exit hello_exit(void)”函数;通过module_exit()函数指定卸载函数,如“module_exit(hello_exit);”,指定卸载函数为hello_exit();当通过rmmod命令卸载模块时,hello_exit()将被自动执行。

3、MODULE_AUTHOR();是作者信息,MODULE_LICENSE();是模块许可证声明。

通常,加载函数完成初始化工作、申请资源等,卸载函数则完成释放资源、去初始化工作;两者的功能是相反的。

 

三、Makefile编写及编译

Makefile代码:

# to build modules

obj-m := hello.o

KERNELDIR ?= /data/arm-linux/kernel/tiny4412/linux-3.5
PWD := $(shell pwd)

all: modules

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

clean:
	rm -rf *.o *.ko *mod* *.sy* *ord* .*cmd .tmp*

注释:

obj-m:表示编译成模块,obj-y表示编译进内核。

KERNELDIR:内核源码的路径,编译与运行环境的内核版本应一致,即与ARM板上跑的linux系统的版本一致,“?=”表示若为空则为之赋值。(此处我指定的路径为tiny4412开发板附带的3.5版本内核)

当执行make clean时,当清除make时所生成的所有文件。

 

编译:make

错误信息:

  ERROR: Kernel configuration is invalid.
         include/generated/autoconf.h or include/config/auto.conf are missing.
         Run 'make oldconfig && make prepare' on kernel src to fix it.

原因:内核没有编译好,要先编译成功内核才要用,提示你运行“make oldconfig && make prepare”,最终可行的执行如下:

make oldconfig && make prepare && make scripts

编译通过:

make -C /data/arm-linux/kernel/tiny4412/linux-3.5 M=/data/project/driver/hello modules
make[1]: Entering directory '/data/arm-linux/kernel/tiny4412/linux-3.5'
  CC [M]  /data/project/driver/hello/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /data/project/driver/hello/hello.mod.o
  LD [M]  /data/project/driver/hello/hello.ko
make[1]: Leaving directory '/data/arm-linux/kernel/tiny4412/linux-3.5'

在当前目录生成了内核模块文件 hello.ko

 

四、运行

首先,将生成的内核模块文件hello.ko移到开发板上

1、加载模块:执行insmod命令进行加载


[root@FriendlyARM /mnt]# ls 
hello.ko
[root@FriendlyARM /mnt]# insmod hello.ko 
[  909.890000] Hello World enter ++

 

2、查看模块:执行命令lsmod可查看当前加载的内核模块


[root@FriendlyARM /mnt]# lsmod 
hello 570 0 - Live 0xbf278000 (O)
libertas_sdio 8423 0 [permanent], Live 0xbf25a000 (O)
libertas 54371 1 libertas_sdio,[permanent], Live 0xbf247000 (O)
zd1211rw 44215 0 [permanent], Live 0xbf237000 (O)
rt2800usb 12324 0 [permanent], Live 0xbf22f000 (O)

可见,确实有hello模块存在。

 

3、卸载模块:执行rmmod命令进行卸载


[root@FriendlyARM /mnt]# rmmod hello
[  976.995000] Hello world exit --

注意:加载时的参数是文件名("xxx.ko"),卸载时是模块名("hello")。

 

由上可见,加载、卸载模块时确实是自动执行了指定的加载函数与卸载函数。

 

你可能感兴趣的:(linux,driver)