设备类(Device Class)​​ 和 ​​设备节点(Device Node)​​是深入 Linux 设备管理和驱动模型的核心基础。它们就像“骨骼”与“门户”,共同构建了 Linux 与硬件交互的核心桥梁。

一、设备类与设备节点 

1. ​​设备类 (/sys/class/)

作用:

       设备类是一个内核抽象,用于对设备进行逻辑分类和在内核内部管理。提供标准化接口统一管理框架。它定义了一类设备应该怎么操作。
核心价值:
       ① 驱动开发简化:驱动开发者只需专注于硬件操作(填充设备类规定的操作函数集 `struct file_operations`、`struct block_device_operations` 等),而无需从头造轮子实现`open/read/write/ioctl`等逻辑流程。
        ②应用开发简化:*应用开发者只要知道设备类型(字符/块)通用接口(文件操作),就可以读写 任何此类设备,无需关心底层是硬盘、U盘还是内存盘。
        ③内核组织性:** 在内核内部,设备类有效地将数以万计的驱动和硬件实例归类管理。

分类:

        第一类:字符设备驱动

                字符设备特点:按字节,顺序进行读写访问设备。不具备缓冲功能,对这种设备的读写是实时的。大部分的设备属于字符设备。如:传感器、鼠标、显示器、键盘、RTC、LED。

        第二类:块设备驱动—U盘(USB口是蓝色的—3.0口---高速口)

                块设备:按照一定字节大小(如512K)的块,随机读写访问的大容量 FLASH硬盘

        第三类:网路设备—网卡

                    网络设备:使用套接字来实现网络数据的收发的设备—网口—标准口。如有线网卡、无线网卡、蓝牙。

体现:/sys/class/目录是其在用户空间的直观体现。每个子目录代表一种接口规范(`tty`, `block`, `input`, `net`等)。

2、设备节点 (/dev/)

作用:

        用户空间程序与内核驱动和设备交互的桥梁或入口点​​用户程序像读写普通文件一样打开 (open)、读写 (read/write)、控制 (ioctl) 这个文件,内核会将这些操作​​拦截​​并​​重定向​​到与该设备节点相关联的设备驱动(通过其主设备号找到驱动)和设备实例(通过次设备号)。
核心价值:
        ①统一的访问模型:“一切皆文件”哲学的核心体现。应用程序只需使用标准文件 I/O 操作(`open`, `read`, `write`, `ioctl`, `close`)即可与硬件交互。
        ②实例标识:主设备号 用来表示某一类驱动,如鼠标,键盘都可以归类到USB驱动中。次设备号 用来表示这个驱动下的各个设备。比如第几个鼠标,第几个键盘等。
 体现: /dev/ 目录下的特殊文件(`/dev/sda1`, `/dev/ttyUSB0`, `/dev/input/event0`等),由 `udev` 根据内核信息动态管理。

设备节点与设备号的关系总结:

  • 设备号是内核用来识别驱动和设备的​​内部数字标识​​;设备节点是用户空间程序访问硬件或内核功能的​​文件路径入口​​。
  • ​​每个设备节点在其元数据中​​存储了一个设备号​​(主+次)。这个设备号就是指向内核中具体驱动和设备的“指针”。
  • ​​无论是手动 mknod 还是 udev 自动创建,​​都需要知道要绑定的设备号是什么​​,才能创建出指向正确驱动和设备的节点。

总结:设备类定义了设备的类型,并指导了设备节点的创建和管理,它们通过设备号和 sysfs(虚拟文件系统) 联系在一起.

二、模块简介

        Linux操作系统以其广泛的兼容性和灵活性著称,能够运行在各种不同的硬件体系架构上,如ARM架构和x86架构等。此外,Linux还支持无数种I/O设备(包括传感器等),每种设备通常需要一个特定的驱动程序才能正常工作。由于这些设备和硬件的多样性,将所有可能需要的驱动程序都直接编译进内核并不现实,也不高效。因此,Linux内核采用了模块化的设计思想。

模块化设计的优势:

①最小内核镜像:Linux发行版通常包含一个最小的内核镜像,这个镜像只包含通用的、基础的功能。这样做的好处是减少了内核的大小,提高了启动速度,同时也减少了内存占用。

②动态加载和卸载:除了最小内核镜像外,其他功能(如特定的驱动程序)以模块的形式存在。这些模块在系统运行时可以按需动态加载到内核中,也可以在不使用时动态卸载。这种机制提高了系统的灵活性和可扩展性。

③硬件兼容性:模块化设计使得Linux能够更容易地支持各种硬件设备。当新的硬件设备出现时,只需开发相应的驱动程序模块,而无需重新编译整个内核。

④安全性:通过动态加载和卸载模块,系统管理员可以更容易地管理内核功能,限制不必要的内核模块加载,从而在一定程度上提高系统的安全性。

常见的模块:

(1)Linux系统非必须的驱动程序(必要的驱动程序已经整合到内核代码中了)

(2)供其他程序使用的工具函数集合,类似于用户空间的库函数。

(3)Linux系统非必须的,扩展的功能。

        在内核配置菜单中,可以将模块设置编译进内核(内核启动时自动加载模块,无法卸载,模块成为内核的一部分),配置选项<*>; 也可以设置为编译成外部模块内核启动时不会自动加载模块,需要手动命令加载和卸载),配置选项<M>; 也可以将模块设置为不编译配置选项<>。 使用:make menuconfig 

三、模块的基本构成

1、头文件

最基本的是以下两个头文件,后续用到的宏都在这两个头文件中定义

#include <linux/module.h>

#include <linux/init.h>

补充:内核源码里面的API函数—调用对应的头文件的时候,会自动从顶层路径下的include文件夹里面查找

注意:在驱动模块代码中不能使用任何标准C库提供的函数,即不能包含标准C库头文件,如#include <stdio.h>、 #include<string.h>、#include <unistd.h>等,也不能进行浮点运算。

内核为模块提供了专用的字符串处理接口<对应头文件#include <linux/string.h>>和打印函数printk(定义在<#include <linux/printk.c>>)。

2、全局变量的定义和子函数的实现(可选)

        在Linux内核模块开发中,全局变量的定义和子函数的实现是模块功能实现的基础。当这些全局变量或函数需要被其他模块使用时,就需要通过符号导出机制来使它们对其他模块可见。同时,模块在加载时也可以接受外部传递的参数,这通过模块传参机制实现。

①符号导出

        符号导出是指将模块中的全局变量或函数导出,以便其他模块可以使用它们--->模块与模块之间调用全局函数或变量。这通过EXPORT_SYMBOL或EXPORT_SYMBOL_GPL宏来实现。

1:不指定许可证情形

EXPORT_SYMBOL(需要导出的变量名或函数名)不做许可证声明的模块也可以使用该全局变量或函数导出,不限制使用模块的许可证类型。

内核源码里面的导出函数使用:

/* platform dependent support */
EXPORT_SYMBOL(strcat);
EXPORT_SYMBOL(strcpy);
EXPORT_SYMBOL(strlen);
EXPORT_SYMBOL(strncpy);
EXPORT_SYMBOL(strncat);
EXPORT_SYMBOL(strchr);
EXPORT_SYMBOL(strrchr);
EXPORT_SYMBOL(memmove);

2:指定许可证情形

EXPORT_SYMBOL_GPL(需要导出的变量名或函数名):须做GPLGPLv2Dual BSD/GPL 可证声明的模块才可以使用该全局变量或函数导出

注意:只有导出后,其它模块才能使用

②模块传参

        模块传参允许在加载模块时传递参数给模块中的全局变量。这通过module_param和module_param_array宏来实现。

1:非数组的单个变量的情形

     原型:module_param(name,type,perm);

     参数:

             name:变量的名字

             type:变量的类型

             perm:访问的权限

2:数组变量的情形

     原型:module_param_array(name, type,&num,perm);

参数:

        name:表示数组的名字;

        type:表示数组元素的类型;

        num :存放传入参数元素数量,使用时候这里要写一个变量的地址。

        perm:表示参数的访问权限;

宏参数的说明:

(1)name :需要传入参数的变量名。

(2)type:参数类型,需要与变量类型兼容

 注意:char* p;  要将这个p的类型传递进来的话,需要使用类型:charp

(3)perm:参数对应文件的访问权限,权限值在<linux/stat.h>中定义

#define  S_IRWXU   00700

#define  S_IRUSR    00400

#define  S_IWUSR   00200

如果perm取以上的值,则内核会自动生成文件 /sys/module/模块名/parameters/参数名(开发板),将参数的值保存在该文件中,perm的值指明了该文件的访问权限。如果perm取值0, 则不会生成该文件,即不指定权限。

3、模块加载函数(模块的入口)

驱动程序的入口函数的基本写法:

名称自定义,建议以“设备名或模块名_init”的形式---作用:驱动程序要看到__init才能确定后面的函数是入口函数

__init-->只在编译到内核时有效,此类函数会在系统初始化时加载到虚拟内存空间的.init段,自动被调用,在完成后被废弃,所有占用内存空间被收回。

/驱动模块的入口函数实现
static int __init xxx_init(void)
{return 0;
}//标记该函数是模块加载函数
module_init(xxx_init);

4、模块卸载函数(模块的出口)

出口函数主要的功能当将对应的驱动程序卸载掉的时候,会执行出口函数里面的内容。

名称自定义,建议以“设备名或模块名 __exit”的形式

__exit-->只在编译到内核时有效,编译到内核时此类函数被废弃,所占用的内存被释放,永远不会被调用。

//驱动模块的出口函数实现
static void __exit  xxx_exit(void)
{//函数实现
}//标记该函数为卸载函数
module_exit(xxx_exit);

5、模块许可证声明

作用:防止侵权的问题

下面是常见的3种声明,只需要声明一种

MODULE_LICENSE(GPL);   

MODULE_LICENSE(“GPL v2”);

MODULE_LICENSE(“Dual BSD/GPL”);

其他的许可证:“GPL and additional rights”、 “Dual MPL/GPL”

不做模块许可声明的后果:

1)内核会抱怨:模块污染了内核,但是不会影响功能的使用。

2)EXPORT_SYSMBOL_GPL导出的变量或函数无法被调用,除非调用中做了兼容GPL的许可声明。

6、模块相关信息(可选)

常见的有模块的作者,模块的描述,模块的版本等.

MODULE_AUTHOR(“模块作者”);

MODULE_DESCRIPTION(“针对模块的简单描述”); --功能描述

MODULE_VERSION(“模块版本V1.1”);

内核源码里面:MODULE_AUTHOR("Fenghua Yu <fenghua.yu@intel.com>");

7、模块编译流程

编写模块代码

使用C语言编写内核模块代码,通常包括模块入口函数(module_init)模块出口函数(module_exit)以及模块许可声明(MODULE_LICENSE)

设置头文件

包含必要的头文件,如<linux/module.h>、<linux/init.h>等。

③编写Makefile

        1:定义变量--->在Makefile中定义变量,如内核源码目录(KERNEL_DIR)要编译的模块名(obj-m)

        2:编写编译规则--->编写make命令的规则,用于编译内核模块

使用的make命令模式如下:

make -C <内核源码目录> M = <模块源码目录> modules

-C <内核源码目录>:告诉make切换到指定的内核源码目录并执行后续的命令。这个目录通常包含内核的配置文件(如.config)、Makefile以及所有内核源码文件。

M=<模块源码目录>:指定模块源码的根目录。这个目录应该包含一个Makefile,该Makefile定义了要编译的模块(通过obj-m变量)。

modules:这是传递给内核构建系统的目标,指示它编译指定的模块。

④编译模块

1:进入模块目录--->打开终端,进入包含模块代码和Makefile的目录。

2:执行make命令--->在该目录下执行make命令,根据Makefile中的规则编译模块。

3:生成.ko文件--->编译成功后,会生成一个以.ko为后缀的文件,这是内核模块的二进制文件

8、模块加载的命令

insmod  

作用:加载对应驱动模块,服务于入口函数 

用法: insmod 模块名.ko  [模块参数列表]

功能: 向内核中加载指定的模块

选项: 传入给模块内部使用的参数,将参数赋值给模块内部的变量

 

rmmod

用法:rmmod [-f] 模块名

功能:从内核中将指定的模块移除,服务于出口函数

注意:正常情况下,rmmod 无法移除正在被使用的,或设计为不可移除的。

但是如果指定了-f或-force,并且在编译内核时CONFIG_MODULE_FORCE_UNLOAD被设置有效,则rmmod会强制移除指定的模块,不安全,不建议使用。

lsmod

功能 : 依据/proc/modules中的内容,列出已载入内核的模块信息

输出信息:

module(模块名)   Size (模块大小 ,字节数)    Used(被引用计数)   by(被....使用)

lsmod | grep   模块名称的关键词    //常用该命令查看感兴趣的模块是否已载入内核

depmod -a

作用:同步模块与模块之间的依赖关系,放到modules.dep文件里面。

modprobe

用法:modprobe [-r] 模块名

功能:向内核中的加载指定的模块,或从内核中移除指定的被引用次数为0的模块

选项:没有 -r 选项    表示加载(类似:insmod作用)   

有 -r选项    表示移除

参数:模块名,不是文件名 ----一定不要加后缀.ko

备注:该命令是智能版的insmod 和rmmod,它可以依据模块的依赖关系将执行模块及其依赖的被引用次数为0的模块一并加载或移除。但前提是必须事先准备好/lib/modules/内核版本号/modules.dep,并且将相关模块文件放在/lib/modules/内核版本号/目录下。

modinfo    

功能:显示指定模块的详细信息

使用:modinfo 模块名

备注:必须事先准备好/lib/modules/内核版本号/modules.dep 文件。

 

四、模块代码编写与实现

示例1:使用模块出入函数,观察现象
//驱动模块代码实现
#include <linux/module.h>
#include <linux/init.h>
//模块的入口实现
static int __init xxx_init(void)
{//输出观察一下printk("hello\n");return 0;
}//模块的出口实现
static void __exit xxx_exit(void)
{//输出观察一下printk("world\n");}module_init(xxx_init);//标记该函数为入口函数
module_exit(xxx_exit);//标记该函数为卸载函数
MODULE_LICENSE("GPL");//模块许可证声明

编写一个Makefile的文件:

#设置obj-m目标

obj-m += hello.o # 指定了要编译的模块目标文件,hello.o是由hello.c(或其他相关文件)编译而来的。编译后,会生成hello.ko,这是Linux内核模块的文件格式。

#内核源码的路径

KDIR ?=/home/huzhiyuan/work/rk3399/kernel-rockchip  #?= 表示如果KDIR没有被定义,则使用后面的值作为默认值。

#编译指令

modules:

make -C $(KDIR) M=$(PWD) modules

#这部分定义了一个名为modules的目标。

# -C $(KDIR) 告诉make切换到KDIR指定的目录(内核源码目录)去执行make命令。

# M=$(PWD) 指定了当前模块源码目录的路径(PWD是当前工作目录的完整路径)。

# modules 是告诉内核构建系统我们要编译的是模块。

clean:

make -C $(KDIR) M=$(PWD) clean

# 这部分定义了一个名为clean的目标(或规则)。

# 它用于清理编译过程中生成的文件。

# 命令与modules目标类似,但最后是clean,告诉内核构建系统我们要清理模块编译生成的文件。

使用的指令:

①加载模块

        root@SOM-RK3399v2:~# insmod hello.ko

        root@SOM-RK3399v2:~# dmesg

        dmesg 用于显示内核环缓冲区(ring buffer)的内容

        对应的现象:

 ②卸载模块

        root@SOM-RK3399v2:~# rmmod hello.ko

        root@SOM-RK3399v2:~# dmesg

        dmesg 用于显示内核环缓冲区(ring buffer)的内容

        对应的现象:

注意:insmod这个指令会运行入口函数,rmmod 这个指令会运行出口函数

示例2:验证printk的输出等级

#define KERN_EMERG KERN_SOH "0" /* system is unusable */

#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */

#define KERN_CRIT KERN_SOH "2" /* critical conditions */

#define KERN_ERR KERN_SOH "3" /* error conditions */

#define KERN_WARNING KERN_SOH "4" /* warning conditions */

#define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */

#define KERN_INFO KERN_SOH "6" /* informational */

#define KERN_DEBUG KERN_SOH "7" /* debug-level messages */

日志等级的主要作用是区分消息的重要性和紧急程度,而不是决定消息的输出顺序。

注意:①内核有一个控制台日志级别设置,只有级别高于或等于此设置的消息才会被输出到控制台,默认设置为4;如果一个消息的级别低于控制台日志级别,那么它就不会被输出到控制台,即使它的数字等级更低。

//驱动模块代码实现
#include <linux/init.h>
#include <linux/module.h>//模块的入口实现
static int __init hello_init(void) 
{printk("hello\n");//将其他等级输出一下printk(KERN_DEBUG "hello world--7\n");printk(KERN_INFO "hello world--6\n");printk(KERN_NOTICE "hello world--5\n");printk(KERN_WARNING "hello world--4\n");printk(KERN_ERR "hello world--3\n");printk(KERN_CRIT "hello world--2\n");printk(KERN_ALERT "hello world--1\n");printk(KERN_EMERG "hello world--0\n");return 0;
}
//模块的出口实现
static void __exit hello_exit(void)
{printk("world\n");//将其他等级输出一下printk(KERN_DEBUG "good bye--7\n");printk(KERN_INFO "good bye--6\n");printk(KERN_NOTICE "good bye--5\n");printk(KERN_WARNING "good bye--4\n");printk(KERN_ERR "good bye--3\n");printk(KERN_CRIT "good bye--2\n");printk(KERN_ALERT "good bye--1\n");printk(KERN_EMERG "good bye--0\n");
}module_init(hello_init);//标记该函数为入口函数
module_exit(hello_exit);//标记该函数为卸载函数
MODULE_LICENSE("GPL");//模块许可证声明

 现象:

示例3:模块与模块之间调用对应的函数-->符号导出

模块A

//驱动模块代码实现
#include <linux/init.h>
#include <linux/module.h>static int add(int a,int b)
{return a+b;
}
//导出函数
EXPORT_SYMBOL_GPL(add);static int sub(int a,int b)
{return a-b;
}
//导出函数
EXPORT_SYMBOL_GPL(sub);//模块的入口实现
static int __init hello_init(void) 
{printk("hello\n");//将其他等级输出一下printk(KERN_DEBUG "hello world--7\n");printk(KERN_INFO "hello world--6\n");printk(KERN_NOTICE "hello world--5\n");printk(KERN_WARNING "hello world--4\n");printk(KERN_ERR "hello world--3\n");printk(KERN_CRIT "hello world--2\n");printk(KERN_ALERT "hello world--1\n");printk(KERN_EMERG "hello world--0\n");return 0;
}static void __exit hello_exit(void)
{printk("world\n");//将其他等级输出一下printk(KERN_DEBUG "good bye--7\n");printk(KERN_INFO "good bye--6\n");printk(KERN_NOTICE "good bye--5\n");printk(KERN_WARNING "good bye--4\n");printk(KERN_ERR "good bye--3\n");printk(KERN_CRIT "good bye--2\n");printk(KERN_ALERT "good bye--1\n");printk(KERN_EMERG "good bye--0\n");
}module_init(hello_init);//标记该函数为入口函数
module_exit(hello_exit);//标记该函数为卸载函数
MODULE_LICENSE("GPL");//模块许可证声明

模块B

//驱动模块代码实现
#include <linux/init.h>
#include <linux/module.h>//声明外部函数
extern int add(int a,int b);
extern int sub(int a,int b);//模块的入口实现
static int __init hello_init(void) 
{printk("hello\n");//将其他等级输出一下printk(KERN_DEBUG "hello world--7\n");printk(KERN_INFO "hello world--6\n");printk(KERN_NOTICE "hello world--5\n");printk(KERN_WARNING "hello world--4\n");printk(KERN_ERR "hello world--3\n");printk(KERN_CRIT "hello world--2\n");printk(KERN_ALERT "hello world--1\n");printk(KERN_EMERG "hello world--0\n");printk("add:%d\r\n", add(25,25));return 0;
}static void __exit hello_exit(void)
{printk("world\n");//将其他等级输出一下printk(KERN_DEBUG "good bye--7\n");printk(KERN_INFO "good bye--6\n");printk(KERN_NOTICE "good bye--5\n");printk(KERN_WARNING "good bye--4\n");printk(KERN_ERR "good bye--3\n");printk(KERN_CRIT "good bye--2\n");printk(KERN_ALERT "good bye--1\n");printk(KERN_EMERG "good bye--0\n");printk("sub:%d\r\n", sub(100,25));
}module_init(hello_init);//标记该函数为入口函数
module_exit(hello_exit);//标记该函数为卸载函数
MODULE_LICENSE("GPL");//模块许可证声明

现象:

 

示例4:模块与模块之间调用对应的变量-->符号导出

模块A

//驱动模块代码实现
#include <linux/init.h>
#include <linux/module.h>int add=55;
//导出函数
EXPORT_SYMBOL_GPL(add);int sub=66;
//导出函数
EXPORT_SYMBOL_GPL(sub);//模块的入口实现
static int __init hello_init(void) 
{printk("hello\n");//将其他等级输出一下printk(KERN_DEBUG "hello world--7\n");printk(KERN_INFO "hello world--6\n");printk(KERN_NOTICE "hello world--5\n");printk(KERN_WARNING "hello world--4\n");printk(KERN_ERR "hello world--3\n");printk(KERN_CRIT "hello world--2\n");printk(KERN_ALERT "hello world--1\n");printk(KERN_EMERG "hello world--0\n");return 0;
}static void __exit hello_exit(void)
{printk("world\n");//将其他等级输出一下printk(KERN_DEBUG "good bye--7\n");printk(KERN_INFO "good bye--6\n");printk(KERN_NOTICE "good bye--5\n");printk(KERN_WARNING "good bye--4\n");printk(KERN_ERR "good bye--3\n");printk(KERN_CRIT "good bye--2\n");printk(KERN_ALERT "good bye--1\n");printk(KERN_EMERG "good bye--0\n");
}module_init(hello_init);//标记该函数为入口函数
module_exit(hello_exit);//标记该函数为卸载函数
MODULE_LICENSE("GPL");//模块许可证声明

 模块B

//驱动模块代码实现
#include <linux/init.h>
#include <linux/module.h>//声明外部变量
extern int add;
extern int sub;//模块的入口实现
static int __init hello_init(void) 
{printk("hello\n");//将其他等级输出一下printk(KERN_DEBUG "hello world--7\n");printk(KERN_INFO "hello world--6\n");printk(KERN_NOTICE "hello world--5\n");printk(KERN_WARNING "hello world--4\n");printk(KERN_ERR "hello world--3\n");printk(KERN_CRIT "hello world--2\n");printk(KERN_ALERT "hello world--1\n");printk(KERN_EMERG "hello world--0\n");printk("add:%d\r\n", add);return 0;
}
//模块的出口实现
static void __exit hello_exit(void)
{printk("world\n");//将其他等级输出一下printk(KERN_DEBUG "good bye--7\n");printk(KERN_INFO "good bye--6\n");printk(KERN_NOTICE "good bye--5\n");printk(KERN_WARNING "good bye--4\n");printk(KERN_ERR "good bye--3\n");printk(KERN_CRIT "good bye--2\n");printk(KERN_ALERT "good bye--1\n");printk(KERN_EMERG "good bye--0\n");printk("sub:%d\r\n", sub);
}module_init(hello_init);//标记该函数为入口函数
module_exit(hello_exit);//标记该函数为卸载函数
MODULE_LICENSE("GPL");//模块许可证声明

示例5:模块与模块之间传参 -->模块传参
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>int num;
static int age = 22; 
static char *name = "hello";
static int arr[5]={1,2,3,4,5};module_param(age, int, 0644); // 定义名为age的模块参数,类型为int,权限为0644
module_param(name,charp, 0644); // 定义名为name的模块参数,类型为charp,权限为0644
module_param_array(arr,int,&num,S_IRWXU);// 定义名为arr的模块参数,类型为int,权限为S_IRWXU(00700)static int __init hello_init(void)
{int i;printk("Age: %d\n", age);printk("Name: %s\n", name);for(i = 0;i < 5;i++){printk("arr[%d]=%d\n",i,arr[i]);}return 0;
}static void __exit hello_exit(void)
{printk("Goodbye!\n");
}module_init(hello_init);//标记该函数为入口函数
module_exit(hello_exit);//标记该函数为卸载函数
MODULE_LICENSE("GPL");//模块许可证声明

现象:

修改前:

修改后:

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频压缩、码率与流媒体传输知识总结

&#x1f3a5; 视频压缩、码率与流媒体传输知识总结 本笔记整理了 I/P/B 帧结构、码率计算、文件大小估算、压缩格式对比、推流带宽建议等视频工程常见技术要点。 一、单帧与未压缩视频数据量估算 分辨率&#xff1a;19201080&#xff08;1080p&#xff09; 色深&#xff1a;…

嵌入式C++学习路线

&#x1f680; 嵌入式C学习路线图 从C语言基础到嵌入式C高手的完整路径 &#x1f4cb; 学习进度追踪 总体目标&#xff1a; 20-26周完成全部学习内容 前置条件&#xff1a; C语言基础 STM32开发经验 学习方式&#xff1a; 理论学习 实践项目 阶段1: C基础过渡 (2-3周) 目标…

VSCode1.101.1Win多语言语言编辑器便携版安装教程

软件下载 【名称】&#xff1a; VSCode1.101.1 【大小】&#xff1a; 120M 【语言】&#xff1a; 简体中文 【安装环境】&#xff1a; Win10/Win11 【迅雷网盘下载链接】&#xff08;务必手机注册&#xff09;&#xff1a; 迅雷 【网站下载链接】: 其他网盘 软件介绍 VSCod…

ssh 服务和 rsync 数据同步

目录 一、ssh服务 1、概述 2、命令解析 远程登录命令 远程拷贝命令 3、登录方式配置 1、用户名密码登录 2、公钥验证登录 二、rsync 数据同步 1、rsync概述 2、rsync运行原理 3、rsync部署 一、ssh服务 1、概述 ssh服务&#xff0c;一种远程管理连接工具&#xf…

使用随机森林实现目标检测

核心实现思路 滑动窗口策略&#xff1a;在图像上滑动固定大小的窗口&#xff0c;对每个窗口进行分类多维特征提取&#xff1a;结合统计特征、纹理特征、边缘特征、形状特征等随机森林分类&#xff1a;训练二分类器判断窗口是否包含目标后处理优化&#xff1a;使用非极大值抑制…

3.6 move_base导航初体验

1.环境搭建 在工作空间src下git wpr_simulation&#xff0c;安装install_for_noetic.sh&#xff0c;然后再回退工作空间进行编译 下载参数文件 git clone https://github.com/6-robot/wpb_home.git下载需要魔法&#xff0c;在这里可以使用手机热点进行平替 进入脚本文件夹 …

Mysql高级——MVCC(多版本并发控制)

MySQL MVCC&#xff08;多版本并发控制&#xff09;详解 MVCC&#xff08;Multi-Version Concurrency Control&#xff09;是 MySQL InnoDB 存储引擎实现的一种并发控制机制&#xff0c;用于在保证事务隔离性的同时&#xff0c;提高数据库的并发性能。下面从原理、实现、事务隔…

Oracle union连接的怎么排序

在Oracle数据库中&#xff0c;使用UNION或UNION ALL操作符来合并两个或多个查询结果时&#xff0c;如果想对这些合并后的结果进行排序&#xff0c;通常有两种方法可以实现&#xff1a; 方法1&#xff1a;在最后的查询结果上使用ORDER BY 你可以在所有使用UNION或UNION ALL合并…

uni-app总结2-所需知识储备和学习途径

使用uni-app进行跨平台开发&#xff0c;开发者不用去掌握各个平台的开发语言&#xff0c;只需一套代码即可完成多端的产品输出。那么使用uni-app需要掌握什么呢&#xff0c;这里给大家分享一下。 Vue.js uni-app里是通过Vue来开发的&#xff0c;所以首先肯定是要掌握Vue语言。…

如何高效实现公司文件管理

要实现公司文件管理的高效&#xff0c;企业应聚焦统一文件规范、部署文档管理系统、强化权限控制、推动协同编辑、实施定期清理、推进文化建设、引入可视化分析。其中&#xff0c;统一文件规范是文件高效管理的基础。若缺乏清晰的命名规则与分类体系&#xff0c;即便配备了先进…

多模态大语言模型arxiv论文略读(124)

MediConfusion: Can you trust your AI radiologist? Probing the reliability of multimodal medical foundation models ➡️ 论文标题&#xff1a;MediConfusion: Can you trust your AI radiologist? Probing the reliability of multimodal medical foundation models …

nacos的总结

服务发现与健康监测&#xff1a;Nacos 支持多种服务注册方式&#xff0c;包括 API、SDK 和 Annotation 等&#xff0c;服务消费者可以通过 DNS 或 RPC 方式方便地发现服务。其健康检查机制通过主动和被动的方式实时监测服务实例的健康状态&#xff0c;确保流量不会被发送到不健…

低轨导航 | 低轨卫星导航PNT模型,原理,公式,matlab代码

一、PNT模型原理 低轨卫星PNT(定位、导航、授时)模型利用低轨星座的快速几何构型变化和强信号特性,通过三类核心观测值实现增强定位: 几何增强原理 低轨卫星速度7km/s(比GNSS快8-10倍)5分钟内观测几何变化相当于地面站24小时变化量加速模糊度收敛和误差分离信号增强原理…

基于python的查询工具,查询手机号的卡号归属地

本文介绍了一个利用Python进行电话号码归属地查询的代码示例。代码使用requests库发送HTTP请求&#xff0c;伪装浏览器UA头&#xff0c;通过lxml库解析网页数据&#xff0c;并运用XPath提取号码归属地信息。程序构建了查询URL&#xff0c;发送GET请求后解析返回的HTML内容&…

AI面试系统选型HR应考虑哪些问题?

北森人才管理研究院发布的《2025 企业校园招聘 AI 应用实用指南》数据显示&#xff1a;全球 44% 的企业已在招聘环节部署AI技术&#xff0c;72% 的 HR 每周至少使用一次 AI 工具&#xff0c;87% 的 HR 认为 AI 能显著提升招聘效率。 来源于《北森2025 企业校园招聘 AI 应用实用…

Redis02

redis的持久化机制 1.redis为什么需要持久化 redis本身运行时数据保存在内存中&#xff0c;那么在关闭redis的进程或者关闭计算机后数据肯定被会操作系统从内存中清掉。 redis持久化方式有两种: RDB AOF redis默认采用了一种持久化方式&#xff0c;即RDB &#xff08;Redi…

Gartner发布网络安全组织设计指南:设计网络安全组织的五项原则和六种主要安全组织类型

安全和风险管理领导者经常寻求一种通用的模型来组织其职能&#xff0c;这可能导致效率低下和需求得不到满足。然而&#xff0c;目前并没有一个标准的组织模型。这项研究可以帮助他们根据企业实际情况&#xff0c;设计出最合适的网络安全组织。 主要发现 许多安全和风险管理 (SR…

简述redis的单线程模式

在redis版本6之前&#xff0c;网络IO和键值对读写都是由一个线程来完成的。而redis的其他功能&#xff0c;比如持久化、异步删除、集群数据同步等&#xff0c;是由其他线程完成的。 为什么采用单线程 多线程有助于提升吞吐率&#xff08;系统同时处理的请求数&#xff09;&am…

WebSocket深度指南:从零基础到生产级应用

📚目录 1. WebSocket基础概念深度解析 2. WebSocket协议技术详解 3. WebSocket生命周期与状态管理 4. Spring Boot WebSocket完整实现 5. 完整聊天室项目实战 6. 高级功能与扩展应用 1. WebSocket基础概念深度解析 1.1 什么是WebSocket?深度理解 WebSocket是HTML5开…

复现 apache HTTPD 换行解析漏洞(CVE-2017-15715)

一、漏洞环境 docker环境 http://192.168.99.124:8082二、漏洞原理 Apache HTTPD是一款HTTP服务器&#xff0c;它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞&#xff0c;在解析PHP时&#xff0c;1.php\x0A将被按照PHP后缀进行解析&#xff0c;导致…