本文主要是介绍嵌入式 linux下kernel中kobject之kobject_uevent.c文件分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj); //过滤函数,kset中的kobj是否需要处理
const char *(*name)(struct kset *kset, struct kobject *kobj); //返回name
int (*uevent)(struct kset *kset, struct kobject *kobj, //uevent函数
struct kobj_uevent_env *env);
};
struct kobj_uevent_env {
char *envp[UEVENT_NUM_ENVP]; //buf中每一段字符串的开始地址
int envp_idx; //有几个envp,或者说buf中有几段字符串
char buf[UEVENT_BUFFER_SIZE]; //缓存(可以保存好几段字符串)
int buflen; //buf尾
};
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])
调用kset-> uevent_ops-> uevent()函数
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
将格式化信息保存进env->buf中,关于env结构体可参考kobject.c的文件分析开头
int kobject_action_type(const char *buf, size_t count, enum kobject_action *type)
查找buf属于哪一个action动作,属于的动作返回到type中
由于kref.c代码很少,所以就把这两个文件合在一起分析了
//原子的操作refcount,将其设置为1
void kref_init(struct kref *kref)
{
atomic_set(&kref->refcount,1);
smp_mb();
}
//计数加1
void kref_get(struct kref *kref)
{
atomic_inc(&kref->refcount);
smp_mb__after_atomic_inc();
}
//计数减1,为0则调用release函数
int kref_put(struct kref *kref, void (*release)(struct kref *kref))
{
if (atomic_dec_and_test(&kref->refcount)) {
release(kref);
return 1;
}
return 0;
}
//关于uevent,目前猜测他应该是和热插拔有关
u64 uevent_seqnum;
char uevent_helper[UEVENT_HELPER_PATH_LEN] = //256
CONFIG_UEVENT_HELPER_PATH; // "/sbin/hotplug",这个定义在autoconf中
static DEFINE_SPINLOCK(sequence_lock);
static struct sock *uevent_sock;
//这几个状态都是定义在kobject.h的枚举结构之中
static const char *kobject_actions[] = {
[KOBJ_ADD] = "add",
[KOBJ_REMOVE] = "remove",
[KOBJ_CHANGE] = "change",
[KOBJ_MOVE] = "move",
[KOBJ_ONLINE] = "online",
[KOBJ_OFFLINE] = "offline",
};
跳读一下,在分析kobject.c文件时,我们看得最多的是kobject_uevent和kobject_uevent_env函数,所以我们先来看看这两个函数是干什么用的。
//这个基本没什么说的了,直接调用了env
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
return kobject_uevent_env(kobj, action, NULL);
}
EXPORT_SYMBOL_GPL(kobject_uevent);
//好长一个函数啊,为了方便理解,我们先从kobject.c中拿出一个调用实例来比较
//在rename函数中有这样的调用:
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
其中envp的第一个元素保存的是更名前的kobj路径,第二个元素为空
sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
envp[0] = devpath_string;
envp[1] = NULL;
这个函数很长,但也是超级的绕,绕到最后,其实就一个核心操作:
retval = uevent_ops->uevent(kset, kobj, env);
其中kset为kobj的宿主kset, kobj的参数传入,而env则是一些字符串信息
int kobject_uevent_env(
struct kobject *kobj, //操作obj
enum kobject_action action, //动作( KOBJ_MOVE )
char *envp_ext[]) //这个信息也只是添加进env->buf
{
struct kobj_uevent_env *env;
const char *action_string = kobject_actions[action]; //根据动作参数获取字符串"move"
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;
//--------------------------------------------------------------------------------------------
//这一块代码的功能是找到kobj的宿主kset,然后从中提取出热插拔的操作函数
//--------------------------------------------------------------------------------------------
//找到根kobj,或者关联上了kset宿主
top_kobj = kobj;
while (!top_kobj->kset && top_kobj->parent)
top_kobj = top_kobj->parent;
//根obj没有关联kset,出错
if (!top_kobj->kset) {
pr_debug("kobject attempted to send uevent without kset!\n");
return -EINVAL;
}
//获取宿主kset,并从kset中提取出热插拔操作的ops
kset = top_kobj->kset;
uevent_ops = kset->uevent_ops;
//--------------------------------------------------------------------------------------------
//如果ops的filter()和name()函数有指定,则先执行他们
//--------------------------------------------------------------------------------------------
//如果ops存在 且 filter函数(过滤函数)有设置,则执行该函数
//filter的作用应该是看看kobj是否被kset过滤到
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject filter function caused the event to drop!\n");
return 0;
}
//给subsystem指定name,如果ops->name()函数有指定,则从该函数返回,否则就获取kset->kobj的name。
if (uevent_ops && uevent_ops->name)
subsystem = uevent_ops->name(kset, kobj);
else
subsystem = kobject_name(&kset->kobj);
//name为空,出错返回
if (!subsystem) {
pr_debug("unset subsystem caused the event to drop!\n");
return 0;
}
//--------------------------------------------------------------------------------------------
//申请一块env结构体,并填充他的env->buf字符串信息
//--------------------------------------------------------------------------------------------
//申请一快env内存
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
//获取kobj的完整路径
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
//这里出现了一个新的函数,add_uevent_var,OK,我们先中断这里,跳到后面去先分析这个函数
//分析完add_uevent_var函数后,发现这个函数只是将参数的字符串保存进env->buf中,所以这一串函数只是保存一些字符串信息
retval = add_uevent_var(env, "ACTION=%s", action_string); //动作名
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath); //kobj路径
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem); //subsys名
if (retval)
goto exit;
//如果参数三不为空,再保存参数三的字符串信息
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, envp_ext[i]);
if (retval)
goto exit;
}
}
//--------------------------------------------------------------------------------------------
//最后执行ops->uevent函数
//绕了个大圈,最后却又调用了ops中指定的uevent
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug ("%s - uevent() returned %d\n",
__FUNCTION__, retval);
goto exit;
}
}
//将全局变量uevent_seqnum加1
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;
//帮助信息,uevent_helper在申明时就已经给了一个默认的路径
//这一块的代码实际和这个函数的关联不是很大了,所以可以不用理会
if (uevent_helper[0]) {
char *argv [3];
argv [0] = uevent_helper; // "/sbin/hotplug"
argv [1] = (char *)subsystem; // kset的name
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;
//让内核空间的驱动程序启用用户空间的若干应用程序,这个函数的简介可参考这个网址:http://linux.chinaunix.net/techdoc/beginner/2009/05/05/1110318.shtml
call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC);
}
exit:
kfree(devpath); //释放kobj路径
kfree(env); //释放env
return retval;
}
EXPORT_SYMBOL_GPL(kobject_uevent_env);
//直接从上面抓他的一个引用来说问题:
retval = add_uevent_var(env, "ACTION=%s", action_string);
//分析完这个函数后,发现这个函数只是将format中的字符保存进env->buf中
int add_uevent_var(
struct kobj_uevent_env *env,
const char *format, ...)
{
va_list args; //获取格式化列表的参数
int len;
//idx大于等于envp指针数组的大小,出错返回
if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
printk(KERN_ERR "add_uevent_var: too many keys\n");
WARN_ON(1);
return -ENOMEM;
}
//将格式化字符打印进env->buf中,
//开始地址由env->buflen指定
//最大长度由env->buf的大小 减去 env->buflen开始长度限制,以免越界
va_start(args, format);
len = vsnprintf(&env->buf[env->buflen],
sizeof(env->buf) - env->buflen,
format, args);
va_end(args);
//实际操作的长度在buf中越界了(实际是不可能的),出错返回
if (len >= (sizeof(env->buf) - env->buflen)) {
printk(KERN_ERR "add_uevent_var: buffer size too small\n");
WARN_ON(1);
return -ENOMEM;
}
//记录本段字符串的开始地址
env->envp[env->envp_idx++] = &env->buf[env->buflen];
env->buflen += len + 1;
return 0;
}
EXPORT_SYMBOL_GPL(add_uevent_var);
//根据输入的buf查找该buf属于哪一个action命令
int kobject_action_type(
const char *buf, //要处理的字符串
size_t count, //要处理的字符串的长度
enum kobject_action *type) //返回action类型
{
enum kobject_action action;
int ret = -EINVAL;
//从buf中剔除最后一个回车换行字符
if (count && buf[count-1] == '\n')
count--;
if (!count)
goto out;
for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
if (strncmp(kobject_actions[action], buf, count) != 0) //字符串不相等
continue;
//长度不相等,以免将“a”,“ab”两个误匹配
if (kobject_actions[action][count] != '\0')
continue;
//找到匹配的字符串
*type = action;
ret = 0;
break;
}
out:
return ret;
}
//创建一个sock,准备发送信息到网络上
static int __init kobject_uevent_init(void)
{
uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
1, NULL, NULL, THIS_MODULE);
if (!uevent_sock) {
printk(KERN_ERR
"kobject_uevent: unable to create netlink socket!\n");
return -ENODEV;
}
return 0;
}
postcore_initcall(kobject_uevent_init); //这个函数放置在指定区域,初始化执行
这篇关于嵌入式 linux下kernel中kobject之kobject_uevent.c文件分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!