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
相关推荐
赖small强3 小时前
【Linux驱动开发】 Linux字符设备开发详细指南
linux·驱动开发·字符设备
p66666666683 小时前
【☀Linux驱动开发笔记☀】linux下led驱动(非设备树)_03
linux·驱动开发·笔记·嵌入式硬件·学习
以琦琦为中心3 小时前
在RK3568开发板嵌入式开发中,配置NFS服务是实现与Ubuntu虚拟机之间文件共享的常用方法
linux·运维·ubuntu·rk3568
Nimsolax3 小时前
Linux网络DNS与ICMP
linux·网络
千年糊涂4 小时前
STM32使用薄膜压力传感器
stm32·单片机·嵌入式硬件
赖small强4 小时前
【Linux驱动开发】Linux UART 通信详解:从硬件到驱动再到应用
linux·驱动开发·uart
赖small强4 小时前
【Linux驱动开发】Linux 设备驱动中的阻塞与非阻塞 I/O:机制、源码与示例
linux·驱动开发·阻塞与非阻塞
yolo_guo4 小时前
opencv 学习: QA_02 什么是图像中的高频成分和低频成分
linux·c++·opencv·计算机视觉
q***56384 小时前
在 Ubuntu 22.04 上安装和配置 Nginx 的完整指南
linux·nginx·ubuntu