The zen of kobjects (linux kobjects 要旨)

来源:互联网 发布:浙江人事考试机考网络 编辑:程序博客网 时间:2024/05/17 01:58
The "kobject" structure first made its appearance in the 2.5.45 developmentkernel. It was initially meant as a simple way of unifying kernel codewhich manages reference counted objects. The kobject has since encountereda bit of "mission creep," however; it is now the glue that holds much ofthe device model and its sysfs interface together. It is rare for a driverwriter to have to work with kobjects directly; they are usually hidden instructures created by higher-level code. Kobjects have a certain tendencyto leak through the intervening layers, however, and make their presenceknown. So a familiarity with what they are and how they work is a goodthing to have. This document will cover the kobject type and relatedtopics, but will gloss over most of the interactions between kobjects andsysfs (those will be covered separately, later on).

Part of the difficulty in understanding the driver model - and the kobjectabstraction upon which it is built - is that there is no obvious startingplace. Dealing with kobjects requires understanding a few different types,all of which make reference to each other. In an attempt to make thingseasier, we'll take a multi-pass approach, starting with vague terms andadding detail as we go. To that end, here are some quick definitions ofsome terms we will be working with.

  • A kobject is an object of type struct kobject. Kobjects have a name and a reference count. A kobject also has a parent pointer (allowing kobjects to be arranged into hierarchies), a specific type, and, perhaps, a representation in the sysfs virtual filesystem.

    Kobjects are generally not interesting on their own; instead, they are usually embedded within some other structure which contains the stuff the code is really interested in.

  • A ktype is a type associated with a kobject. The ktype controls what happens when a kobject is no longer referenced and the kobject's default representation in sysfs.

  • A kset is a group of kobjects all of which are embedded in structures of the same type. The kset is the basic container type for collections of kobjects. Ksets contain their own kobjects, for what it's worth. Among other things, that means that a kobject's parent is usually the kset that contains it, though things do not normally have to be that way.

    When you see a sysfs directory full of entries, generally each of those entries corresponds to a kobject in the same kset.

  • A subsystem is a collection of ksets which, collectively, make up a major sub-part of the kernel. Subsystems normally correspond to the top-level directories in sysfs.

We'll look at how to create and manipulate all of these types. A bottom-upapproach will be taken, so we'll go back to kobjects.

Embedding kobjects

It is rare (even unknown) for kernel code to create a standalone kobject;instead, kobjects are used to control access to a larger, domain-specificobject. To this end, kobjects will be found embedded in other structures.If you are used to thinking of things in object-oriented terms, kobjectscan be seen as a top-level, abstract class from which other classes arederived. A kobject implements a set of capabilities which are notparticularly useful by themselves, but which are nice to have in otherobjects. The C language does not allow for the direct expression ofinheritance, so other techniques - such as structure embedding - must beused.

So, for example, the 2.6.0-test6 version of struct cdev, thestructure describing a char device, is:

    struct cdev {    struct kobject kobj;struct module *owner;struct file_operations *ops;struct list_head list;    };

If you have a struct cdev structure, finding its embedded kobjectis just a matter of using thekobj pointer. Code that works withkobjects will often have the opposite problem, however: given astructkobject pointer, what is the pointer to the containing structure?You should avoid tricks (such as assuming that the kobject is at thebeginning of the structure) and, instead, use thecontainer_of()macro, found in<linux/kernel.h>:

container_of(pointer, type, member)

where pointer is the pointer to the embedded kobject,type is the type of the containing structure, andmemberis the name of the structure field to whichpointer points. Thereturn value fromcontainer_of() is a pointer to the giventype. So, for example, a pointer to astruct kobjectembedded within astruct cdev called "kp" could be converted to apointer to the containing structure with:

    struct cdev *device = container_of(kp, struct cdev, kobj);

Programmers will often define a simple macro for "back-casting" kobjectpointers to the containing type.

Initialization of kobjects

Code which creates a kobject must, of course, initialize that object. Someof the internal fields are setup with a (mandatory) call tokobject_init():

    void kobject_init(struct kobject *kobj);

Among other things, kobject_init() sets the kobject's referencecount to one.Callingkobject_init() is not sufficient, however. Kobject usersmust, at a minimum, set the name of the kobject; this is the name that willbe used in sysfs entries. If you dig through the kernel source,you will find code which copies a string directly into the kobject'sname field, but that approach should be avoided. Instead, use:

    int kobject_set_name(struct kobject *kobj, const char *format, ...);

This function takes a printk-style variable argument list.Believe it or not, it is actually possible for this operation to fail;conscientious code should check the return value and react accordingly.

The other kobject fields which should be set, directly or indirectly, bythe creator are its ktype, kset, and parent. We will get to those shortly.

Reference counts

One of the key functions of a kobject is to serve as a reference counterfor the object in which it is embedded. As long as references to theobject exist, the object (and the code which supports it) must continue toexist. The low-level functions for manipulating a kobject's referencecounts are:

    struct kobject *kobject_get(struct kobject *kobj);    void kobject_put(struct kobject *kobj);

A successful call to kobject_get() will increment the kobject'sreference counter and return the pointer to the kobject. If, however, thekobject is already in the process of being destroyed, the operation willfail andkobject_get() will returnNULL. This returnvalue must always be tested, or no end of unpleasant race conditions couldresult.

When a reference is released, the call to kobject_put() willdecrement the reference count and, possibly, free the object. Note thatkobject_init() sets the reference count to one, so the code whichsets up the kobject will need to do akobject_put() eventually torelease that reference.

Note that, in many cases, the reference count in the kobject itself may notbe sufficient to prevent race conditions. The existence of a kobject (andits containing structure) may well, for example, require the continuedexistence of the module which created that kobject. It would not do tounload that module while the kobject is still being passed around. That iswhy thecdev structure we saw above contains astructmodule pointer. The reference counting forstruct cdev isimplemented as follows:

    struct kobject *cdev_get(struct cdev *p)    {    struct module *owner = p->owner;    struct kobject *kobj;    if (owner && !try_module_get(owner))    return NULL;    kobj = kobject_get(&p->kobj);    if (!kobj)    module_put(owner);    return kobj;    }

Creating a reference to a cdev structure requires creating areference also to the module which owns it. Socdev_get() usestry_module_get() to attempt to increment that module's usagecount. If that operation succeeds,kobject_get() is used toincrement the kobject's reference count as well. That operation couldfail, of course, so the code checks the return value fromkobject_get() and releases its reference to the module if thingsdon't work out.

Hooking into sysfs

An initialized kobject will perform reference counting without trouble, butit will not appear in sysfs. To create sysfs entries, kernel code mustpass the object tokobject_add():

    int kobject_add(struct kobject *kobj);

As always, this operation can fail. The function:

    void kobject_del(struct kobject *kobj);

will remove the kobject from sysfs.

There is a kobject_register() function, which is really just thecombination of the calls tokobject_init() andkobject_add(). Similarly,kobject_unregister() will callkobject_del(), then callkobject_put() to release theinitial reference created withkobject_register() (or reallykobject_init()).

ktypes and release methods

One important thing still missing from the discussion is what happens to akobject when its reference count reaches zero. The code which created thekobject generally does not know when that will happen; if it did, therewould be little point in using a kobject in the first place. Evenpredicatable object lifecycles become more complicated when sysfs isbrought in; user-space programs can keep a reference to a kobject (bykeeping one of its associated sysfs files open) for an arbitrary period oftime.

The end result is that a structure protected by a kobject cannot be freedbefore its reference count goes to zero. The reference count is not underthe direct control of the code which created the kobject. So that codemust be notified asynchronously whenever the last reference to one of itskobjects goes away.

This notification is done through a kobject's release() method.Usually such a method has a form like:

    void 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);    }

One important point cannot be overstated: every kobject must have arelease() method, and the kobject must persist (in a consistent state)until that method is called. If these constraints are not met, the code isflawed.

Interestingly, the release() method is not stored in the kobjectitself; instead, it is associated with the ktype. So let us introducestruct kobj_type:

    struct kobj_type {    void (*release)(struct kobject *);    struct sysfs_ops*sysfs_ops;    struct attribute**default_attrs;    };

This structure is used to describe a particular type of kobject (or, morecorrectly, of containing object). Every kobject needs to have anassociatedkobj_type structure; a pointer to that structure can beplaced in the kobject'sktype field at initialization time, or(more likely) it can be defined by the kobject's containing kset.

The release field in struct kobj_type is, of course, apointer to therelease() method for this type of kobject. Theother two fields (sysfs_ops anddefault_attrs) controlhow objects of this type are represented in sysfs; they are beyond thescope of this document.

ksets

In many ways, a kset looks like an extension of the kobj_typestructure; a kset is a collection of identical kobjects. But, whilestruct kobj_type concerns itself with thetype of anobject,struct kset is concerned with aggregation and collection.The two concepts have been separated so that objects of identical type canappear in distinct sets.

A kset serves these functions:

  • It serves as a bag containing a group of identical objects. A kset can be used by the kernel to track "all block devices" or "all PCI device drivers."

  • A kset is the directory-level glue that holds the device model (and sysfs) together. Every kset contains a kobject which can be set up to be the parent of other kobjects; in this way the device model hierarchy is constructed.

  • Ksets can support the "hotplugging" of kobjects and influence how hotplug events are reported to user space.

In object-oriented terms, "kset" is the top-level container class; ksetsinherit their own kobject, and can be treated as a kobject as well.

[ksets and kobjects]A kset keeps its children in a standard kernel linked list. Kobjects pointback to their containing kset via their kset field.In almost allcases, the contained kobjects also have a pointer to the kset (or, strictly, itsembedded kobject) in theirparent field. So, typically, a ksetand its kobjects look something like what you see in the diagram to theright. Do bear in mind that (1) all of the contained kobjects in thediagram are actually embedded within some other type, possibly even otherksets, and (2) it is not required that a kobject's parent be thecontaining kset.

For initialization and setup, ksets have an interface very similar to thatof kobjects. The following functions exist:

    void kset_init(struct kset *kset);    int kset_add(struct kset *kset);    int kset_register(struct kset *kset);    void kset_unregister(struct kset *kset);

For the most part, these functions just call the analogouskobject_ function on the kset's embedded kobject.

For managing the reference counts of ksets, the situation is about thesame:

    struct kset *kset_get(struct kset *kset);    void kset_put(struct kset *kset);

A kset, too, has a name, which is stored in the embedded kobject. So, ifyou have a kset calledmy_set, you would set its name with:

    kobject_set_name(my_set->kobj, "The name");

Ksets also have a pointer (in the ktype field) to thekobj_type structure describing the kobjects it contains. This type will be applied to any kobject whichdoes not contain a pointer to its ownkobj_type structure.

Another attribute of a kset is a set of hotplug operations; theseoperations are invoked whenever a kobject enters or leaves the kset. Theyare able to determine whether a user-space hotplug event is generated forthis change, and to affect how that event is presented. The hotplugoperations are beyond the scope of this document; they will be discussedlater with sysfs.

One might ask how, exactly, a kobject is added to a kset, given that nofunctions which perform that function have been presented. The answer isthat this task is handled bykobject_add(). When a kobject ispassed tokobject_add(), its kset member should point tothe kset to which the kobject will belong.kobject_add() willhandle the rest. There is currently no other way to add a kobject to akset without directly messing with the list pointers.

Finally, a kset contains a subsystem pointer (called subsys). Soit must be time to talk about subsystems.

Subsystems

A subsystem is a representation for a high-level portion of the kernel as awhole. It is actually a simple structure:

    struct subsystem {    struct ksetkset;    struct rw_semaphorerwsem;    };

A subsystem, thus, is really just a wrapper around a kset. In fact, lifeis not quite that simple; a single subsystem can contain multiple ksets.This containment is represented by thesubsys pointer instruct kset; so, if there are multiple ksets in a subsystem, itwill not be possible to find all of them directly from thesubsystem structure.

Every kset must belong to a subsystem; the subsystem's rwsemsemaphore is used to serialize access to a kset's internal linked list.

Subsystems are often declared with a special macro:

    decl_subsys(char *name, struct kobj_type *type,                 struct kset_hotplug_ops *hotplug_ops);

This macro just creates a struct subsystem (its name is the name given to the macro with_subsys appended) with theinternal kset initialized with the giventype andhotplug_ops.

Subsystems have the usual set of setup and teardown functions:

    void subsystem_init(struct subsystem *subsys);    int subsystem_register(struct subsystem *subsys);    void subsystem_unregister(struct subsystem *subsys);    struct subsystem *subsys_get(struct subsystem *subsys)    void subsys_put(struct subsystem *subsys);

Most of these operations just act upon the subsystem's kset.

Kobject initialization again

Now that we have covered all of that stuff, we can talk in detail about howa kobject should be prepared for its existence in the kernel. Here are allof thestruct kobject fields which must be initialized somehow:

  • name and k_name - the name of the object. These fields should always be initialized withkobject_set_name().

  • refcount is the kobject's reference count; it is initialized by kobject_init()

  • parent is the kobject's parent in whatever hierarchy it belongs to. It can be set explicitly by the creator. Ifparent isNULL when kobject_add() is called, it will be set to the kobject of the containing kset.

  • kset is a pointer to the kset which will contain this kobject; it should be set prior to callingkobject_add().

  • ktype is the type of the kobject. If the kobject is contained within a kset, and that kset has a type set in itsktype field, then this field in the kobject will not be used. Otherwise it should be set to a suitablekobj_type structure.

Often, much of the initialization of a kobject is handled by the layer thatmanages the containing kset. Thus, to get back to our old example, a chardriver might create astruct cdev, but it need not worry aboutsetting any of the fields in the embedded kobject - except for the name.Everything else is handled by the char device layer.

Looking forward

So far, we have covered the operations used to set up and manipulatekobjects. The core concept is relatively simple: kobjects can be used to(1) maintain a reference count for an object and clean up when theobject is no longer used, and (2) create a hierarchical data structurethrough kset membership.

What is missing so far is how kobjects represent themselves to user space.The sysfs interface to kobjects makes it easy to export information to (andto receive information from) user space. The symbolic linking features ofsysfs allow the creation of pointers across distinct kobject hierarchies.Stay tuned for a description of how all that works.


转载于:

1。 https://lwn.net/Articles/51437/

0 0
原创粉丝点击