探究Linux下的Uevent事件机制 (linux uevent事件)

概述

随着信息技术的高速发展,操作系统的功能越来越强大,越来越多的应用程序需要操作系统向它们发送事件通知。Linux操作系统为了快速响应外部设备的插拔事件,引入了Uevent事件机制。本文将从以下几个方面深入:Uevent定义、Uevent事件源、Uevent与udev、Uevent处理机制等。

Uevent定义

Uevent全称为Userspace Event,即用户空间事件。用户空间事件是Linux内核为用户空间提供给的一个机制,用于通知操作系统和相关程序发生了某些特定事件。这些事件通常管辖于管理UEVENT_HOTPLUG和UEVENT_COLDPLUG两个宏过程中来调度。

Uevent事件源

Uevent事件源指的是Uevent事件的来源。Linux中Uevent事件源主要来自两个方面:驱动程序和内核自身。

1. 驱动程序:当一个设备插入或者被拔出时,系统会自动将事件通知发给内核,内核通过驱动程序将事件通知送到用户空间(udev)。实际上,这个程序可以处理任何事件,不仅是设备插拔事件,同时也包括内核之间的消息事件等。

2. 内核自身:对于内核内部事件,如内存的初始分配、网络状态变化等Uevent可以及时通知用户空间的相关进程。

Uevent与udev

Uevent与udev是两个概念密不可分的东西,udev作为一个用户空间的设备管理程序,由驱动程序向内核捕获Uevent。当udev收到内核的Uevent事件时,它首先会侦听内核事件,然后在匹配设备节点和正则表达式方面执行是否需要更改udev规则,从而在设备节点中添加或删除属性。同时,udev会随时保持与内核同步,以确保设备变化的信息是实时的。

Uevent处理机制

Uevent的处理机制可以分为内核部分和用户空间部分两个环节。

1. 内核部分:内核创建uevent结构体,内部赋值、获取子系统、查询属性并赋值等操作。在完成所有的处理后,内核会调用uevent_netlink_send()函数将uevent结构体发送到用户空间。

2. 用户空间部分:当内核将uevent发送到用户空间之后,用户空间的udev进程会处理uevent事件通知,从而更新/dev目录下与该设备相关的信息等。同时,udev进程还发射事件通知给可以调用事件监听的用户空间应用程序。

本文介绍了Linux下的Uevent事件机制,包括Uevent定义、Uevent事件源、Uevent与udev、Uevent处理机制等相关内容。通过深入探究Uevent事件机制的原理和工作原理,我们可以更好地理解Linux操作系统的核心功能,进一步提升系统的应用性能。

相关问题拓展阅读:

在linux下用什么IO模型好些

接触和学习Linux已模配一年有余了. 对 Linux 的设备模型(bus+device+driver+sysfs+uevent)理解尚困运未深刻.所以,写份简单的代码旦尺指强化理解. 我自己写的 bus, 和在 bus 上注册 device 和 driver. 并让 device 和 driver 成功的match. 还未实现 uevent 时间…

如何手动创建一个设备节点,写出主要命令及参数

Linux下生成驱动设备节点文件的方法有3个:1、手动mknod;2、利用devfs;3、利用udev

在刚开始写Linux

设备驱动程序

的时候晌明帆,很多时候都是利用mknod命令手动创建设备宴雹节点,实际上

Linux内核

为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点。

在2.6.17以前,在/dev目录下生成设备文件很容易,

devfs_mk_bdev

devfs_mk_cdev

devfs_mk_symlink

devfs_mk_dir

devfs_remove

这几个是纯devfs的api,2.6.17以前可用,但后来devfs被sysfs+udev的形式取代,同时期sysfs

文件系统

可以用的api:

class_device_create_file,在2.6.26以后也不行了,现在,使用的是device_create ,从2.6.18开始可用

struct device *device_create(struct class *class, struct device *parent,

dev_t devt, const char *fmt, …)

从2.6.26起又多了一个参数drvdata: the data to be added to the device for callbacks

不会用可以给此参数赋NULL

struct device *device_create(struct class *class, struct device *parent,

dev_t devt, void *drvdata, const char *fmt, …)

下面着重讲解第三种方法udev

在驱动用加入对udev的支持主要做的就是:在驱动初始化的代码里调用class_create(…)为该设备创建一个class,再为每个设备调用device_create(…)( 在2.6较早的内核中用class_device_create)创建对应的设备。

内核中定义的struct class

结构体

,顾名思义,一个struct class结构体

型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

struct class和class_create(…) 以及device_create(…)都包含在在/include/linux/device.h中,使用的时候一定要包含这个

头槐唯文件

,否则

编译器

会报错。

struct class定义在头文件include/linux/device.h中

class_create(…)在/drivers/base/class.c中实现

device_create(…)函数在/drivers/base/core.c中实现

class_destroy(…),device_destroy(…)也在/drivers/base/core.c中实现调用过程类似如下:

static struct class *spidev_class;

/*—*/

static int __devinit spidev_probe(struct spi_device *spi)

{

….

dev =device_create(spidev_class, &spi->dev, spidev->devt,

spidev, “spidev%d.%d”,

spi->master->bus_num, spi->chip_select);

}

static int __devexit spidev_remove(struct spi_device *spi)

{

……

device_destroy(spidev_class, spidev->devt);

…..

return 0;

}

static struct spi_driver spidev_spi = {

.driver = {

.name =”spidev”,

.owner = THIS_MODULE,

},

.probe = spidev_probe,

.remove = __devexit_p(spidev_remove),

};

/*—*/

static int __init spidev_init(void)

{

….

spidev_class =class_create(THIS_MODULE, “spidev”);

if (IS_ERR(spidev_class)) {

unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);

return PTR_ERR(spidev_class);

}

….

}

module_init(spidev_init);

static void __exit spidev_exit(void)

{

……

class_destroy(spidev_class);

……

}

module_exit(spidev_exit);

MODULE_DESCRIPTION(“User mode SPI device interface”);

MODULE_LICENSE(“GPL”);

下面以一个简单字符设备驱动来展示如何使用这几个函数

#include

#include

#include

#include

#include

#include

int HELLO_MAJOR = 0;

int HELLO_MINOR = 0;

int NUMBER_OF_DEVICES = 2;

struct class *my_class;

//struct cdev cdev;

//dev_t devno;

struct hello_dev {

struct device *dev;

dev_t chrdev;

struct cdev cdev;

};

static struct hello_dev *my_hello_dev = NULL;

struct file_operations hello_fops = {

.owner = THIS_MODULE

};

static int __init hello_init (void)

{

int err = 0;

struct device *dev;

my_hello_dev = kzalloc(sizeof(struct hello_dev), GFP_KERNEL);

if (NULL == my_hello_dev) {

printk(“%s kzalloc failed!\n”,__func__);

return -ENOMEM;

}

devno = MKDEV(HELLO_MAJOR, HELLO_MINOR);

if (HELLO_MAJOR)

err= register_chrdev_region(my_hello_dev->chrdev, 2, “memdev”);

else

{

err = alloc_chrdev_region(&my_hello_dev->chrdev, 0, 2, “memdev”);

HELLO_MAJOR = MAJOR(devno);

}

if (err) {

printk(“%s alloc_chrdev_region failed!\n”,__func__);

goto alloc_chrdev_err;

}

printk(“MAJOR IS %d\n”,HELLO_MAJOR);

cdev_init(&(my_hello_dev->cdev), &hello_fops);

my_hello_dev->cdev.owner = THIS_MODULE;

err = cdev_add(&(my_hello_dev->cdev), my_hello_dev->chrdev, 1);

if (err) {

printk(“%s cdev_add failed!\n”,__func__);

goto cdev_add_err;

}

printk (KERN_INFO “Character driver Registered\n”);

my_class =class_create(THIS_MODULE,”hello_char_class”); //类名为hello_char_class

if(IS_ERR(my_class))

{

err = PTR_ERR(my_class);

printk(“%s class_create failed!\n”,__func__);

goto class_err;

}

dev = device_create(my_class,NULL,my_hello_dev->chrdev,NULL,”memdev%d”,0); //设备名为memdev

if (IS_ERR(dev)) {

err = PTR_ERR(dev);

gyro_err(“%s device_create failed!\n”,__func__);

goto device_err;

}

printk(“hello module initialization\n”);

return 0;

device_err:

device_destroy(my_class, my_hello_dev->chrdev);

class_err:

cdev_del(my_hello_dev->chrdev);

cdev_add_err:

unregister_chrdev_region(my_hello_dev->chrdev, 1);

alloc_chrdev_err:

kfree(my_hello_dev);

return err;

}

static void __exit hello_exit (void)

{

cdev_del (&(my_hello_dev->cdev));

unregister_chrdev_region (my_hello_dev->chrdev,1);

device_destroy(my_class, devno);//delete device node under /dev//必须先删除设备,再删除class类

class_destroy(my_class); //delete class created by us

printk (KERN_INFO “char driver cleaned up\n”);

}

module_init (hello_init);

module_exit (hello_exit);

MODULE_LICENSE (“GPL”);

这样,模块加载后,就能在/dev目录下找到memdev这个设备节点了。

例2:内核中的drivers/i2c/i2c-dev.c

在i2cdev_attach_adapter中调用device_create(i2c_dev_class, &adap->dev,

MKDEV(I2C_MAJOR, adap->nr), NULL,

“i2c-%d”, adap->nr);

这样在dev目录就产生i2c-0 或i2c-1节点

接下来就是udev应用,udev是

应用层

的东西,udev需要内核sysfs和tmpfs的支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提供存放空间

udev的源码可以在去相关网站下载,然后就是对其在运行环境下的移植,指定交叉编译环境,修改Makefile下的CROSS_COMPILE,如为mipsel-linux-,DESTDIR=,或直接make CROSS_COMPILE=mipsel-linux-,DESTDIR= 并install

把主要生成的udevd、udevstart拷贝rootfs下的/in/目录内,udev的

配置文件

udev.conf和rules.d下的rules文件拷贝到rootfs下的/etc/目录内

并在rootfs/etc/init.d/rcS中添加以下几行:

echo “Starting udevd…”

/in/udevd –daemon

/in/udevstart

(原rcS内容如下:

# mount filesystems

/bin/mount -t proc /proc /proc

/bin/mount -t sysfs sysfs /sys

/bin/mount -t tmpfs tmpfs /dev

# create necessary devices

/bin/mknod /dev/null c 1 3

/bin/mkdir /dev/pts

/bin/mount -t devpts devpts /dev/pts

/bin/mknod /dev/audio c 14 4

/bin/mknod /dev/ts c 10 16

这样当系统启动后,udevd和udevstart就会解析配置文件,并自动在/dev下创建设备节点文件

如何在linux ubuntu中使用android手机程序

这个恩一般我记得连接Android的操作系统你得给u添加一个驱动到/etc/udev/下面绝孝毁哦。ubuntu现在是基于uevent驱动的热插拔机制。

基于uevent驱动加载机制,需并备要再/etc/udev/rules.d/51-android.rules文件,内容如下SUBSYSTEM==”u”, SYSFS{idVendor}==”18d1″, MODE=”0666″同时慎毕chmod修改777权限。供u驱动使用

linux uevent事件的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux uevent事件,探究Linux下的Uevent事件机制,在linux下用什么IO模型好些,如何手动创建一个设备节点,写出主要命令及参数,如何在linux ubuntu中使用android手机程序的信息别忘了在本站进行查找喔。


数据运维技术 » 探究Linux下的Uevent事件机制 (linux uevent事件)