Linux设备模型分析之kset
上一篇博客我们分析了Linux设备模型中kobject的注册和使用,在这一篇文章中,我们来看一下kset的用法。
首先我们看一个使用kset的例子,代码如下:
- #include <linux/device.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/string.h>
- #include <linux/sysfs.h>
- #include <linux/stat.h>
- MODULE_AUTHOR("haoyu");
- MODULE_LICENSE("Dual BSD/GPL");
- struct my_kobject
- {
- int value;
- struct kobject kobj;
- };
- struct my_kobject my_kobj;
- void kobject_release(struct kobject *kobject);
- ssize_t kobject_attr_show(struct kobject *kobject, struct attribute *attr,char *buf);
- ssize_t kobject_attr_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);
- struct attribute kobject_attr1 = {
- .name = "name",
- .mode = S_IRWXUGO,
- };
- struct attribute kobject_attr2 = {
- .name = "value",
- .mode = S_IRWXUGO,
- };
- static struct attribute *kobject_def_attrs[] = {
- &kobject_attr1,
- &kobject_attr2,
- NULL,
- };
- struct sysfs_ops kobject_sysfs_ops =
- {
- .show = kobject_attr_show,
- .store = kobject_attr_store,
- };
- struct kobj_type ktype =
- {
- .release = kobject_release,
- .sysfs_ops = &kobject_sysfs_ops,
- .default_attrs = kobject_def_attrs,
- };
- void kobject_release(struct kobject *kobject)
- {
- printk("kobject release.\n");
- }
- ssize_t kobject_attr_show(struct kobject *kobject, struct attribute *attr,char *buf)
- {
- int count = 0;
- struct my_kobject *my_kobj = container_of(kobject, struct my_kobject, kobj);
- printk("kobject attribute show.\n");
- if(strcmp(attr->name, "name") == 0)
- count = sprintf(buf, "%s\n", kobject->name);
- else if(strcmp(attr->name, "value") == 0)
- count = sprintf(buf, "%d\n", my_kobj->value);
- else
- printk("no this attribute.\n");
- return count;
- }
- ssize_t kobject_attr_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)
- {
- int val;
- struct my_kobject *my_kobj = container_of(kobject, struct my_kobject, kobj);
- printk("kobject attribute store.\n");
- if(strcmp(attr->name, "name") == 0)
- printk("Can not change name.\n");
- else if(strcmp(attr->name, "value") == 0)
- {
- val = buf[0] - '0';
- if(val == 0 || val == 1)
- my_kobj->value = val;
- else
- printk("value is '0' or '1'\n");
- }
- else
- printk("no this attribute.\n");
- return count;
- }
- int kset_filter(struct kset *kset, struct kobject *kobj)
- {
- printk("UEVENT: filter. kobj %s.\n",kobj->name);
- return 1;
- }
- const char *kset_name(struct kset *kset, struct kobject *kobj)
- {
- static char buf[20];
- printk("UEVENT: name. kobj %s.\n",kobj->name);
- sprintf(buf,"%s","kset_test");
- return buf;
- }
- int kset_uevent(struct kset *kset, struct kobject *kobj,
- struct kobj_uevent_env *env)
- {
- int i = 0;
- printk("UEVENT: uevent. kobj %s.\n",kobj->name);
- while( i< env->envp_idx){
- printk("%s.\n",env->envp[i]);
- i++;
- }
- return 0;
- }
- struct kset_uevent_ops uevent_ops =
- {
- .filter = kset_filter,
- .name = kset_name,
- .uevent = kset_uevent,
- };
- struct kset *kset_parent;
- struct kset kset_child;
- static int kset_test_init(void)
- {
- printk("kboject test init.\n");
- kset_parent = kset_create_and_add("kset_parent", &uevent_ops, NULL);
- my_kobj.kobj.kset = kset_parent;
- kobject_init_and_add(&my_kobj.kobj,&ktype,NULL,"kobject_test");
- kobject_set_name(&kset_child.kobj,"kset_child");
- kset_child.kobj.kset = kset_parent;
- kset_register(&kset_child);
- return 0;
- }
- static void kset_test_exit(void)
- {
- printk("kobject test exit.\n");
- kobject_del(&my_kobj.kobj);
- kset_unregister(kset_parent);
- kset_unregister(&kset_child);
- }
- module_init(kset_test_init);
- module_exit(kset_test_exit);
该模块执行结果如下图所示:
kset的注册使用kset_create_and_add函数,该函数定义如下:
- 820/**
- 821 * kset_create_and_add - create a struct kset dynamically and add it to sysfs
- 822 *
- 823 * @name: the name for the kset
- 824 * @uevent_ops: a struct kset_uevent_ops for the kset
- 825 * @parent_kobj: the parent kobject of this kset, if any.
- 826 *
- 827 * This function creates a kset structure dynamically and registers it
- 828 * with sysfs. When you are finished with this structure, call
- 829 * kset_unregister() and the structure will be dynamically freed when it
- 830 * is no longer being used.
- 831 *
- 832 * If the kset was not able to be created, NULL will be returned.
- 833 */
- 834struct kset *kset_create_and_add(const char *name,
- 835 const struct kset_uevent_ops *uevent_ops,
- 836 struct kobject *parent_kobj)
- 837{
- 838 struct kset *kset;
- 839 int error;
- 840
- 841 kset = kset_create(name, uevent_ops, parent_kobj);
- 842 if (!kset)
- 843 return NULL;
- 844 error = kset_register(kset);
- 845 if (error) {
- 846 kfree(kset);
- 847 return NULL;
- 848 }
- 849 return kset;
- 850}
该函数首先调用kset_create创建kset,然后调用kset_register将kset注册到系统中。
先来看kset_create函数是如何创建kset的,该函数定义如下:
- 776/**
- 777 * kset_create - create a struct kset dynamically
- 778 *
- 779 * @name: the name for the kset
- 780 * @uevent_ops: a struct kset_uevent_ops for the kset
- 781 * @parent_kobj: the parent kobject of this kset, if any.
- 782 *
- 783 * This function creates a kset structure dynamically. This structure can
- 784 * then be registered with the system and show up in sysfs with a call to
- 785 * kset_register(). When you are finished with this structure, if
- 786 * kset_register() has been called, call kset_unregister() and the
- 787 * structure will be dynamically freed when it is no longer being used.
- 788 *
- 789 * If the kset was not able to be created, NULL will be returned.
- 790 */
- 791static struct kset *kset_create(const char *name,
- 792 const struct kset_uevent_ops *uevent_ops,
- 793 struct kobject *parent_kobj)
- 794{
- 795 struct kset *kset;
- 796 int retval;
- 797
- 798 kset = kzalloc(sizeof(*kset), GFP_KERNEL);
- 799 if (!kset)
- 800 return NULL;
- 801 retval = kobject_set_name(&kset->kobj, name);
- 802 if (retval) {
- 803 kfree(kset);
- 804 return NULL;
- 805 }
- 806 kset->uevent_ops = uevent_ops;
- 807 kset->kobj.parent = parent_kobj;
- 808
- 809 /*
- 810 * The kobject of this kset will have a type of kset_ktype and belong to
- 811 * no kset itself. That way we can properly free it when it is
- 812 * finished being used.
- 813 */
- 814 kset->kobj.ktype = &kset_ktype;
- 815 kset->kobj.kset = NULL;
- 816
- 817 return kset;
- 818}
798行,为kset分配空间。
801行,设置kset->kobj的名字,代表该kset的名字。
806行,设置kset->uevent_ops。
807行,设置kset->kobj.parent。
814行,将kset->kobj.ktype设置为&kset_ktype。kset_ktype我们在下面介绍。
815行,将kset->kobj.kset设置为NULL,即该kset不属于任何kset。
kset_ktype的定义如下:
- 771static struct kobj_type kset_ktype = {
- 772 .sysfs_ops = &kobj_sysfs_ops,
- 773 .release = kset_release,
- 774};
为了分析方便,我们先来看kset_release的定义:
- 763static void kset_release(struct kobject *kobj)
- 764{
- 765 struct kset *kset = container_of(kobj, struct kset, kobj);
- 766 pr_debug("kobject: '%s' (%p): %s\n",
- 767 kobject_name(kobj), kobj, __func__);
- 768 kfree(kset);
- 769}
这个函数取得kset空间指针并释放内存空间。
再来看kobj_sysfs_ops的定义,注意其作用,当用户读写kset->kobj的属性文件时,就会调用kset->kobj.ktype.sysfs_ops的show和store函数,即kobj_sysfs_ops的show和store函数:
- 703const struct sysfs_ops kobj_sysfs_ops = {
- 704 .show = kobj_attr_show,
- 705 .store = kobj_attr_store,
- 706};
kobj_attr_show函数定义如下:
- 678/* default kobject attribute operations */
- 679static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
- 680 char *buf)
- 681{
- 682 struct kobj_attribute *kattr;
- 683 ssize_t ret = -EIO;
- 684
- 685 kattr = container_of(attr, struct kobj_attribute, attr);
- 686 if (kattr->show)
- 687 ret = kattr->show(kobj, kattr, buf);
- 688 return ret;
- 689}
该函数首先通过container_of宏取得包含指定attribute的kobj_attribute,然后调用kobj_attribute的show函数。
kobj_attr_store函数定义如下:
- 691static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
- 692 const char *buf, size_t count)
- 693{
- 694 struct kobj_attribute *kattr;
- 695 ssize_t ret = -EIO;
- 696
- 697 kattr = container_of(attr, struct kobj_attribute, attr);
- 698 if (kattr->store)
- 699 ret = kattr->store(kobj, kattr, buf, count);
- 700 return ret;
- 701}
该函数首先通过container_of宏取得包含指定attribute的kobj_attribute,然后调用kobj_attribute的store函数。
至此,kset_create函数我们就分析完了,回到kset_create_and_add函数中,下面我们要分析的是kset_register函数,该函数定义如下:
- 708/**
- 709 * kset_register - initialize and add a kset.
- 710 * @k: kset.
- 711 */
- 712int kset_register(struct kset *k)
- 713{
- 714 int err;
- 715
- 716 if (!k)
- 717 return -EINVAL;
- 718
- 719 kset_init(k);
- 720 err = kobject_add_internal(&k->kobj);
- 721 if (err)
- 722 return err;
- 723 kobject_uevent(&k->kobj, KOBJ_ADD);
- 724 return 0;
- 725}
719行,调用kset_init初始化kset。
720行,调用kobject_add_internal将kobject注册到系统中,在/sys下建立目录结构和属性文件。该函数我们在前一篇博客<<Linux设备模型之kobject>>已经分析过,这里不再详细分析。
723行,调用kobject_uevent函数发送KOBJ_ADD事件。
- 667/**
- 668 * kset_init - initialize a kset for use
- 669 * @k: kset
- 670 */
- 671void kset_init(struct kset *k)
- 672{
- 673 kobject_init_internal(&k->kobj);
- 674 INIT_LIST_HEAD(&k->list);
- 675 spin_lock_init(&k->list_lock);
- 676}
673行,调用kobject_init_internal初始化kset->kobj。这个函数我们在上一篇博客<<Linux设备模型之kobject>>中已经分析过了。
kobject_uevent函数定义如下:
- 319/**
- 320 * kobject_uevent - notify userspace by sending an uevent
- 321 *
- 322 * @action: action that is happening
- 323 * @kobj: struct kobject that the action is happening to
- 324 *
- 325 * Returns 0 if kobject_uevent() is completed with success or the
- 326 * corresponding error when it fails.
- 327 */
- 328int kobject_uevent(struct kobject *kobj, enum kobject_action action)
- 329{
- 330 return kobject_uevent_env(kobj, action, NULL);
- 331}
这个函数直接调用了kobject_uevent_env函数,定义如下:
- 119/**
- 120 * kobject_uevent_env - send an uevent with environmental data
- 121 *
- 122 * @action: action that is happening
- 123 * @kobj: struct kobject that the action is happening to
- 124 * @envp_ext: pointer to environmental data
- 125 *
- 126 * Returns 0 if kobject_uevent_env() is completed with success or the
- 127 * corresponding error when it fails.
- 128 */
- 129int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
- 130 char *envp_ext[])
- 131{
- 132 struct kobj_uevent_env *env;
- 133 const char *action_string = kobject_actions[action];
- 134 const char *devpath = NULL;
- 135 const char *subsystem;
- 136 struct kobject *top_kobj;
- 137 struct kset *kset;
- 138 const struct kset_uevent_ops *uevent_ops;
- 139 u64 seq;
- 140 int i = 0;
- 141 int retval = 0;
- 142#ifdef CONFIG_NET
- 143 struct uevent_sock *ue_sk;
- 144#endif
- 145
- 146 pr_debug("kobject: '%s' (%p): %s\n",
- 147 kobject_name(kobj), kobj, __func__);
- 148
- 149 /* search the kset we belong to */
- 150 top_kobj = kobj;
- 151 while (!top_kobj->kset && top_kobj->parent)
- 152 top_kobj = top_kobj->parent;
- 153
- 154 if (!top_kobj->kset) {
- 155 pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
- 156 "without kset!\n", kobject_name(kobj), kobj,
- 157 __func__);
- 158 return -EINVAL;
- 159 }
- 160
- 161 kset = top_kobj->kset;
- 162 uevent_ops = kset->uevent_ops;
- 163
- 164 /* skip the event, if uevent_suppress is set*/
- 165 if (kobj->uevent_suppress) {
- 166 pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
- 167 "caused the event to drop!\n",
- 168 kobject_name(kobj), kobj, __func__);
- 169 return 0;
- 170 }
- 171 /* skip the event, if the filter returns zero. */
- 172 if (uevent_ops && uevent_ops->filter)
- 173 if (!uevent_ops->filter(kset, kobj)) {
- 174 pr_debug("kobject: '%s' (%p): %s: filter function "
- 175 "caused the event to drop!\n",
- 176 kobject_name(kobj), kobj, __func__);
- 177 return 0;
- 178 }
- 179
- 180 /* originating subsystem */
- 181 if (uevent_ops && uevent_ops->name)
- 182 subsystem = uevent_ops->name(kset, kobj);
- 183 else
- 184 subsystem = kobject_name(&kset->kobj);
- 185 if (!subsystem) {
- 186 pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
- 187 "event to drop!\n", kobject_name(kobj), kobj,
- 188 __func__);
- 189 return 0;
- 190 }
- 191
- 192 /* environment buffer */
- 193 env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
- 194 if (!env)
- 195 return -ENOMEM;
- 196
- 197 /* complete object path */
- 198 devpath = kobject_get_path(kobj, GFP_KERNEL);
- 199 if (!devpath) {
- 200 retval = -ENOENT;
- 201 goto exit;
- 202 }
- 203
- 204 /* default keys */
- 205 retval = add_uevent_var(env, "ACTION=%s", action_string);
- 206 if (retval)
- 207 goto exit;
- 208 retval = add_uevent_var(env, "DEVPATH=%s", devpath);
- 209 if (retval)
- 210 goto exit;
- 211 retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
- 212 if (retval)
- 213 goto exit;
- 214
- 215 /* keys passed in from the caller */
- 216 if (envp_ext) {
- 217 for (i = 0; envp_ext[i]; i++) {
- 218 retval = add_uevent_var(env, "%s", envp_ext[i]);
- 219 if (retval)
- 220 goto exit;
- 221 }
- 222 }
- 223
- 224 /* let the kset specific function add its stuff */
- 225 if (uevent_ops && uevent_ops->uevent) {
- 226 retval = uevent_ops->uevent(kset, kobj, env);
- 227 if (retval) {
- 228 pr_debug("kobject: '%s' (%p): %s: uevent() returned "
- 229 "%d\n", kobject_name(kobj), kobj,
- 230 __func__, retval);
- 231 goto exit;
- 232 }
- 233 }
- 234
- 235 /*
- 236 * Mark "add" and "remove" events in the object to ensure proper
- 237 * events to userspace during automatic cleanup. If the object did
- 238 * send an "add" event, "remove" will automatically generated by
- 239 * the core, if not already done by the caller.
- 240 */
- 241 if (action == KOBJ_ADD)
- 242 kobj->state_add_uevent_sent = 1;
- 243 else if (action == KOBJ_REMOVE)
- 244 kobj->state_remove_uevent_sent = 1;
- 245
- 246 /* we will send an event, so request a new sequence number */
- 247 spin_lock(&sequence_lock);
- 248 seq = ++uevent_seqnum;
- 249 spin_unlock(&sequence_lock);
- 250 retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
- 251 if (retval)
- 252 goto exit;
- 253
- 254#if defined(CONFIG_NET)
- 255 /* send netlink message */
- 256 mutex_lock(&uevent_sock_mutex);
- 257 list_for_each_entry(ue_sk, &uevent_sock_list, list) {
- 258 struct sock *uevent_sock = ue_sk->sk;
- 259 struct sk_buff *skb;
- 260 size_t len;
- 261
- 262 /* allocate message with the maximum possible size */
- 263 len = strlen(action_string) + strlen(devpath) + 2;
- 264 skb = alloc_skb(len + env->buflen, GFP_KERNEL);
- 265 if (skb) {
- 266 char *scratch;
- 267
- 268 /* add header */
- 269 scratch = skb_put(skb, len);
- 270 sprintf(scratch, "%s@%s", action_string, devpath);
- 271
- 272 /* copy keys to our continuous event payload buffer */
- 273 for (i = 0; i < env->envp_idx; i++) {
- 274 len = strlen(env->envp[i]) + 1;
- 275 scratch = skb_put(skb, len);
- 276 strcpy(scratch, env->envp[i]);
- 277 }
- 278
- 279 NETLINK_CB(skb).dst_group = 1;
- 280 retval = netlink_broadcast_filtered(uevent_sock, skb,
- 281 0, 1, GFP_KERNEL,
- 282 kobj_bcast_filter,
- 283 kobj);
- 284 /* ENOBUFS should be handled in userspace */
- 285 if (retval == -ENOBUFS)
- 286 retval = 0;
- 287 } else
- 288 retval = -ENOMEM;
- 289 }
- 290 mutex_unlock(&uevent_sock_mutex);
- 291#endif
- 292
- 293 /* call uevent_helper, usually only enabled during early boot */
- 294 if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
- 295 char *argv [3];
- 296
- 297 argv [0] = uevent_helper;
- 298 argv [1] = (char *)subsystem;
- 299 argv [2] = NULL;
- 300 retval = add_uevent_var(env, "HOME=/");
- 301 if (retval)
- 302 goto exit;
- 303 retval = add_uevent_var(env,
- 304 "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
- 305 if (retval)
- 306 goto exit;
- 307
- 308 retval = call_usermodehelper(argv[0], argv,
- 309 env->envp, UMH_WAIT_EXEC);
- 310 }
- 311
- 312exit:
- 313 kfree(devpath);
- 314 kfree(env);
- 315 return retval;
- 316}
133由参数action取得代表事件类型的字符串。kobject_actions定义如下:
- 40/*
- 41 * The actions here must match the index to the string array
- 42 * in lib/kobject_uevent.c
- 43 *
- 44 * Do not add new actions here without checking with the driver-core
- 45 * maintainers. Action strings are not meant to express subsystem
- 46 * or device specific properties. In most cases you want to send a
- 47 * kobject_uevent_env(kobj, KOBJ_CHANGE, env) with additional event
- 48 * specific variables added to the event environment.
- 49 */
- 50enum kobject_action {
- 51 KOBJ_ADD,
- 52 KOBJ_REMOVE,
- 53 KOBJ_CHANGE,
- 54 KOBJ_MOVE,
- 55 KOBJ_ONLINE,
- 56 KOBJ_OFFLINE,
- 57 KOBJ_MAX
- 58};
- 42/* the strings here must match the enum in include/linux/kobject.h */
- 43static const char *kobject_actions[] = {
- 44 [KOBJ_ADD] = "add",
- 45 [KOBJ_REMOVE] = "remove",
- 46 [KOBJ_CHANGE] = "change",
- 47 [KOBJ_MOVE] = "move",
- 48 [KOBJ_ONLINE] = "online",
- 49 [KOBJ_OFFLINE] = "offline",
- 50};
149 - 162行,因为只有kset才包含处理uevent的函数,即kset.uevent_ops,所以这里查找kobj所属的kset,如果kobj->kset不存在,就沿着kobj->parent向上查找,直到找到一个不为空的kset为止。如果确实没有找到kset,则退出。
165 - 170行,如果kobj->uevent_suppress的值为1,表示禁止发出uevent事件,退出。
172 - 178行,如果uevent_ops不为空,并且实现了uevent_ops->filter,则执行uevent_ops->filter,如果uevent_ops->filter返回值为0,表示这个uevent被过滤了,退出。
181 - 190行,如果uevent_ops不为空,并且实现了uevent_ops->name,则通过uevent_ops->name获得子系统名,如果uevent_ops为空,或者没有实现uevent_ops->name,则以kset->kobj的名字作为子系统名。
193行,创建kobj_uevent_env结构体变量env,用来保存环境变量。
198行,通过调用kobject_get_path函数取得kobj在/sys系统中的路径,保存在devpath变量中。
205 - 213行,通过调用add_uevent_var函数将事件名,kobj路径和子系统名加入到环境变量env中。
216 - 222行,如果有通过参数传递的环境变量,也调用add_uevent_var函数加入到环境变量env中。
225 - 233行,如果uevent_ops不为空,并且实现了uevent_ops->uevent函数,则调用uevent_ops->uevent。
241 - 244行,如果要发送的事件是KOBJ_ADD或KOBJ_REMOVE,则相应将kobj->state_add_uevent_sent或kobj->state_remove_uevent_sent置为1。
247 - 252行,将发送事件序列数加1,添加到环境变量env中。
254 - 291行,条件编译部分我们不分析。
294行,先来看uevent_helper的定义:
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
#define UEVENT_HELPER_PATH_LEN 256
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
所以uevent_helper就是"/sbin/hotplug"。
300 - 306行,将HOME和PATH加入环境变量env中。
308 - 309行,执行用户空间的/sbin/hotplug程序,传递环境变量为env。
至此, kobject_uevent_env -> kobject_uevent -> kset_register -> kset_create_and_add函数就分析完了,kset被创建和注册到系统中。
Linux设备模型分析之kset相关推荐
- linux kset subsystem 3.10内核,Linux设备模型分析之kset(基于3.10.1内核)
作者:刘昊昱 内核版本:3.10.1 一.kset结构定义 kset结构体定义在include/linux/kobject.h文件中,其内容如下: 142/** 143 * struct kset - ...
- Linux设备模型分析之kset(基于3.10.1内核)
一.kset结构定义 kset结构体定义在include/linux/kobject.h文件中,其内容如下: [cpp] view plaincopy 142/** 143 * struct kset ...
- Linux设备模型分析之kobject(基于3.10.1内核)
一.kobject结构定义 kobject是Linux设备模型的最底层数据结构,它代表一个内核对象. kobject结构体定义在include/linux/kobject.h文件中: [cpp] vi ...
- Linux设备模型分析之kobject
一.kobject应用举例 Linux设备模型最基本的组成元素是kobject,我们先来看一个kobject的应用例子,该程序在Ubuntu 10.10, 2.6.32-38-generic-pae内 ...
- linux设备模型——总线,驱动,设备间的关系
设备模型之kobject,kset及其关系 关于linux设备模型kobject,kset,ktype 设备驱动基础0:设备模型之kobject,kset及其关系 设备模型之总线,驱动,设备 Linu ...
- linux设备模型之kset/kobj/ktype分析
1. 概述 今天来聊一下Linux设备模型的基石:kset/kobject/ktype. sysfs文件系统提供了一种用户与内核数据结构进行交互的方式,可以通过mount -t sysfs sysfs ...
- Linux设备模型:kset, kobj, ktype
参照本站转载LWN文章:<kobject/kset/ktype documentation and example code updated> 本文转自:LoyenWang 目录 1. 概 ...
- Linux设备模型剖析系列一(基本概念、kobject、kset、kobj_type)
CSDN链接: Linux设备模型剖析系列一(基本概念.kobject.kset.kobj_type) Linux设备模型剖析系列之二(uevent.sysfs) Linux设备模型剖析系列之三(de ...
- Linux设备模型 kobject kset
http://www.wowotech.net/device_model/13.html 1. 前言 在"Linux内核的整体架构"中,蜗蜗有提到,由于Linux支持世界上几乎所有 ...
最新文章
- QEMU — VirtIO 的网络实现
- 聚焦和增强卷积神经网络
- 深入理解Lustre文件系统-第1篇 前言
- Nagios远程监控软件的安装与配置详解
- 解决 Windows 和 Ubuntu 时间不一致的问题--转载
- 细说 Vue.js 3.2 关于响应式部分的优化
- 基于easyui开发Web版Activiti流程定制器详解(六)——Draw2d的扩展(一)
- 一般使用作为微型计算机必备,远程教育统考复习之计算机应用基础单选模拟复习题(一)...
- Matlab多种群遗传算法
- 【Tensorflow】基于卷积神经网络实现车牌的识别
- 线性约束最优化问题的Frank-Wolfe方法
- Kubernetes 学习笔记(一)--- 基本概念及利用kubeadm部署K8S
- 计算机无法还原,win7系统不能还原如何解决_win7电脑无法还原系统怎么办-win7之家...
- 像玩游戏一样做游戏 | Google Play 开发者故事
- Pathon基础篇数据类型之——数值、字符串与列表内容
- 沧海桑田:Linux 系统启动的演进模型与发展变化
- javac不是内部或外部命令,也不是可运行的程序 或批处理文件的细节问题(window10)
- RN-路由---基础
- SQL48 将所有获取奖金的员工当前的薪水增加10%
- yui2 datatable转换至yui3
热门文章
- MayaDay1:各种复制
- redis主从数据丢失解决方案
- Windows MSDT RCE(CVE-2022-30190)复现
- 工业用微型计算机资料,《工业用微型计算机》串讲资料(一).doc
- 乐学python编程做个游戏很简单_乐学Python编程-做个游戏很简单
- 获取手机数据信号强度dbm数值
- 共建全场景智慧广州,加速人工智能和数字经济产业发展
- 2024届,该怎么找工作?
- 【有利可图网】PS教程:PS设计打造海报中常见的中文特效艺术字
- signature=ac75cb7977a45c0f7d8a73dca59a4c27,Mac版闪退