Linux下的uevent

本文解析了Linux中uevent节点的实现机制,通过深入研究device_add函数和 uevent操作,展示了如何在设备创建时自定义信息发送,以及power_supply_changed_work和kobject_uevent如何通过netlink广播传递状态变化。同时,探讨了sysfs中的kset和ktype示例,包括自定义属性和sysfs操作,以及应用层如何监听这些uevent消息。

查找linux的uevent节点(find /sys -name uevent),大概有1000多个,那这些节点是怎么实现的呢。

 

 drivers/base/core.c

有如下代码,每创建一个device,都会创建一个event节点

static ssize_t uevent_show(struct device *dev, struct device_attribute *attr,char *buf)
{
	...
	retval = kset->uevent_ops->uevent(kset, &dev->kobj, env);
	...	
}

static ssize_t uevent_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
{
	
}
static DEVICE_ATTR_RW(uevent);

int device_add(struct device *dev)
{
	...
	device_create_file(dev, &dev_attr_uevent);
	...
}

来个demo看下

#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

static struct class *demo_class;
static struct cdev demo_cdev;
dev_t demo_number = 0;
int demo_open (struct inode *inode, struct file *filp)
{
    return 0;
}

static  struct file_operations demo_nod_fops =
{
    .owner = THIS_MODULE,
    .open  = demo_open,
};

int demo_uevent(struct device *dev, struct kobj_uevent_env *env)
{
	int ret = 0;
	char *p="aaa";
	char *q="bbb";

	ret = add_uevent_var(env, "demo_NAME=%s",p);
	ret = add_uevent_var(env, "demo_NAME=%s",q);

	return ret;
}

static int __init example_init(void)
{
    	int ret;

	demo_number=MKDEV(0,0);
	ret=alloc_chrdev_region(&demo_number,0,1,"6");
	if(ret<0)
	{
		printk("alloc chardev region err\n");
	 	return ret;
	}
	cdev_init(&demo_cdev,&demo_nod_fops);
	demo_cdev.owner=THIS_MODULE;
	ret=cdev_add(&demo_cdev,demo_number,1);
	if(ret<0)
	{
		printk("add demo cdev err!\n");
		return ret;
	}
	demo_class=class_create(THIS_MODULE,"demo");
	if(IS_ERR(demo_class))
	{
		printk("create demo class err!\n");
		return -1;
	}
	device_create(demo_class,NULL,demo_number,NULL,"66"); 
	demo_class->dev_uevent = demo_uevent;
	return 0;
}

static void __exit example_exit(void)
{
	cdev_del(&demo_cdev);
	device_destroy(demo_class,demo_number);
	unregister_chrdev_region(demo_number, 1);
   	class_destroy(demo_class);
}

module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Greg 
<think> 首先,用户的问题是关于在Linux系统中利用uevent检测SD卡热插拔的方法。用户提供了几个引用,我需要参考这些引用来构建答案。 回顾用户提供的引用: - 引用[1]:描述了udev/mdev工作机制,依赖内核的uevent来处理热插拔事件。 - 引用[2]:解释了uevent_helper机制,如何调用mdev来处理事件,提到/sbin/mdev和/etc/mdev.conf。 - 引用[3]:讨论了SD卡检测的具体过程,包括mmc_rescan和mmc_attach_sd。 - 引用[4]:提到需要一个线程来监控设备状态,使用netlink等方式感知设备上线。 用户的问题是:“我想查找在Linux系统中利用uevent检测SD卡热插拔的方法”。所以,我需要解释如何利用uevent机制来检测SD卡的插入和移除。 关键点: - uevent是内核发送的事件,当设备状态改变时(如插入或移除)。 - 用户空间程序(如udev或mdev)监听这些事件并处理。 - 对于SD卡,内核模块如mmc_block会处理硬件扫描,并触发uevent。 从引用中提取相关部分: - SD卡热插拔涉及mmc_rescan(引用[3]),当检测到SD卡插入时,会触发事件。 - uevent通过netlink套接字或uevent_helper发送给用户空间(引用[1]和[2])。 - 用户空间可以使用线程监控netlink套接字来接收这些事件(引用[4])。 我的回答需要: - 遵循系统级指令:使用$...$和$$...$$格式处理数学表达式(但这里可能不涉及数学)。 - 使用中文回答。 - 结构清晰,逐步解决问题。 - 在引用的段落末尾添加引用标识,如[^1]。 - 生成相关问题。 组织结构: 1. **介绍uevent机制**:解释uevent是什么,如何工作。 - 引用[1]:内核发送热插拔事件到用户空间。 - 引用[2]:uevent_helper机制调用用户空间程序。 2. **SD卡热插拔的具体过程**: - 引用[3]:SD卡插入时,内核扫描总线,调用mmc_attach_sd,触发事件。 3. **如何在用户空间检测uevent**: - 使用netlink套接字监控事件。 - 或依赖uevent_helper如mdev。 - 引用[4]:提到线程监控设备状态。 4. **实践方法**: - 建议使用udev规则或编写程序监听netlink。 - 对于嵌入式系统,使用mdev。 5. **总结**。 添加引用标识: - 在提到相关内容时自然添加。 生成相关问题: - 在回答结束时添加“相关问题”部分。 完整回答结构: - 引言:简要回答用户问题。 - 详细解释:逐步描述机制和方法。 - 结论。 - 相关问题。 确保语言中文。 </think> ### Linux系统中利用uevent检测SD卡热插拔的方法 在Linux系统中,检测SD卡的热插拔(如插入或移除)主要依赖于内核的uevent机制。这是一种高效的事件通知系统,核心是通过用户空间程序监听内核发送的设备状态变化事件。以下我将逐步解释该机制的运作原理,并提供具体的检测方法。整个过程涉及底层内核触发、事件传输和用户空间处理三个层次。我会基于您提供的引用内容进行说明,确保回答真实可靠。 #### 1. **uevent机制概述** - 当SD卡插入或移除时,内核的MMC/SD驱动(如`mmc_block`驱动)会检测硬件变化。例如,插入SD卡后,内核通过`mmc_rescan`扫描总线[^3]。如果检测成功(如上电后返回0),会调用`mmc_attach_sd`函数绑定总线与SD卡,并触发一个uevent事件[^3]。 - uevent是一种热插拔事件,内核通过netlink套接字或uevent_helper机制将其发送到用户空间[^1]。事件包含详细的设备信息(如设备类型、动作类型如"add"或"remove"),帮助用户空间程序识别SD卡状态变化[^1]。 - 用户空间程序(如udev或mdev)监听这些事件,并根据规则文件(如`/etc/mdev.conf`)在`/dev`目录下创建设备文件或执行自定义操作[^2]。 #### 2. **检测SD卡热插拔的核心方法** 要利用uevent检测SD卡热插拔,用户空间可以有多种实现方式。核心是监控uevent通道,以下是两种常用方法: - **方法1: 使用netlink套接字直接监听(推荐用于自定义程序)** - netlink是内核与用户空间通信的双向套接字机制。内核通过它发送uevent事件。 - 步骤: 1. 创建一个netlink套接字,绑定到`NETLINK_KOBJECT_UEVENT`类型。 2. 接收事件数据包:每个事件包含一行ASCII字符串,格式如`ACTION=add`或`ACTION=remove`,以及设备路径(如`SUBSYSTEM=mmc`)。 3. 筛选SD卡事件:检查事件中的`SUBSYSTEM`是否为"mmc"或"block",以及`ACTION`是否为"add"(插入)或"remove"(移除)。 - 优点:实时性强,无需依赖外部工具。 - 示例代码(C语言简化版): ```c #include <sys/socket.h> #include <linux/netlink.h> #include <unistd.h> #include <stdio.h> #include <string.h> int main() { struct sockaddr_nl addr; int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); addr.nl_groups = 1; // 接收所有事件 bind(sock, (struct sockaddr*)&addr, sizeof(addr)); char buf[1024]; while (1) { recv(sock, buf, sizeof(buf), 0); if (strstr(buf, "SUBSYSTEM=mmc") && strstr(buf, "ACTION=add")) { printf("SD card inserted\n"); } else if (strstr(buf, "SUBSYSTEM=mmc") && strstr(buf, "ACTION=remove")) { printf("SD card removed\n"); } } close(sock); return 0; } ``` - 注意事项:此方法需要root权限运行,适用于嵌入式系统或自定义监控应用。内核会持续发送事件,因此程序需作为守护进程运行[^1][^4]。 - **方法2: 依赖uevent_helper机制(如mdev或udev)** - uevent_helper是内核的辅助机制:内核通过读取`/proc/sys/kernel/hotplug`文件调用用户空间程序(默认为`/sbin/mdev`)[^2]。 - 步骤: 1. 配置`/sbin/mdev`:在嵌入式系统(如BusyBox环境),mdev作为轻量级工具处理事件。它读取环境变量(如`ACTION`和`DEVPATH`)来响应SD卡事件。 2. 规则文件:编辑`/etc/mdev.conf`,添加规则以检测SD卡。例如: ``` mmcblk[0-9]p[0-9] 0:0 660 * /usr/local/bin/sd_event.sh $ACTION ``` 这里,`sd_event.sh`是一个脚本,根据`$ACTION`变量(add或remove)执行操作。 3. 示例脚本`sd_event.sh`: ```bash #!/bin/sh if [ "$1" = "add" ]; then echo "SD card inserted" > /dev/kmsg mount /dev/mmcblk0p1 /mnt/sd # 挂载SD卡 elif [ "$1" = "remove" ]; then echo "SD card removed" > /dev/kmsg umount /mnt/sd # 卸载SD卡 fi ``` 4. 重启服务:确保mdev守护进程运行,或使用`echo /sbin/mdev > /proc/sys/kernel/hotplug`激活helper。 - 优点:简单易用,无需编码,适合标准桌面或嵌入式系统。udev是更强大的替代品,在桌面Linux中常用,原理类似[^2][^4]。 无论采用哪种方法,内核SD卡驱动的扫描是关键:插入SD卡时,`mmc_rescan`函数通过上电检测触发事件;如果失败(返回非0值),则跳过绑定[^3]。用户空间线程通过netlink等方式感知状态变化后,可调用挂载(mount)或卸载(umount)函数处理SD卡[^4]。 #### 3. **实践建议和注意事项** - **测试环境**:在开发板上使用Linux内核(版本4.x或以上),确保MMC驱动已启用(如`CONFIG_MMC=y`)。 - **调试技巧**: - 查看内核日志:`dmesg | grep mmc`,观察SD卡插入/移除的uevent触发。 - 监控事件:运行`udevadm monitor --kernel`(用于udev系统)实时输出事件信息。 - **常见问题**: - 事件丢失:确保用户空间程序有权限处理事件(如使用sudo)。 - 性能影响:netlink监听可能占用资源,建议在嵌入式系统中优化线程逻辑[^4]。 - **安全提示**:避免直接操作/dev文件,优先使用标准工具减少错误。 总之,利用uevent检测SD卡热插拔的核心是监听内核事件:通过netlink套接字或uevent_helper机制,用户空间程序可以实时响应SD卡状态变化。对于初学者,优先使用mdev/udev规则;开发者则推荐netlink实现自定义控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值