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
相关推荐
知识分享小能手1 分钟前
Ubuntu入门学习教程,从入门到精通,Ubuntu 22.04的Linux网络配置(14)
linux·学习·ubuntu
皇族崛起8 分钟前
【视觉多模态】- scannet 数据的 Ubuntu 百度网盘全速下载
linux·ubuntu·3d建模·dubbo
刻BITTER20 分钟前
在TRAE 上安装PlatformIO
c++·单片机·嵌入式硬件·arduino
CAU界编程小白22 分钟前
Linux系统编程系列之进程控制(下)
linux·进程控制
m0_555762901 小时前
I.MX8 Plus —— 系统总线与互连
嵌入式硬件
RisunJan1 小时前
Linux命令-ifconfig命令(配置和显示网络接口的信息)
linux·运维·服务器
LaoWaiHang1 小时前
Linux基础知识04:pwd命令与cd命令
linux
lbb 小魔仙1 小时前
【Linux】100 天 Linux 入门:从命令行到 Shell 脚本,告别“光标恐惧”
linux·运维·服务器
小张成长计划..2 小时前
【Linux】1:基本指令
linux
OliverH-yishuihan2 小时前
在win10上借助WSL用VS2019开发跨平台项目实例
linux·c++·windows