嵌入式 linux下kernel中kobject之kobject_uevent.c文件分析

2024-01-05 11:58

本文主要是介绍嵌入式 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文件分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/572791

相关文章

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念