IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux字符驱动开发《01_Linux内核模块》 -> 正文阅读

[系统运维]Linux字符驱动开发《01_Linux内核模块》

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

开发环境:ubuntu18.04
硬件开发板:imx6ull
内核:Linux-4.9.88

一、内核模块概念

概念性知识不做科普(复制、粘贴),大家可以参照野火的教程可以理解一些概念,对代码为什么这样写有帮助,知其所以然!
需要了解微内核和宏内核的区分?
为什么需要引入内核模块?
编写Linux设备驱动和内核模块有什么关系?

二、内核模块的文件格式

.ko 文件在数据组织形式上是ELF(Excutable And Linking Format) 格式,是一种普通的可重定位目标文件。这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件,静态链接库也可以归为这一类。

三、内核模块的过程分析

通俗的理解:编译出来的.ko文件是经过内核处理过的代码,所以在我们执行加载的过程中内核会有办法去利用.ko文件。

内核相关的文件一般都在kernel目录下

D:\imx6ull\imx-linux4.9.88\kernel\module.c

2. 内核模块加载过程insmod

我们在终端输入insmod的时候,通过文件系统将.ko 模块读到用户空间的一块内存中,然后执行系统调用sys_init_module() 解析模块,,将内核在vmalloc 区分配与ko 文件大小相同的内存来暂存.ko 文件,暂存好之后解析ko 文件。

SYSCALL_DEFINE3(init_module, void __user *, umod,
		unsigned long, len, const char __user *, uargs)
{
	int err;
	struct load_info info = { };  //存储内核模块的.ko文件的信息
	
	err = may_init_module();

	pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n",umod, len, uargs);
	//vmalloc 分配空间info,从用户空间拷贝.Ko信息到info,info->hdr指向ko的elf hedader
	err = copy_module_from_user(umod, len, &info); 

	return load_module(&info, uargs, 0); //接下来就是复杂的对.ko信息的处理
}

对.ko文件的解析是一个复杂的工作,load_module 包括内核模块的重定位和加载等许多操作,.ko文件中的各个section 的init 段和core 段分配到最终的运行地址,然后开始执行初始化,.ko加载完成后init段会被释放,只保留core段来运行。

2. 内核模块卸载过程rmmod

在终端输入rmod指令,最终在系统内核中需要调用sys_delete_module进行实现。

SYSCALL_DEFINE2(delete_module, const char __user *, name_user,unsigned int, flags)
{
	struct module *mod;
	char name[MODULE_NAME_LEN];
	int ret, forced = 0;

    //从用户空间获取模块的名字
	if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
		return -EFAULT;
	name[MODULE_NAME_LEN-1] = '\0';

	if (mutex_lock_interruptible(&module_mutex) != 0)
		return -EINTR;

	mod = find_module(name);  //查找进程中的module名,返回句柄

	if (!list_empty(&mod->source_list))/* 如果有模块的依赖,先卸载依赖. */
	/* Doing init or already dying? */
	if (mod->state != MODULE_STATE_LIVE) //状态处理:如在初始化阶段

	if (mod->init && !mod->exit) /* 如何有模块的依赖,先卸载依赖. */

	/* Stop the machine so refcounts can't move and disable module. */
	ret = try_stop_module(mod, flags, &forced);
	
	mutex_unlock(&module_mutex);
	/* Final destruction now no one is using it. */
	if (mod->exit != NULL)
		mod->exit();
	//告诉通知链module_notify_list 上的监听者, 模块状态变为MODULE_STATE_GOING
	blocking_notifier_call_chain(&module_notify_list,MODULE_STATE_GOING, mod);
	klp_module_going(mod);
	ftrace_release_mod(mod);
	async_synchronize_full();//等待所有异步函数调用完成

	/* Store the name of the last unloaded module for diagnostic purposes */
	strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));

	free_module(mod);
	return 0;
out:
	mutex_unlock(&module_mutex);
	return ret;
}

3. 内核符号的导出 EXPORT_SYMBOL

符号指的就是内核模块中使用EXPORT_SYMBOL 声明的函数和变量
当模块被装进内核,所有导出的符号都会记录在公共的内核符号表中

//这两个宏定义会在编译的时候存放到ELF文件中
EXPORT_SYMBOL(name)
EXPORT_SYMBOL_GPL(name) //name 为我们要导出的标志

四、内核模块的实验

1.头文件的引用

#include <linux/module.h>:包含了内核加载module_init()/卸载module_exit() 函数和内核模块信息相关函数的声明
#include <linux/init.h>:包含一些内核模块相关节区的宏定义(前面可知一般都要用到)
#include <linux/kernel.h>:包含内核提供的各种函数,如printk

注意:
内核模块运行的过程中,他不能依赖于C 库函数,因此用不了printf 函数,需要使用单独的打印输出函数printk。

2.模块加载函数

当通过insmod 或modprobe 命令加载内核模块时,模块的加载函数module_init就会自动被内核执行,完成本模块相关的初始化工作。

static int __init demo_module_init(void)
{
	printk(KERN_INFO "demo module init !!!");
	return 0;
}

module_init(demo_module_init);

3.模块卸载函数

当执行rmmod 命令卸载模块时,模块卸载函数module_exit就会自动被内核执行,完成相关清理工作。

static void __exit demo_module_exit(void)
{
	printk(KERN_INFO "demo module exit !!!");
}

module_exit(demo_module_exit);

注意:为什么需要加__init ?
在头文件/linux/init.h中有对应的宏定义:

D:\imx6ull\imx-linux4.9.88\include\linux\init.h

__init修饰函数 ,__initdata修饰变量

带init表示将可执行文件放到init区(变量需要加__initdata),该区只能用于模块的初始化阶段,初始化完毕之后就会被释放。

4.附加信息

MODULE_LICENSE()GPL许可证(必须有)
MODULE_AUTHOR()作者
MODULE_DESCRIPTION()模块描述
MODULE_ALIAS()别名

3.Makefile文件

KERNEL_DIR := /home/book/100ask_imx6ull-sdk/Linux-4.9.88
arch = arm 
CROSS_COMPILE = arm-linux-gnueabihf-

obj-m += demo_module.o


all:
	make -C $(KERNEL_DIR) M=$(CURDIR) modules

clean:
	make -C $(KERNEL_DIR) M=$(CURDIR) clean
	rm -rf modules.order

-C 表示改变目录,转到内核的目录的Makefile去执行,M=(CURDIR)表示又返回当前的目录来执行当前的Makefile和内核一起编译。

五、验证

insmod
这里把常用到的命令列举一下

insmod <xxx.ko> 安装
rmmod <xxx.ko>  卸载
lsmod  列举
cat /proc/modules  lsmod读取的也是/proc/modules
modprobe <xxx.ko>  检查依赖并安装
modprobe -r <xxx.ko> 检查依赖并卸载
modinfo <xxx>  模块信息

补充内容

1.vmalloc 与malloc ?

2.strncpy_from_user 用于短的字符串拷贝

3. 内核信息打印

cat /proc/sys/kernel/printk   //查看当前系统printk的打印等级
dmesg   //打印内核的所有信息 但是缓冲区大小可能有限制
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 21:35:42  更:2022-03-21 21:39:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 0:45:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码