怎么了选择
分类: LINUX
2012-01-13 11:25:49
接着上一篇文章,分析 kset ,
我们已经知道了kset 内嵌了kobject 来表示自身的节点,创建kset 就要完成其内嵌kobject ,注册kset 时会产生一个事件,事件而最终会调用uevent_ops 字段指向结构中的函数,这个事件是通过用户空间的hotplug 程序处理。下面我们一步一步分析。
内核同样提供了创建和注册kset 的函数kset_create_and_add()
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
kset = kset_create (name, uevent_ops, parent_kobj);
if (!kset)
return NULL;
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
输入参数有一个kset_uevent_ops 类型的结构变量,其结构包含三个函数指针,我们在后面的分析到这三个函数在什么时候被调用,kset_uevent_ops 结构定义如下:
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
继续看上面的函数,先调用kset_create () 创建一个kset ,接着调用kset_register() 注册它。
static struct kset *kset_create(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int retval;
kset = kzalloc(sizeof(*kset), GFP_KERNEL);
if (!kset)
return NULL;
retval = kobject_set_name(&kset->kobj, name);
if (retval) {
kfree(kset);
return NULL;
}
kset->uevent_ops = uevent_ops;
kset->kobj.parent = parent_kobj;
/*
* The kobject of this kset will have a type of kset_ktype and belong to
* no kset itself. That way we can properly free it when it is
* finished being used.
*/
kset->kobj.ktype = &kset_ktype;
kset->kobj.kset = NULL;
return kset;
}
为kset 分配内存,如我们上面分析,初始化了kset 内嵌的kobject (这里还未将kobject 注册到文件系统),另外用输入参数初始化kset 的uevent_ops 字段。
接着看kset 的注册函数kset_register() :
int kset_register(struct kset *k)
{
int err;
if (!k)
return -EINVAL;
kset_init(k);
err = kobject_add_internal(&k->kobj);
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
在这里终于看到调用kobject_add_internal ()将kset 内嵌的kobject 注册到文件系统,这个函数我们在上面已经分析。
我们上面说到注册kset 会产生一个事件,就是在这里调用了kobject_uevent(&k->kobj, KOBJ_ADD)
kobject_uevent() 在/lib/ kobject_uevent.c 中:
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
return kobject_uevent_env(kobj, action, NULL);
}
转入kobject_uevent_env() :
这个函数比较长,我们分段分析
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
struct kobj_uevent_env *env;
const char *action_string = kobject_actions[action];
const char *devpath = NULL;
const char *subsystem;
struct kobject *top_kobj;
struct kset *kset;
struct kset_uevent_ops *uevent_ops;
u64 seq;
int i = 0;
int retval = 0;
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)
top_kobj = top_kobj->parent;
if (!top_kobj->kset) {
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
"without kset!/n", kobject_name(kobj), kobj,
__func__);
return -EINVAL;
}
kset = top_kobj->kset;
uevent_ops = kset-> uevent_ops;
如果如果kobj 的kset 和parent 字段都不存在,说明找不到所属kset ,也就没有uevent_ops ,不能产生事件,返回错误信息;相反则找到了存在kset 的kobj 或父kobject (依次往上找),并赋值给uevent_ops 。
/* skip the event, if uevent_suppress is set*/
if (kobj-> uevent_suppress) {
pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
"caused the event to drop!/n",
kobject_name(kobj), kobj, __func__);
return 0;
}
如果设置了uevent_suppress 字段,说明不希望产生事件,忽略事件正确返回。注意驱动程序将在适当的地方产生改事件。
/* skip the event, if the filter returns zero. */
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject: '%s' (%p): %s: filter function "
"caused the event to drop!/n",
kobject_name(kobj), kobj, __func__);
return 0;
}
如果uevent_ops->filter 返回0 ,同样忽略事件正确返回。
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__);
return 0;
}
获得子系统的名称,不存在则返回。
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
分配一个kobj_uevent_env 结构内存,用于存放环境变量的值。
/* complete object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
获得引发事件的kobject 在sysfs 中的路径。
/* default keys */
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
/* 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]);
if (retval)
goto exit;
}
}
调用add_uevent_var()kobj_uevent_env 填充action_string,kobject 路径, 子系统名称以及其他指定环境变量。
/* let the kset specific function add its stuff */
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
"%d/n", kobject_name(kobj), kobj,
__FUNCTION__, retval);
goto exit;
}
}
调用uevent_ops 的uevent 函数,编程人员可在此函数中实现自定义的功能。
/*
* Mark "add" and "remove" events in the object to ensure proper
* events to userspace during automatic cleanup. If the object did
* send an "add" event, "remove" will automatically generated by
* the core, if not already done by the caller.
*/
if (action == KOBJ_ADD)
kobj->state_add_uevent_sent = 1;
else if (action == KOBJ_REMOVE)
kobj->state_remove_uevent_sent = 1;
设置KOBJ_ADD 和KOBJ_REMOVE 的标志。
/* we will send an event, so request a new sequence number */
spin_lock(&sequence_lock);
seq = ++uevent_seqnum;
spin_unlock(&sequence_lock);
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
if (retval)
goto exit;
#if defined(CONFIG_NET)
/* send netlink message */
if (uevent_sock) {
struct sk_buff *skb;
size_t len;
/* 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(uevent_sock, skb, 0, 1,
GFP_KERNEL);
/* ENOBUFS should be handled in userspace */
if (retval == -ENOBUFS)
retval = 0;
} else
retval = -ENOMEM;
}
#endif
/* call uevent_helper, usually only enabled during early boot */
if (uevent_helper[0]) {
char *argv [3];
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
retval = add_uevent_var(env, "HOME=/");
if (retval)
goto exit;
retval = add_uevent_var(env,
"PATH=/sbin:/bin:/usr/sbin:/usr/bin");
if (retval)
goto exit;
添加HOME 和PATH 环境变量。
retval = call_usermodehelper(argv[0], argv,
env->envp, UMH_WAIT_EXEC);
}
exit:
kfree(devpath);
kfree(env);
return retval;
}
调用hotplug 函数。
看一下kset_unregister ()
void kset_unregister (struct kset *k)
{
if (!k)
return;
kobject_put(&k-> kobj);
}
减少其内嵌的kobj 计数,为0 则释放其内存空间。
已经分析完kobject 和kset ,linux 的设备模型就是基于这两个数据结构的,在此基础上,后续将分析设备模型中的device 、driver 、和bus 。