Linux内核编程(十二)热插拔
本文目录
- 一、知识点
- 1. 热插拔概念
- 2. 热插拔机制
- 3. Netlink机制
- 二、内核发送uevent事件到用户空间
- 1. kobject发送uevent事件
- 2. udevadm命令查看
- ★示例代码:
- ★优化:完善kset_uevent_ops(热插拔事件结构体)
- 三、用户空间使用Netlink接收uevent事件
- 1. 创建socket
- 2. 绑定socket
- 3. 接收uevent事件信息
- ★示例代码
一、知识点
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事件信息,结果如下所示。
相关文章:

Linux内核编程(十二)热插拔
本文目录 一、知识点1. 热插拔概念2. 热插拔机制3. Netlink机制 二、内核发送uevent事件到用户空间1. kobject发送uevent事件2. udevadm命令查看★示例代码:★优化:完善kset_uevent_ops(热插拔事件结构体) 三、用户空间使用Netlin…...

七夕警示:探索社工库与网络搜索下的个人隐私泄露与保护策略
随着七夕节的脚步日益临近,空气中弥漫着浪漫与温馨的气息。这个充满爱意的节日,我们沉浸在与心爱之人共享甜蜜时光的同时,不应忽视网络安全和个人隐私保护的重要性。在数字化时代,个人信息泄露的风险无处不在,如何在享…...

Redis-哨兵监控(sentinel)
是什么 Docs 吹哨人巡查监控后台master主机是否故障,如果故障了根据投票数自动将某一个从库转换为新主库,继续对外服务 作用:无人值守运维 能干嘛 1.主从监控 监控主从redis的库是否运行正常 2.消息通知 哨兵可以将故障转移的结果发送给客户端 3.…...
RISC-V反汇编调试记录分享
RISC-V反汇编调试记录分享 本文记录一次使用反汇编进行调试分析。 最近在 rtthread 下适配 MilkV Duo 的硬件定时器驱动时遇到了一些问题,demo 运行时报以下错误: Unhandled Exception 2:Illegal Instruction scause:0x0x0000000000000002,stval:0x0x…...
python上下文管理器 with的使用
python上下文管理器 with是从Python一个语法糖,它是一种上下文管理协议,目的在于把我们之前常见一个开发 try,except 和finally 关键字和一些文件开关闭合资源分配释放等问题都简化。 总结起来使用python 提供的with主要的作用是: 实现自动…...

24/8/8算法笔记 决策树构建鸢尾花
决策树是一种由算法自动设计的模型。在机器学习中,构建决策树的过程通常遵循以下步骤: 特征选择:算法会评估每个特征,并选择一个特征作为节点分裂的依据。这个选择基于某种准则,如信息增益(ID3算法…...

数据库扩展新篇章:主流分库分表中间件全解析
摘要: 随着企业数据量的激增,传统的单体数据库架构已经无法满足日益增长的性能需求和数据管理复杂性。分库分表技术作为解决这一问题的有效手段,通过将数据水平或垂直地分散到多个数据库中,提高了系统的扩展性和处理能力。本文将详…...
python看图片猜价格游戏,frame 和PhotoImage的使用
import tkinter.messagebox import tkinter import randomwindow tkinter.Tk()window.geometry(800x400)window.title(猜数字游戏)good_price random.randint(10, 100) input_price random.randint(1, 100)def sumit():global good_priceif entry.get() "" or en…...
未来展望:等保测评在网络安全领域的持续创新与发展
在数字化浪潮席卷全球的今天,网络安全已成为维护国家安全、社会稳定和经济发展的关键基石。作为网络安全保障体系的核心组成部分,等级保护测评(简称“等保测评”)在应对日益复杂多变的网络威胁中发挥着不可替代的作用。展望未来&a…...
构建深度学习驱动的多目标检测系统:YOLO模型及应用
随着计算机视觉技术的飞速发展,多目标检测在各种实际应用中发挥着越来越重要的作用。本文将j简单介绍如何构建一个基于深度学习的多目标检测系统,包括数据准备、模型训练、UI界面开发和部署的完整流程。如有部署的想法,想要(UI界面…...
算法刷题笔记 染色法判定二分图(染色法例题 C++实现)
文章目录 题目描述二分图介绍和基本思路实现代码(C) 题目描述 给定一个n个点m条边的无向图,图中可能存在重边和自环。请你判断这个图是否是二分图。 输入格式 第一行包含两个整数n和m。接下来m行,每行包含两个整数u和v…...
在Ubuntu上安装OpenBLAS和Eigen
安装 openblas 直接使用 apt-get 命令即可安装: sudo apt-get install libopenblas-dev检查是否安装成功,可以用下面的示例代码 example.cpp: #include <stdio.h> #include <stdlib.h> #include "cblas.h"int main(…...
Vue前端面试基础(一)
Vue面试题目详解可以涵盖多个方面,从基础知识到高级特性,再到实际应用和性能优化等。以下是一些常见的Vue面试题目及其详解: 1. Vue双向绑定原理 详解: Vue的双向绑定原理是通过数据劫持结合发布者-订阅者模式实现的。Vue在内部…...

使用Gitlab实现monorepo多项目CICD
CI/CD是什么 CI/CD(Continuous Intergration/Continuous Delpoy),即持续集成/持续部署,或称为持续集成/持续交付,作为一套面向开发和运维团队的解决方案,CI/CD 主要解决集成新代码和向用户频繁交付应用的问…...
设计模式实战:银行账户管理系统的设计与实现
问题描述 设计一个银行账户管理系统,支持不同类型的账户(如储蓄账户、支票账户)进行存取款操作,并能够在账户余额发生变化时通知相关观察者(如用户、银行系统)。系统需要确保账户操作的灵活性和可扩展性。 设计分析 策略模式 策略模式定义了一系列算法,并将每个算法…...

⭕️【论文阅读】《Interactive Class-Agnostic Object Counting》
[2309.05277] Interactive Class-Agnostic Object Counting (arxiv.org) code: cvlab-stonybrook/ICACount: [ICCV23] Official Pytorch Implementation of Interactive Class-Agnostic Object Counting (github.com) 目录 Abstract Abstract 我们提出了一个新…...

高效的编程学习方法和技巧
编程小白如何成为大神?大学新生的最佳入门攻略 编程已成为当代大学生的必备技能,但面对众多编程语言和学习资源,新生们常常感到迷茫。如何选择适合自己的编程语言?如何制定有效的学习计划?如何避免常见的学习陷阱&…...
sublime text插件开发
手工开发了一些ST的py插件,记录过程中遇到的一些问题。 ST3/ST4 begin_edit问题 报错: begin_edit() missing 2 required positional arguments: edit_token and cmdST3时已经不能直接调view.begin_edit方法了,需要通过runCommandTextComm…...

【Linux网络】网络层协议:IP
本篇博客整理了 TCP/IP 分层模型中网络层的 IP 协议,旨在让读者更加深入理解网络协议栈的设计和网络编程。 目录 一、网络层 二、IP 报头 1)报头与有效载荷的分离 2)有效载荷的上交 3)源 IP 与目的 IP 4)生存时间…...
分布式接口文档聚合,Solon 是怎么做的?
1、分布式接口文档聚合,是什么? 如果你有 “22” 个不同的服务(比如微服务),每个服务都有自己的接口文档。每个服务的文档各自打开,估计你会觉得很麻烦的? 再如果,它们是用 openap…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...