3-Linux驱动开发-简单内核模块代码详解

单一模块hellomodule

html 复制代码
代码结构:
	模块加载函数(必须) moudel_init
	模块卸载函数(必须) module_exit
	模块许可证声明(必须) MODULE_LICENSE
	模块参数
	模块导出符号
	模块的其他相关信息

hellomodule.c

c 复制代码
//以下头文件都包含在以下目录
///home/kun/kernal_code/ebf_linux_kernel-ebf_4.19_star/include/linux
#include <linux/module.h>//包含内核模块信息声明的相关函数
#include <linux/init.h>//包含了 module_init() 和 module_exit() 函数的声明
#include <linux/kernel.h>//包含内核提供的各种函数,如 printk

static int __init hello_init(void)
 {
    printk(KERN_EMERG "[ KERN_EMERG ]  Hello  Module Init\n");
    printk( "[ default ]  Hello  Module Init\n");
 return 0;
}
/*
初始化函数 (hello_init)
static: C 语言关键字,表示此函数只在本文件内可见。
int: 它必须返回一个 int 值。0表示初始化成功。如果返回非 0 值,insmod 命令将会失败。
__init:宏,目的告诉编译器,这个函数(hello_init)只在初始化时使用一次。内核加载成功后,会把这个函数占用的内存释放。
printk:内核版的 printf。
KERN_EMERG:日志级别,紧急情况(Emergency),当日志级别非常高时,内核日志不仅会进入 dmesg,还会被推送到所有当前活动的终端(Tty)上。
printk 函数
	#define KERN_EMERG "<0>"通常是系统崩溃前的信息
	#define KERN_ALERT "<1>"需要立即处理的消息
	#define KERN_CRIT "<2>"严重情况
	#define KERN_ERR "<3>"错误情况
	#define KERN_WARNING "<4>"有问题的情况
	#define KERN_NOTICE "<5>"注意信息
	#define KERN_INFO "<6>"普通消息
	#define KERN_DEBUG "<7>"调试信息
*/

static void __exit hello_exit(void)
{
    printk("[ default ]   Hello  Module Exit\n");
}
/*
退出函数 (hello_exit)
卸载 (rmmod) 时执行的函数。
__exit:宏。它告诉编译器,这个函数只在模块卸载时才需要。
*/

module_init(hello_init);//宏,注册hello_init函数,告诉内核:当这个 .ko 文件被 insmod 加载时,请调用 hello_init 函数。
module_exit(hello_exit);//宏,注册hello_exit函数,告诉内核:当这个模块被 rmmod 卸载时,请调用 hello_exit 函数。

MODULE_LICENSE("GPL2");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("hello world module");
MODULE_ALIAS("test_module");
/*
模块元数据 (Module Metadata):描述信息,明模块的作者、功能和许可证
MODULE_LICENSE("GPL2"):模块的许可证,表示模块代码接受的软件许可协议,Linux 内核遵循 GPL V2 开源协议,内
核模块与 linux 内核保持一致即可。
MODULE_AUTHOR / MODULE_DESCRIPTION,描述作者和功能
MODULE_ALIAS("test_module"):别名允许您使用 modprobe(insmod 的智能版本)通过别名 test_module 来加载 hellomodule.ko。
*/

对应Makefile

bash 复制代码
KERNEL_DIR=/home/kun/kernal_code/ebf_linux_kernel-ebf_4.19_star/build_image/build
ARCH=arm
CROSS_COMPILE=arm-none-linux-gnueabihf-
export  ARCH  CROSS_COMPILE
#m 代表 Module(模块),请求编译一个内核模块
obj-m := hellomodule.o
#$(MAKE)等于 make 命令
#-C(Change Directory)切换目录,切换到KERNEL_DIR
#M= 代表Module(要编译的模块) $(CURDIR) 是一个内置变量,代表当前所在的目录/hellomodule
#modules,编译成的具体目标
all:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules

.PHONE:clean copy

clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean	

copy:
	sudo  cp  *.ko  /home/embedfire/workdir

使用内核模块

html 复制代码
#插入这个内核模块 insmod仅用于测试用
debian@lubancat:/mnt/host_projects$ sudo insmod hellomodule.ko
#插入成功的默认内容输出
Message from syslogd@lubancat at Nov 13 21:12:09 ...
 kernel:[  666.015524] [ KERN_EMERG ]  Hello  Module Init
#利用管道查看此内核module
debian@lubancat:/mnt/host_projects$ lsmod | grep hellomodule
hellomodule            16384  0
#删除这个内核模块
debian@lubancat:/mnt/host_projects$ sudo rmmod hellomodule.ko

系统自动加载模块

bash 复制代码
#复制 .ko 文件到官方目录
#挂载位置文件放入/lib/modules/$(uname -r)/
#$(...) (命令替换) uname -r 内核版本字符串
sudo cp hellomodule.ko /lib/modules/$(uname -r)/

#注册更新模块数据库
sudo depmod -a #全局系统命令

#添加到开机自启列表
sudo nano /etc/modules
#写入hellomodule

#重启后查看
lsmod | grep hellomodule

内核模块传参与符号共享

函数说明

html 复制代码
extern 
声明一个变量

static int itype=0;
阻止任何C文件通过 C 语言的链接器直接看到或访问这个变量。

module_param(name, type, permission)
加载时给模块传递数值,暴露接口给用户,加载时输入
permission规则
	一共四位
		开头0代表八进制。
		后面是三个组Owner,Group,Others
	权限数字
		r (Read - 读) = 4
		w (Write - 写) = 2
		x (Execute - 执行) = 1

EXPORT_SYMBOL()
把函数或变量的内存地址,放入内核的全局符号表中,用于模块间通信,允许其他模块调用。

calculation.h

c 复制代码
//目的就是告诉别处有定义,编译时候不要报错
#ifndef __CALCULATION_H__
#define __CALCULATION_H__
//这个变量在别处定义
extern int itype;
//声明函数原型
int my_add(int a, int b);
int my_sub(int a, int b);
#endif

calculation.c

c 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

#include "calculation.h"

static int __init calculation_init(void)
 {
    printk(KERN_ALERT "calculation  init!\n");
    printk(KERN_ALERT "itype+1 = %d, itype-1 = %d\n", my_add(itype,1), my_sub(itype,1));    
    return 0;
 }

 static void __exit calculation_exit(void)
{
 printk(KERN_ALERT "calculation  exit!\n");
}


module_init(calculation_init);
module_exit(calculation_exit);

MODULE_LICENSE("GPL2");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("calculation module");
MODULE_ALIAS("calculation_module");

parametermodule.c

c 复制代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

static int itype=0;
module_param(itype,int,0);

static bool btype=0;
module_param(btype,bool,0700);

static char ctype=0;
module_param(ctype,byte,0);

static char  *stype=0;
module_param(stype,charp,0644);

static int __init param_init(void)
 {
 printk(KERN_ALERT "param init!\n");
 printk(KERN_ALERT "itype=%d\n",itype);
 printk(KERN_ALERT "btype=%d\n",btype);
 printk(KERN_ALERT "ctype=%d\n",ctype);
 printk(KERN_ALERT "stype=%s\n",stype);
 return 0;
}

static void __exit param_exit(void)
{
 printk(KERN_ALERT "module exit!\n");
}

EXPORT_SYMBOL(itype);

 int my_add(int a, int b)
  {
    return a+b;
 }

EXPORT_SYMBOL(my_add);

int my_sub(int a, int b)
 {
    return a-b;
 }

EXPORT_SYMBOL(my_sub);

module_init(param_init);
module_exit(param_exit);

MODULE_LICENSE("GPL2");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("module_param");
MODULE_ALIAS("module_param");

make后生成了相关文件

calculation.ko依赖parametermodule.ko中的参数itype和定义的函数(EXPORT_SYMBOL暴露的)

板子上使用时:

bash 复制代码
#要先加载parametermodule.ko再加载calculation.ko, 卸载类似出栈,module_param的传参
sudo insmod parametermodule.ko itype=123 btype=1 ctype=200 stype=abc
sudo insmod calculation.ko
相关推荐
lcreek37 分钟前
Linux信号机制详解:阻塞信号集与未决信号集
linux·操作系统·系统编程
Y1rong1 小时前
STM32之中断(二)
stm32·单片机·嵌入式硬件
Y1rong1 小时前
STM32之中断(一)
stm32·单片机·嵌入式硬件
shandianchengzi1 小时前
【记录】Tailscale|部署 Tailscale 到 linux 主机或 Docker 上
linux·运维·docker·tailscale
John Song2 小时前
Linux机器怎么查看进程内存占用情况
linux·运维·chrome
sichuanwuyi2 小时前
Wydevops工具的价值分析
linux·微服务·架构·kubernetes·jenkins
LaoZhangGong1232 小时前
学习TCP/IP的第3步:和SYN相关的数据包
stm32·单片机·网络协议·tcp/ip·以太网
小郭团队2 小时前
2_1_七段式SVPWM (经典算法)算法理论与 MATLAB 实现详解
嵌入式硬件·算法·硬件架构·arm·dsp开发
持戒波罗蜜2 小时前
ubuntu20解决intel wifi 驱动问题
linux·驱动开发·嵌入式硬件·ubuntu
不做无法实现的梦~2 小时前
使用ros2来跑通mid360的驱动包
linux·嵌入式硬件·机器人·自动驾驶