sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/core-api/kobjectmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget$/translations/zh_TW/core-api/kobjectmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget$/translations/it_IT/core-api/kobjectmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget$/translations/ja_JP/core-api/kobjectmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget$/translations/ko_KR/core-api/kobjectmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget$/translations/sp_SP/core-api/kobjectmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhnote)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]h paragraph)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hh5Documentation/translations/zh_CN/disclaimer-zh_CN.rsthKhhubah}(h]h ]h"]h$]h&]uh1hhhhhhhhNubh field_list)}(hhh](hfield)}(hhh](h field_name)}(hOriginalh]hOriginal}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhQ/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/core-api/kobject.rsthKubh field_body)}(h#Documentation/core-api/kobject.rst h]h)}(h"Documentation/core-api/kobject.rsth]h"Documentation/core-api/kobject.rst}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h翻译h]h翻译}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh)}(h-司延腾 Yanteng Si h]h)}(h,司延腾 Yanteng Si h](h司延腾 Yanteng Si <}(hjhhhNhNubh reference)}(hsiyanteng@loongson.cnh]hsiyanteng@loongson.cn}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:siyanteng@loongson.cnuh1jhjubh>}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj ubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhtarget)}(h.. _cn_core_api_kobject.rst:h]h}(h]h ]h"]h$]h&]refidcn-core-api-kobject-rstuh1jDhKhhhhhhubhsection)}(hhh](htitle)}(hI关于kobjects、ksets和ktypes的一切你没想过需要了解的东西h]hI关于kobjects、ksets和ktypes的一切你没想过需要了解的东西}(hjYhhhNhNubah}(h]h ]h"]h$]h&]uh1jWhjThhhhhK ubh)}(hhh](h)}(hhh](h)}(h作者h]h作者}(hjmhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjjhhhKubh)}(h/Greg Kroah-Hartman h]h)}(hj}h](hGreg Kroah-Hartman <}(hjhhhNhNubj)}(hgregkh@linuxfoundation.orgh]hgregkh@linuxfoundation.org}(hjhhhNhNubah}(h]h ]h"]h$]h&]refuri!mailto:gregkh@linuxfoundation.orguh1jhjubh>}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj{ubah}(h]h ]h"]h$]h&]uh1hhjjubeh}(h]h ]h"]h$]h&]uh1hhhhKhjghhubh)}(hhh](h)}(h最后一次更新h]h最后一次更新}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubh)}(hDecember 19, 2007 h]h)}(hDecember 19, 2007h]hDecember 19, 2007}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhjghhubeh}(h]h ]h"]h$]h&]uh1hhjThhhhhKubh)}(hw根据Jon Corbet于2003年10月1日为lwn.net撰写的原创文章改编,网址是: https://lwn.net/Articles/51437/h](hX根据Jon Corbet于2003年10月1日为lwn.net撰写的原创文章改编,网址是: }(hjhhhNhNubj)}(hhttps://lwn.net/Articles/51437/h]hhttps://lwn.net/Articles/51437/}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurijuh1jhjubeh}(h]h ]h"]h$]h&]uh1hhhhKhjThhubh)}(hX}理解驱动模型和建立在其上的kobject抽象的部分的困难在于,没有明显的切入点。 处理kobjects需要理解一些不同的类型,所有这些类型都会相互引用。为了使事情 变得更简单,我们将多路并进,从模糊的术语开始,并逐渐增加细节。那么,先来 了解一些我们将要使用的术语的简明定义吧。h]hX}理解驱动模型和建立在其上的kobject抽象的部分的困难在于,没有明显的切入点。 处理kobjects需要理解一些不同的类型,所有这些类型都会相互引用。为了使事情 变得更简单,我们将多路并进,从模糊的术语开始,并逐渐增加细节。那么,先来 了解一些我们将要使用的术语的简明定义吧。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjThhubh block_quote)}(hXN- 一个kobject是一个kobject结构体类型的对象。Kobjects有一个名字和一个 引用计数。一个kobject也有一个父指针(允许对象被排列成层次结构),一个 特定的类型,并且,通常在sysfs虚拟文件系统中表示。 Kobjects本身通常并不引人关注;相反它们常常被嵌入到其他包含真正引人注目 的代码的结构体中。 任何结构体都 **不应该** 有一个以上的kobject嵌入其中。如果有的话,对象的引用计 数肯定会被打乱,而且不正确,你的代码就会出现错误。所以不要这样做。 - ktype是嵌入一个kobject的对象的类型。每个嵌入kobject的结构体都需要一个 相应的ktype。ktype控制着kobject在被创建和销毁时的行为。 - 一个kset是一组kobjects。这些kobjects可以是相同的ktype或者属于不同的 ktype。kset是kobjects集合的基本容器类型。Ksets包含它们自己的kobjects, 但你可以安全地忽略这个实现细节,因为kset的核心代码会自动处理这个kobject。 当你看到一个下面全是其他目录的sysfs目录时,通常这些目录中的每一个都对应 于同一个kset中的一个kobject。 我们将研究如何创建和操作所有这些类型。将采取一种自下而上的方法,所以我们 将回到kobjects。 h](h bullet_list)}(hhh]h list_item)}(hX一个kobject是一个kobject结构体类型的对象。Kobjects有一个名字和一个 引用计数。一个kobject也有一个父指针(允许对象被排列成层次结构),一个 特定的类型,并且,通常在sysfs虚拟文件系统中表示。 h]h)}(hX一个kobject是一个kobject结构体类型的对象。Kobjects有一个名字和一个 引用计数。一个kobject也有一个父指针(允许对象被排列成层次结构),一个 特定的类型,并且,通常在sysfs虚拟文件系统中表示。h]hX一个kobject是一个kobject结构体类型的对象。Kobjects有一个名字和一个 引用计数。一个kobject也有一个父指针(允许对象被排列成层次结构),一个 特定的类型,并且,通常在sysfs虚拟文件系统中表示。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]bullet-uh1jhhhKhjubj )}(hXUKobjects本身通常并不引人关注;相反它们常常被嵌入到其他包含真正引人注目 的代码的结构体中。 任何结构体都 **不应该** 有一个以上的kobject嵌入其中。如果有的话,对象的引用计 数肯定会被打乱,而且不正确,你的代码就会出现错误。所以不要这样做。 h](h)}(hKobjects本身通常并不引人关注;相反它们常常被嵌入到其他包含真正引人注目 的代码的结构体中。h]hKobjects本身通常并不引人关注;相反它们常常被嵌入到其他包含真正引人注目 的代码的结构体中。}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj9ubh)}(h任何结构体都 **不应该** 有一个以上的kobject嵌入其中。如果有的话,对象的引用计 数肯定会被打乱,而且不正确,你的代码就会出现错误。所以不要这样做。h](h任何结构体都 }(hjKhhhNhNubhstrong)}(h **不应该**h]h 不应该}(hjUhhhNhNubah}(h]h ]h"]h$]h&]uh1jShjKubh 有一个以上的kobject嵌入其中。如果有的话,对象的引用计 数肯定会被打乱,而且不正确,你的代码就会出现错误。所以不要这样做。}(hjKhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK!hj9ubeh}(h]h ]h"]h$]h&]uh1j hhhKhjubj)}(hhh](j)}(hktype是嵌入一个kobject的对象的类型。每个嵌入kobject的结构体都需要一个 相应的ktype。ktype控制着kobject在被创建和销毁时的行为。 h]h)}(hktype是嵌入一个kobject的对象的类型。每个嵌入kobject的结构体都需要一个 相应的ktype。ktype控制着kobject在被创建和销毁时的行为。h]hktype是嵌入一个kobject的对象的类型。每个嵌入kobject的结构体都需要一个 相应的ktype。ktype控制着kobject在被创建和销毁时的行为。}(hjzhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK$hjvubah}(h]h ]h"]h$]h&]uh1jhjsubj)}(hX一个kset是一组kobjects。这些kobjects可以是相同的ktype或者属于不同的 ktype。kset是kobjects集合的基本容器类型。Ksets包含它们自己的kobjects, 但你可以安全地忽略这个实现细节,因为kset的核心代码会自动处理这个kobject。 h]h)}(hX一个kset是一组kobjects。这些kobjects可以是相同的ktype或者属于不同的 ktype。kset是kobjects集合的基本容器类型。Ksets包含它们自己的kobjects, 但你可以安全地忽略这个实现细节,因为kset的核心代码会自动处理这个kobject。h]hX一个kset是一组kobjects。这些kobjects可以是相同的ktype或者属于不同的 ktype。kset是kobjects集合的基本容器类型。Ksets包含它们自己的kobjects, 但你可以安全地忽略这个实现细节,因为kset的核心代码会自动处理这个kobject。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK'hjubah}(h]h ]h"]h$]h&]uh1jhjsubeh}(h]h ]h"]h$]h&]j7j8uh1jhhhK$hjubh)}(h当你看到一个下面全是其他目录的sysfs目录时,通常这些目录中的每一个都对应 于同一个kset中的一个kobject。h]h当你看到一个下面全是其他目录的sysfs目录时,通常这些目录中的每一个都对应 于同一个kset中的一个kobject。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK+hjubh)}(h我们将研究如何创建和操作所有这些类型。将采取一种自下而上的方法,所以我们 将回到kobjects。h]h我们将研究如何创建和操作所有这些类型。将采取一种自下而上的方法,所以我们 将回到kobjects。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK.hjubeh}(h]h ]h"]h$]h&]uh1j hhhKhjThhubjS)}(hhh](jX)}(h嵌入kobjectsh]h嵌入kobjects}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jWhjhhhhhK3ubh)}(hX`内核代码很少创建孤立的kobject,只有一个主要的例外,下面会解释。相反, kobjects被用来控制对一个更大的、特定领域的对象的访问。为此,kobjects会被 嵌入到其他结构中。如果你习惯于用面向对象的术语来思考问题,那么kobjects可 以被看作是一个顶级的抽象类,其他的类都是从它派生出来的。一个kobject实现了 一系列的功能,这些功能本身并不特别有用,但在其他对象中却很好用。C语言不允 许直接表达继承,所以必须使用其他技术——比如结构体嵌入。h]hX`内核代码很少创建孤立的kobject,只有一个主要的例外,下面会解释。相反, kobjects被用来控制对一个更大的、特定领域的对象的访问。为此,kobjects会被 嵌入到其他结构中。如果你习惯于用面向对象的术语来思考问题,那么kobjects可 以被看作是一个顶级的抽象类,其他的类都是从它派生出来的。一个kobject实现了 一系列的功能,这些功能本身并不特别有用,但在其他对象中却很好用。C语言不允 许直接表达继承,所以必须使用其他技术——比如结构体嵌入。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK5hjhhubh)}(h(对于那些熟悉内核链表实现的人来说,这类似于“list_head”结构本身很少有用, 但总是被嵌入到感兴趣的更大的对象中)。h]h(对于那些熟悉内核链表实现的人来说,这类似于“list_head”结构本身很少有用, 但总是被嵌入到感兴趣的更大的对象中)。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK`` 中找到::h](hX如果你有一个uio_map结构体,找到其嵌入的kobject只是一个使用kobj成员的问题。 然而,与kobjects一起工作的代码往往会遇到相反的问题:给定一个结构体kobject 的指针,指向包含结构体的指针是什么?你必须避免使用一些技巧(比如假设 kobject在结构的开头),相反,你得使用container_of()宏,其可以在 }(hj/hhhNhNubj)}(h````h]h}(hj7hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj/ubh 中找到:}(hj/hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKGhjhhubj)}(hcontainer_of(ptr, type, member)h]hcontainer_of(ptr, type, member)}hjOsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKMhjhhubh)}(h其中:h]h其中:}(hj]hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKOhjhhubj )}(h* ``ptr`` 是一个指向嵌入kobject的指针, * ``type`` 是包含结构体的类型, * ``member`` 是 ``指针`` 所指向的结构体域的名称。 h]j)}(hhh](j)}(h0``ptr`` 是一个指向嵌入kobject的指针,h]h)}(hjth](j)}(h``ptr``h]hptr}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjvubh) 是一个指向嵌入kobject的指针,}(hjvhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKQhjrubah}(h]h ]h"]h$]h&]uh1jhjoubj)}(h'``type`` 是包含结构体的类型,h]h)}(hjh](j)}(h``type``h]htype}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 是包含结构体的类型,}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKRhjubah}(h]h ]h"]h$]h&]uh1jhjoubj)}(h?``member`` 是 ``指针`` 所指向的结构体域的名称。 h]h)}(h>``member`` 是 ``指针`` 所指向的结构体域的名称。h](j)}(h ``member``h]hmember}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 是 }(hjhhhNhNubj)}(h ``指针``h]h指针}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh% 所指向的结构体域的名称。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKShjubah}(h]h ]h"]h$]h&]uh1jhjoubeh}(h]h ]h"]h$]h&]j7*uh1jhhhKQhjkubah}(h]h ]h"]h$]h&]uh1j hhhKQhjhhubh)}(hcontainer_of()的返回值是一个指向相应容器类型的指针。因此,例如,一个嵌入到 uio_map结构 **中** 的kobject结构体的指针kp可以被转换为一个指向 **包含** uio_map 结构体的指针,方法是::h](hwcontainer_of()的返回值是一个指向相应容器类型的指针。因此,例如,一个嵌入到 uio_map结构 }(hjhhhNhNubjT)}(h**中**h]h中}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jShjubh> 的kobject结构体的指针kp可以被转换为一个指向 }(hjhhhNhNubjT)}(h **包含**h]h包含}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jShjubh( uio_map 结构体的指针,方法是:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKUhjhhubj)}(h?struct uio_map *u_map = container_of(kp, struct uio_map, kobj);h]h?struct uio_map *u_map = container_of(kp, struct uio_map, kobj);}hj3sbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKYhjhhubh)}(h为了方便起见,程序员经常定义一个简单的宏,用于将kobject指针 **反推** 到包含 类型。在早期的 ``drivers/uio/uio.c`` 中正是如此,你可以在这里看到::h](hV为了方便起见,程序员经常定义一个简单的宏,用于将kobject指针 }(hjAhhhNhNubjT)}(h **反推**h]h反推}(hjIhhhNhNubah}(h]h ]h"]h$]h&]uh1jShjAubh! 到包含 类型。在早期的 }(hjAhhhNhNubj)}(h``drivers/uio/uio.c``h]hdrivers/uio/uio.c}(hj[hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjAubh, 中正是如此,你可以在这里看到:}(hjAhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK[hjhhubj)}(hstruct uio_map { struct kobject kobj; struct uio_mem *mem; }; #define to_map(map) container_of(map, struct uio_map, kobj)h]hstruct uio_map { struct kobject kobj; struct uio_mem *mem; }; #define to_map(map) container_of(map, struct uio_map, kobj)}hjssbah}(h]h ]h"]h$]h&]j-j.uh1jhhhK^hjhhubh)}(hf其中宏的参数“map”是一个指向有关的kobject结构体的指针。该宏随后被调用::h]he其中宏的参数“map”是一个指向有关的kobject结构体的指针。该宏随后被调用:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKehjhhubj)}(h#struct uio_map *map = to_map(kobj);h]h#struct uio_map *map = to_map(kobj);}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKghjhhubeh}(h]kobjectsah ]h"]嵌入kobjectsah$]h&]uh1jRhjThhhhhK3ubjS)}(hhh](jX)}(hkobjects的初始化h]hkobjects的初始化}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jWhjhhhhhKkubh)}(h当然,创建kobject的代码必须初始化该对象。一些内部字段是通过(强制)调用kobject_init() 来设置的::h]h当然,创建kobject的代码必须初始化该对象。一些内部字段是通过(强制)调用kobject_init() 来设置的:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKmhjhhubj)}(hAvoid kobject_init(struct kobject *kobj, struct kobj_type *ktype);h]hAvoid kobject_init(struct kobject *kobj, struct kobj_type *ktype);}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKphjhhubh)}(hktype是正确创建kobject的必要条件,因为每个kobject都必须有一个相关的kobj_type。 在调用kobject_init()后,为了向sysfs注册kobject,必须调用函数kobject_add()::h]hktype是正确创建kobject的必要条件,因为每个kobject都必须有一个相关的kobj_type。 在调用kobject_init()后,为了向sysfs注册kobject,必须调用函数kobject_add():}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKrhjhhubj)}(hdint kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);h]hdint kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKuhjhhubh)}(hX0这将正确设置kobject的父级和kobject的名称。如果该kobject要与一个特定的kset相关 联,在调用kobject_add()之前必须分配kobj->kset。如果kset与kobject相关联,则 kobject的父级可以在调用kobject_add()时被设置为NULL,则kobject的父级将是kset 本身。h]hX0这将正确设置kobject的父级和kobject的名称。如果该kobject要与一个特定的kset相关 联,在调用kobject_add()之前必须分配kobj->kset。如果kset与kobject相关联,则 kobject的父级可以在调用kobject_add()时被设置为NULL,则kobject的父级将是kset 本身。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKxhjhhubh)}(h由于kobject的名字是在它被添加到内核时设置的,所以kobject的名字不应该被直接操作。 如果你必须改变kobject的名字,请调用kobject_rename()::h]h由于kobject的名字是在它被添加到内核时设置的,所以kobject的名字不应该被直接操作。 如果你必须改变kobject的名字,请调用kobject_rename():}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK}hjhhubj)}(h?int kobject_rename(struct kobject *kobj, const char *new_name);h]h?int kobject_rename(struct kobject *kobj, const char *new_name);}hj sbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKhjhhubh)}(hkobject_rename()函数不会执行任何锁定操作,也不会对name进行可靠性检查,所以调用 者自己检查和串行化操作是明智的选择h]hkobject_rename()函数不会执行任何锁定操作,也不会对name进行可靠性检查,所以调用 者自己检查和串行化操作是明智的选择}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h有一个叫kobject_set_name()的函数,但那是历史遗产,正在被删除。如果你的代码需 要调用这个函数,那么它是不正确的,需要被修复。h]h有一个叫kobject_set_name()的函数,但那是历史遗产,正在被删除。如果你的代码需 要调用这个函数,那么它是不正确的,需要被修复。}(hj&hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hA要正确访问kobject的名称,请使用函数kobject_name()::h]h@要正确访问kobject的名称,请使用函数kobject_name():}(hj4hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(h6const char *kobject_name(const struct kobject * kobj);h]h6const char *kobject_name(const struct kobject * kobj);}hjBsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKhjhhubh)}(h有一个辅助函数可以同时初始化和添加kobject到内核中,令人惊讶的是,该函数被称为 kobject_init_and_add()::h]h有一个辅助函数可以同时初始化和添加kobject到内核中,令人惊讶的是,该函数被称为 kobject_init_and_add():}(hjPhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hint kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...);h]hint kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...);}hj^sbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKhjhhubh)}(hK参数与上面描述的单个kobject_init()和kobject_add()函数相同。h]hK参数与上面描述的单个kobject_init()和kobject_add()函数相同。}(hjlhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id1ah ]h"]kobjects的初始化ah$]h&]uh1jRhjThhhhhKkubjS)}(hhh](jX)}(hUeventsh]hUevents}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jWhjhhhhhKubh)}(h当一个kobject被注册到kobject核心后,你需要向全世界宣布它已经被创建了。这可以通 过调用kobject_uevent()来实现::h]h当一个kobject被注册到kobject核心后,你需要向全世界宣布它已经被创建了。这可以通 过调用kobject_uevent()来实现:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hEint kobject_uevent(struct kobject *kobj, enum kobject_action action);h]hEint kobject_uevent(struct kobject *kobj, enum kobject_action action);}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKhjhhubh)}(h当kobject第一次被添加到内核时,使用 *KOBJ_ADD* 动作。这应该在该kobject的任 何属性或子对象被正确初始化后进行,因为当这个调用发生时,用户空间会立即开始寻 找它们。h](h2当kobject第一次被添加到内核时,使用 }(hjhhhNhNubhemphasis)}(h *KOBJ_ADD*h]hKOBJ_ADD}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 动作。这应该在该kobject的任 何属性或子对象被正确初始化后进行,因为当这个调用发生时,用户空间会立即开始寻 找它们。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h当kobject从内核中移除时(关于如何做的细节在下面), **KOBJ_REMOVE** 的uevent 将由kobject核心自动创建,所以调用者不必担心手动操作。h](hJ当kobject从内核中移除时(关于如何做的细节在下面), }(hjhhhNhNubjT)}(h**KOBJ_REMOVE**h]h KOBJ_REMOVE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jShjubhW 的uevent 将由kobject核心自动创建,所以调用者不必担心手动操作。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]ueventsah ]h"]ueventsah$]h&]uh1jRhjThhhhhKubjS)}(hhh](jX)}(h 引用计数h]h 引用计数}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jWhjhhhhhKubh)}(hkobject的关键功能之一是作为它所嵌入的对象的一个引用计数器。只要对该对象的引用 存在,该对象(以及支持它的代码)就必须继续存在。用于操作kobject的引用计数的低 级函数是::h]hkobject的关键功能之一是作为它所嵌入的对象的一个引用计数器。只要对该对象的引用 存在,该对象(以及支持它的代码)就必须继续存在。用于操作kobject的引用计数的低 级函数是:}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hZstruct kobject *kobject_get(struct kobject *kobj); void kobject_put(struct kobject *kobj);h]hZstruct kobject *kobject_get(struct kobject *kobj); void kobject_put(struct kobject *kobj);}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKhjhhubh)}(h`对kobject_get()的成功调用将增加kobject的引用计数器值并返回kobject的指针。h]h`对kobject_get()的成功调用将增加kobject的引用计数器值并返回kobject的指针。}(hj&hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h当引用被释放时,对kobject_put()的调用将递减引用计数值,并可能释放该对象。请注 意,kobject_init()将引用计数设置为1,所以设置kobject的代码最终需要kobject_put() 来释放该引用。h]h当引用被释放时,对kobject_put()的调用将递减引用计数值,并可能释放该对象。请注 意,kobject_init()将引用计数设置为1,所以设置kobject的代码最终需要kobject_put() 来释放该引用。}(hj4hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h因为kobjects是动态的,所以它们不能以静态方式或在堆栈中声明,而总是以动态方式分 配。未来版本的内核将包含对静态创建的kobjects的运行时检查,并将警告开发者这种不 当的使用。h]h因为kobjects是动态的,所以它们不能以静态方式或在堆栈中声明,而总是以动态方式分 配。未来版本的内核将包含对静态创建的kobjects的运行时检查,并将警告开发者这种不 当的使用。}(hjBhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX 如果你使用struct kobject只是为了给你的结构体提供一个引用计数器,请使用struct kref 来代替;kobject是多余的。关于如何使用kref结构体的更多信息,请参见Linux内核源代 码树中的文件Documentation/core-api/kref.rsth]hX 如果你使用struct kobject只是为了给你的结构体提供一个引用计数器,请使用struct kref 来代替;kobject是多余的。关于如何使用kref结构体的更多信息,请参见Linux内核源代 码树中的文件Documentation/core-api/kref.rst}(hjPhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id2ah ]h"] 引用计数ah$]h&]uh1jRhjThhhhhKubjS)}(hhh](jX)}(h创建“简单的”kobjectsh]h创建“简单的”kobjects}(hjihhhNhNubah}(h]h ]h"]h$]h&]uh1jWhjfhhhhhKubh)}(hX#有时,开发者想要的只是在sysfs层次结构中创建一个简单的目录,而不必去搞那些复杂 的ksets、显示和存储函数,以及其他细节。这是一个应该创建单个kobject的例外。要 创建这样一个条目(即简单的目录),请使用函数::h]hX"有时,开发者想要的只是在sysfs层次结构中创建一个简单的目录,而不必去搞那些复杂 的ksets、显示和存储函数,以及其他细节。这是一个应该创建单个kobject的例外。要 创建这样一个条目(即简单的目录),请使用函数:}(hjwhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjfhhubj)}(hQstruct kobject *kobject_create_and_add(const char *name, struct kobject *parent);h]hQstruct kobject *kobject_create_and_add(const char *name, struct kobject *parent);}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKhjfhhubh)}(h这个函数将创建一个kobject,并将其放在sysfs中指定的父kobject下面的位置。要创 建与此kobject相关的简单属性,请使用::h]h这个函数将创建一个kobject,并将其放在sysfs中指定的父kobject下面的位置。要创 建与此kobject相关的简单属性,请使用:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjfhhubj)}(hJint sysfs_create_file(struct kobject *kobj, const struct attribute *attr);h]hJint sysfs_create_file(struct kobject *kobj, const struct attribute *attr);}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKhjfhhubh)}(h或者::h]h或者:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjfhhubj)}(hPint sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);h]hPint sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKhjfhhubh)}(h这里使用的两种类型的属性,与已经用kobject_create_and_add()创建的kobject, 都可以是kobj_attribute类型,所以不需要创建特殊的自定义属性。h]h这里使用的两种类型的属性,与已经用kobject_create_and_add()创建的kobject, 都可以是kobj_attribute类型,所以不需要创建特殊的自定义属性。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjfhhubh)}(ht参见示例模块, ``samples/kobject/kobject-example.c`` ,以了解一个简单的 kobject和属性的实现。h](h参见示例模块, }(hjhhhNhNubj)}(h%``samples/kobject/kobject-example.c``h]h!samples/kobject/kobject-example.c}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh9 ,以了解一个简单的 kobject和属性的实现。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjfhhubeh}(h]id3ah ]h"]创建“简单的”kobjectsah$]h&]uh1jRhjThhhhhKubjS)}(hhh](jX)}(hktypes和释放方法h]hktypes和释放方法}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jWhjhhhhhKubh)}(hX以上讨论中还缺少一件重要的事情,那就是当一个kobject的引用次数达到零的时候 会发生什么。创建kobject的代码通常不知道何时会发生这种情况;首先,如果它知 道,那么使用kobject就没有什么意义。当sysfs被引入时,即使是可预测的对象生命 周期也会变得更加复杂,因为内核的其他部分可以获得在系统中注册的任何kobject 的引用。h]hX以上讨论中还缺少一件重要的事情,那就是当一个kobject的引用次数达到零的时候 会发生什么。创建kobject的代码通常不知道何时会发生这种情况;首先,如果它知 道,那么使用kobject就没有什么意义。当sysfs被引入时,即使是可预测的对象生命 周期也会变得更加复杂,因为内核的其他部分可以获得在系统中注册的任何kobject 的引用。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX最终的结果是,一个由kobject保护的结构体在其引用计数归零之前不能被释放。引 用计数不受创建kobject的代码的直接控制。因此,每当它的一个kobjects的最后一 个引用消失时,必须异步通知该代码。h]hX最终的结果是,一个由kobject保护的结构体在其引用计数归零之前不能被释放。引 用计数不受创建kobject的代码的直接控制。因此,每当它的一个kobjects的最后一 个引用消失时,必须异步通知该代码。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h一旦你通过kobject_add()注册了你的kobject,你绝对不能使用kfree()来直接释 放它。唯一安全的方法是使用kobject_put()。在kobject_init()之后总是使用 kobject_put()以避免错误的发生是一个很好的做法。h]h一旦你通过kobject_add()注册了你的kobject,你绝对不能使用kfree()来直接释 放它。唯一安全的方法是使用kobject_put()。在kobject_init()之后总是使用 kobject_put()以避免错误的发生是一个很好的做法。}(hj.hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h`这个通知是通过kobject的release()方法完成的。通常这样的方法有如下形式::h]h_这个通知是通过kobject的release()方法完成的。通常这样的方法有如下形式:}(hj<hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hvoid my_object_release(struct kobject *kobj) { struct my_object *mine = container_of(kobj, struct my_object, kobj); /* Perform any additional cleanup on this object, then... */ kfree(mine); }h]hvoid my_object_release(struct kobject *kobj) { struct my_object *mine = container_of(kobj, struct my_object, kobj); /* Perform any additional cleanup on this object, then... */ kfree(mine); }}hjJsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhKhjhhubh)}(hX有一点很重要:每个kobject都必须有一个release()方法,而且这个kobject必 须持续存在(处于一致的状态),直到这个方法被调用。如果这些约束条件没有 得到满足,那么代码就是有缺陷的。注意,如果你忘记提供release()方法,内 核会警告你。不要试图通过提供一个“空”的释放函数来摆脱这个警告。h]hX有一点很重要:每个kobject都必须有一个release()方法,而且这个kobject必 须持续存在(处于一致的状态),直到这个方法被调用。如果这些约束条件没有 得到满足,那么代码就是有缺陷的。注意,如果你忘记提供release()方法,内 核会警告你。不要试图通过提供一个“空”的释放函数来摆脱这个警告。}(hjXhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h如果你的清理函数只需要调用kfree(),那么你必须创建一个包装函数,该函数 使用container_of()来向上造型到正确的类型(如上面的例子所示),然后在整个 结构体上调用kfree()。h]h如果你的清理函数只需要调用kfree(),那么你必须创建一个包装函数,该函数 使用container_of()来向上造型到正确的类型(如上面的例子所示),然后在整个 结构体上调用kfree()。}(hjfhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h注意,kobject的名字在release函数中是可用的,但它不能在这个回调中被改 变。否则,在kobject核心中会出现内存泄漏,这让人很不爽。h]h注意,kobject的名字在release函数中是可用的,但它不能在这个回调中被改 变。否则,在kobject核心中会出现内存泄漏,这让人很不爽。}(hjthhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h有趣的是,release()方法并不存储在kobject本身;相反,它与ktype相关。 因此,让我们引入结构体kobj_type::h]h有趣的是,release()方法并不存储在kobject本身;相反,它与ktype相关。 因此,让我们引入结构体kobj_type:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hXstruct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; const struct attribute_group **default_groups; const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid); };h]hXstruct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; const struct attribute_group **default_groups; const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid); };}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhMhjhhubh)}(hX 这个结构提用来描述一个特定类型的kobject(或者更正确地说,包含对象的 类型)。每个kobject都需要有一个相关的kobj_type结构;当你调用 kobject_init()或kobject_init_and_add()时必须指定一个指向该结构的 指针。h]hX 这个结构提用来描述一个特定类型的kobject(或者更正确地说,包含对象的 类型)。每个kobject都需要有一个相关的kobj_type结构;当你调用 kobject_init()或kobject_init_and_add()时必须指定一个指向该结构的 指针。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM hjhhubh)}(h当然,kobj_type结构中的release字段是指向这种类型的kobject的release() 方法的一个指针。另外两个字段(sysfs_ops 和 default_groups)控制这种 类型的对象如何在 sysfs 中被表示;它们超出了本文的范围。h]h当然,kobj_type结构中的release字段是指向这种类型的kobject的release() 方法的一个指针。另外两个字段(sysfs_ops 和 default_groups)控制这种 类型的对象如何在 sysfs 中被表示;它们超出了本文的范围。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hwdefault_groups 指针是一个默认属性的列表,它将为任何用这个 ktype 注册 的 kobject 自动创建。h]hwdefault_groups 指针是一个默认属性的列表,它将为任何用这个 ktype 注册 的 kobject 自动创建。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]ktypesah ]h"]ktypes和释放方法ah$]h&]uh1jRhjThhhhhKubjS)}(hhh](jX)}(hksetsh]hksets}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jWhjhhhhhMubh)}(h一个kset仅仅是一个希望相互关联的kobjects的集合。没有限制它们必须是相 同的ktype,但是如果它们不是相同的,就要非常小心。h]h一个kset仅仅是一个希望相互关联的kobjects的集合。没有限制它们必须是相 同的ktype,但是如果它们不是相同的,就要非常小心。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h一个kset有以下功能:h]h一个kset有以下功能:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj )}(hX- 它像是一个包含一组对象的袋子。一个kset可以被内核用来追踪“所有块 设备”或“所有PCI设备驱动”。 - kset也是sysfs中的一个子目录,与kset相关的kobjects可以在这里显示 出来。每个kset都包含一个kobject,它可以被设置为其他kobject的父对象; sysfs层次结构的顶级目录就是以这种方式构建的。 - Ksets可以支持kobjects的 "热插拔",并影响uevent事件如何被报告给 用户空间。 在面向对象的术语中,“kset”是顶级的容器类;ksets包含它们自己的kobject, 但是这个kobject是由kset代码管理的,不应该被任何其他用户所操纵。 kset在一个标准的内核链表中保存它的子对象。Kobjects通过其kset字段指向其 包含的kset。在几乎所有的情况下,属于一个kset的kobjects在它们的父 对象中都有那个kset(或者,严格地说,它的嵌入kobject)。 由于kset中包含一个kobject,它应该总是被动态地创建,而不是静态地 或在堆栈中声明。要创建一个新的kset,请使用:: struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj); h](j)}(hhh](j)}(h它像是一个包含一组对象的袋子。一个kset可以被内核用来追踪“所有块 设备”或“所有PCI设备驱动”。 h]h)}(h它像是一个包含一组对象的袋子。一个kset可以被内核用来追踪“所有块 设备”或“所有PCI设备驱动”。h]h它像是一个包含一组对象的袋子。一个kset可以被内核用来追踪“所有块 设备”或“所有PCI设备驱动”。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM hjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hkset也是sysfs中的一个子目录,与kset相关的kobjects可以在这里显示 出来。每个kset都包含一个kobject,它可以被设置为其他kobject的父对象; sysfs层次结构的顶级目录就是以这种方式构建的。 h]h)}(hkset也是sysfs中的一个子目录,与kset相关的kobjects可以在这里显示 出来。每个kset都包含一个kobject,它可以被设置为其他kobject的父对象; sysfs层次结构的顶级目录就是以这种方式构建的。h]hkset也是sysfs中的一个子目录,与kset相关的kobjects可以在这里显示 出来。每个kset都包含一个kobject,它可以被设置为其他kobject的父对象; sysfs层次结构的顶级目录就是以这种方式构建的。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM#hjubah}(h]h ]h"]h$]h&]uh1jhjubj)}(hcKsets可以支持kobjects的 "热插拔",并影响uevent事件如何被报告给 用户空间。 h]h)}(hbKsets可以支持kobjects的 "热插拔",并影响uevent事件如何被报告给 用户空间。h]hfKsets可以支持kobjects的 “热插拔”,并影响uevent事件如何被报告给 用户空间。}(hj8hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM'hj4ubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]j7j8uh1jhhhM hjubh)}(h在面向对象的术语中,“kset”是顶级的容器类;ksets包含它们自己的kobject, 但是这个kobject是由kset代码管理的,不应该被任何其他用户所操纵。h]h在面向对象的术语中,“kset”是顶级的容器类;ksets包含它们自己的kobject, 但是这个kobject是由kset代码管理的,不应该被任何其他用户所操纵。}(hjRhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM*hjubh)}(hXkset在一个标准的内核链表中保存它的子对象。Kobjects通过其kset字段指向其 包含的kset。在几乎所有的情况下,属于一个kset的kobjects在它们的父 对象中都有那个kset(或者,严格地说,它的嵌入kobject)。h]hXkset在一个标准的内核链表中保存它的子对象。Kobjects通过其kset字段指向其 包含的kset。在几乎所有的情况下,属于一个kset的kobjects在它们的父 对象中都有那个kset(或者,严格地说,它的嵌入kobject)。}(hj`hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM-hjubh)}(h由于kset中包含一个kobject,它应该总是被动态地创建,而不是静态地 或在堆栈中声明。要创建一个新的kset,请使用::h]h由于kset中包含一个kobject,它应该总是被动态地创建,而不是静态地 或在堆栈中声明。要创建一个新的kset,请使用:}(hjnhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM1hjubj)}(hstruct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj);h]hstruct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj);}hj|sbah}(h]h ]h"]h$]h&]j-j.uh1jhhhM4hjubeh}(h]h ]h"]h$]h&]uh1j hhhM hjhhubh)}(h*当你完成对kset的处理后,调用::h]h)当你完成对kset的处理后,调用:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM8hjhhubj)}(h%void kset_unregister(struct kset *k);h]h%void kset_unregister(struct kset *k);}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhM:hjhhubh)}(h来销毁它。这将从sysfs中删除该kset并递减其引用计数值。当引用计数 为零时,该kset将被释放。因为对该kset的其他引用可能仍然存在, 释放可能发生在kset_unregister()返回之后。h]h来销毁它。这将从sysfs中删除该kset并递减其引用计数值。当引用计数 为零时,该kset将被释放。因为对该kset的其他引用可能仍然存在, 释放可能发生在kset_unregister()返回之后。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM<hjhhubh)}(hg一个使用kset的例子可以在内核树中的 ``samples/kobject/kset-example.c`` 文件中看到。h](h2一个使用kset的例子可以在内核树中的 }(hjhhhNhNubj)}(h"``samples/kobject/kset-example.c``h]hsamples/kobject/kset-example.c}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh 文件中看到。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM@hjhhubh)}(h{如果一个kset希望控制与它相关的kobjects的uevent操作,它可以使用 结构体kset_uevent_ops来处理它::h]hz如果一个kset希望控制与它相关的kobjects的uevent操作,它可以使用 结构体kset_uevent_ops来处理它:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMChjhhubj)}(hstruct kset_uevent_ops { int (* const filter)(struct kobject *kobj); const char *(* const name)(struct kobject *kobj); int (* const uevent)(struct kobject *kobj, struct kobj_uevent_env *env); };h]hstruct kset_uevent_ops { int (* const filter)(struct kobject *kobj); const char *(* const name)(struct kobject *kobj); int (* const uevent)(struct kobject *kobj, struct kobj_uevent_env *env); };}hjsbah}(h]h ]h"]h$]h&]j-j.uh1jhhhMFhjhhubh)}(h过滤器函数允许kset阻止一个特定kobject的uevent被发送到用户空间。 如果该函数返回0,该uevent将不会被发射出去。h]h过滤器函数允许kset阻止一个特定kobject的uevent被发送到用户空间。 如果该函数返回0,该uevent将不会被发射出去。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMMhjhhubh)}(hname函数将被调用以覆盖uevent发送到用户空间的kset的默认名称。默 认情况下,该名称将与kset本身相同,但这个函数,如果存在,可以覆盖 该名称。h]hname函数将被调用以覆盖uevent发送到用户空间的kset的默认名称。默 认情况下,该名称将与kset本身相同,但这个函数,如果存在,可以覆盖 该名称。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMPhjhhubh)}(h当uevent即将被发送至用户空间时,uevent函数将被调用,以允许更多 的环境变量被添加到uevent中。h]h当uevent即将被发送至用户空间时,uevent函数将被调用,以允许更多 的环境变量被添加到uevent中。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMThjhhubh)}(hX?有人可能会问,鉴于没有提出执行该功能的函数,究竟如何将一个kobject 添加到一个kset中。答案是这个任务是由kobject_add()处理的。当一个 kobject被传递给kobject_add()时,它的kset成员应该指向这个kobject 所属的kset。 kobject_add()将处理剩下的部分。h]hX?有人可能会问,鉴于没有提出执行该功能的函数,究竟如何将一个kobject 添加到一个kset中。答案是这个任务是由kobject_add()处理的。当一个 kobject被传递给kobject_add()时,它的kset成员应该指向这个kobject 所属的kset。 kobject_add()将处理剩下的部分。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMWhjhhubh)}(hX%如果属于一个kset的kobject没有父kobject集,它将被添加到kset的目 录中。并非所有的kset成员都必须住在kset目录中。如果在添加kobject 之前分配了一个明确的父kobject,那么该kobject将被注册到kset中, 但是被添加到父kobject下面。h]hX%如果属于一个kset的kobject没有父kobject集,它将被添加到kset的目 录中。并非所有的kset成员都必须住在kset目录中。如果在添加kobject 之前分配了一个明确的父kobject,那么该kobject将被注册到kset中, 但是被添加到父kobject下面。}(hj. hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM\hjhhubeh}(h]ksetsah ]h"]ksetsah$]h&]uh1jRhjThhhhhMubjS)}(hhh](jX)}(h 移除Kobjecth]h 移除Kobject}(hjG hhhNhNubah}(h]h ]h"]h$]h&]uh1jWhjD hhhhhMcubh)}(hX当一个kobject在kobject核心注册成功后,在代码使用完它时,必须将其 清理掉。要做到这一点,请调用kobject_put()。通过这样做,kobject核 心会自动清理这个kobject分配的所有内存。如果为这个对象发送了 ``KOBJ_ADD`` uevent,那么相应的 ``KOBJ_REMOVE`` uevent也将被发送,任何其他的 sysfs内务将被正确处理。h](hX当一个kobject在kobject核心注册成功后,在代码使用完它时,必须将其 清理掉。要做到这一点,请调用kobject_put()。通过这样做,kobject核 心会自动清理这个kobject分配的所有内存。如果为这个对象发送了 }(hjU hhhNhNubj)}(h ``KOBJ_ADD``h]hKOBJ_ADD}(hj] hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjU ubh uevent,那么相应的 }(hjU hhhNhNubj)}(h``KOBJ_REMOVE``h]h KOBJ_REMOVE}(hjo hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjU ubhI uevent也将被发送,任何其他的 sysfs内务将被正确处理。}(hjU hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMehjD hhubh)}(hXp如果你需要分两次对kobject进行删除(比如说在你要销毁对象时无权睡眠), 那么调用kobject_del()将从sysfs中取消kobject的注册。这使得kobject “不可见”,但它并没有被清理掉,而且该对象的引用计数仍然是一样的。在稍 后的时间调用kobject_put()来完成与该kobject相关的内存的清理。h]hXp如果你需要分两次对kobject进行删除(比如说在你要销毁对象时无权睡眠), 那么调用kobject_del()将从sysfs中取消kobject的注册。这使得kobject “不可见”,但它并没有被清理掉,而且该对象的引用计数仍然是一样的。在稍 后的时间调用kobject_put()来完成与该kobject相关的内存的清理。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMkhjD hhubh)}(hX:kobject_del()可以用来放弃对父对象的引用,如果循环引用被构建的话。 在某些情况下,一个父对象引用一个子对象是有效的。循环引用必须通过明 确调用kobject_del()来打断,这样一个释放函数就会被调用,前一个循环 中的对象会相互释放。h]hX:kobject_del()可以用来放弃对父对象的引用,如果循环引用被构建的话。 在某些情况下,一个父对象引用一个子对象是有效的。循环引用必须通过明 确调用kobject_del()来打断,这样一个释放函数就会被调用,前一个循环 中的对象会相互释放。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMphjD hhubeh}(h]kobjectah ]h"] 移除kobjectah$]h&]uh1jRhjThhhhhMcubjS)}(hhh](jX)}(h示例代码出处h]h示例代码出处}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jWhj hhhhhMwubh)}(h关于正确使用ksets和kobjects的更完整的例子,请参见示例程序 ``samples/kobject/{kobject-example.c,kset-example.c}`` ,如果 您选择 ``CONFIG_SAMPLE_KOBJECT`` ,它们将被构建为可加载模块。h](hP关于正确使用ksets和kobjects的更完整的例子,请参见示例程序 }(hj hhhNhNubj)}(h6``samples/kobject/{kobject-example.c,kset-example.c}``h]h2samples/kobject/{kobject-example.c,kset-example.c}}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh ,如果 您选择 }(hj hhhNhNubj)}(h``CONFIG_SAMPLE_KOBJECT``h]hCONFIG_SAMPLE_KOBJECT}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh+ ,它们将被构建为可加载模块。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMyhj hhubeh}(h]id4ah ]h"]示例代码出处ah$]h&]uh1jRhjThhhhhMwubeh}(h](kobjectsksetsktypesjQeh ]h"](I关于kobjects、ksets和ktypes的一切你没想过需要了解的东西cn_core_api_kobject.rsteh$]h&]uh1jRhhhhhhhK expect_referenced_by_name}j jFsexpect_referenced_by_id}jQjFsubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(jWN generatorN datestampN source_linkN source_urlN toc_backlinksentryfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerj& error_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourceh، _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}refids}jQ]jFasnameids}(j jQj j jjjj|jjjcj`jjjjjA j> j j j j u nametypes}(j j jjjjcjjjA j j uh}(jQjTj jTjjj|jjjj`jjjfjjj> jj jD j j u footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}j4 KsRparse_messages]transform_messages]hsystem_message)}(hhh]h)}(hhh]h=Hyperlink target "cn-core-api-kobject-rst" is not referenced.}hj sbah}(h]h ]h"]h$]h&]uh1hhj ubah}(h]h ]h"]h$]h&]levelKtypeINFOsourceh،lineKuh1j uba transformerN include_log]5Documentation/translations/zh_CN/core-api/kobject.rst(NNNNta decorationNhhub.