kobject和uevent的一点想法--待续

本文深入解析Linux内核中的kobject结构及其与uevent事件的关系。介绍了kobject的初始化、添加、释放过程,以及uevent事件的触发条件和发送机制,包括netlink广播和用户空间助手程序的调用。通过对kobject和uevent的分析,有助于理解Linux设备驱动程序的事件通知机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文件路径:

include/linux/kobject.h

lib/kobject_uevent.c

lib/kobject.c

 

在我的理解中uevent事件和kobject绑定在一起的

 

几种kobject能够产生的事件属性

enumkobject_action {

KOBJ_ADD,//添加

KOBJ_REMOVE,//移除

KOBJ_CHANGE,//发生了变化

KOBJ_MOVE,//移动

KOBJ_ONLINE,//存在

KOBJ_OFFLINE,//不在

KOBJ_MAX//最大

};

 

structkobject {

constchar *name; //本目录的名字

structlist_head entry; //链表

structkobject *parent; //父目录吧。。父亲,可以使用kobj,kset或者sys root

structkset *kset;//顶层

structkobj_type *ktype; //相应的操作

structsysfs_dirent *sd;//sysfs 下的目录名,根据parent设置或者会设置位sysroot下

structkref kref;//引用计数当用kobject_put设置为0时,则调用kobject_cleanup完全释放内存

unsignedint state_initialized:1; //释放被初始化,我的理解是当申请了kobject后记得设为0阿

unsignedint state_in_sysfs:1;//是否在sys下

unsignedint state_add_uevent_sent:1;//是否为增加的action

unsignedint state_remove_uevent_sent:1;//是否为remove的acton

unsignedint uevent_suppress:1;//不发送uevent事件

};

 

structkobj_type {

void(*release)(struct kobject *kobj);

conststruct sysfs_ops *sysfs_ops;

structattribute **default_attrs;

conststruct kobj_ns_type_operations *(*child_ns_type)(struct kobject*kobj);

constvoid *(*namespace)(struct kobject *kobj);

};

 

以上是kobject的结构体,那么接下来我来分析下kobject,然后穿插着说一下这个结构体,

那么一开始当然是从kobject_init开始

 

kobject_init

*This function will properly initialize a kobject such that it canthen

*be passed to the kobject_add() call.

*

*After this function is called, the kobject MUST be cleaned up by acall

*to kobject_put(), not by a call to kfree directly to ensure that allof

*the memory is cleaned up properly.

 

根据kobject_init的解释,最后释放的时候要掉用kobject_put能够释放所有的内存,然而接下来要掉用的是kobject_add

kobject_add

/**

*kobject_add - the main kobject add function

*@kobj: the kobject to add

*@parent: pointer to the parent of the kobject.

*@fmt: format to name the kobject with.

*

*The kobject name is set and added to the kobject hierarchy in this

*function.

*

*If @parent is set, then the parent of the @kobj will be set to it.

*If @parent is NULL, then the parent of the @kobj will be set to the

*kobject associted with the kset assigned to this kobject. If nokset

*is assigned to the kobject, then the kobject will be located in the

*root of the sysfs tree.

*

*If this function returns an error, kobject_put() must be called to

*properly clean up the memory associated with the object.

*Under no instance should the kobject that is passed to this function

*be directly freed with a call to kfree(), that can leak memory.

*

*Note, no "add" uevent will be created with this call, thecaller should set

*up all of the necessary sysfs files for the object and then call

*kobject_uevent() with the UEVENT_ADD parameter to ensure that

*userspace is properly notified of this kobject's creation.

*/

 

关于这个函数重要的由以下几点

一:你现在设置的这个name将会添加到kobject的层次结构中也就是添加一个文件目录

二:如果设置了parent,那么kobj就会设置到parent下

如果parent==NULL,首先呢kobj就会设置到他的kset上,如果kset还没有,就会设置到sys下,

代码确实是这样写的

 

关于kobject的初始化最后说一句就是释放记得用kobject_put,根据这么多的函数说明来看只有他才会释放所有的内存

 

 

接下来我们来了解下所谓的uevent事件,我们从上报开始

/**

*kobject_uevent - notify userspace by sending an uevent

*

*@action: action that is happening

*@kobj: struct kobject that the action is happening to

*

*Returns 0 if kobject_uevent() is completed with success or the

*corresponding error when it fails.

*/

intkobject_uevent(struct kobject *kobj, enum kobject_action action)

{

returnkobject_uevent_env(kobj, action, NULL);

}

通过uevent时间上报到用户空间

 

接下来调用kobject_uevent_env

kobject_uevent_env

/**

*kobject_uevent_env - send an uevent with environmental data

*

*@action: action that is happening

*@kobj: struct kobject that the action is happening to

*@envp_ext: pointer to environmental data

*

*Returns 0 if kobject_uevent_env() is completed with success or the

*corresponding error when it fails.

*/

intkobject_uevent_env(struct kobject *kobj, enum kobject_action action,

char *envp_ext[])

{

structkobj_uevent_env *env;

constchar *action_string = kobject_actions[action];

constchar *devpath = NULL;

constchar *subsystem;

structkobject *top_kobj;

structkset *kset;

conststruct kset_uevent_ops *uevent_ops;

u64seq;

inti = 0;

intretval = 0;

#ifdefCONFIG_NET

structuevent_sock *ue_sk;

#endif

 

pr_debug("kobject:'%s' (%p): %s\n",

kobject_name(kobj), kobj, __func__);

 

/*search the kset we belong to */

top_kobj= kobj;

while(!top_kobj->kset && top_kobj->parent) //寻找最近的kset

top_kobj= top_kobj->parent;

 

if(!top_kobj->kset) { //没有kset不能发送uevent事件

pr_debug("kobject:'%s' (%p): %s: attempted to send uevent "

"without kset!\n", kobject_name(kobj), kobj,

__func__);

return-EINVAL;

}

 

kset= top_kobj->kset; //get the closest kset

uevent_ops= kset->uevent_ops;//get the kset's ops

 

/*skip the event, if uevent_suppress is set*/

if(kobj->uevent_suppress) {//拒绝发送uevent事件

pr_debug("kobject:'%s' (%p): %s: uevent_suppress "

"caused the event to drop!\n",

kobject_name(kobj), kobj, __func__);

return0;

}

/*skip the event, if the filter returns zero. */

if(uevent_ops && uevent_ops->filter)

if(!uevent_ops->filter(kset, kobj)) {//在set的filter里会去设置屏蔽掉一些uevent事件不能发送

pr_debug("kobject:'%s' (%p): %s: filter function "

"caused the event to drop!\n",

kobject_name(kobj), kobj, __func__);

return0;

}

 

/*originating subsystem */

if(uevent_ops && uevent_ops->name) //返回子系统的名字

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

else

subsystem= kobject_name(&kset->kobj);

if(!subsystem) {

pr_debug("kobject:'%s' (%p): %s: unset subsystem caused the "

"event to drop!\n", kobject_name(kobj), kobj,

__func__);

return0;

}

 

/*environment buffer */

env= kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);//申请要发送的buffer

if(!env)

return-ENOMEM;

 

/*complete object path */

devpath= kobject_get_path(kobj, GFP_KERNEL); //获取该kobj所有的路径(sys下)

if(!devpath) {

retval= -ENOENT;

gotoexit;

}

 

/*default keys */

retval= add_uevent_var(env, "ACTION=%s",action_string);//当前该kobj发生的动作

if(retval)

gotoexit;

retval= add_uevent_var(env, "DEVPATH=%s",devpath);//当前该kobj的完整路径

if(retval)

gotoexit;

retval= add_uevent_var(env, "SUBSYSTEM=%s",subsystem);//当前的kobj所代表的子系统即kobj->name

if(retval)

gotoexit;

 

/*keys passed in from the caller */

if(envp_ext) {

for(i = 0; envp_ext[i]; i++) {

retval= add_uevent_var(env, "%s",envp_ext[i]);//把环境变量挨个的放到env中,所有的长度最大为2048,数据最多位32个

if(retval)

gotoexit;

}

}

 

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

if(uevent_ops && uevent_ops->uevent) {

retval= uevent_ops->uevent(kset, kobj, env);//检查以下有没有不让带的env参数

if(retval) {

pr_debug("kobject:'%s' (%p): %s: uevent() returned "

"%d\n", kobject_name(kobj), kobj,

__func__, retval);

gotoexit;

}

}

 

/*

* Mark "add" and "remove" events in the object toensure proper

* events to userspace during automatic cleanup. If the object did

* send an "add" event, "remove" willautomatically generated by

* the core, if not already done by the caller.

*/

if(action == KOBJ_ADD)

kobj->state_add_uevent_sent= 1;

elseif (action == KOBJ_REMOVE)

kobj->state_remove_uevent_sent= 1;

 

/*we will send an event, so request a new sequence number */

spin_lock(&sequence_lock);

seq= ++uevent_seqnum;//发送的uevent队列编号可以检查一共发送了多少个uevent事件

spin_unlock(&sequence_lock);

retval= add_uevent_var(env, "SEQNUM=%llu", (unsigned longlong)seq);//增加最后一个env也意味着我们能带的参数最多只有31个

if(retval)

gotoexit;

 

#ifdefined(CONFIG_NET)

/*send netlink message */

mutex_lock(&uevent_sock_mutex);

list_for_each_entry(ue_sk,&uevent_sock_list, list) {

structsock *uevent_sock = ue_sk->sk;

structsk_buff *skb;

size_tlen;

 

/*allocate message with the maximum possible size */

len= strlen(action_string) + strlen(devpath) + 2;

skb= alloc_skb(len + env->buflen, GFP_KERNEL);

if(skb) {

char*scratch;

 

/*add header */

scratch= skb_put(skb, len);

sprintf(scratch,"%s@%s", action_string, devpath);

 

/*copy keys to our continuous event payload buffer */

for(i = 0; i < env->envp_idx; i++) {

len= strlen(env->envp[i]) + 1;

scratch= skb_put(skb, len);

strcpy(scratch,env->envp[i]);

}

 

NETLINK_CB(skb).dst_group= 1;

retval= netlink_broadcast_filtered(uevent_sock, skb,

0, 1, GFP_KERNEL,

kobj_bcast_filter,

kobj);

/*ENOBUFS should be handled in userspace */

if(retval == -ENOBUFS)

retval= 0;

}else

retval= -ENOMEM;

}

mutex_unlock(&uevent_sock_mutex);

#endif

 

/*call uevent_helper, usually only enabled during early boot */

if(uevent_helper[0] && !kobj_usermode_filter(kobj)){//如果设置了uevent_helper的路径才会有可能调用

char*argv [3];

 

argv[0] = uevent_helper;

argv[1] = (char *)subsystem;

argv[2] = NULL;

retval= add_uevent_var(env, "HOME=/");

if(retval)

gotoexit;

retval= add_uevent_var(env,

"PATH=/sbin:/bin:/usr/sbin:/usr/bin");

if(retval)

gotoexit;

 

retval= call_usermodehelper(argv[0], argv,

env->envp, UMH_WAIT_EXEC);

}

 

exit:

kfree(devpath);

kfree(env);

returnretval;

}

 

现在我们知道了什么情况下会发送uevent事件和那些条件不能发送,那么接下来讨论的是如何发送的,

由我们分析的kobject_uevent_env可知,有两种uevent的上报机制,一种是直接调用user空间的程序,(需要设置相应的CONFIG_UEVENT_HELPER_PATH才有可能会调用)

 

但我们这里着重的理解一下netlink的机制

 

明天再去搞一搞,然后通过netlink接口获取所有的uevent事件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值