一、编译的流程
#独立模块编译的编译流程
mkdir modules #创建一个文件夹modules
vi hello.c #创建一个c文件 hello.c
vi Makefile #创建一个Makefile文件
make #输入编译命令make,此时会看到modules文件夹中生成了一个.ko文件
sudo insmod hello.ko #用insmod命令载入模块
lsmod | grep hello #lsmod命令查看所有模块,这里用grep查找所有模块中有无hello
modinfo hello.ko #查看内核模块文件相关信息,包含大小、依赖关系等内容
dmesg | tail -l #用dmesg查看系统log,这里查找所有log中最末尾的那几条log
sudo rmmod hello.ko #rmmod删除模块
dmesg | tail -l #查看log
1.1 Makefile文件
Makefile 文件描述了 Linux 系统下 C/C++ 工程的编译规则,它用来自动化编译 C/C++ 项目。一旦写编写好 Makefile 文件,只需要一个 make 命令,整个工程就开始自动编译,不再需要手动执行 GCC 命令。
一个中大型 C/C++ 工程的源文件有成百上千个,它们按照功能、模块、类型分别放在不同的目录中,Makefile 文件定义了一系列规则,指明了源文件的编译顺序、依赖关系、是否需要重新编译等。
1.2 .ko文件
是kernel object文件(内核模块),该文件的意义就是把内核的一些功能移动到内核外边,需要的时候插入内核,不需要时卸载。
1.3 insmod & rmmod
insmod命令用于将给定的模块加载到内核中。Linux有许多功能是通过模块的方式,在需要时才载入kernel。如此可使kernel较为精简,进而提高效率,以及保有较大的弹性。这类可载入的模块,通常是设备驱动程序。
rmmod命令用于从当前运行的内核中移除指定的内核模块。执行rmmod指令,可删除不需要的模块。Linux操作系统的核心具有模块化的特性,应此在编译核心时,务须把全部的功能都放如核心。你可以将这些功能编译成一个个单独的模块,待有需要时再分别载入它们。
1.4 两种内核编译方法
(1)独立模块编译
就是本小节最开始所示的流程。单独一个Makefile。在下一小节将会给出。
(2)放入内核源码中编译
这种方法的编译配置和(1)有所不同。
①把自己的内核代码放入到内核合适的位置,一般测试用的模块放在bsp/kernel/kernel4.14/drivers/misc下面
②把自己开发的功能添加到linux内核的配置选项中,使用户能够选中这项功能并编译。
在Kconfig文件结尾,endmenu的前面加入一个config选项
config 2020_HELLO
bool "This is my first drive "
default y
help
The driver hello.
③构建或修改Makefile。在misc文件夹中的Makefile里添加
obj-$(CONFIG_2020_HELLO) += hello.o
也可以在Makefile中直接添加:
obj-y += 模块文件夹名/
④执行make
看make后的config的menu。用make menuconfig命令。
下面所示为独立模块编译。
1.5 编译log查看
(1)kernel代码中插入打log的函数pr_info()或者printk()
(2)查看log用dmesg命令或cat /proc/kmsg看全部的log。dmesg | tail -l看最后几条
二、第一个内核模块代码
//hello.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/delay.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("kthread test");
MODULE_VERSION("0.1");
static struct task_struct *my_task = NULL;
static int my_kthread(void *data) {
char *str = (char *)data;
pr_info("my kthread data: %s\n", str);
pr_info("my kthread smp_processor_id %d\n", smp_processor_id());
while(!kthread_should_stop()) {
msleep(5000);
pr_info("my kthread: living. smp_processor_id %d\n", smp_processor_id());
pr_info("=========================================\n");
}
pr_info("my kthread: stop\n");
return 0;
}
static int __init my_init(void)
{
pr_info("my init.\n");
pr_info("smp_processor_id %d\n", smp_processor_id());
my_task = kthread_run(my_kthread, "hello my kthread", "mykthread-%s", "test");
pr_info("my init finish.\n");
pr_info("=========================================\n");
return 0;
}
static void __exit my_exit(void)
{
pr_info("my exit.\n");
pr_info("smp_processor_id %d\n", smp_processor_id());
if (my_task) {
pr_info("stop kthread\n");
kthread_stop(my_task);
}
pr_info("my exit finish.\n");
pr_info("=========================================\n");
}
module_init(my_init);
module_exit(my_exit);
#Makefile
obj-m := hello.o #
KERNELBUILD :=/lib/modules/$(shell uname -r)/build
default:
make -C $(KERNELBUILD) M=$(shell pwd) modules #注意这一行最前面必须是tab
clean:
rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers *.tmp_versions
#实际操作
di@ubuntu:~/modules/hello$ make clean #删除之前的载入模块是生成的多余文件
rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers *.tmp_versions
di@ubuntu:~/modules/hello$ make
make -C /lib/modules/4.2.0-27-generic/build M=/home/di.shen/modules/hello modules
make[1]: Entering directory `/usr/src/linux-headers-4.2.0-27-generic'
CC [M] /home/di.shen/modules/hello/hello.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/di.shen/modules/hello/hello.mod.o
LD [M] /home/di.shen/modules/hello/hello.ko
make[1]: Leaving directory `/usr/src/linux-headers-4.2.0-27-generic'
di@ubuntu:~/modules/hello$ sudo insmod hello.ko
di@ubuntu:~/modules/hello$ lsmod | grep hello
hello 16384 0
di@ubuntu:~/modules/hello$ dmesg | tail -l
[ 8279.854305] my kthread data: hello my kthread
[ 8279.854306] my kthread smp_processor_id 3
[ 8284.865161] my kthread: living. smp_processor_id 4
[ 8284.865163] =========================================
[ 8289.878954] my kthread: living. smp_processor_id 5
[ 8289.878959] =========================================
[ 8294.892851] my kthread: living. smp_processor_id 0
[ 8294.892856] =========================================
[ 8299.906715] my kthread: living. smp_processor_id 1
[ 8299.906717] =========================================
di@ubuntu:~/modules/hello$ sudo rmmod hello.ko
di@ubuntu:~/modules/hello$ dmesg | tail -l
[ 8365.086795] my kthread: living. smp_processor_id 3
[ 8365.086796] =========================================
[ 8369.316343] my exit.
[ 8369.316345] smp_processor_id 0
[ 8369.316345] stop kthread
[ 8370.100650] my kthread: living. smp_processor_id 4
[ 8370.100652] =========================================
[ 8370.100667] my kthread: stop
[ 8370.100721] my exit finish.
[ 8370.100726] =========================================
三、问题解决
WARNING: “mcount” xxx.ko undefined!
解决方法是:重装(升级)gcc,重装时必须先remove原来版本的gcc。
1.sudo apt-get remove gcc
2.sudo apt-get install gcc