微内核、宏内核、内核模块、printk

news2024/12/23 8:37:47

文章目录

  • 一、微内核和宏内核
  • 二、内核模块
  • 三、内核模块code
  • 四、Makefile
      • 注意
      • 注:+=,?=, := 区别
  • 五、prink
    • 查看prink打印优先级
    • 修改printk的打印优先级
  • 六、内核模块参数
    • module_param与module_param_array宏定义的使用
  • 七、内核符号表--全局共享函数接口与变量
    • 1.内核符号表
    • 2.用到的宏定义EXPORT_SYMBOL、EXPORT_SYMBOL_GPL
  • 八、多个源文件组合为内核模块

一、微内核和宏内核

微内核:内核中只有最基本的调度、内存管理。驱动、文件系统等都是用户态的守护进程去实现的。优点是超级稳定,驱动等的错误只会导致相应进程死掉,不会导致整个系统都崩溃,做驱动开发时,发现错误,只需要kill掉进程,修正后重启进程就行了,比较方便。缺点是效率低。

宏内核:简单来说,就是把很多东西都集成进内核,例如linux内核,除了最基本的进程、线程管理、内存管理外,文件系统,驱动,网络协议等等都在内核里面。优点是效率高。缺点是稳定性差,开发过程中的bug经常会导致整个系统挂掉。

二、内核模块

驱动程序在内核中,都是独立的模块,例如led驱动、蜂鸣器驱动,它们驱动之间没有相互的联系,可以**通过应用程序将两个驱动联系在一起。

内核模块编译成功之后,会输出*.ko(kernel object)文件。

加载内核模块到内核:insmod led_drv.ko

从内核卸载内核模块:rmmod led_drv

若查看当前的内核模块是否加载成功,使用命令: lsmod

驱动是安装在内存中正在运行的内核上

C语言应用程序内核模块
运行空间用户空间内核空间
入口mainmodule_init函数指定
出口-module_exit函数指定
编译gccMakefile
运行./直接运行insmod
退出exitrmmod

三、内核模块code

参考内核源码:

\kernel\drivers\watchdog\wdt.c

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

static int __init wdt_init(void)//入口函数
{
    return 0;
}
static void __exit wdt_exit(void)//出口函数
{
    
}
module_init(wdt_init);//驱动入口 insmod
module_exit(wdt_exit);//驱动出口 rmmod
MODULE_AUTHOR("Alan Cox");//作者信息
MODULE_DESCRIPTION("Driver for ISA ICS watchdog cards (WDT500/501)");//模块功能说明
MODULE_LICENSE("GPL");//许可证:驱动遵循GPL协议

__init的初始化函数,往往只是被调用一次,在调用完成后,该函数不应该再被调用。所以它占用的内存必须得释放,只要该函数加上__init关键字就能够达到这个目的。

__exit也是用于修饰清除退出函数,和__init的效果也是一样,被调用之后,就会释放内存。

在kernel\arch\um\include\shared\init.h文件中

#define __init		__section(.init.text)
#define __exit		__section(.exit.text)

这个标志符和函数声明放在一起,表示gcc编译器在编译的时候需要把这个函数放.text.init、.exit.text section中

__ection属性专门用于确定标记了该对象的存储位置。 ELF二进制格式将目标文件排列为命名部分,使用这样的属性允许程序员更精确地指定标记目标的信息将放置在目标对象中的位置

四、Makefile

参考文档:

kernel/Documentation/kbuild/makefiles.txt

kernel/Documentation/kbuild/modules.txt

	$(obj-m) specify object files which are built as loadable
	kernel modules.
	A module may be built from one source file or several source
	files. In the case of one source file, the kbuild makefile
	simply adds the file to $(obj-m).
obj-m +=led_drv.o
KERNEL_DIR :=linux/kernel
CROSS_COMPILE :=linux/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
PWD:=$(shell pwd)

default:
	$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modules

clean:
	rm  *.o *.order .*.cmd  *.mod.c *.symvers .tmp_versions -rf

obj-m+=led_drv.o

make的第一阶段将源程序编译为目标文件led_drv.o

make的第二阶段将led_drv.ko编译成一个模块,即led_drv.ko。

KERNEL_DIR :=linux/kernel

内核源码路径:查找编译所需的头文件、函数原型、Makefile…

CROSS_COMPILE:=linux/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-

交叉编译工具,内核使用4.8版本进行编译

PWD:=$(shell pwd)

当前内核模块源码路径

$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modules

使用make命令的时候,传递多个参数,并调用内核源码下的Makefie文件,使用该Makefile文件中的工具,将led_drv.o文件编译为一个内核模块led_drv.ko。

注意

make命令对路径是很敏感的,不能有中文或空格出现

注:+=,?=, := 区别

= 是最基本的赋值

:= 是覆盖之前的值

?= 是如果没有被赋值过就赋予等号后面的值

+= 是添加等号后面的值

五、prink

\kernel\include\linux\printk.h

#define KERN_EMERG	"<0>"	/* system is unusable			*/
#define KERN_ALERT	"<1>"	/* action must be taken immediately	*/
#define KERN_CRIT	"<2>"	/* critical conditions			*/
#define KERN_ERR	"<3>"	/* error conditions			*/
#define KERN_WARNING	"<4>"	/* warning conditions			*/
#define KERN_NOTICE	"<5>"	/* normal but significant condition	*/
#define KERN_INFO	"<6>"	/* informational			*/
#define KERN_DEBUG	"<7>"	/* debug-level messages			*/

查看prink打印优先级

cat /proc/sys/kernel/printk
#7 7 1 7

该printk文件总共有4个值。

7:控制台打印级别,默认的消息日志级别优先级高于该值,消息就能够被打印到控制台。

7:默认的消息日志级别,例如printk(“hello\n”);

1:最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)

7:缺省的控制台日志级别:控制台日志级别的缺省值

修改printk的打印优先级

echo 7 3 1 7 > /proc/sys/kernel/printk 
cat /proc/sys/kernel/printk
#7       3       1       7
#include <linux/kernel.h>
printk("<3>""hello world\n");
printk(KERN_ERR"hello world\n");

六、内核模块参数

模块参数允许用户在加载模块的时候,通过命令行指令参数值,内核支持的参数:bool、invbool(反转bool值)、charp(字符串指针)、short、int、long、ushort、uint、ulong类型,这些类型可以对应于整型、数组、字符串。

module_param与module_param_array宏定义的使用

module_param(name,type,perm)
module_param_array(name,type,nump,perm)

name:变量的名字

type:变量或数组元素的类型

nump:保存数组元素个数的指针,可选。默认写NULL。

perm:在sysfs文件系统中对应的文件的权限属性,决定哪些用户能够传递哪些参数,如果该用户权限过低,则无法通过命令行传递参数给该内核模块。

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

static int baud = 9600;       //baud变量
static int port[4]={0,1,2,3}; //port数组
static int port_cnt=0;        //记录module_param_array传递数组的长度
static char *name="vcom";     //name变量

//通过以下宏定义来接收命令行的参数
module_param(baud,int,0644); 					//rw- r-- r--
module_param_array(port,int,&port_cnt,0644);	//rw- r-- r--
module_param(name,charp,0644);					//rw- r-- r--

//入口函数
static int __init myled_init(void)
{

	printk("myled init\n");
	
	printk("baud=%d\n",baud);
	printk("port=%d %d %d %d ,port_cnt=%d\n",port[0],port[1],port[2],port[3],port_cnt);
	printk("name=%s\n",name);
	return 0;
}

//出口函数
static void __exit myled_exit(void)
{
	printk("myled exit\n");
}

module_init(myled_init);//驱动程序的入口:insmod led_drv.ko
module_exit(myled_exit);//驱动程序的出口:rmmod led_drv

MODULE_LICENSE("GPL");				//许可证:驱动遵循GPL协议

​ make编译模块

obj-m +=led_drv.o
KERNEL_DIR :=linux/kernel
CROSS_COMPILE :=linux/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
PWD:=$(shell pwd)

default:
	$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modules
	$(MAKE) clean
clean:
	rm  *.o *.order .*.cmd  *.mod.c *.symvers .tmp_versions -rf

​ 加载模块

insmod led_drv.ko baud=115200 port=1,2,3,4 name="tcom"

​ 查看参数

ls -l /sys/module/led_drv/parameters/
#total 0
#-rw-r--r--    1 root     root          4096 Dec  1 02:06 baud
#-rw-r--r--    1 root     root          4096 Dec  1 02:06 name
#-rw-r--r--    1 root     root          4096 Dec  1 02:06 port

七、内核符号表–全局共享函数接口与变量

1.内核符号表

内核符号表是记录了内核中所有的符号(函数、全局变量)的地址及名字,这个符号表被嵌入到内核镜像中,使得内核可以在运行过程中随时获得一个符号地址对应的符号名。

注:

符号:指的是函数、全局变量的地址及名字。

2.用到的宏定义EXPORT_SYMBOL、EXPORT_SYMBOL_GPL

EXPORT_SYMBOL(符号名):导出的符号可以给其他模块使用。

EXPORT_SYMBOL_GPL(符号名):导出的符号只能让符合GPL协议的模块才能使用。

​ sum.c

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

static unsigned int g_count=0x100000;

static int myadd(int a,int b)
{
	
	return (a+b);
	
}
EXPORT_SYMBOL_GPL(myadd);
EXPORT_SYMBOL_GPL(g_count);
MODULE_LICENSE("GPL");				//许可证:驱动遵循GPL协议
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

extern int myadd(int a,int b);
extern unsigned int g_count;

//入口函数
static int __init myled_init(void)
{

	printk("myled init\n");
	
	printk("myadd(10+110)=%d",myadd(10,110));

	return 0;
}
//出口函数
static void __exit myled_exit(void)
{
	printk("myled exit\n");
}

module_init(myled_init);//驱动程序的入口:insmod led_drv.ko
module_exit(myled_exit);//驱动程序的出口:rmmod led_drv
MODULE_LICENSE("GPL");				//许可证:驱动遵循GPL协议

​ 加载模块

​ 加载成功后,能够在内核符号表找到myadd函数,说明了该函数已经嵌入了内核符号表。

grep -r myadd /proc/kallsyms 
#cd125000 t myadd        [sum]

八、多个源文件组合为内核模块

有些时候不想通过符号表全局共享函数与全局变量,可以使用以下方法实现多个文件编译,实现局部访问。
\kernel\Documentation\kbuild\makefiles.txt
在这里插入图片描述

Makefile

在这个Makefile中,obj-m说明最终生成的内核模块目标文件是led_drv.ko,并且它必须依赖两个文件led.c和sum.c。因此,通过模块名加-objs的形式可以定义整个模块所包含的文件。

obj-m +=led_drv.o
led_drv-objs=led.o sum.o

KERNEL_DIR :=linux/kernel
CROSS_COMPILE :=linux/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
PWD:=$(shell pwd)

default:
	$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modules
	$(MAKE) clean
clean:
	rm  *.o *.order .*.cmd  *.mod.c *.symvers .tmp_versions -rf

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

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

相关文章

2022年最新中国科学院期刊分区表变化 | 生物类、医学类

fenqu❝早上刷到了新发布的《2022年中国科学院文献情报中心期刊分区表》&#xff08;https://mp.weixin.qq.com/s/QVfwnGPCjvOaLtBvGM184g&#xff09;&#xff0c;今天利用有限的非卧床时间浅爬了一下生物和医学大类2022和2021年的数据&#xff0c;做个简单的对比。&#xff0…

学习记录-mybatis+vue+elementUi实现批量删除

老规矩&#xff0c;还是从后端到前端还是分为四步骤 步骤一 ①写SQL语句 mapper接口代码&#xff1a; /*** 批量删除* param ids*/void deleteByIds(Param("ids") int[] ids);实现语句&#xff1a; <delete id"deleteByIds">deletefrom tb_brandwh…

本周推荐 | 设计模式在淘宝营销价格体系的实践

推荐语&#xff1a;本文详细描述责任链、中介者、适配器等多种设计模式在淘宝营销价格服务中的应用&#xff0c;从而实现了一套可扩展性的架构&#xff0c;应对灵活多变营销价格需求。——大淘宝技术研发工程师 小枫每年淘宝都有双11、双12等大促&#xff0c;我们价格服务小组的…

【车载开发系列】UDS诊断---常见的ISO标准集合

【车载开发系列】UDS诊断—常见的ISO标准集合 常见的ISO标准集合【车载开发系列】UDS诊断---常见的ISO标准集合一.前言二.基于CAN总线ISO1&#xff09;ISO118982&#xff09;ISO115193&#xff09;ISO15765三.UDS诊断1&#xff09;ISO142292&#xff09;ISO14229系列规范四.CAN…

我国余热发电行业现状:政策及企业积极性双管齐下 市场发展潜力大

根据观研报告网发布的《中国余热发电行业现状深度研究与发展前景预测报告&#xff08;2022-2029年&#xff09;》显示&#xff0c;余热发电是指利用生产过程中多余的热能转换为电能的技术。余热发电技术可以回收利用如水泥、玻璃、钢铁、冶金等行业的余热资源&#xff0c;将余热…

深入浅出synchronized关键字

前言 无论在日常工作还是面试过程中&#xff0c;synchronized关键字作为并发场景下的操作&#xff0c;是一定要掌握的&#xff0c;本文从synchronized的使用方式、原理及优化三个方面&#xff0c;对synchronized关键字作一个系统化的说明。 使用方式 synchronized主要有三种…

Apache DolphinScheduler 发布 3.1.2 版本,Python API 实现优化

点亮 ⭐️ Star 照亮开源之路https://github.com/apache/dolphinscheduler近日&#xff0c;Apache DolphinScheduler 发布了 3.1.2 版本。此版本主要基于 3.1.1 版本进行了 6 处 Python API 优化&#xff0c;19 处 Bug 修复&#xff0c;并更新了 4 个文档。其中较为重要的 Bug…

HashMap1.7和1.8源码解析

1.HashMap &#xff08;1&#xff09;数据结构 在JDK1.7中&#xff0c;HashMap中的数据结构是数组单链表的组合&#xff1b;在JDK1.8中的HashMap存储结构是由数组、链表、红黑树这三种数据结构形成。 &#xff08;2&#xff09;JDK1.7中HashMap源码分析 &#xff08;2.1&am…

Portal门户常见宕机或性能低问题分析与七大解决之道

不论使用什么产品构建门户&#xff0c;只要是基于Java技术的&#xff0c;就很容易宕机或出现性能低的问题。因为Portal产品比较复杂&#xff0c;且集成的数据特别多&#xff0c;架构特别复杂&#xff0c;涉及到的数据通信特别多。宕机或性能低也通常是用户较为头疼的问题。经常…

excel文本函数应用:单元格中的数字和字母,如何判断?

文本字符是Excel中除了数字以外的另一种非常常用的数据类型&#xff0c;Excel也提供了大量的文本函数。利用这些函数我们可以用来判断字符串开头是否为数字、字符串是否同时包含了数字和英文、字符串是否包含了指定字符&#xff0c;可以用来转换英文字母的大小&#xff0c;可以…

Java基础之Stream的使用078

1. Stream 的使用 Stream 是什么&#xff1f; Stream 是数据渠道&#xff0c;用于操作数据源&#xff08;数组、集合等&#xff09;所生成的元素序列。 Java8两大最为重要的改变就是 Lambda表达式 与 Stream API&#xff0c;这两种改变的引入带来的是新的抽象方式 &#xff08…

目标检测之Fast RCNN概述

基本原理 Fast Rcnn主要步骤为 利用SR算法生成候选区域利用VGG16网络进行特征提取利用第一步生成的候选区域在特征图中得到对应的特征矩阵利用ROI pooling将特征矩阵缩放到相同大小并平展得到预测结果 相对于RCNN的优化 主要有三个改进 不再将每一个候选区域依次放入CNN网络…

【基于通道-空间注意的高分辨率锐化】

Channel–spatial attention-based pan-sharpening of very high-resolution satellite images &#xff08;基于通道-空间注意的很高分辨率卫星影像全色锐化&#xff09; 全色锐化处理旨在生成新的合成输出图像&#xff0c;其保留全色的空间细节和多光谱图像输入的光谱细节。…

【服务器端程序的演进过程】

目录 1 服务器端程序的演进过程 阶段一:静态服务器 阶段二:普通动态服务器 阶段三: 以用户共享内容为主的互联网生态 阶段四: 微服务时代(有高并发需求或特征的网站) 2 Java服务器项目分类 3 微服务概述 3.1 什么是微服务 3.2 为什么需要微服务 3.3 怎么搭建微服务项…

C#获取计算机详细的软件和硬件信息

利用System.Management提供的类可以用于读取本地计算机设备的各种数据&#xff0c;包括操作系统、软件、硬件的各种详细信息&#xff0c;内容很丰富。 System.Management的命名空间下&#xff0c;ManagementObjectSearcher类用于查询特定类型的设备&#xff0c;ManagementObjec…

转行做“程序员”很难?这里有几个建议...

“是什么&#xff1f;为什么&#xff1f;怎么样&#xff1f;”的灵魂三连问在我们生活中比比皆是&#xff0c;目的是为了清晰思考和看到事物的本质。对于编程学习也是一样&#xff0c;需要带着疑问从本质上去学习编。 本人是某985高校的本硕连读&#xff0c;非计算机科班出身&…

利器 | AppCrawler 自动遍历测试实践(三):动手实操与常见问题汇总

1080469 14.7 KB 上两篇文章介绍了自动遍历的测试需求、工具选择和 AppCrawler 的环境安装、启动及配置文件字段基本含义&#xff0c;这里将以实际案例更加细致的说明配置文件的用法和一些特殊场景的处理。 下面我们继续之前的例子&#xff0c;在雪球搜索框输入搜索内容后的页面…

CloudFlare系列--功能介绍与常用配置

原文网址&#xff1a;CloudFlare系列--功能与特性的介绍_IT利刃出鞘的博客-CSDN博客 简介 本文介绍CloudFlare的功能与常用的配置。 功能介绍 CloudFlare是世界最强的网络服务商。它可以提供如下服务&#xff1a; 防御DDoS攻击 世界最强防御DDos攻击的厂商。域名注册 世界最…

JavaWeb语法四:多线程案例

目录 1.单例模式 1.1&#xff1a;饿汉模式 1.2&#xff1a;懒汉模式 2.阻塞式队列 2.1:生产者消费者模型 2.2&#xff1a;阻塞队列的模拟实现 3.线程池 3.1&#xff1a;标准库中的线程池 3.2&#xff1a;模拟实现线程池 前言&#xff1a;前一篇我们讲了线程不安全的原因…

SAP UI5 里 FlexBox 的使用方法

ScrollContainer 的使用方式&#xff1a; ScrollContainer 是一个控件&#xff0c;可以在有限的屏幕区域内显示任意内容&#xff0c;并提供滚动以使所有内容都可访问。注意&#xff0c;为了避免影响用户使用体验&#xff0c;不要嵌套沿相同方向滚动的滚动区域。例如&#xff…