Linux 驱动之内核相关基础知识学习

知识图

Linux内核模块
一.初识Linux设备驱动
什么是Linux驱动并认识Linux源码
二.编写第一个驱动helloworl
最简单的Linux驱动结构
三.如何编写驱动程序
1.将驱动编译成内核模块
1.Makefile文件的编写
2.模块加载命令
2.将驱动编译进内核
1.menuconfig图像化配置界面
2.Kconfig语法
四.驱动模块传参
1.传递基本类型参数
2.传递数组类型参数
3.传递字符串类型参数
五.内核模块符号表
1.模块间的相互依赖,A模块调用B模块中的函数

文章目录

    • 知识图
    • 认识Linux 设备驱动
      • 驱动的作用
      • 无操作系统时的驱动(裸机)
      • 有操作系统时的驱动(Linux系统)
      • 驱动的分类
      • Linux 源码下载
      • Linux 源码目录结构
    • 编写第一个驱动
      • 最简单的Linux驱动结构
    • 如何编译驱动程序
      • Linux内核模块的编译
        • 什么是Linux内核模块
        • 例子
        • Linux 内核模块命令
      • 将驱动编译在Linux内核里面
        • make menuconfig图形化配置界面
        • 与make menuconfig有关的文件
          • Makefile,config,Kconfig关系
          • Kconfig文件
          • config文件和.config文件
        • Kconfig语法
          • mainmenu
          • 配置选项
          • 依赖关系
          • 可选择项
          • 注释
          • source
    • 驱动模块传参
      • 驱动传参的意义
        • 驱动可以传递的参数类型
        • 如何给驱动传递参数
        • 参数的读写权限
        • 例子
    • 内核符号表
      • 符号表
      • 内核符号表导出
      • 例子

认识Linux 设备驱动

驱动的作用

驱动的作用
从字面上解释,驱动就是“让硬件动起来",所以驱动是直接和硬件打交道的,是底层硬件和上层软件的桥梁。

无操作系统时的驱动(裸机)

有的时候并不一定需要操作系统,比如用单片机进行简单的通断控制,从编程角度来说,直接控制寄存器就可以了,也就是直接和硬件打交道。

有操作系统时的驱动(Linux系统)

有了操作系统以后,编写驱动就变的比较复杂,要基于Linux的各种驱动框架进行编程。但是当驱动都按照系统给出的框架进行编程以后,就可以提供一个统一的接口给应用程序调用。

应用
操作系统
驱动
硬件

驱动的分类

Linux将驱动分为三类。

字符设备:字符设备指那些必须以串行顺序依次进行访问的设备,如鼠标。

网络设备:块设备可以按照任意顺序进行访问,如硬盘。

块设备:网络设备是面向数据包的接收和发送。

Linux 源码下载

我们可以在https://www.kernel.org/下载到最新的Linux内核源码。历史版本可以在 https://www.kernel.org/pub/下载历史版本,一般我们使用的半导体产商提供的源码:NPX(恩智浦),MTK(联发科),ALLWINNER(全智),RK,TI(德州仪器)等等。

编译介绍

Linux 源码目录结构

Linux内核源码包含多级目录,形成一个巨大的树状结构,进入源码所在的目录,就是Linux源码的顶层目录。例举linux-5.15

目录说明
arch架构相关目录,里面存放了许多CPU的架构,如arm,X86,MIPs等。
block存放块设备相关代码,在Linux 中用block表示块设备。比如硬盘,SD卡,都是块设备
certs存储了认证 和签名 相关代码
crypto存放加密算法目录
Documentation存放官方Linux内核文档
drivers驱动目录,里面存放了Linux系统支持的硬件设备驱动源码
firmware存放固件目录
fs存放支持的文件系统的代码目录,比如fat,ext2,ext3 等。
include存放公共的头文件目录
init存放Linux内核启动初始化的代码
ipc存放进程间通信代码
kernel存放内核本身的代码文件夹
lib存放库函数的文件夹
mm存放内存管理的目录,mm就是memory management的缩写
net存放网络相关代码,比如TCP/IP协议栈
samples内核实列代码
scripts存放脚本的文件夹
security存放安全相关代码
sound存放音频相关代码
tools存放Linux用到的工具文件夹
usr和Linux内核的启动有关代码
virt内核虚拟机相关代码

编写第一个驱动

最简单的Linux驱动结构

一个最简单的Linux驱动主要由以下几个部分组成:
(1)头文件(必须有)
驱动需要包含内核相关头文件。必须包含<linux/module.h><linux/init.h>

(2)驱动加载函数。(必须有)
当加载驱动的时候,驱动加载函数会自动被内核执行。

(3)驱动卸载函数(必须有)
当卸载驱动的时候,驱动卸载函数会自动被内核执行。

(4)许可证声明(必须有)
Linux内核是开源的,遵守GPL协议,驱动在加载的时候也要遵守相关的协议,可以接收的License有"GPL"、“GPL v2”、"GPL and additional rights"、"Dual BSD/GPL"、"Dual MIT/GPL"、"Dual MPL/GPL"。内核驱动中最常见的是GPL v2

(5)模块参数(可选)
模块参数是模块被加载的时候传递给内核模块的值。
可以声明驱动的作者信息和代码的版本信息。

如何编译驱动程序

第一种编译方法:将驱动放在Linux内核里面,然后编译Linux内核。将驱动编译到Linux内核里面。
第二种编译方法:将驱动编译成内核模块,独立于Linux内核以外。

编写驱动程序
编译进Linux内核
编译成Linux内核模块

Linux内核模块的编译

什么是Linux内核模块

内核模块是Linux系统中一个特殊的机制,可以将一些使用频率很少或者暂时不用的功能编译成内核模块,在需要的时候在动态加载到内核里面。
使用内核模块可以减小内核的体积,加快启动速度。并且可以在系统运行的时候插入或者卸载驱动,无需重启系统。内核模块的后缀是.ko

Makefile解析:

# 表示把目标文件helloworld.o作为模块进行编译。obj就是object的缩写,-m表示编译成模块。
obj-m += helloworld.o

# 使用绝对路径的方式指定内核源码的路径。
# KDIR:=/lib/modules/$(shell uname -r)/build 在ubuntu 编译,
# 在某版本的linux内核源码编译
KDIR:=/home/fengzc/study/linux-5.15/

# 获取Makefile文件所在的路径
PWD?=$(shell pwd)

# 进到KDIR目录,使用PWD路径下源码和Makefile文件编译驱动模块
make -C $(KDIR) M=$(PWD) modules

# 清除编译文件
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order

例子

  1. 驱动源码helloworld.c

    #include <linux/module.h>
    #include <linux/init.h>
    
    static int helloworld_init(void){
            printk("helloworld init\n");
            return 0;
    }
    
    static void helloworld_exit(void){
            printk("helloworld exit");
    }
    
    module_init(helloworld_init);
    module_exit(helloworld_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("FZC");
    MODULE_VERSION("V1.0");
    
  2. 在驱动源码目录下创建Makefile文件

    obj-m += helloworld.o
    KDIR:=/home/fengzc/study/linux-5.15/
    PWD?=$(shell pwd)
    all:
            make -C $(KDIR) M=$(PWD) modules
    clean:
            rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
    
  3. [Linux 源码下载](Linux 源码下载)的内核源码编译通过,内核不编译通过,无法编译内核模块

  4. 设置环境变量(看设置)

    #设置架构,x86,arm64等
    export ARCH=arm64
    export CROSS_COMPILE=xxxxxx
    
  5. 输入make编译,编译成功后驱动源码目录下,生成helloworld.ko

Linux 内核模块命令

模块加载命令

  • insmod命令

    功能:载入Linux内核模块

    语法:

    insmod 模块名

    举例:

    insmod helloworld.ko

  • modprobe命令
    功能:加载Linux内核模块,同时这个模块所依赖的模块也同时被加载语法

    语法:

    modprobe 模块名

    举例:

    modprobe helloworld.ko

模块卸载命令

  • rmmode 命令

    功能:移除已经载入Linux的内核模块

    语法:rmmod 模块名
    rmmod helloworld.ko

查看模块信息命令

  • lsmod命令

    功能:列出已经载入Linux的内核模块
    也可以使用命令cat /proc/modules来查看模块是否加载成功。

  • modinfo命令

    功能:查看内核模块信息

    语法:modinfo 模块名

    举例:
    modinfo helloworld.ko

将驱动编译在Linux内核里面

make menuconfig图形化配置界面

  1. 打开图形化配置界面
    使用命令export ARCH=arm设置平台架构,平台架构是armarm64,还是mips要根据实际开发板架构选择。然后在内核源码的顶层目录下,输入命令make menuconfig

  2. 常见错误

    • 如果提示错误"'make menuconfig' requires the ncurses libraries",请使用命令sudo apt-get install libncurses5-dev安装ncurses
    • 提示"Your display is too small to run Menuconfig!"这个错误是因为控制终端的窗口太小,放大窗口或者全屏操作即可。
    • 提示"make:*** No rule to make target 'menuconig'. Stop."这个错误是没有在内核源码的顶层目录输入make menuconfig
  3. 图形化配置界面操作

    • 移动

      使用键盘的上,下,左,右按键可以移动光标。

    • 搜索功能

      输入“/”即可弹出搜索界面,然后输入我们要搜索的内容即可

    • 配置驱动选项状态操作

      • 把驱动编译成模块,用M来表示
      • 驱动编译到内核里面,用*
      • 不编译

      使用“空格”按键来配置这三种不同的状态。
      选项的状态有[],<>,()三种表示状态,其中

      []表示有两种状态,只能设置成选中或者不选中,

      <>M表示有三种状态,可以设置成选中,不选中,和编译成模块。

      ()表示用来存放字符串或者16进制数。

与make menuconfig有关的文件

Makefile,config,Kconfig关系

用做菜类比他们之间的关系:

Makefile文件相当于菜的做法。

Kconfig文件相当于饭店的菜单

.config文件相当于我们使用饭店的菜单点完的菜品

Kconfig文件

Kconfig文件是图形化配置界面的的源文件,图形化配置界面中的选项由Kconfig文件决定。当我们执行命令make menuconfig命令的时候,内核的配置工具会读取内核源码目录下的arch/xxx/Kconfigxxx是命令export ARCH=arm中的ARCH的值。然后生成对应的配置界面供开发者使用。

config文件和.config文件

config文件和.config文件都是Linux内核的配置文件,config文件位于Linux内核源码的arch/$(ARCH)/configs目录下,是Linux系统默认的配置文件。.config文件位于Linux内核源码的顶层目录下,编译linux内核时会使用.config文件里面的配置来编译内核镜像。
.config存在,make menuconfig界面的默认配置即当前.config文件的配置,若修改了图形化配置界面的设置并保存,则.config文件会被更新
.config文件不存在,make menuconfig界面的默认配置则为Kconfig文件中的默认配置。使用命令make xxx_defconfig命令会根据arch/$(ARCH)/configs目录下默认文件生成.config文件。

.config
make menuconfig
make 编译内核

Kconfig语法

mainmenu

可以用menu,endmenu来生成菜单,menu是菜单开始的标志,endmenu是菜单结束的标志。这俩个是成对出现的。

menu
	config HELLOWORLD
endmenu

配置选项

使用关键字config来定义一个新的选项。每个选项都必须指定类型,类型包括bool,tristate,string,hex,int。最常见的是bool,tristate,string这三个。其中:

bool类型有俩种值:y和n

tristat有三种值:y、m和n

string:为字符串类型

help表示帮助信息,当我们在图形化界面按下h按键,弹出来的就是help的内容。

举例:

config HELLOWORLD
	bool "hello world support"
	default y
	help
		hello world
依赖关系

Kconfig中的依赖关系可以用depends onselect

直接举例说明:
depends on表示直接依赖关系:

config A
	depends on B
#表示选项A依赖选项B,只有当B选项被选中时,A选项才可以被选中。	

select表示反向依赖关系:

config A
	select B
#在A选项被选中的情况下,B选项自动被选中。
可选择项

使用choiceendchoice定义可选择项。

直接举例说明:

choice
	bool "a“
config b
	boot b1
config c
	boot c1
...
endchoice
注释

Kconfig中使用comment用于注释,不过此注释非彼注释,这个注释是在图形化界面中显示一行注释。
举例:

config TEST_CONFIG
	bool "test"
	default y
	help
		just test
comment "just test"
source

source用于读取另一个Kconfig文件,如source "init/Kconfig"就是读取init目录下的Kconfig文件。

例子

1、/home/fengzc/study/linux-5.15/drivers/char创建helloworld文件夹

2、/home/fengzc/study/linux-5.15/drivers/char/helloworld创建Kconfig

config HELLOWORLD
	bool "hello world support"
	default y
	help
		hello world

3、/home/fengzc/study/linux-5.15/drivers/char/Kconfig里增加

source "drivers/char/helloworld/Kconfig"

4、通过make menuconfig命令,查看Device Drivers ---> Character devices ---> [*] hello world support ,然后save保存退出

5、编写/home/fengzc/study/linux-5.15/drivers/char/helloworld驱动

helloworld.c

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

static int helloworld_init(void){
	printk("helloworld init\n");
	return 0;
}

static void helloworld_exit(void){
	printk("helloworld exit");
}

module_init(helloworld_init);
module_exit(helloworld_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FZC");
MODULE_VERSION("V1.0");

6、编译写Makefile

obj-$(CONFIG_HELLOWORLD) += helloworld.o
KDIR:=/home/fengzc/study/linux-5.15/
PWD?=$(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order

7、/home/fengzc/study/linux-5.15/drivers/char/Makefile修改添加

obj-y                           += helloworld/

8、查看有没有编译Linux内核里(烧录镜像有没有helloworld日志打印)

驱动模块传参

驱动传参的意义

优势:

  1. 通过驱动传参,可以让驱动程序更加灵活。兼容性更强。
  2. 可以通过驱动传参,设置安全校验,防止驱动被盗用。

不足:

  1. 使驱动代码变得复杂化
  2. 增加了驱动的资源占用。

驱动可以传递的参数类型

c语言中常用的数据类型内核大部分都支持驱动传参。这里将内核支持的驱动传递参数的类型分成三类:

基本类型:char,bool,int,long,short,byte,ushort,uint。

数组:array

家符串:string

如何给驱动传递参数

驱动支持的参数类型有基本类型,数组,字符串。这三个类型分别对应函数:

module_param:传递基本类型函数

函数功能:传递基本类型参数给驱动

函数原型:module_param(name,type,perm)

函数参数:

name:要传递给驱动代码中的变量的名字。

type:参数类型。

perm:参数的读写权限。

module_param_array:传递数组类型函数

函数功能:传递数组类型参数给驱动

函数原型: module_param_array(name,type,nump,perm)

函数参数:

name:要传递给驱动代码中的变量的名字。

type:参数类型。

nump:数组的长度。

perm:参数的读写权限。

module_param_string:传递字符串类型函数

函数功能:传递字符串类型参数给驱动

函数原型:module_param(name,string,len,perm)

函数参数:

name:要传递给驱动代码中的变量的名字。

string:驱动程序中变量的名字,要和参数name的名字保持一致。

len:字符串的大小。

perm:参数的读写权限。

MODULE_PARM_DESC函数

函数功能:描述模块参数的信息。

include/linux/moduleparam.h

定义函数原型: MODULE_PARM_DESC(_parm, desc)

函数参数:

_parm:要描述的参数的参数名称。

desc:描述信息。

这三个函数在Linux内核源码include/linux/moduleparam.h中有定义,如下:

#define module_param(name, type, perm)                          \
        module_param_named(name, name, type, perm)
        
#define module_param_array(name, type, nump, perm)              \
        module_param_array_named(name, name, type, nump, perm)
        
#define module_param_string(name, string, len, perm)                    \
        static const struct kparam_string __param_string_##name         \
                = { len, string };                                      \
        __module_param_call(MODULE_PARAM_PREFIX, name,                  \
                            &param_ops_string,                          \
                            .str = &__param_string_##name, perm, -1, 0);\
        __MODULE_PARM_TYPE(name, "string")
        

参数的读写权限

读写权限在include/linux/stat.hinclude/uapi/linux/stat.h下有定义,一般使用S_IRUGO,也可以使用数字表示,如444表示S_IRUGO
include/uapi/linux/stat.h

# S_I不管,R:可读,W:可写,X:可执行,U、USR:用户所有者user,G、GRP:用户组,O,TH:其他人
#define S_IRWXU 00700  
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100

#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010

#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001

include/linux/stat.h

#define S_IRWXUGO       (S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO       (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
#define S_IRUGO         (S_IRUSR|S_IRGRP|S_IROTH)
#define S_IWUGO         (S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO         (S_IXUSR|S_IXGRP|S_IXOTH)

例子

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

static int a = 0;
static int array[5] = {0};
static int array_size;
static char str1[10] = {}


module_param(a, int ,S_IRUGO)
MODULE_PARAM_DESC(a, "e.g. a = 1");

module_param_array(array, int, &array_size, S_IRUGO);
MODULE_PARAM_DESC(array, "e.g. array=1, 2, ");

module_param_string(str, str1, sizeof(str1), S_IRUGO);
MODULE_PARAM_DESC(str, "e.g. str = hellow")

static int helloworld_init(void){
	int i = 0;
	printk("a is %d\n, a");
	for(i = 0;i< array_size;i ++){
		printk("array[%d] is %d\n", i, 
	}
	printk("str1 is %s\n", str1);
	printk("helloworld init\n");
	return 0;
}

static void helloworld_exit(void){
	printk("helloworld exit");
}

module_init(helloworld_init);
module_exit(helloworld_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FZC");
MODULE_VERSION("V1.0");

编译成内核模块 ,通过命令insmod helloworld.ko a=1 array=1,2,3 str=hello

内核符号表

驱动程序可以编译成内核模块,也就是KO文件。每个KO文件是相互独立的,也就是说模块之间无法互相访问。但是在某些使用场景下要互相访问,如B模块要用A模块中的函数。此时符号表的概念就引入了

符号表

所谓“符号"就是内核中的函数名,全局变量名等。符号表就是用来记录这些“符号”的文件。

内核符号表导出

模块可以使用一下宏EXPORT_SYMBOLEXPORT_SYMBOL_GPL导出符号到内核符号表中。
例:

EXPORT_SYMBOL(符号名);

EXPORT_SYMBOL_GPL(符号名);//只适用于包含GPL许可的模块。

导出去的符号可以被其他模块使用。使用前只需要声明一下即可。

例子

a.c

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

extern int add(int a, int b);


int add(int a, int b){
	return a + b;
}

EXPORT_SYMBOL(add);

static int helloA_init(void){
	printk("helloA init\n");
	return 0;
}

static void helloA_exit(void){
	printk("helloA exit");
}

module_init(helloA_init);
module_exit(helloA_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FZC");
MODULE_VERSION("V1.0");


b.c

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

extern int add(int a, int b);

static int helloB_init(void){
	printk("helloB init\n");
	int a = 0;
	a = add(1,2);
	printk("a value = %d", a);
	return 0;
}

static void helloB_exit(void){
	printk("helloB exit");
}

module_init(helloB_init);
module_exit(helloB_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FZC");
MODULE_VERSION("V1.0");


  1. 编译a.c,得到a.koModule.symvers,然后将它复制b.c的同级目录
  2. 编译b.c得到b.ko
  3. 先加载insmod a.ko,然后insmod b.ko,这样b模块就可以使用a模块的函数了
  4. 卸载先rmmod b.ko,然后rmmod a.ko

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

完美日记母公司再度携手中国妇基会,以“创美人生”助力女性成长

撰稿 | 多客 来源 | 贝多财经 当春时节&#xff0c;梦想花开。和煦的三月暖阳&#xff0c;唤醒的不止是满城春意&#xff0c;更有逸仙电商“创美人生”公益项目播撒的一份希望。 3月8日“国际妇女节”当日&#xff0c;为积极响应我国促进共同富裕的政策倡导&#xff0c;助力相…

C语言--自定义类型详解

目录结构体结构体的声明特殊的声明结构的自引用typedef的使用结构体变量的定义和初始化结构体的内存对齐为什么存在内存对齐&#xff1f;修改默认对齐数结构体传参位段位段的内存分配位段的跨平台问题枚举联合联合类型的定义联合在内存中开辟空间联合大小的计算结构体 结构体的…

Linux之磁盘分区、挂载

文章目录一、Linux分区●原理介绍●硬盘说明查看所有设备挂载情况挂载的经典案例二、磁盘情况查询基本语法应用实例磁盘情况-工作实用指令一、Linux分区 ●原理介绍 Linux来说无论有几个分区&#xff0c;分给哪一目录使用&#xff0c;它归根结底就只有一个根目录&#xff0c;…

可编程线性直流电源的特性有哪些?

可编程线性直流电源是一种高性能、高精度的电源设备&#xff0c;其主要特性包括以下几点&#xff1a;1、高稳定性&#xff1a;可编程线性直流电源具有极高的输出稳定性&#xff0c;能够保证输出电压、电流和功率的精度和稳定性。通常来说&#xff0c;稳定性能够达到0.01%或更高…

Linux的诞生过程

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。座右铭&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石&#xff0c;故能成其高。个人主页&#xff1a;小李会科技的…

Android Lancet Aop 字节编码修复7.1系统Toast问题(WindowManager$BadTokenException)

近期在Bugly上出现7.1以下设备上出现大量BadTokenException&#xff1a; android.view.WindowManager$BadTokenExceptionUnable to add window -- token android.os.BinderProxy6c0415d is not valid; is your activity running?报错堆栈&#xff0c;如下所示&#xff1a; …

数据分析师CDA认证 Level Ⅰ笔记

**黑色字体部分为考纲&#xff0c;蓝色字体部分为笔记&#xff0c;仅供参考 PART 1 数据分析概念与职业操守 1、数据分析概念、方法论、角色 【领会】 数据分析基本概念&#xff08;数据分析、数据挖掘、大数据&#xff09; 数据分析目的及其意义 数据分析方法与流程 数据分析的…

【网络安全工程师】从零基础到进阶,看这一篇就够了

学前感言 1.这是一条需要坚持的道路&#xff0c;如果你只有三分钟的热情那么可以放弃往下看了。 2.多练多想&#xff0c;不要离开了教程什么都不会&#xff0c;最好看完教程自己独立完成技术方面的开发。 3.有问题多google,baidu…我们往往都遇不到好心的大神&#xff0c;谁…

【Leetcode】队列实现栈和栈实现队列

目录 一.【Leetcode225】队列实现栈 1.链接 2.题目再现 3.解法 二.【Leetcode232】栈实现队列 1.链接 2.题目再现 3.解法 一.【Leetcode225】队列实现栈 1.链接 队列实现栈 2.题目再现 3.解法 这道题给了我们两个队列&#xff0c;要求去实现栈&#xff1b; 首先&…

8大核心语句,带你深入python

人生苦短 我用python 又来给大家整点好东西啦~ 咱就直接开练噜&#xff01;内含大量代码配合讲解 python 安装包资料:点击此处跳转文末名片获取 1. for - else 什么&#xff1f;不是 if 和 else 才是原配吗&#xff1f; No&#xff0c;你可能不知道&#xff0c; else 是个…

Cache的地址结构,tag到底与Cache什么关系,Cache容量与总容量,Cache行长,Cache字地址?

目录.Cache映射的问题一.Cache的三种映射重点&#xff1a;那么我说1.直接映射2.全相联映射3.组相联映射4.总结三种映射二.Cache的三个字眼(例题)1.Cache字地址多少位&#xff08;字地址即按字编址&#xff09;2.Cache容量与总容量3.Cache行长一.Cache的三种映射 重点&#xff…

C++ 类与对象

结构体与类&#xff1a;在C语言中结构体可以存储一些不同类型的数据&#xff0c;这个功能就很强大了&#xff0c;但是这些数据都是不安全的我们可以在主函数中随意修改它&#xff0c;在C中的类可以很好的解决这个问题。类就相当于C语言中的结构体一样&#xff0c;C结构体&#…

GC 垃圾回收机制

文章目录JVM 的内存模型对象存活&#xff1f;引用计数算法可达性分析算法垃圾收集标记-清除算法标记-复制算法标记-整理算法垃圾收集器垃圾收集器发展Serial / Serial OldParallel Scavenge / Parallel OldParNew / CMSG1ZGC扩展JVM 的内存模型 Java 虚拟机&#xff08;Java V…

转速/线速度/角速度计算FC

工业应用中很多设备控制离不开转速、线速度的计算,这篇博客给大家汇总整理。张力控制的开环闭环方法中也离不开转速和线速度的计算,详细内容请参看下面的文章链接: PLC张力控制(开环闭环算法分析)_plc的收卷张力控制系统_RXXW_Dor的博客-CSDN博客里工业控制张力控制无处不…

按键修改阈值功能、报警功能、空气质量功能实现(STM32)

按键修改阈值功能 要使用按键&#xff0c;首先要定义按键。通过查阅资料&#xff0c;可知按键的引脚如图所示&#xff1a;按键1&#xff08;S1&#xff09;通过KEY0与PA0连接&#xff0c;按键2&#xff08;S2&#xff09;通过KEY1与PE2连接&#xff0c;按键3&#xff08;S3&…

收到6家大厂offer,我把问烂了的《Java八股文》打造成3个文档。共1700页!!

前言大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了&#xff0c;考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些程序员了。这不&#…

【LINUX】初识文件系统

文章目录一、前言二、回顾C语言文件操作三、初识系统调用openreadwriteclose四、文件系统初识五、结语一、前言 二、回顾C语言文件操作 int main() {FILE* fp fopen("log.txt", "w");if (fp NULL){perror("fopen");}int cnt 0;fputs("…

spring2

1.Spring配置数据源1.1 数据源&#xff08;连接池&#xff09;的作用 数据源(连接池)是提高程序性能如出现的事先实例化数据源&#xff0c;初始化部分连接资源使用连接资源时从数据源中获取使用完毕后将连接资源归还给数据源常见的数据源(连接池)&#xff1a;DBCP、C3P0、BoneC…

js实现一个简单的扫雷

目录先看下最终的效果&#xff1a;首先来分析一个扫雷游戏具有哪些功能分析完成后我们就开始一步步的实现1. 相关html和css2. 我们使用类来完成相应功能3. 之后我们则是要定义一个地图4. 对地图进行渲染5. 对开始按钮添加点击事件6. 现在我们可以实现鼠标左击扫雷的功能7. 给单…

网络知识汇总

文章目录一、 [网络拓扑结构](https://blog.csdn.net/weixin_52140964/article/details/127786157)二、[空间组网](https://blog.csdn.net/weixin_43509834/article/details/123225995)三、卫星网络发展历程四、SDN的由来五、ECMP、WCMP一、 网络拓扑结构 又称分布式结构。 任…