uevent 驱动_笔记之-uevent机制

本文介绍了Linux内核中的uevent机制,这是一种内核与用户空间通信的方式,主要用于设备驱动模型中的热插拔事件通知。文章阐述了uevent事件类型、触发点以及kobject_uevent()函数的角色。此外,还提及了用户空间的响应程序,如udev和mdev,它们分别处理uevent消息,实现模块自动加载和设备节点的管理。
摘要由CSDN通过智能技术生成

uevent机制

==========

简单的梳理一下框架,清晰自己的思路。

uevent, user space event. 内核与用户空间的一种通信机制,基于netlink机制,主要用于设备驱动模型,例如热插拔。

也就是说在某个时刻触发某个事件并通知给用户空间。

事件:

enum kobject_action {

KOBJ_ADD,

KOBJ_REMOVE,

KOBJ_CHANGE,

KOBJ_MOVE,

KOBJ_ONLINE,

KOBJ_OFFLINE,

KOBJ_MAX

};

触发点:

kobject_uevent()

如device_add()中的触发:

kobject_uevent(&dev->kobj, KOBJ_ADD);

在kobject_uevent()中会先执行集成在kset中的kset_uevent_ops, 之后再通过netlink传递给用户空间。

struct kset_uevent_ops {

int (* const filter)(struct kset *kset, struct kobject *kobj);

const char *(* const name)(struct kset *kset, struct kobject *kobj);

int (* const uevent)(struct kset *kset, struct kobject *kobj,

struct kobj_uevent_env *env);

};

filter用于判断uevent是否要发出去,name用于得到subsystem的名字,uevent用于填充env变量。

我们可以看到kobject_uevent()中的代码:

if (uevent_ops && uevent_ops->filter)

if (!uevent_ops->filter(kset, kobj)) {

......

}

/* originating subsystem */

if (uevent_ops && uevent_ops->name) {

subsystem = uevent_ops->name(kset, kobj);

......

}

......

/* let the kset specific function add its stuff */

if (uevent_ops && uevent_ops->uevent) {

retval = uevent_ops->uevent(kset, kobj, env);

......

}

最后调用netlink接口发送出去,用户层会根据所设置的env变量来启动相应操作,至于env变量设置内容我不作深究。

以上就是内核中uevent机制的简述。

-----------------------------------------------------------------------------------------------------

下面就简单提一下应用层的一些机制(注:以下我并没有详细去分析,而是抄录某位大虾的原创):

uevent的用户空间程序有两个,一个是udev,一个是mdev。

udev通过netlink监听uevent消息,它能完成两个功能:

1.自动加载模块

2.根据uevent消息在dev目录下添加、删除设备节点。

另一个是mdev,mdev在busybox的代码包中能找到,它通过上节提到的uevent_helper函数被调用。

udev的模块自动加载过程:

etc目录下有一个uevent规则文件/etc/udev/rules.d/50-udev.rules

udev程序收到uevent消息后,在这个规则文件里匹配,如果匹配成功,则执行这个匹配定义的shell命令。

例如,规则文件里有这么一行:

ACTION=="add", SUBSYSTEM=="?*", ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}"

所以,当收到uevent的add事件后,shell能自动加载在MODALIAS中定义的模块。

mdev的模块自动加载过程与之类似,它的配置文件在/etc/mdev.conf中。例如:

$MODALIAS=.* 0:0 660 @modprobe "$MODALIAS"

这条规则指的是:当收到的环境变量中含有MODALIAS,那么加载MODALIAS代表的模块。

mdev的详细说明在busybox的docs/mdev.txt中。

以下是一个简单的 g_uevent_device_new 函数实现的示例代码: ```c #include <glib.h> #include <gio/gio.h> #define UEVENT_BUFFER_SIZE 2048 typedef struct _GUeventDevice GUeventDevice; struct _GUeventDevice { GObject parent_instance; gchar *syspath; gchar *subsystem; gchar *devtype; gchar **filters; GRegex **regex_filters; guint n_filters; GIOChannel *io_channel; guint io_watch_id; gchar io_buffer[UEVENT_BUFFER_SIZE]; }; G_DEFINE_TYPE(GUeventDevice, g_uevent_device, G_TYPE_OBJECT); static gboolean g_uevent_device_io_watch_callback(GIOChannel *channel, GIOCondition condition, gpointer user_data) { GUeventDevice *self = G_UEVENT_DEVICE(user_data); if (condition & G_IO_IN) { gsize bytes_read = 0; gsize io_buffer_size = sizeof(self->io_buffer) - 1; if (g_io_channel_read_chars(channel, self->io_buffer, io_buffer_size, &bytes_read, NULL) != G_IO_ERROR_NONE) { g_warning("Failed to read from uevent device"); return G_SOURCE_REMOVE; } /* Null-terminate the read buffer */ self->io_buffer[bytes_read] = '\0'; /* Process the uevent messages in the buffer */ gchar *line_start = self->io_buffer; gchar *line_end = NULL; while ((line_end = strchr(line_start, '\n'))) { *line_end = '\0'; gchar **kv_pairs = g_strsplit(line_start, "=", -1); /* Process the key-value pairs in the uevent message */ for (guint i = 0; kv_pairs[i]; i += 2) { gchar *key = kv_pairs[i]; gchar *value = kv_pairs[i + 1]; /* Filter out the key-value pairs that don't match any of the regex filters */ gboolean filter_match = FALSE; for (guint j = 0; j < self->n_filters; j++) { if (g_regex_match(self->regex_filters[j], key, 0, NULL)) { filter_match = TRUE; break; } } if (!filter_match) { continue; } g_message("Received uevent message: %s=%s", key, value); } g_strfreev(kv_pairs); line_start = line_end + 1; } } return G_SOURCE_CONTINUE; } static void g_uevent_device_init(GUeventDevice *self) { self->syspath = NULL; self->subsystem = NULL; self->devtype = NULL; self->filters = NULL; self->regex_filters = NULL; self->n_filters = 0; self->io_channel = NULL; self->io_watch_id = 0; } static void g_uevent_device_class_init(GUeventDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = (GObjectFinalizeFunc) g_free; } GUeventDevice * g_uevent_device_new(const gchar *syspath, const gchar *subsystem, const gchar *devtype, gchar **filters) { GUeventDevice *self = g_object_new(G_TYPE_UEVENT_DEVICE, NULL); self->syspath = g_strdup(syspath); self->subsystem = g_strdup(subsystem); self->devtype = g_strdup(devtype); self->filters = g_strdupv(filters); /* Compile the regex filters */ for (guint i = 0; self->filters[i]; i++) { GRegex *regex_filter = g_regex_new(self->filters[i], 0, 0, NULL); if (!regex_filter) { g_warning("Failed to compile regex filter: %s", self->filters[i]); continue; } self->regex_filters = g_realloc(self->regex_filters, (self->n_filters + 1) * sizeof(GRegex *)); self->regex_filters[self->n_filters++] = regex_filter; } /* Open the uevent device file */ gchar *uevent_path = g_build_filename(self->syspath, "uevent", NULL); GError *error = NULL; self->io_channel = g_io_channel_new_file(uevent_path, "r", &error); if (!self->io_channel) { g_warning("Failed to open uevent device: %s", error->message); g_error_free(error); g_object_unref(self); return NULL; } /* Add a watch for the IO channel */ self->io_watch_id = g_io_add_watch(self->io_channel, G_IO_IN, g_uevent_device_io_watch_callback, self); g_free(uevent_path); return self; } ``` 这个示例代码演示了如何使用 GLib 和 GIO 库来创建一个 GUeventDevice 对象,该对象可以通过监听 Linux 内核的 uevent 消息来获取设备事件。GUeventDevice 对象可以指定一些过滤器,以只接收特定的 uevent 消息。在对象的销毁期间,将释放所有内存和资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值