Linux内核模块

内核模块

基础知识

模块是一种向LInux内核添加设备驱动程序,文件系统及其其他组件的有效方法,不需要编译新内核或者重启系统

模块有如下优点:

  • 通过使用模块,内核发布者能够预先编译大量驱动程序,而不会致使内核映像的尺寸发送膨胀
  • 内核开发者可以将实验性的代码打包到模块中,模块可以卸载,修改代码或者重新打包后可以重新装载

模块添加和删除

从用户的角度来看,模块可以通过两个不同的系统程序添加到运行的内核中。它们分别是modprobe和insmod。modprobe在识别出目标模块所依赖的模块之后,在内核也会使用insmod,在用户空间对模块的处理也是基于insmod。

在处理该系统调用时,模块代码首先复制到内核内存中。接下来是重定位工作和解决模块中未定义的引用。因为模块使用了持久编译到内核中的函数,在模块本身编译时无法确定这些函数的地址,所以需要在这里处理未定义的引用。

处理未解决的引用,为与内核的剩余部分协作,模块必须使用内核提供的函数。这些可能是通用的辅助函数,比如几乎内核每一部分都会使用的printk或kmalloc。

  • insmod只加载一个单一的模块到内核中,而该模块可能只依赖内核中已存在的代码,并不关注所有依赖代码是通过模块动态加载,还是持久编译到内核中。
  • modprobe在识别出目标模块所依赖的模块之后,在内核也会使用insmod。在用户空间对模块的处理基于insmod
  • 在加载模块时所需要的操作,与通过ld和ld.so借助于动态库链接应用程序的操作。从外部来看,模块只是普通的可重定位目标文件。fe命令的输出表明模块文件是可重定位的,这是用户空间程序设计中一专业术语。可重位文件的函数都不会引用绝对地址,而只是指向代码中的相对地址,因此可以在内存的任意偏移地址加载,当然在映像加载到内存时,映像中的地址要由动态连接器ld.so进行适当的修改即可。重定位的工作由内核自身执行,而不是动态装载器。
  • ramfs模块允许在内存中建立一个文件系统(称为RAM磁盘),因而须调用register filesystem函数将自身添加到内核中可用文件系统的列表。此模块还使用内核代码中generic_file_read/generic_file_write标准函数,基本大多数内核文件系统都会使用这些普通函数。

导出函数查看

内核提供了一个所有导出函数的列表。该列表给出了所有导出函数的内存地址和对应函数名,可以通过proc文件系统访问,即文件/proc/kallsyms

T表示符号位于代码段,D表示符号位于数据段

kKcHF5fNljmPCeT

动手实践-内核模块的插入删除

代码

helloworld.c

  • hello_init为模块的初始化入口
  • hello_exit为模块的退出回调
  • module_init module_exit为指定初始化入口和模块卸载回调
  • MODULE_LICENSE指定GLP开源协议,不然很多内核函数不准使用,并且打印警告信息
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init hello_init(void) {
    printk(KERN_INFO "Hello, World!\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Goodbye, World!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example kernel module");

Makefile编译

或者直接使用这句指令

make -C /lib/modules/ ( u n a m e − r ) / b u i l d M = (uname -r)/build M= (unamer)/buildM=(pwd) modules

obj-m:=helloworld.o

CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL:=$(shell uname -r)
LINUX_KERNEL_PATH:=/usr/src/linux-headers-5.15.0-91-generic # 需要根据自己的需要,进行修改

all:
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules

clean:
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

如下是cmake版本

# 指定最低的 CMake 版本要求
cmake_minimum_required(VERSION 3.10)

# 设置项目名称
project(MyKernelModule)

# 指定内核源代码目录(可能需要根据你的系统修改此路径)
set(KERNEL_SOURCE_DIR /lib/modules/$(uname -r)/build)

# 设置模块名称
set(MODULE_NAME my_module)

# 添加内核模块的源文件
set(MODULE_SRC my_module.c)

# 添加内核模块作为构建目标
add_custom_target(
    ${MODULE_NAME}.ko ALL
    COMMAND make -C ${KERNEL_SOURCE_DIR} M=${CMAKE_CURRENT_SOURCE_DIR} modules
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    SOURCES ${MODULE_SRC}
)

# 添加清理目标
add_custom_target(
    clean_module
    COMMAND make -C ${KERNEL_SOURCE_DIR} M=${CMAKE_CURRENT_SOURCE_DIR} clean
)

JKNGmQZIWaHn4il

加载测试

加载
# 这是最普通的加载,
sudo insmod helloworld.ko
# 自动加载
sudo depmod
sudo modprobe helloworld
查看加载结果
sudo lsmod | grep helloworld

helloworld             12288  0
查看模块信息
root@VM-16-3-ubuntu:~/projects/test/modult# modinfo helloworld
filename:       /lib/modules/5.15.0-91-generic/helloworld.ko
description:    A simple example kernel module
author:         Your Name
license:        GPL
srcversion:     BD8DFFD87E8FF6F61A8F9C8
depends:
retpoline:      Y
name:           helloworld
vermagic:       5.15.0-91-generic SMP mod_unload modversions
查看内核日志
sudo dmesg | grep ello
[  237.298841] helloworld: loading out-of-tree module taints kernel.
[  237.298843] helloworld: module verification failed: signature and/or required key missing - tainting kernel
[  237.299466] Hello world!
删除模块
sudo rmmod helloworld

sudo lsmod | grep helloworld

sudo dmesg

[6328199.995893] Hello, World!
[6328337.483051] Goodbye, World!

生成符号

使用nm指令可以查看ko文件内所有生成的符号

T表示符号位于代码段,D表示符号位于数据段

root@VM-16-3-ubuntu:~/projects/test/modult# nm helloworld.ko
0000000000000000 T cleanup_module
                 U __fentry__
0000000000000000 t hello_exit
0000000000000000 t hello_init
0000000000000000 T init_module
0000000000000018 r _note_8
0000000000000000 r _note_9
                 U _printk
0000000000000000 D __this_module
000000000000002b r __UNIQUE_ID_author119
000000000000006b r __UNIQUE_ID_depends121
0000000000000000 r __UNIQUE_ID_description120
000000000000003c r __UNIQUE_ID_license118
0000000000000080 r __UNIQUE_ID_name119
0000000000000074 r __UNIQUE_ID_retpoline120
0000000000000048 r __UNIQUE_ID_srcversion122
0000000000000090 r __UNIQUE_ID_vermagic118
0000000000000000 r ____versions
                 U __x86_return_thunk
  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@马云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值