内核模块(编译方法)

news2024/10/2 1:39:56

目录

一、向内核添加新功能

1.1 静态加载法:

1.2 动态加载法:

a、新功能源码与Linux内核源码在同一目录结构下时

b、新功能源码与Linux内核源码不在同一目录结构下时

c、主机ubuntu下使用ko文件

d、开发板Linux下使用ko文件

二、内核模块基础代码解析

Linux内核的插件机制——内核模块

三、内核模块的多源文件编程

四、 内核模块信息宏


一、向内核添加新功能

1.1 静态加载法:

即新功能源码与内核其它代码一起编译进uImage文件内

1. 新功能源码与Linux内核源码在同一目录结构下

   在linux-3.14/driver/char/目录下编写myhello.c,文件内容如下:

#include <linux/module.h>
#include <linux/kernel.h>
   
int __init myhello_init(void)
{

	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("myhello is running\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	return 0;
}

void __exit myhello_exit(void)
{
	printk("myhello will exit\n");
}

MODULE_LICENSE("GPL");

module_init(myhello_init);

module_exit(myhello_exit);
  

2. 给新功能代码配置Kconfig

   cd  ~/fs4412/linux-3.14/drivers/char

   vim Kconfig

(就是给make menuconfig中添加一个菜单项) 

   #39行处添加如下内容:

   config MY_HELLO

      tristate "This is a hello test"

      help

         This is a test for kernel new function

 

 

3. 给新功能代码改写Makefile

   #进入myhello.c的同级目录

   cd  ~/fs4412/linux-3.14/drivers/char

   vim Makefile

   #拷贝18行,粘贴在下一行,修改成:

   obj-$(CONFIG_MY_HELLO)     += myhello.o

 

 

4. make menuconfig  界面里将新功能对应的那项选择成<*>

   cd  ~/fs4412/linux-3.14

   make menuconfig

   #make menuconfig如果出错,一般是两个原因:

   #1. libncurses5-dev没安装

   #2. 命令行界面太小(太矮或太窄或字体太大了)

 

 

 选成和内核一起编译

5. make uImage

 

6. cp arch/arm/boot/uImage ~/tftpboot

 

7. 启动开发板观察串口终端中的打印信息

 

1.2 动态加载法:

即新功能源码与内核其它源码不一起编译,而是独立编译成内核的插件(被称为内核模块)文件.ko

a、新功能源码与Linux内核源码在同一目录结构下时

1. 给新功能代码配置Kconfig

2. 给新功能代码改写Makefile

3. make menuconfig  界面里将新功能对应的那项选择成<M>

4. make uImage

5. cp arch/arm/boot/uImage /tftpboot

这个时候启动就没有那些打印了

6. make modules

   make modules会在新功能源码的同级目录下生成相应的同名.ko文件

(生成的ko文件只适用于开发板linux)

   注意此命令执行前,开发板的内核源码已被编译

 

b、新功能源码与Linux内核源码不在同一目录结构下时

1. cd  ~/Linux_4412

2. mkdir mydrivercode

3. cd mydrivercode

4. cp  ../linux-3.14/drivers/char/myhello.c  .

 

5. vim Makefile

添加这个makefile文件

ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/book/Linux_4412/kernel/linux-3.14
ROOTFS ?= /home/book/nfs_rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)

modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
    rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else
obj-m += myhello.o


endif

 

 

6. make    (生成的ko文件适用于主机ubuntu linux)

/lib/modules/$(shell uname -r)/build

这是ubuntu自己的内核编译体系 

7. make   ARCH=arm          

(生成的ko文件适用于开发板linux,注意此命令执行前,开发板的内核源码已被编译)

c、主机ubuntu下使用ko文件

这里报错了因为我最后一次是用arm编译的

 

这次就没问题了 ,这个命令是给内核安装模块

lsmod #查看已被插入的内核模块有哪些,显示的是插入内核后的模块名

 这样太多了,不好找。可以用下面的命令

这样就能看安装成功与否 

这个名字叫模块名和我们的KO文件名是两个。

dmesg #查看内核的打印信息

内核打印和应用打印是分开的不然屏幕太乱了。内核打印信息在一个文件了。运行这个命令就能查看。 

sudo rmmod ??? #,此处为插入内核后的模块名,此时将已被插入的内核模块从内核中移除 ----- 相当于卸载插件

一定要加sudo 

sudo dmesg -C  #清除内核已打印的信息

 

现在打印信息就是空的了。 

d、开发板Linux下使用ko文件

 

 

#在串口终端界面开发板Linux命令行下执行

insmod ./???.ko  #将内核模块插入正在执行的内核中运行 ----- 相当于安装插件

lsmod #查看已被插入的内核模块有哪些

rmmod ??? #将已被插入的内核模块从内核中移除 ----- 相当于卸载插件

内核随时打印信息,我们可以在串口终端界面随时看到打印信息,不需要dmesg命令查看打印信息

 在rm时提示我们需要创建一个文件夹。创建完了以后就能直接删除了。同样删除时会打印我们写的那句话。

二、内核模块基础代码解析

Linux内核的插件机制——内核模块

类似于浏览器、eclipse这些软件的插件开发,Linux提供了一种可以向正在运行的内核中插入新的代码段、在代码段不需要继续运行时也可以从内核中移除的机制,这个可以被插入、移除的代码段被称为内核模块。

主要解决:

1. 单内核扩展性差的缺点

2. 减小内核镜像文件体积,一定程度上节省内存资源

3. 提高开发效率

4. 不能彻底解决稳定性低的缺点:内核模块代码出错可能会导致整个系统崩溃

内核模块的本质:一段隶属于内核的“动态”代码,与其它内核代码是同一个运行实体,共用同一套运行资源,只是存在形式上是独立的。

```c

#include <linux/module.h> //包含内核编程最常用的函数声明,如printk

#include <linux/kernel.h> //包含模块编程相关的宏定义,如:MODULE_LICENSE



/*该函数在模块被插入进内核时调用,主要作用为新功能做好预备工作

  被称为模块的入口函数



  __init的作用 :

1. 一个宏,展开后为:__attribute__ ((__section__ (".init.text")))   实际是gcc的一个特殊链接标记

2. 指示链接器将该函数放置在 .init.text区段

3. 在模块插入时方便内核从ko文件指定位置读取入口函数的指令到特定内存位置

*/

int __init myhello_init(void)

{

    /*内核是裸机程序,不可以调用C库中printf函数来打印程序信息,

    Linux内核源码自身实现了一个用法与printf差不多的函数,命名为printk (k-kernel)

    printk不支持浮点数打印*/

   printk("#####################################################\n");

   printk("#####################################################\n");

   printk("#####################################################\n");

   printk("#####################################################\n");

   printk("myhello is running\n");

   printk("#####################################################\n");

   printk("#####################################################\n");

   printk("#####################################################\n");

   printk("#####################################################\n");

   return 0;

}



/*该函数在模块从内核中被移除时调用,主要作用做些init函数的反操作

  被称为模块的出口函数



  __exit的作用:

1.一个宏,展开后为:__attribute__ ((__section__ (".exit.text")))   实际也是gcc的一个特殊链接标记

2.指示链接器将该函数放置在 .exit.text区段

3.在模块插入时方便内核从ko文件指定位置读取出口函数的指令到另一个特定内存位置

*/

void __exit myhello_exit(void)

{

   printk("myhello will exit\n");

}



/*

MODULE_LICENSE(字符串常量);

字符串常量内容为源码的许可证协议 可以是"GPL" "GPL v2"  "GPL and additional rights"  "Dual BSD/GPL"  "Dual MIT/GPL" "Dual MPL/GPL"等, "GPL"最常用



其本质也是一个宏,宏体也是一个特殊链接标记,指示链接器在ko文件指定位置说明本模块源码遵循的许可证

在模块插入到内核时,内核会检查新模块的许可证是不是也遵循GPL协议,如果发现不遵循GPL,则在插入模块时打印抱怨信息:

   myhello:module license 'unspecified' taints kernel

   Disabling lock debugging due to kernel taint

也会导致新模块没法使用一些内核其它模块提供的高级功能

*/

MODULE_LICENSE("GPL");



/*

module_init 宏

1. 用法:module_init(模块入口函数名)

2. 动态加载模块,对应函数被调用

3. 静态加载模块,内核启动过程中对应函数被调用

4. 对于静态加载的模块其本质是定义一个全局函数指针,并将其赋值为指定函数,链接时将地址放到特殊区段(.initcall段),方便系统初始化统一调用。

5. 对于动态加载的模块,由于内核模块的默认入口函数名是init_module,用该宏可以给对应模块入口函数起别名

*/

module_init(myhello_init);



/*

module_exit宏

1.用法:module_exit(模块出口函数名)

2.动态加载的模块在卸载时,对应函数被调用

3.静态加载的模块可以认为在系统退出时,对应函数被调用,实际上对应函数被忽略

4.对于静态加载的模块其本质是定义一个全局函数指针,并将其赋值为指定函数,链接时将地址放到特殊区段(.exitcall段),方便系统必要时统一调用,实际上该宏在静态加载时没有意义,因为静态编译的驱动无法卸载。

5.对于动态加载的模块,由于内核模块的默认出口函数名是cleanup_module,用该宏可以给对应模块出口函数起别名

*/

module_exit(myhello_exit);

```

__init

c——i——s——o——ko

程序从c到ko要经历四个步骤这个宏就是为了链接。

在使用insmod命令插入时内核会将被标记的这部分拿过去放到指定的内存空间里方便后续进行调用。

__exit

一般就是和__init相反的操作我们叫做出口函数 

模块三要素:入口函数 出口函数 MODULE__LICENSE

百度百科-验证

三、内核模块的多源文件编程

ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)

KERNELDIR ?= 目标板linux内核源码顶层目录的绝对路径

ROOTFS ?= 目标板根文件系统顶层目录的绝对路径

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build

endif

PWD := $(shell pwd)

modules:

   $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:

   $(MAKE) -C $(KERNELDIR) M=$(PWD) INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:

   rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

obj-m += hello.o

endif

Makefile中:

obj-m用来指定模块名,注意模块名加.o而不是.ko

可以用 模块名-objs 变量来指定编译到ko中的所有.o文件名

(每个同名的.c文件对应的.o目标文件)

hello-obj = f1.o f2.o f3.o

一个目录下的Makefile可以编译多个模块:

添加:obj-m += 下一个模块名.o

上面是hello模块下面是xyz模块。多个模块可以共用一个makefile 

+=    追加

四、 内核模块信息宏

MODULE_AUTHOR(字符串常量); //字符串常量内容为模块作者说明

MODULE_DESCRIPTION(字符串常量); //字符串常量内容为模块功能说明

MODULE_ALIAS(字符串常量); //字符串常量内容为模块别名

        这些宏用来描述一些当前模块的信息,可选宏

        这些宏的本质是定义static字符数组用于存放指定字符串内容,这些字符串内容链接时存放在.modinfo字段,可以用modinfo命令来查看这些模块信息,用法:

modinfo  模块文件名

#include <linux/module.h>
#include <linux/kernel.h>

int __init myhello_init(void)
{
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	printk("myhello is running\n");
	printk("#####################################################\n");
	printk("#####################################################\n");
	return 0;
}
void __exit myhello_exit(void)
{
	printk("myhello will exit\n");
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tianyu Xin");
MODULE_DESCRIPTION("This is a simple test");
MODULE_ALIAS("Hi");

module_init(myhello_init);
module_exit(myhello_exit);

 

 

企业做项目时用。自己玩的时候加不加都行。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/348157.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

链表题目总结 -- 回文链表

目录一. 从中心开始找最大的回文字符串1. 思路简述2. 代码3. 总结二. 判断是否为回文字符串1. 思路简述2. 代码3.总结三. 判断是否是回文链表1. 思路简述2. 代码3. 总结4. 优化解法一. 从中心开始找最大的回文字符串 题目链接&#xff1a;没有。给定一个字符串s&#xff0c;从…

平面电路和非平面电路

主要区别 参考&#xff1a;https://www.eda365.com/article-192836-1.html 平面电路&#xff1a;在平面上的每个元件的两端都可以不用交叉而连接到电路&#xff1b; 非平面电路&#xff1a;在平面上存在元件两端无法不交叉线路连接到电路。 例子&#xff08;上面参考连接中&a…

继企业级信息系统开发学习1.1 —— Spring配置文件管理Bean

骑士救美计划采用构造方法注入属性值1、创建救美任务类2、创建救美骑士类2、创建救美骑士类3、创建旧救美骑士测试类3、配置救美骑士Bean5、创建新救美骑士测试类采用构造方法注入属性值 1、创建救美任务类 在net.huawei.spring.day01包里创建RescueDamselQuest类 Rescue Da…

【重点】Selenium + Nightwatch 自动化测试环境搭建

开始搭建 1. 创建项目 我们来找个地方新建一个目录&#xff0c;起名为 "my-test-toolkit"&#xff0c;然后在目录内使用终端运行 npm init -y 生成项目配置文件package.json。 2. 安装工具 然后我们将安装 Selenium 与 Nightwatch。 安装 selenium-standalone&…

在哔站黑马程序员学习Spring—Spring Framework—(五)spring的第二特征AOP面向切面编程

一、AOP概念、作用AOP和OOP一样都是一种编程思想&#xff0c;用来指导我们做程序的。OOP面向对象编程指导我们做类、对象、继承、封装、多态等。AOP面向切面编程作用&#xff1a;在不惊动原始设计&#xff08;不改变源代码&#xff09;的基础上为其进行功能增强。核心&#xff…

2022年全国职业院校技能大赛网络空间安全A模块(1)

目录 模块A 基础设施设置与安全加固 一、项目和任务描述&#xff1a; 二、服务器环境说明 三、具体任务&#xff08;每个任务得分以电子答题卡为准&#xff09; A-1任务一 登录安全加固 1.密码策略&#xff08;Windows&#xff0c;Linux&#xff09; a.设置最短密码长度为…

AC/DC 基础

一、概念&#xff1a; AC转换成DC的基本方法有变压器方式和开关方式&#xff0c;如下图1、2所示&#xff1b;整流的基本方法有全波整流和半波整流&#xff0c;如下图3所示。 图1 变压器方式 图2 开关方式 图3 整流方式 二、转换方式 1、变压器方式 变压器方式首先需要通过变压…

< 算法基础 之 二分查找 >

算法基础 之 二分查找前言&#x1f449; “ 二分查找 ” 原理及实现&#x1f449; 实际案例&#xff1a;> 基础案例 - 搜索下标示例 1示例 2解决方案> 进阶案例 - 搜索二维矩阵示例 1示例 2解决方案往期内容 &#x1f4a8;前言 在开发中&#xff0c;我们常常会需要查找某…

java无重复字符的最长子串

给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “…

GEE学习笔记九十二:Sentinel-2 最新去云方法总结

下面使用例子的原始影像截图如下&#xff1a; 第一种方法&#xff1a;使用QA波段去云 这是我们最常用的方法&#xff0c;具体原理就是利用QA60波段标记实现去云&#xff0c;具体代码如下&#xff1a; var s2 ee.ImageCollection("COPERNICUS/S2"), point /* …

B树与B+树

B树 B树的定义 1970年&#xff0c;R.Bayer和E.mccreight提出了一种适用于外查找的树&#xff0c;它是一种平衡的多叉树&#xff0c;称为B树&#xff08;或B-树、B_树&#xff09;。我们描述一颗B树时需要指定它的阶数&#xff0c;阶数表示了一个结点最多有多少个孩子结点&…

[Android Studio]Android 数据存储--SQLite数据库存储

&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Android Debug&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Topic 发布安卓学习过程中遇到问题解决过程&#xff0c;希望我的解决方案可以对小伙伴们有帮助。 &#x1f4cb;笔记目…

Leetcode_part2

文章目录[406. 根据身高重建队列](https://leetcode.com/problems/queue-reconstruction-by-height/)Solution1 先排序 再插队[409. 最长回文串](https://leetcode.com/problems/longest-palindrome/)Solution1[415. 字符串相加](https://leetcode.com/problems/add-strings/)S…

上采样学习

最近邻 简单来说就是x方向和y方向分别复制 #!/usr/bin/env python # _*_ coding:utf-8 _*_ import numpy as np import torch from cv2 import cv2 from torch import nndef numpy2tensor(x: np.ndarray) -> torch.Tensor:"""(H,W) -> (1, 1, H, W)(H,W…

ShardingSphere-Proxy5按月分表

0、软件版本 ShardingSphere-Proxy&#xff1a; 5.2.0 MySQL&#xff1a; 8.0.30 系统&#xff1a; win10 1、ShardingSphere-Proxy下载 我们可以在 官网 找到最新版ShardingSphere-Proxy下载&#xff0c;也可以在ShardingSphere仓库中下载 2、ShardingSphere-Proxy配置 …

shell编程经典案例,建议收藏

1、编写hello world脚本 #!/bin/bash# 编写hello world脚本echo "Hello World!"2、通过位置变量创建 Linux 系统账户及密码 #!/bin/bash# 通过位置变量创建 Linux 系统账户及密码#$1 是执行脚本的第一个参数,$2 是执行脚本的第二个参数 useradd "$1" …

Linux C++ 200行完成线程池类

文章目录1、atomic使用2、volatile关键字3、条件变量4、成员函数指针使用5、线程池6、主线程先退出对子线程影响7、return、exit、pthread_exit区别8、进程和线程的区别1、atomic使用 原子操作&#xff0c;不可分割的操作&#xff0c;要么完整&#xff0c;要么不完整。 #includ…

vscode开发基于Vue的健身房管理系统node.js

该系统的基本功能包括管理员、会员、教练三个角色功能模块。 对于管理员可以使用的功能模块主要有首页、个人中心&#xff0c;系统公告管理、健身常识管理&#xff0c;会员管理、教练管理、教练考勤管理、会员咨询管理、商品信息管理、团课管理、团课预约管理、论坛管理、系统管…

十三、二叉排序树

1、先看一个需求 给你一个数列 {7, 3, 10, 12, 5, 1, 9} &#xff0c;要求能够高效的完成对数据的查询和添加 2、解决方案分析 使用数组 数组未排序&#xff0c;优点&#xff1a;直接在数组尾添加&#xff0c;速度快。缺点&#xff1a;查找速度慢 数组排序&#xff0c;优点&…

如何推出 NFT 游戏并获得收益?- 综合指南

NFT Gaming - 简介 - NFT 是代表独特内容所有权的数字资产&#xff0c;例如游戏中的一件艺术品或收藏品。它们建立在区块链解决方案之上&#xff0c;允许这些数字资产的真正所有权和稀缺性。 在游戏行业&#xff0c;NFT 用于创建玩家可以拥有和交易的独一无二的游戏内物品。这允…