sysfs 是 Linux userspace 和 kernel 进行交互的一个媒介。通过 sysfs,userspace 可以主动去读写 kernel 的一些数据,同样的, kernel 也可以主动将一些“变化”告知给 userspace。也就是说,通过sysfs,userspace 和 kernel 的交互,本质上是双向的
userspace 通过 sysfs 访问 kernel 数据的方法,便是大名鼎鼎的 show() / store() 方法
uevent是通过netlink实现的,首先在内核中调用netlink_kernel_create函数创建一个socket套接字,当有事件发生时,通过kobject_uevent函数最终调用netlink_broadcast_fillted函数向用户发送数据;同时在用户空间有监听事件,则kernel的变化,用户空间即刻知晓
uevent初始化:
uevent_net_init()创建类型为NETLINK_KOBJECT_UEVENT的socket,并将其放入uevent_sock_list链表上。uevent_net_exit()则将其从uevent_socket_list中摘除,并且释放socket相关资源
对uevent_helper设置:
Linux下热插拔通知用户空间
对uevent_helper设置,可以对/proc/sys/kernel/hotplug写可执行文件路径即可。然后在内核触发uevent事件的之后调用相关可执行文件进行处理
或者还可以对/proc/kernel/uevent_helper写入可执行文件路径
usermode helper用于帮助在内核空间启动一个用户空间程序
通过内核发送uevent很简单,将数据代表环境变量的字符串组装好后,选择合适的action,指定对应的kobject设备即可
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
kobject_uevent_env(kobj, action, NULL);
retval = netlink_broadcast_filtered(uevent_sock, skb,0, 1, GFP_KERNEL,kobj_bcast_filter,kobj);
uevent发送可以通过kobject_uevent(),或者通过kobject_uevent_env()附加更多uevent信息。
kobject_uevent_env()主要分为两部分,一是通过netlink_broadcast_filtered()将socket信息发出去;另一个是通过uevent helper将uevent调用指定的uevent_helper进行处理,通常是热插拔程序mdev、udevd等
其中kobject_uevent函数中的action对应:
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
而 kobject_uevent() 其实就是直接调用了 kobject_uevent_env() 函数。一切的操作,将在该函数中完成,比如 kset uevent ops (struct kset_uevent_ops)的获取、字符串的填充组合、netlink message 的发送等,这些 uevent ops 在 start_kernel() 就会被注册
发送格式一般为:action@devpatch
change@/devices/virtual/thermal/cooling_device0
ACTION=change
DEVPATH=/devices/virtual/thermal/cooling_device0
SUBSYSTEM=thermal
NAME=user_cooling
STATE=1
TEMP=90
SEQNUM=747
用户空间会首先创建一个socket,并绑定到AF_NETLINK地址族上,然后recv接收消息,处理内核传递上来的message
创建socket——》recv接收uevent信息——》解析接收到的uevent信息——》地址族是AF_NETLINK类型的socket,协议类型是NETLINK_KOBJECT_UEVENT——》将当前socket绑定到AFNETLINK上,并设置本进程为处理消息的进程
busybox下的mdev;
drivers/power/supply/power_supply_core.c
drivers/power/supply/power_supply_sysfs.c
power_supply_init
power_supply_class->dev_uevent = power-suply_uevent
初始化workqueue,后续用于调度;
INIT_WORK(&psy->changed_work, power_supply_changed-work);
驱动中检测到硬件发生变化时,调用power_supply_changed函数,进而调用changed_work
schedule_work(&psy->changed_work);
添加环境变量,回调kset中注册的power-supply-uevent,将msg以socketbuffer的格式打包
power_supply_changed-work
kobject-uevent(&psy->dev.kobj, KOBJ_CHANGE);
kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])
if(envp_ext != NULL)
add_uevent_var(env, “%s”, envp_ext[i]);
if(uevent_ops && uevent_ops->uevent) // uevent_ops = kset->uevent_ops; uevent_ops->uevent = dev_uevent
uevent_ops->uevent(kset, kobj, env); // power_supply_class->dev_uevent = power_supply_uevent; class类kset
power_supply_uevent;
add_uevent-var(env, “ACTION=%s”, action_string);
add_uevent_var(env, “DEVPATH=%s”, devpath);
add_uevent_var(env, “SUBSYSTEM=%s”, subsystem);
kobject_uevent_net_boardcast(kobj, env, action_string, devpath);
alloc_uevent_skb // sockect buffer的放置有关 header: action@devpath + socket_buffer
scratch = skb_put(skb, len); /add header/
sprintf(scratch, “%s@%s”, action_string, devpath);
skb_put_data(skb, env->buf, env_buflen);
hardware/interfaces/health/utils/libhealthloop/HealthLoop.cpp