Linux内核学习 篇-00:构建学习环境并运行一个HelloWorld模块

“Linux内核学习”系列博文致力为每一位有兴趣学习Linux内核的同志排除学习道路上的障碍,给大家提供最好的帮助,补足市面上各种材料书籍解释不周、报错多等问题。
本系列博文使用的系统版本和编译器版本等均在《Linux内核学习 篇-00》中有详细介绍。



  • 注意事项

  • 本系列博文默认您有一定的:Linux、VIM、C、操作系统基础。
  • 为了方便大家的阅读学习,可能会引用其他博文资讯中的内容,所有引用都会将来源附在文末。
  • 如果参考本文学习过程中遇到任何报错或问题,欢迎您在评论区发起提问,我会将所有问题的原因以及解决方法完善在对应博文的“后话”中,感谢您的贡献。
  • 持续更新中,欢迎点赞收藏关注,三连不迷路。

正文

笔者平时学习生活使用的是Ubuntu,抱着更了解自己的系统,甚至定制属于自己的内核功能等愿望,开始了Linux内核的学习。最初,我直接在工作机上进行各种操作。但是频繁遇到各种问题,诸如:内核编译器版本与模块编译器版本不一致等,这些都会导致内核模块无法加载。
因此笔者建议:即使您平时就是使用Linux的各种发行版进行工作学习,为了保证不破坏自己的环境,请专门搭建一个全新的Linux虚拟机,用来学习接下来的内容。我认为这是完全值得的。

[一] 环境搭建

[1] 下载安装虚拟机/Ubuntu

本系列博文使用的镜像/软件版本如下,可以点击直接下载:
虚拟机:VMware Workstation Pro 16.2.2
Ubuntu:ubuntu-22.04.1-live-server-amd64
Ubuntu建议用种子,会比HTTP快一点。
安装过程很简单,在网络设置环节需要手动DHCP一下,在DISK设置环节需要取消第二个X,不再赘述,不熟悉的同志可以搜索:虚拟机安装Ubuntu live server

[2] 安装GCC、VIM等必要工具

为了尽可能避免奇怪的问题,我决定不进行换源操作,直接进行安装

sudo apt update
sudo apt upgrade
sudo apt install gcc vim make libncurses-dev flex bison libssl-dev libelf-dev dwarves

[二] 重新编译内核

进行内核学习和开发的第一步是配置和重新编译内核,这么做为了以后各种工具的版本问题,也可以让我们先熟悉一下内核的编译启用过程。
首先运行uname -r查看当前内核版本
在这里插入图片描述

然后运行sudo apt install linux-source-5.15.0,下载Linux内核代码。请将版本号替换为您自己的内核版本。
下载好后,进入以下目录进行解压和配置:

cd /usr/src/linux-source-5.15.0
sudo tar -xjvf linux-source-5.15.0.tar.bz2
cd linux-source-5.15.0
sudo vim .config
# 如果没有.config文件,就使用sudo make menuconfig生成一个,配置页面出来后直接退出就可以

输入/查找字符串:CONFIG_SYSTEM_TRUSTED_KEYS和下面的CONFIG_SYSTEM_REVOCATION_KEYS,将此两行字符串注释掉,然后执行:

sudo make bzImage -j8
sudo make modules -j8
# 这里的`-j8`是编译使用的CPU核心数,这个要根据同志们创建虚拟机时分配的核心数来决定。
sudo make modules_install
sudo make install
reboot

要等比较久,只需要等就好了,需要选择的话直接回车。
重启后再次执行uname -r,可以看到此时的查询结果已经与原来的不同了,说明我们确实成功编译并更换了系统的内核:
在这里插入图片描述

[三] 编写并加载一个helloworld模块

可以先把home目录清理一下,然后在home目录:

mkdir helloworld
cd helloworld
touch helloworld.c
touch Makefile

然后在helloworld.c中输入以下代码:

#include <linux/init.h>
#include <linux/module.h>

static int __init helloworld_init(void){
	printk("Hausa_ said hello to Linux kernel");
	return 0;
}

static void __exit helloworld_exit(void){
	printk("Hausa_ said bye to Linux kernel");
}

module_init(helloworld_init);
module_exit(helloworld_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Hausa_ hausahan@gmail.com");
MODULE_DESCRIPTION("Hausa_'s helloworld module");
MODULE_ALIAS("helloworld");

麻雀虽小,五脏俱全。尽管这个模块只有两个函数,但是却完全可以加载运行。
helloworld_init()函数是该模块初始化/入口函数,helloworld_exit()是销毁/出口函数;
module_init()module_exit()linux/init.h中声明的函数,这两个函数告诉内核:某某函数是这个模块的入口/出口函数。
对于最下面一部分大写的函数,是在linux/modules,h中声明的,分别表明了该模块接受的软件许可协议、作者信息、描述以及别名。
printk()函数类似C语言常见的printf,只是前者会将字符串输出到内核模块日志中而不是当前终端上。
接下来我们来编译这个模块,在Makefile中输入以下代码:

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

CONFIG_MODULE_SIG=n
obj-m := helloworld.o
all:
	$(MAKE) -C $(KERNELDIR) M=$(shell pwd) modules;
clean:
	rm -rf *.o *.ko *.mod *.symvers *.order *.mod

KERNELDIR为正在运行的Linux内核编译目录,obj-m表示要生成的模块
在终端输入make来进行编译。
编译后查看当前目录文件,多了很多文件,其中一个helloworld.ko即为我们需要的文件,我们可以用filemodinfo命令来检查一下编译结果是否正确:
在这里插入图片描述可以看到文件格式为x86-64的ELF文件,并且内核各种信息都正常表示了。
在终端输入sudo insmod helloworld.ko来加载我们的模块,然后可以输入以下命令查看模块打印信息或检查其是否被加载:

sudo dmesg
sudo lsmod | grep helloworld

在这里插入图片描述
最后,可以用sudo rmmod helloworld来将模块卸载。
此外,加载模块后,系统会在/sys/module/目录下为模块新建一个目录,对于此例会建helloworld目录,卸载模块后该目录会删除。
本篇结束,有任何报错都可以在评论区反映,我会帮助解决。


后话

如果本文的点赞阅读收藏量还看得过去,我会持续输出同系列文章,感谢大家的支持。

报错

在最后make的一步可能会报错,原因可能是缩进处使用了空格而非tab。请将缩进更改为tab

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值