研究UEVENT相关东西,看到2篇优秀的博文,转载与此
http://blog.chinaunix.net/u3/92745/showart_2145668.html
LINUX設備驅動之設備模型一--kobject
LINUX設備驅動驅動程序模型的核心數據結構是kobject,kobject數據結構在\linux\kobject.h中定義:?
struct kobject {
?????? const char???????????? *name;
?????? struct list_head?????? entry;
?????? struct kobject???????? *parent;
?????? struct kset???????????? *kset;
?????? struct kobj_type???? *ktype;
?????? struct sysfs_dirent? *sd;
?????? struct kref???????????? kref;
?????? unsigned int state_initialized:1;
?????? unsigned int state_in_sysfs:1;
?????? unsigned int state_add_uevent_sent:1;
?????? unsigned int state_remove_uevent_sent:1;
?????? unsigned int uevent_suppress:1;
};
每個kobject都有它的父節點parent、kset、kobj_type指針,這三者是驅動模型的基本結構,kset是kobject的集合,在\linux\kobject.h中定義:
struct kset {
?????? struct list_head list;
?????? spinlock_t list_lock;
?????? struct kobject kobj;
?????? struct kset_uevent_ops *uevent_ops;
};
可以看到每個kset內嵌了一個kobject(kobj字段),用來表示其自身節點,其list字段指向了所包含的kobject的鏈表頭。我們在后面的分析中將看到kobject如果沒有指定父節點,parent將指向其kset內嵌的kobject。
每個kobject都有它的kobj_type字段指針,用來表示kobject在文件系統中的操作方法,kobj_type結構也在\linux\kobject.h中定義:
struct kobj_type {
?????? void (*release)(struct kobject *kobj);
?????? struct sysfs_ops *sysfs_ops;
?????? struct attribute ** default_attrs;
};
release方法是在kobject釋放是調用,sysfs_ops指向kobject對應的文件操作,default_attrskobject的默認屬性,sysfs_ops的將使用default_attrs屬性(在后面的分析中我們將會看到)。
從上面的分析我們可以想象到kobject、kset、kobj_type的層次結構:
500)this.width=500;" width="500" border="0">
我們可以把一個kobject添加到文件系統中去(實際上是添加到其父節點所代表的kset中去),內核提供kobject_create_and_add()接口函數:
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
?????? struct kobject *kobj;
?????? int retval;
?
?????? kobj = kobject_create();
?????? if (!kobj)
????????????? return NULL;
?
?????? retval = kobject_add(kobj, parent, "%s", name);
?????? if (retval) {
????????????? printk(KERN_WARNING "%s: kobject_add error: %d\n",
????????????? ?????? __func__, retval);
????????????? kobject_put(kobj);
????????????? kobj = NULL;
?????? }
?????? return kobj;
}
kobject _create()為要創建的kobject分配內存空間并對其初始化。
struct kobject *kobject_create(void)
{
?????? struct kobject *kobj;
?
?????? kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
?????? if (!kobj)
????????????? return NULL;
?
?????? kobject_init(kobj, &dynamic_kobj_ktype);
?????? return kobj;
}
kobject_init()對kobject基本字段進行初始化,用輸入參數設置kobj_type屬性。
這里粘出代碼以供參考:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
?????? char *err_str;
?
?????? if (!kobj) {
????????????? err_str = "invalid kobject pointer!";
????????????? goto error;
?????? }
?????? if (!ktype) {
????????????? err_str = "must have a ktype to be initialized properly!\n";
????????????? goto error;
?????? }
?????? if (kobj->state_initialized) {
????????????? /* do not error out as sometimes we can recover */
????????????? printk(KERN_ERR "kobject (%p): tried to init an initialized "
????????????? ?????? "object, something is seriously wrong.\n", kobj);
????????????? dump_stack();
?????? }
?
?????? kobject_init_internal(kobj);
?????? kobj->ktype = ktype;
?????? return;
?
error:
?????? printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
?????? dump_stack();
}
static void kobject_init_internal(struct kobject *kobj)
{
?????? if (!kobj)
????????????? return;
?????? kref_init(&kobj->kref);
?????? INIT_LIST_HEAD(&kobj->entry);
?????? kobj->state_in_sysfs = 0;
?????? kobj->state_add_uevent_sent = 0;
?????? kobj->state_remove_uevent_sent = 0;
?????? kobj->state_initialized = 1;
}
接著看kobject_add()函數:
int kobject_add(struct kobject *kobj, struct kobject *parent,
????????????? const char *fmt, ...)
{
?????? va_list args;
?????? int retval;
?
?????? if (!kobj)
????????????? return -EINVAL;
?
?????? if (!kobj->state_initialized) {
????????????? printk(KERN_ERR "kobject '%s' (%p): tried to add an "
????????????? ?????? "uninitialized object, something is seriously wrong.\n",
????????????? ?????? kobject_name(kobj), kobj);
????????????? dump_stack();
????????????? return -EINVAL;
?????? }
?????? va_start(args, fmt);
?????? retval = kobject_add_varg(kobj, parent, fmt, args);
?????? va_end(args);
?
?????? return retval;
}
在上面的初始化中已把位變量設位1
va_start(args, fmt)和va_end(args)使用可變參數(可見參數用法不在這里分析),在kobject_add_varg中將把fmt指向的內容賦給kobject的name字段。下面我們詳細看看kobject_add_varg函數:
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
???????????????????? ??? const char *fmt, va_list vargs)
{
?????? int retval;
?
?????? retval = kobject_set_name_vargs(kobj, fmt, vargs);
?????? if (retval) {
????????????? printk(KERN_ERR "kobject: can not set name properly!\n");
????????????? return retval;
?????? }
?????? kobj->parent = parent;
?????? return kobject_add_internal(kobj);
}
kobject_set_name_vargs(kobj, fmt, vargs),如果kobj的name字段指向的內容為空,則為分配一個內存空間并用fmt指向的內容初始化,把地址賦給kobj的name字段。
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
??????????????????????????? ? va_list vargs)
{
?????? const char *old_name = kobj->name;
?????? char *s;
?
?????? if (kobj->name && !fmt)
????????????? return 0;
?
?????? kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
?????? if (!kobj->name)
????????????? return -ENOMEM;
?
?????? /* ewww... some of these buggers have '/' in the name ... */
?????? while ((s = strchr(kobj->name, '/')))
????????????? s[0] = '!';
?
?????? kfree(old_name);
?????? return 0;
}
char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
{
?????? unsigned int len;
?????? char *p;
?????? va_list aq;
?
?????? va_copy(aq, ap);
?????? len = vsnprintf(NULL, 0, fmt, aq);
?????? va_end(aq);
?
?????? p = kmalloc(len+1, gfp);
?????? if (!p)
????????????? return NULL;
?
?????? vsnprintf(p, len+1, fmt, ap);
?
?????? return p;
}
繼續kobject_add_varg()返回kobject_add_internal(kobj),就是在這個函數理為kobj創建文件系統結構:
static int kobject_add_internal(struct kobject *kobj)
{
?????? int error = 0;
?????? struct kobject *parent;
?
?????? if (!kobj)
????????????? return -ENOENT;
?????? if (!kobj->name || !kobj->name[0]) {
????????????? WARN(1, "kobject: (%p): attempted to be registered with empty "
???????????????????? ?"name!\n", kobj);
????????????? return -EINVAL;
?????? }
檢查kobj和它的name字段,不存在則返回錯誤信息。
?
?????? parent = kobject_get(kobj->parent);
獲得其父節點,并增加父節點的計數器,kobject結構中的kref字段用于容器的計數,kobject_get和kobject_put分別增加和減少計數器,如果計數器為0,則釋放該kobject,kobject_get返回該kobject。
?????? /* join kset if set, use it as parent if we do not already have one */
?????? if (kobj->kset) {
????????????? if (!parent)
???????????????????? parent = kobject_get(&kobj->kset->kobj);
????????????? kobj_kset_join(kobj);
????????????? kobj->parent = parent;
?????? }
在這里我們可以看到,如果調用kobject_create_and_add()時參數parent設為NULL,則會去檢查kobj的kset是否存在,如果存在就會把kset所嵌套的kobj作為其父節點,并把kobj添加到kset中去。
?????? ?????? pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
????????????? ?kobject_name(kobj), kobj, __func__,
????????????? ?parent ? kobject_name(parent) : "<NULL>",
????????????? ?kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
打印一些調試信息,接著為kobj創建目錄:
?????? error = create_dir(kobj);
?????? if (error) {
????????????? kobj_kset_leave(kobj);
????????????? kobject_put(parent);
????????????? kobj->parent = NULL;
?
????????????? /* be noisy on error issues */
????????????? if (error == -EEXIST)
???????????????????? printk(KERN_ERR "%s failed for %s with "
???????????????????? ?????? "-EEXIST, don't try to register things with "
???????????????????? ?????? "the same name in the same directory.\n",
???????????????????? ?????? __func__, kobject_name(kobj));
????????????? else
???????????????????? printk(KERN_ERR "%s failed for %s (%d)\n",
???????????????????? ?????? __func__, kobject_name(kobj), error);
????????????? dump_stack();
?????? } else
????????????? kobj->state_in_sysfs = 1;
?
?????? return error;
}
如果創建不成功,則回滾上面的操作,成功的話則設置kobj的state_in_sysfs標志。
在看看create_dir()函數中具體創建了那些內容:
static int create_dir(struct kobject *kobj)
{
?????? int error = 0;
?????? if (kobject_name(kobj)) {
????????????? error = sysfs_create_dir(kobj);
????????????? if (!error) {
???????????????????? error = populate_dir(kobj);
???????????????????? if (error)
??????????????????????????? sysfs_remove_dir(kobj);
????????????? }
?????? }
?????? return error;
}
sysfs_create_dir()先為kobj創建了一個目錄文件
int sysfs_create_dir(struct kobject * kobj)
{
?????? struct sysfs_dirent *parent_sd, *sd;
?????? int error = 0;
?
?????? BUG_ON(!kobj);
?
?????? if (kobj->parent)
????????????? parent_sd = kobj->parent->sd;
?????? else
????????????? parent_sd = &sysfs_root;
?
?????? error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
?????? if (!error)
????????????? kobj->sd = sd;
?????? return error;
}
如果kobj->parent為NULL,就把&sysfs_root作為父節點sd,即在/sys下面創建結點。
然后調用populate_dir:
static int populate_dir(struct kobject *kobj)
{
?????? struct kobj_type *t = get_ktype(kobj);
?????? struct attribute *attr;
?????? int error = 0;
?????? int i;
?
?????? if (t && t->default_attrs) {
????????????? for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
???????????????????? error = sysfs_create_file(kobj, attr);
???????????????????? if (error)
??????????????????????????? break;
????????????? }
?????? }
?????? return error;
}
得到kobj的kobj_type,歷遍kobj_type的default_attrs并創建屬性文件,文件的操作會回溯到sysfs_ops的show和store會調用封裝了attribute的kobj_attribute結構的store和show方法(在后面的代碼中將會分析)。
由于上面kobject_init(kobj, &dynamic_kobj_ktype)用默認dynamic_kobj_ktype作為kobj_type參數,而dynamic_kobj_ktype的default_attrs為NULL,所以這里沒有創建屬性文件。
至此,我們已經知道了kobject_create_and_add()函數創建kobject,掛到父kobject,并設置其kobj_type,在文件系統中為其創建目錄和屬性文件等。
另外,如果我們已靜態定義了要創建的kobject,則可以調用kobject_init_and_add()來注冊kobject,其函數如下:
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
???????????????????? ?struct kobject *parent, const char *fmt, ...)
{
?????? va_list args;
?????? int retval;
?
?????? kobject_init(kobj, ktype);
?
?????? va_start(args, fmt);
?????? retval = kobject_add_varg(kobj, parent, fmt, args);
?????? va_end(args);
?
?????? return retval;
}
通過上面的分析我們很輕松就能理解這個函數。
?
內核提供注銷kobject的函數是kobject_del()
void kobject_del(struct kobject *kobj)
{
?????? if (!kobj)
????????????? return;
?
?????? sysfs_remove_dir(kobj);
?????? kobj->state_in_sysfs = 0;
?????? kobj_kset_leave(kobj);
?????? kobject_put(kobj->parent);
?????? kobj->parent = NULL;
}
刪除kobj目錄及其目錄下的屬性文件,清kobj的state_in_sysfs標志,把kobj從kset中刪除,減少kobj->parent的計數并設其指針為空。
?
LINUX設備驅動之設備模型二--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。
轉載于:https://www.cnblogs.com/yuanfang/archive/2010/12/24/1916229.html
總結
以上是生活随笔為你收集整理的研究UEVENT相关东西,看到2篇优秀的博文,转载与此的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过C++ Interop把Window
- 下一篇: 软件加密技巧分析