Linux内核编程(十二)热插拔

本文目录

  • 一、知识点
    • 1. 热插拔概念
    • 2. 热插拔机制
    • 3. Netlink机制
  • 二、内核发送uevent事件到用户空间
    • 1. kobject发送uevent事件
    • 2. udevadm命令查看
    • ★示例代码:
    • ★优化:完善kset_uevent_ops(热插拔事件结构体)
  • 三、用户空间使用Netlink接收uevent事件
    • 1. 创建socket
    • 2. 绑定socket
    • 3. 接收uevent事件信息
    • ★示例代码
  • 实践一:使用udev自动挂载U盘
    • 1. 配置系统支持udev--配置 Buildroot文件系统
    • 2. 创建配置规则
    • 3. 测试
  • 实践二:使用udev自动挂载TF卡
  • 实践三:使用mdev自动挂载U盘
    • 1. 配置系统支持mdev
    • 2. 配置mdev规则
    • 3. 创建脚本文件
    • 4. 测试
  • 实践四:使用mdev自动挂载TF卡
  • 实践五:使用usbmount提高工作效率
  • ★补充问题:无法识别U盘

  

一、知识点

在这里插入图片描述

1. 热插拔概念

   热插拔就是带电插拔,用人话讲就是允许用户在不关闭系统,不切断电源的情况下拆卸或安装硬盘,板卡等设备。热插拔是内核和用户空间之间,通过调用用户空间程序实现交互来实现的。当内核发生了某种热拔插事件时,内核就会调用用户空间的程序来实现交互。

2. 热插拔机制

   热插拔机制有devfs、udev、mdev。其中devfs已经不再使用。嵌入式设备上一般使用mdev,X86上一般用udev,当然嵌入式设备上也可以用udev。与 udev 不同,mdev 的设计更加简洁,是udev的简化版本。

(1) udev是基于Netlink 机制实现的。 工作原理如下:
   ① 当有设备插入或移除时,内核会生成一个 uevent 事件。
   ② 内核通过 Netlink 套接字将 uevent 事件发送给用户空间。
   ③用户空间的 udev 守护进程会打开一个 Netlink 套接字并持续监听,通过监听内核发送的 uevent 来执行相应的热插拔操作,如创建设备节点、设置权限、运行脚本等。

(2)mdev 主要工作机制是基于 uevent_helper。工作原理如下:
   ①当设备插入、移除或状态改变时,内核会生成一个 uevent 事件。
   ②内核通过 uevent_helper 机制调用用户空间的程序来处理这些事件。uevent_helper 的路径存储在 /proc/sys/kernel/hotplug 文件中,通常指向 /sbin/mdev
   ③mdev 作为一个可执行程序被内核调用,通过读取环境变量中的事件信息来处理设备事件。它根据配置文件(通常是 /etc/mdev.conf)中定义的规则,执行相应的操作。

3. Netlink机制

   Linux提供了多种方式实现内核和用户空间的数据交换,比如我们之前讲过的系统调用,sysfs,等,但是这种通信机制均为单工通信机制。而 netlink,是基于 socket 通信机制,具体双工的特点。可以很好的满足内核和用户空间的数据交换。

二、内核发送uevent事件到用户空间

前提:需要创建kest来完成。

1. kobject发送uevent事件

返回 0 表示成功,返回负值表示发生了错误。

int kobject_uevent(struct kobject *kobj, enum kobject_action action)
/*
struct kobject *kobj:指向 kobject 结构体的指针,表示发生事件的内核对象。
enum kobject_action action:表示事件的类型。KOBJ_ADD,     // 表示有新的设备或内核对象被添加到系统中。KOBJ_REMOVE,  // 表示设备或内核对象被从系统中移除。KOBJ_CHANGE,  // 表示设备或内核对象的状态发生变化。KOBJ_MOVE,    // 表示设备或内核对象在系统中被移动到另一个位置。KOBJ_ONLINE,  // 表示设备或内核对象上线,准备使用。KOBJ_OFFLINE, // 表示设备或内核对象下线,不再可用。KOBJ_MAX      // 这是枚举值的最大值,通常用于检查枚举的范围。
*/

2. udevadm命令查看

   udevadm 是 udev 的命令行工具,提供了用于调试和管理 udev 设备管理器的各种功能。它允许用户查询设备信息、模拟设备事件、测试规则和管理 udev 数据库等。

udevadm info //用于显示设备的详细信息。常用选项包括 --query=all(显示所有信息)和 --name=DEVICE(指定设备节点,例如 /dev/sda)。
udevadm monitor //用于实时监视 udev 事件。可以使用 --udev(仅显示 udev 事件)和 --kernel(显示内核事件)选项。
udevadm test //用于测试 udev 规则对设备的作用。常用选项包括 --action=ACTION(指定动作类型,如 add 或 remove),需要指定设备路径,例如 /sys/class/net/eth0。
udevadm trigger //用于手动触发 udev 事件。可以使用 --action=ACTION(指定触发的动作类型,例如 add 或 remove)和 --subsystem-match=SUBSYSTEM(仅触发匹配指定子系统的设备)选项。
udevadm control //用于控制 udev 守护进程的行为。常用选项包括 --reload(重新加载 udev 配置文件和规则)、--stop(停止 udev 守护进程)和 --start(启动 udev 守护进程)。
udevadm settle //用于等待所有当前的 udev 事件处理完毕。可以设置超时时间(秒),使用 --timeout=TIME 选项,默认超时时间是 30 秒。
udevadm --version //显示 udevadm 的版本信息.

★示例代码:

uevent.c

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/slab.h>  // For kzallocstruct kobject *mykobject01;
struct kset *my_kset;
struct kobj_type mytype;static int __init mykobj_init(void)
{int ret;//1. 创建kestmy_kset=kset_create_and_add("my_kset",NULL,NULL); //在sys下创建my_kset目录.//2. 创建kobject1mykobject01= kzalloc(sizeof(struct kobject), GFP_KERNEL); mykobject01->kset=my_kset;ret = kobject_init_and_add(mykobject01, &mytype, NULL, "mykobject01");// 发送uevent事件ret=kobject_uevent(mykobject01,KOBJ_CHANGE);return 0;
}static void __exit mykobj_exit(void)
{kobject_put(mykobject01);kset_unregister(my_kset);
}module_init(mykobj_init);
module_exit(mykobj_exit);MODULE_LICENSE("GPL");

   测试:首先使用udevadm monitor &命令来后台实时监视kobject 的 udev 事件,然后我们将uevent.ko文件加载、卸载时,查看输出信息如下。因为我们检测的是状态改变,只要发送改变就会发送uevent 事件。

在这里插入图片描述

★优化:完善kset_uevent_ops(热插拔事件结构体)

   热插拔事件意思就是当kset目录下有任何变动,包括目录的移动,增加目录或者属性文件等操作。
   当系统配置发生变化时,如添加kset到系统或移动kobject,一个通知会从内核空间发送到用户空间,这就是热插拔事件。热插拔事件会导致用户空间中的处理程序(如udev,mdev)被调用,这些处理程序会通过加载驱动程序,创建设备节点等来响应热插拔事件。

struct kset_uevent_ops {
//过滤事件,决定是否产生事件,如果返回0,将不产生事件。int (* const filter)(struct kset *kset, struct kobject *kobj); 
//向用户空间传递一个合适的字符串const char *(* const name)(struct kset *kset, struct kobject *kobj);
//通过环境变量传递任何热插拔脚本需要的信息,他会在(udev或mdev)调用之前,提供添加环境变量的机会。	int (* const uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);
};

优化代码:
功能:会屏蔽mykobject01发送的uevent事件,只响应mykobject02的事件。

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/slab.h>  // For kzallocstruct kobject *mykobject01;
struct kobject *mykobject02;
struct kset *my_kset;
struct kobj_type mytype;int my_filter(struct kset *kset, struct kobject *kobj)
{if(strcmp(kobj->name,"mykobject01")==0){  //过滤掉mykobject01的uevent事件。   return 0;}else{return 1;}
}const char *my_name(struct kset *kset, struct kobject *kobj)
{return "QJL_test";     //向用户空间传递一个合适的字符串
}int my_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env)
{add_uevent_var(env,"MYDEVICE:%s","QJL");  //添加环境变量return 0;
}struct kset_uevent_ops my_uevent_ops={.filter=my_filter,.name= my_name,.uevent =my_uevent,
};static int __init mykobj_init(void)
{int ret;//1. 创建kestmy_kset=kset_create_and_add("my_kset", &my_uevent_ops, NULL); //在sys下创建my_kset目录.//2. 创建kobject1mykobject01= kzalloc(sizeof(struct kobject), GFP_KERNEL); mykobject01->kset=my_kset;ret = kobject_init_and_add(mykobject01, &mytype, NULL, "mykobject01");//3. 创建kobject2mykobject02= kzalloc(sizeof(struct kobject), GFP_KERNEL); mykobject02->kset=my_kset;ret = kobject_init_and_add(mykobject02, &mytype, NULL, "mykobject02");// 发送uevent事件ret=kobject_uevent(mykobject01, KOBJ_CHANGE);ret=kobject_uevent(mykobject02, KOBJ_ADD);return 0;
}static void __exit mykobj_exit(void)
{kobject_put(mykobject01);kobject_put(mykobject02);kset_unregister(my_kset);
}module_init(mykobj_init);
module_exit(mykobj_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述

三、用户空间使用Netlink接收uevent事件

   因为netlink 是基于socket通信机制,在用户空间使用socket接口,如socket、bind、sendmsg、recvmsg、close 就可以使用 netlink,上手容易。这里我就不再讲解socket的API函数,详情查看:socket使用步骤详情查看地址。

1. 创建socket

  对于netlink,使用下面固定的协议类型。其中protocol指定 netlink协议类型,目前已经支持的协议类型在 linux/netlink.h 中定义,,所以需要包含头文件#include <linux/netlink.h>

int socket(int domain, int type, int protocol);
/*int domain: 选择 AF_NETLINKint type : 选择 SOCK_RAWint protocol :在#include <linux/netlink.h>中选择。
*/

2. 绑定socket

注意:对于sockaddr_nl 结构体成员填写内容:nl_family(AF_NETLINK) 、nl_pad (0) 、nl_pid(0)、nl_groups(1)。

int bind(int sockfd, struct sockaddr* my_addr, int addrlen);
/*
sockfd :socket 描述符
addr:指向一个 struct sockaddr 类型指针。这里我们使用sockaddr_nl结构体,然后进行类型转换。struct sockaddr_nl {__kernel_sa_family_t nl_family; // 套接字地址族。      这里使用 AF_NETLINK。unsigned short       nl_pad;    // 填充,用于对齐。    这里使用 0。__u32                nl_pid;    // 进程标识符 (PID)   也可以设置为0,表示不加入任何多播组。__u32                nl_groups; // 多播组掩.  设置为1时,表示用户空间进程只会接收内核事件的基本组的内核事件。};int addrlen :结构体长度。
*/

3. 接收uevent事件信息

包含头文件:

  #include <sys/types.h>#include <sys/socket.h>

注意 netlink中是不用调用listen 所监听的。可以直接使用recv函数进行接收。

ssize_t recv(int sockfd, void *buf, size_t len, int flags);   // 从套接字接收数据
// 参数:// sockfd - 套接字文件描述符// buf    - 存储接收到数据的缓冲区// len    - 缓冲区的长度// flags  - 操作标志,通常设置为0.
// 返回值:接收到的字节数,如果出错则返回 -1

★示例代码

app.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <linux/netlink.h>
#include <sys/socket.h>  // 修正了包含的头文件
#include <sys/types.h>int main() {int ret;int socketed;ssize_t len;int i;char buf[4096] = {0};// 创建 Netlink 套接字socketed = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);if (socketed < 0) {perror("socket error");  // 更改为 perror 可以输出更详细的错误信息return -1;}// 定义并初始化 sockaddr_nl 结构体struct sockaddr_nl my_sockaddr_nl = {.nl_family = AF_NETLINK,.nl_pad = 0,.nl_pid = 0,  // 绑定到内核.nl_groups = 1  // 监听内核组播消息};// 绑定 Netlink 套接字ret = bind(socketed, (struct sockaddr*)&my_sockaddr_nl, sizeof(struct sockaddr_nl));if (ret < 0) {perror("bind error");  // 错误处理close(socketed);return -1;}// 循环接收并打印消息while (1) {memset(buf, 0, sizeof(buf));  // 清空缓冲区len = recv(socketed, buf, sizeof(buf), 0);for(i=0;i<len; i++){if(buf[i]=='\0') buf[i]='\n';}// 打印接收到的消息printf("%s\n", buf);  // 使用 %.*s 打印指定长度的字符串}close(socketed);  // 关闭套接字return 0;
}

  实验现象:我们使用完善kset_uevent_ops的代码作为内核代码,通过本示例来获取内核中uevent事件信息,结果如下所示。
在这里插入图片描述

实践一:使用udev自动挂载U盘

1. 配置系统支持udev–配置 Buildroot文件系统

  要配置 Buildroot 以支持 udev,进入Buildroot 目录下,通过 menuconfig 图形化配置界面设置 :System configuration —dev management 为 Dynamic using devtmpfs + eudev。即使用udev去管理。配置完成后,编译镜像文件烧录到开发版。

2. 创建配置规则

(1)检查 /etc/udev/rules.d/ 目录是否存在。如果不存在,则使用 mkdir 命令创建该目录。并在该目录下创建 usb 目录。在usb目录下编写 usb-add.shusb-remove.sh 脚本文件,并修改脚本文件的权限为 777。 (脚本名称自定义)

usb-add.sh

#!/bin/sh
/bin/mount -t vfat /dev/$1 /mnt
sync

usb-remove.sh

#!/bin/sh
sync
/bin/umount -l /mnt

(2)在/etc/udev/rules.d/ 目录下创建 01-usb.rules 文件并写入一下内容。(在该目录下所有的规则文件必须以 “.rules” 为后缀名)

KERNEL=="sd[a-z][0-9]", SUBSYSTEM=="block", ACTION=="add", RUN+="/etc/udev/rules.d/usb/usb-add.sh %k" 
SUBSYSTEM=="block", ACTION=="remove", RUN+="/etc/udev/rules.d/usb/usb-remove.sh" 
/*
KERNEL=="sd[a-z][0-9]" : 匹配所有以 "sd" 开头,并且后面跟着一个小写字母和一个数字的设备节点
SUBSYSTEM=="block" : 仅在 SUBSYSTEM 是 "block"(块设备)的情况下才应用规则
ACTION=="add"  :执行的动作。
RUN+= :设备匹配时,执行指定的脚本 "/etc/udev/rules.d/usb/usb-add.sh""%k" 作为参数传递,代表设备名称(例如 sda1)
*/

  当新增一个 usb 设备,执行/etc/udev/rules.d/usb-add.sh脚本文件,并传入参数 sd[a-z][0-9]。当移除一个 usb 设备,执行/etc/udev/rules.d/usb-remove.sh 脚本文件。

3. 测试

   将U盘进行插入,使用df命令查看是否有U盘自动挂载。再将U盘拔出,使用命令df查看U盘是否退出。

实践二:使用udev自动挂载TF卡

  1. 问题一:我们在不先编写本节代码时,给开发板插上TF卡,会发下开发板可以自动识别 TF 卡,这是为什么呢?
      答:这是因为在开发板其他目录下也有很多规则文件,例如在/lib/udev/rules.d目录下就拥有tf的规则文件,即使我们不写,系统也会去该目录下自动调用,从而完成TF卡的自动挂载。
    在这里插入图片描述
  2. 问题二:那么如果我们自己写规则文件的话,应该如何写呢?
      答:其实和U盘的挂载是一样的,唯一的区别就是规则文件中的命名不同,TF卡的命名规则固定为:mmcblk[0-9]p[0-9]。其他步骤完全一致!
    ●步骤如下:
      ①我们可以在/etc/udev/rules.d/ 目录下创建 tf 目录,然后在该目录中添加两个脚本文件。文件命名自定义,最好可以是可以一眼就知道是什么作用的命名方式,脚本内容和U盘的脚本内容完全一致。这里我们使用tf-add.shtf-remove.sh作为命名来演示。
       ②在/etc/udev/rules.d/ 目录下创建02-tf.rules规则文件。内容如下:
KERNEL=="mmcblk[0-9]p[0-9]", SUBSYSTEM=="block", ACTION=="add", RUN+="/etc/udev/rules.d/tf/tf-add.sh %k" 
SUBSYSTEM=="block", ACTION=="remove", RUN+="/etc/udev/rules.d/tf/tf-remove.sh" 
  1. 问题三:既然其他目录文件中有sd/tf卡的规则文件,然后我们自己又写了一个tf卡的规则文件,那么系统到底是执行哪个规则文件呢?
      答:/lib/udev/rules.d目录下的规则文件的优先级要低于/etc/udev/rules.d目录下的规则文件。如果两个目录中的规则文件针对相同的设备做出不同的操作,那么 /etc/udev/rules.d/中的规则会优先应用。
      注意:在同一个目录中,文件名的数字前缀会决定规则的应用顺序。数字越小,优先级越高。例如,10-custom.rules 会在 20-default.rules 之前应用。

实践三:使用mdev自动挂载U盘

在嵌入式设备中,我们常用mdev。

1. 配置系统支持mdev

方法一:
  要配置 Buildroot 以支持 mdev,进入Buildroot 目录下,通过 menuconfig 图形化配置界面设置 :System configuration —dev management 为 Dynamic using devtmpfs + mdev。即使用mdev去管理。配置完成后,编译镜像文件烧录到开发版。

方法二:
  使用命令:echo /sbin/mdev > /proc/sys/kernel/hotplug将 /sbin/mdev 设置为内核的热插拔(hotplug)管理程序。当内核检测到一个设备的插入或移除时,会调用这个程序来处理相关的设备事件。只不过每次开机都需要重新配置,或者将该命令写入开机脚本。

2. 配置mdev规则

/etc/mdev.conf 文件末尾中添加 U 盘热插拔的事件响应规则,具体配置如下:

# U盘插入事件处理
sd[a-z][0-9] 0:0 666 @/etc/mdev/usb_insert.sh# 0:0 666: 设置设备的所有者为 root:root (0:0),并将设备权限设置为 666 (所有用户读写权限)。# U盘移除事件处理
sd[a-z] 0:0 666 $/etc/mdev/usb_remove.sh

  在 mdev中,规则文件中的每一行都是由4个字段组成的,分别是:<设备节点正则表达式> <设备的主设备号:次设备号> <设备的权限> <设备插入或移除时需要执行的命令>
  其中,第四个字段表示设备插入或移除时需要执行的命令,这个字段可以使用@符号或$符号开头来表示不同的含义。
当命令字段以@符号开头时,表示该命令是一个shell命令,需要在shell 中执行。当命令字段以$符号开头时,表示该命令是一个系统命令或可执行文件,可以直接在系统中执行。

3. 创建脚本文件

  在/etc/mdev目录下创建usb_insert.shusb_remove.sh脚本文件。脚本内容如下,并将脚本文件的权限改为0777。

usb_insert.sh

#!/bin/shif [ -d /sys/block/$MDEV ]; then     #$MDEV 是 udev/mdev 自动设置的环境变量,通常表示设备名mount /dev/$MDEV /mnt    #当设备被检测到(通过 /sys/block/$MDEV 判断),它会自动将该设备挂载到 /mnt 目录,并同步数据到磁盘。sync
fi

usb_remove.sh

#!/bin/sh
sync
umount -l /mnt

4. 测试

  将U盘进行插入,使用df命令查看是否有U盘自动挂载。再将U盘拔出,使用命令df查看U盘是否退出。

实践四:使用mdev自动挂载TF卡

  这里的使用步骤和实践三完全一致,脚本内容也完全一致。唯一的不同就是命令规则不同,TF卡的命名规则为mmcblk[0-9]p[0-9]

唯一不同:在 /etc/mdev.conf 文件末尾中添加TF卡热插拔的事件响应规则,具体配置如下:

# U盘插入事件处理
mmcblk[0-9]p[0-9] 0:0 666 @/etc/mdev/tf_insert.sh# 0:0 666: 设置设备的所有者为 root:root (0:0),并将设备权限设置为 666 (所有用户读写权限)。# U盘移除事件处理
mmcblk[0-9] 0:0 666 $/etc/mdev/tf_remove.sh

实践五:使用usbmount提高工作效率

  usbmount 是一个用于自动挂载 USB 存储设备的小工具,在检测到 USB 设备插入时,usbmount 会自动将其挂载到一个预定义的目录中,例如 /media/usb0、/media/usb1 等。它通常在没有完整的桌面环境(如 GNOME 或 KDE)的嵌入式系统或轻量级系统上使用。
  在 Debian 或 Ubuntu 系统上,你可以使用以下命令安装 usbmount:sudo apt-get install usbmount,插入 USB 设备后,系统会自动将其挂载到 /media/usbX 中(X 为数字)。

★补充问题:无法识别U盘

  如果识别不到U盘,则需要看U盘格式是否为FAT32格式,若不是,则格式化为FAT32的文件格式。且配置完文件刷新文件或重新开机使其生效。

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

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

相关文章

Dubbo源码深度解析(四)

接上篇博客《Dubbo源码深度解析(三)》&#xff0c;上篇博文&#xff0c;主要讲的是DubboBootstrap#start()方法中调用到的其他方法&#xff0c;以及讲到ServiceConfig#export()方法的调用链路。其中讲到最核心的方法为ServiceConfig#doExportUrlsFor1Protocol()&#xff0c;还没…

CentOS7 配置 nginx 和 php 方案

配置方案 一、安装软件二、编写配置文件&#xff0c;连接PHP三、引用文件四、测试 鉴于网上教程错综复杂&#xff0c;写下一这篇文章 本教程只需要三步即可 一、安装软件 yum install -y nginx php php-fpm二、编写配置文件&#xff0c;连接PHP 一般情况下在安装完 nginx 后…

python-质因数分解(赛氪OJ)

[题目描述] 已知正整数 n 是两个不同的质数的乘积&#xff0c;试求出两者中较大的那个质数。输入格式&#xff1a; 输入一个正整数 n。输出格式&#xff1a; 输出一个正整数 p&#xff0c;即较大的那个质数。样例 #1样例输入 #1 21样例输出 #1 7提示&#xff1a; 1≤n≤2109 来…

无字母数字的绕过方法

php代码 <?phpif(isset($_GET[code])){$code $_GET[code];if(strlen($code)>35){die("Long.");}if(preg_match("/[A-Za-z0-9_$]/",$code)){die("NO.");}eval($code);}else{highlight_file(__FILE__);} 题目的限制&#xff1a; webshell…

书籍分享:【矩阵力量】豆瓣评分高达9.6,看完感叹《矩阵论》又白学了

书籍分享&#xff1a;【矩阵力量】豆瓣评分高达9.6&#xff0c;看完感叹《矩阵论》又白学了 《矩阵力量》简要介绍书籍下载链接 《矩阵力量》简要介绍 《矩阵力量》是姜伟生精心编写的线性代数的深度理解之作&#xff0c;作者将抽象的线性代数概念用通俗易懂的语言和大量生动形…

Windows下,C# 通过FastDDS高效通信

目录 1、安装FastDDS 库2、使用IDL定义自己的数据格式3、生成DLL3.1 托管 &#xff08;Managed&#xff09;模式3.2 非托管 &#xff08;Unmanaged&#xff09;模式 -- 可用于Unity 代码示例 eprosima Fast DDS is a C implementation of the DDS (Data Distribution Service) …

vscode用快捷键一键生成vue模板

项目中有些代码模块是固定的&#xff0c;如下面的代码所示&#xff0c;为了不重复写这些相同的代码&#xff0c;我们可以使用快键键一键生成模板。 流程&#xff1a; 中文&#xff1a;首选项-> 用户代码片段 -> 输入框中输入vue,找到vue.json文件&#xff08;没有vue.j…

Vue-07.生命周期

生命周期&#xff1a; 生命周期&#xff1a;指一个对象从创建到销毁的全过程 生命周期的八个阶段&#xff1a;每触发一个阶段&#xff0c;就会自动执行一个生命周期方法&#xff08;钩子方法&#xff09; 状态 阶段周期 beforeCreated 创…

内部排序(插入、交换、选择)

一、排序的部分基本概念 1. 算法的稳定性 若待排序表中有两个元素 Ri 和 Rj &#xff0c;其对应的关键字相同即 keyi keyj&#xff0c;且在排序前 Ri 在 Rj 的前面&#xff0c;若使用某一排序算法排序后&#xff0c;Ri 仍然在 Rj 的前面&#xff0c;则称这个排序算法是稳定的…

【MySQL】详解数据库约束、聚合查询和联合查询

数据库约束 约束类型 数据库的约束类型主要包括以下几种&#xff1a; 主键约束&#xff08;Primary Key Constraint&#xff09;&#xff1a;确保表中的每一行都有唯一的标识&#xff0c;且不能为NULL。 外键约束&#xff08;Foreign Key Constraint&#xff09;&#xff1a…

5.ADC(模拟信号转数字信号)

理论 3个ADC控制器 转换&#xff1a;单次转换模式、 连续转换模式 转换时间 采样时间 12.5周期 当ADCCLK(时钟) 14MHz&#xff0c;采样时间为1.5周期&#xff0c;TcoNv(转换时间) 1.5 12.5 14 周期 1us 采样精度&#xff1a;12位/16位(212 4096) 实际电压值 (通道采…

Java面试题--JVM大厂篇之破解 JVM 性能瓶颈:实战优化策略大全

目录 引言: 正文: 1. 常见的JVM性能问题 频繁的GC导致应用暂停 内存泄漏导致的内存不足 线程争用导致的CPU利用率过高 类加载问题导致的启动时间过长 2. 优化策略大全 2.1 代码层面的优化 2.1.1 避免不必要的对象创建 2.1.2 优化数据结构的选择 2.1.3 使用并发工具…

Python爬虫:下载4K壁纸

&#x1f381;&#x1f381;创作不易&#xff0c;关注作者不迷路&#x1f380;&#x1f380; 目录 &#x1f338;完整代码 &#x1f338;分析 &#x1f381;基本思路 &#x1f381;需要的库 &#x1f381;提取图片的链接和标题 &#x1f453;寻找Cookie和User-Agent &…

突破•指针六

听说这是目录哦 数组和指针笔试题解析&#x1fae7;一维数组1&#x1f355;&#x1f355;&#x1f355;&#x1f355;&#x1f355;&#x1f355;&#x1f355; 字符数组1&#x1f354;&#x1f354;&#x1f354;&#x1f354;&#x1f354;&#x1f354;&#x1f354;2&#…

PCL 采样一致性模型介绍

采样一致性可以简单高效的检测出一些具有数学表达式的目标模型。PCL中的sample consensus模块中不仅包含各种的采样一致性估计方法,也包含一些已经编写好的数学模型,下面主要介绍一下PCL中的采样一致性模型。 1. 二维圆模型 pcl::SampleConsensusModelCircle2D< PointT …

AI学习记录 - 自注意力机制的计算流程图

画图不易&#xff0c;如果你从这个图当中得到灵感&#xff0c;大佬赏个赞 过段时间解释一下&#xff0c;为啥这样子计算&#xff0c;研究这个自注意力花了不少时间&#xff0c;网上很多讲概念&#xff0c;但是没有具体的流程图和计算方式总结…

Win11表情符号输入详细教程,一学就会!

在Win11电脑操作中&#xff0c;用户可以根据自己的需求&#xff0c;点击输入想要的表情符合。但许多新手用户不知道怎么操作才能输入&#xff1f;这时候用户按下快捷键&#xff0c;快速打开表情符号选择界面&#xff0c;然后选择需要的表情符号点击输入即可。以下系统之家小编给…

Can GPT-3 Perform Statutory Reasoning?

文章目录 题目摘要相关工作SARAGPT-3 对美国法典的了解GPT-3 在对合成法规进行简单推理时遇到困难结论 题目 GPT-3 可以进行法定推理吗&#xff1f; 论文地址&#xff1a;https://arxiv.org/abs/2302.06100 摘要 法定推理是用事实和法规进行推理的任务&#xff0c;法规是立法机…

Linux嵌入式学习——C++学习(2)

一、标识符的作用域和可见性 &#xff08;一&#xff09;作用域 1、全局作用域 在函数外部声明的变量和函数具有全局作用域。这些变量和函数在程序的任何地方都可以被访问。 2.局部作用域 在函数内部、循环体内部或条件语句内部声明的变量具有局部作用域。这些变量只能在其…

X射线物质质量衰减系数的查询计算方法

最近进行硕士毕业课题&#xff0c;需要各种各样物质的质量衰减系数&#xff08;线性衰减系数&#xff09;&#xff0c;包括高原子序数的金属物质还有一些复杂的化合物或者混合物&#xff0c;之前知道美国的XCOM &#xff1a;XCOM: Photon Cross Sections Database这个数据库可以…