linux设备模型之bus,device,driver分析一

2023-11-08

===============================
本文系本站原创,欢迎转载!
转载请注明出处:http://www.cnblogs.com/gdt-a20

===============================

    内核的开发者将总线,设备,驱动这三者用软件思想抽象了出来,巧妙的建立了其间的关系,使之更形象化。结合前面所学的知识,总的来说其三者间的关系为bus有两条链表,分别用于挂接设备和驱动,指定了其自身bus的device或者driver最后都会分别连接到对应bus的这两条链表上,而总线又有其始端,为bus_kset,一个driver可以对应于几个设备,因此driver同样有其设备链表,用于挂接可以操作的设备,其自身也有bus挂接点,用于将自身挂接到对应bus(每个driver只属于一条总线),而对于device,一个设备只属于一条总线,只能有一个driver与其对应,因此对于device,都是单一的,一个driver挂接点,一个bus挂接点,device与bus相同的是都有始端,device为devices_kset,因此device的注册同时会出现在对应的bus目录和device总目录下。好了,下面就以源码为例分别分析一下bus,device,driver的注册过程。

 

一、bus的注册

      bus的注册比较简单,首先来看一下bus的结构:

     

 1 struct bus_type {
 2     const char        *name;                //名字
 3     struct bus_attribute    *bus_attrs;           //bus属性集
 4     struct device_attribute    *dev_attrs;           //device属性集
 5     struct driver_attribute    *drv_attrs;           //driver属性集
 6     int (*match)(struct device *dev, struct device_driver *drv);
 7     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
 8     int (*probe)(struct device *dev);
 9     int (*remove)(struct device *dev);
10     void (*shutdown)(struct device *dev);
11     int (*suspend)(struct device *dev, pm_message_t state);
12     int (*resume)(struct device *dev);
13     const struct dev_pm_ops *pm;
14     struct bus_type_private *p;                   //bus的私有成员
15 };
16 //其中重点看一下私有成员结构体:
17 struct bus_type_private {
18     struct kset subsys;                           //bus内嵌的kset,代表其自身
19     struct kset *drivers_kset;                    
20     struct kset *devices_kset;
21     struct klist klist_devices;                   //包含devices链表及其操作函数
22     struct klist klist_drivers;                   //driver链表及其操作函数
23     struct blocking_notifier_head bus_notifier;
24     unsigned int drivers_autoprobe:1;              //匹配成功自动初始化标志
25     struct bus_type *bus;                          
26 };

 

  无论是bus,driver,还是device其本身特征都放在私有成员里,其注册时,都会申请并填充这个结构体,下面具体分析一下bus的注册流程,从bus_register开始:

     

 1 int bus_register(struct bus_type *bus)
 2 {
 3     int retval;
 4     struct bus_type_private *priv;
 5     priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);    //进入时bus_type->bus_type_private为NULL
 6     if (!priv)                                                      //该函数主要是对其的设置
 7         return -ENOMEM;
 8     priv->bus = bus;                                                //私有成员的bus回指该bus
 9     bus->p = priv;                                                  //初始化bus->p,即其私有属性
10     BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
11     retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  //设置该bus的名字,bus是kset的封装
12     if (retval)
13         goto out;
14                                                       //bus_kset即为所有bus的总起始端点
15 //围绕bus内嵌的kset初始化,和kset的初始化时围绕
16     priv->subsys.kobj.kset = bus_kset;                //kobj相似,没有parent时,就会用kset的kobj,此处即是
17     priv->subsys.kobj.ktype = &bus_ktype;                    //属性操作级别统一为bus_ktype
18     priv->drivers_autoprobe = 1;                                    //设置该标志,当有driver注册时,会自动匹配devices
19 //上的设备并用probe初始化,
20 //当有device注册时也同样找到  driver并会初始化
21     retval = kset_register(&priv->subsys);                          //注册kset,创建目录结构,以及层次关系
22     if (retval)
23         goto out;
24     retval = bus_create_file(bus, &bus_attr_uevent);                //当前bus目录下生成bus_attr_uevent属性文件
25     if (retval)
26         goto bus_uevent_fail;
27     priv->devices_kset = kset_create_and_add("devices", NULL,       //初始化bus目录下的devices目录,里面级联了该bus下设备,
28                          &priv->subsys.kobj);                    //仍然以kset为原型
29     if (!priv->devices_kset) {
30         retval = -ENOMEM;
31         goto bus_devices_fail;
32     }
33     priv->drivers_kset = kset_create_and_add("drivers", NULL,       //初始化bus目录下的drivers目录,里面级联了该bus下设备的driver
34                          &priv->subsys.kobj);
35     if (!priv->drivers_kset) {
36         retval = -ENOMEM;
37         goto bus_drivers_fail;
38     }
39     klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  //初始化klist_devices里的操作函数成员
40     klist_init(&priv->klist_drivers, NULL, NULL);                            //klist_drivers里的操作函数置空
41     retval = add_probe_files(bus);                                           //增加bus_attr_drivers_probe和bus_attr_drivers_autoprobe
42     if (retval)                                                              //属性文件
43         goto bus_probe_files_fail;
44     retval = bus_add_attrs(bus);                                             //增加默认的属性文件
45     if (retval)
46         goto bus_attrs_fail;
47     pr_debug("bus: '%s': registered/n", bus->name);
48     return 0;
49 bus_attrs_fail:                                                               //以下为错误处理
50     remove_probe_files(bus);
51 bus_probe_files_fail:
52     kset_unregister(bus->p->drivers_kset);
53 bus_drivers_fail:
54     kset_unregister(bus->p->devices_kset);
55 bus_devices_fail:
56     bus_remove_file(bus, &bus_attr_uevent);
57 bus_uevent_fail:
58     kset_unregister(&bus->p->subsys);
59 out:
60     kfree(bus->p);
61     bus->p = NULL;
62     return retval;
63 }

 

   由此可见,bus又是kset的封装,bus_register主要完成了其私有成员bus_type_private的初始化,并初始化了其下的两个目录devices和drivers,及其属性文件,bus有个自己的根目录也就是bus有个起始端点,是bus_kset,经过此番的注册,bus目录下将会出现我们注册的bus,并且其下会有device和driver两个子目录,代表它下面的driver和device链表。

二、driver的注册

  下面看一下driver是怎么和bus关联起来的,首先看下driver的结构:

    

 1 struct device_driver {
 2     const char        *name;            //名字
 3     struct bus_type        *bus;        //其所在的bus
 4     struct module        *owner;
 5     const char        *mod_name;    /* used for built-in modules */
 6     bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */
 7 #if defined(CONFIG_OF)
 8     const struct of_device_id    *of_match_table;
 9 #endif
10     int (*probe) (struct device *dev);        //匹配成功时可能会调用到的函数
11     int (*remove) (struct device *dev);
12     void (*shutdown) (struct device *dev);
13     int (*suspend) (struct device *dev, pm_message_t state);
14     int (*resume) (struct device *dev);
15     const struct attribute_group **groups;
16     const struct dev_pm_ops *pm;
17     struct driver_private *p;                 //私有成员,表示driver
18 };
19 //重点看下driver的私有成员
20 struct driver_private {
21     struct kobject kobj;                      //代表driver自身
22     struct klist klist_devices;               //可以操控的设备链表
23     struct klist_node knode_bus;              //挂接到bus的节点
24     struct module_kobject *mkobj;             //模块相关
25     struct device_driver *driver;             //回指该driver
26 };

 

  如同bus一样,重点的仍是可以代表其自身的私有属性,下面具体看一下driver的注册过程,从driver_register开始:

    

  1 int driver_register(struct device_driver *drv)
  2 {
  3     int ret;
  4     struct device_driver *other;
  5     BUG_ON(!drv->bus->p);
  6     if ((drv->bus->probe && drv->probe) ||           //driver和bus的同名操作函数如果同时存在,会出现警告
  7         (drv->bus->remove && drv->remove) ||         //并且会优先选用bus的
  8         (drv->bus->shutdown && drv->shutdown))
  9         printk(KERN_WARNING "Driver '%s' needs updating - please use "
 10             "bus_type methods/n", drv->name);
 11     other = driver_find(drv->name, drv->bus);        //进入bus的driver链表,确认该driver是否已经注册
 12     if (other) {
 13         put_driver(other);                            //找到了再减少引用计数,并且报错退出
 14         printk(KERN_ERR "Error: Driver '%s' is already registered, "
 15             "aborting.../n", drv->name);
 16         return -EBUSY;
 17     }
 18     ret = bus_add_driver(drv);                       //如果没有注册,那么把该driver加入所在bus
 19     if (ret)
 20         return ret;
 21     ret = driver_add_groups(drv, drv->groups);
 22     if (ret)
 23         bus_remove_driver(drv);
 24     return ret;
 25 }
 26 /****************************************************
 27 × 跟踪一下driver_find(drv->name, drv->bus)
 28 ****************************************************/
 29 struct device_driver *driver_find(const char *name, struct bus_type *bus)
 30 {
 31     struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);  //bus->p->drivers_kset代表bus下
 32     struct driver_private *priv;                                    //的driver目录,此处会遍历bus的    
 33 //driver链表,通过driver内嵌的
 34     if (k) {                                                        //kobj名字比较
 35         priv = to_driver(k);
 36         return priv->driver;                                 //如果找到同名的kobj那么返回该driver
 37     }
 38     return NULL;
 39 }
 40 //看一下kset_find_obj吧:
 41 struct kobject *kset_find_obj(struct kset *kset, const char *name)
 42 {
 43     struct kobject *k;
 44     struct kobject *ret = NULL;
 45     spin_lock(&kset->list_lock);
 46     list_for_each_entry(k, &kset->list, entry) {                   //遍历bus下的driver链表,如果
 47         if (kobject_name(k) && !strcmp(kobject_name(k), name)) {   //找到那么返回找到的kobj,并且把
 48             ret = kobject_get(k);                                  //该driver的kobj引用计数+1
 49             break;
 50         }
 51     }
 52     spin_unlock(&kset->list_lock);
 53     return ret;
 54 }
 55 /************************************************
 56 × 再来跟踪一下driver_register里面的另外一个函数
 57 × bus_add_driver(drv)
 58 ************************************************/
 59 int bus_add_driver(struct device_driver *drv) 
 60 {
 61     struct bus_type *bus;
 62     struct driver_private *priv;
 63     int error = 0;
 64     bus = bus_get(drv->bus);                                        //取得其所在bus的指针
 65     if (!bus)
 66         return -EINVAL;
 67     pr_debug("bus: '%s': add driver %s/n", bus->name, drv->name);   //开始初始化这个driver的私有成员,
 68 //和bus类似
 69     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 70     if (!priv) {
 71         error = -ENOMEM;
 72         goto out_put_bus;
 73     }
 74     klist_init(&priv->klist_devices, NULL, NULL);                   //设备操作函数清空,设备链表初始化
 75     priv->driver = drv;                                             
 76     drv->p = priv;
 77     priv->kobj.kset = bus->p->drivers_kset;                          //kset指定到bus下面
 78     error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,   //建立层次结构和属性文件
 79                      "%s", drv->name);
 80     if (error)
 81         goto out_unregister;
 82     if (drv->bus->p->drivers_autoprobe) {                            //bus的自动匹配如果设置为真,
 83         error = driver_attach(drv);                                  //那么到bus的devices上去匹配设备
 84         if (error)
 85             goto out_unregister;
 86     }
 87     klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);        //把driver挂接到bus的driver链表
 88     module_add_driver(drv->owner, drv);
 89     error = driver_create_file(drv, &driver_attr_uevent);            //以下添加该driver相关属性文件
 90     if (error) {
 91         printk(KERN_ERR "%s: uevent attr (%s) failed/n",
 92             __func__, drv->name);
 93     }
 94     error = driver_add_attrs(bus, drv);
 95     if (error) {
 96         /* How the hell do we get out of this pickle? Give up */
 97         printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n",
 98             __func__, drv->name);
 99     }
100     if (!drv->suppress_bind_attrs) {
101         error = add_bind_files(drv);
102         if (error) {
103             /* Ditto */
104             printk(KERN_ERR "%s: add_bind_files(%s) failed/n",
105                 __func__, drv->name);
106         }
107     }
108     kobject_uevent(&priv->kobj, KOBJ_ADD);
109     return 0;
110 out_unregister:
111     kobject_put(&priv->kobj);
112     kfree(drv->p);
113     drv->p = NULL;
114 out_put_bus:
115     bus_put(bus);
116     return error;
117 }
118 /****************************************************************
119 × 接下来就剩下最终要的匹配函数driver_attach(drv)了,我们来看一下:
120 ****************************************************************/
121 int driver_attach(struct device_driver *drv)                            //遍历bus的设备链表找到
122 {                                                                       //合适的设备就调用__driver_attach,
123     return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);      //NULL表示从头开始遍历
124 }  
125 //============
126 int bus_for_each_dev(struct bus_type *bus, struct device *start,
127              void *data, int (*fn)(struct device *, void *))
128 {
129     struct klist_iter i;
130     struct device *dev;
131     int error = 0;
132     if (!bus)
133         return -EINVAL;
134     klist_iter_init_node(&bus->p->klist_devices, &i,              //进入bus的devices链表
135                  (start ? &start->p->knode_bus : NULL));
136     while ((dev = next_device(&i)) && !error)                     //设备存在则调用fn即__driver_attach
137         error = fn(dev, data);                                    //进行匹配
138     klist_iter_exit(&i);
139     return error;
140 } 
141 /*********************************************
142 × 接着看一下__driver_attach这个函数
143 *********************************************/
144 static int __driver_attach(struct device *dev, void *data)
145 {
146     struct device_driver *drv = data;
147     if (!driver_match_device(drv, dev))                //进行匹配
148         return 0;
149     if (dev->parent)    /* Needed for USB */
150         device_lock(dev->parent);
151     device_lock(dev);
152     if (!dev->driver)                               //如果设备没有指定driver
153         driver_probe_device(drv, dev);              //那么需要初始化匹配到的这个设备
154     device_unlock(dev);
155     if (dev->parent)
156         device_unlock(dev->parent);
157     return 0;
158 }
159 /*********************************************
160 × 又遇到两个分支,囧,先看一下driver_match_device 
161 *********************************************/ 
162 static inline int driver_match_device(struct device_driver *drv,      //bus的match存在就用bus的
163                                       struct device *dev)             //,否则就直接匹配成功...
164 {                                                                     //match通常实现为首先扫描
165     return drv->bus->match ? drv->bus->match(dev, drv) : 1;           //driver支持的id设备表,如果
166 }                                                                     //为NULL就用名字进行匹配
167 /************************************
168 × 再来看一下driver_probe_device这个函数 
169 ************************************/ 
170 int driver_probe_device(struct device_driver *drv, struct device *dev)
171 {
172     int ret = 0;
173     if (!device_is_registered(dev))                              //判断该设备是否已经注册
174         return -ENODEV;
175     pr_debug("bus: '%s': %s: matched device %s with driver %s/n",
176          drv->bus->name, __func__, dev_name(dev), drv->name);
177     pm_runtime_get_noresume(dev);
178     pm_runtime_barrier(dev);
179     ret = really_probe(dev, drv);                               //调用really_probe
180     pm_runtime_put_sync(dev);
181     return ret;
182 }
183 /************************************
184 × 看一下device_is_registered
185 ************************************/
186 static inline int device_is_registered(struct device *dev)
187 {
188     return dev->kobj.state_in_sysfs;                           //在sysfs中表示已经注册
189 }
190 /************************************
191 × 再看really_probe
192 ************************************/
193 static int really_probe(struct device *dev, struct device_driver *drv)
194 {
195     int ret = 0;
196     atomic_inc(&probe_count);
197     pr_debug("bus: '%s': %s: probing driver %s with device %s/n",
198          drv->bus->name, __func__, drv->name, dev_name(dev));
199     WARN_ON(!list_empty(&dev->devres_head));
200     dev->driver = drv;                                     //device的driver初始化成该driver
201     if (driver_sysfs_add(dev)) {                      
202                  printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",
203             __func__, dev_name(dev));
204         goto probe_failed;
205     }
206                                                          //利用probe初始化设备
207     if (dev->bus->probe) {                               //如果bus的probe存在就用bus的,
208         ret = dev->bus->probe(dev);                      //如果bus的不存在driver的存在
209         if (ret)                                         //再用driver的
210             goto probe_failed;
211     } else if (drv->probe) {
212         ret = drv->probe(dev);
213         if (ret)
214             goto probe_failed;
215     }
216     driver_bound(dev);                              //调用driver_bound进行绑定
217     ret = 1;
218     pr_debug("bus: '%s': %s: bound device %s to driver %s/n",
219          drv->bus->name, __func__, dev_name(dev), drv->name);
220     goto done;
221 probe_failed:
222     devres_release_all(dev);
223     driver_sysfs_remove(dev);
224     dev->driver = NULL;
225     if (ret != -ENODEV && ret != -ENXIO) {
226         /* driver matched but the probe failed */
227         printk(KERN_WARNING
228                "%s: probe of %s failed with error %d/n",
229                drv->name, dev_name(dev), ret);
230     }
231     /*
232      * Ignore errors returned by ->probe so that the next driver can try
233      * its luck.
234 */
235     ret = 0;
236 done:
237     atomic_dec(&probe_count);
238     wake_up(&probe_waitqueue);
239     return ret;
240 }
241 /**********************************
242 * 最后跟一下driver_bound(dev)这个函数
243 **********************************/
244 static void driver_bound(struct device *dev)
245 {
246     if (klist_node_attached(&dev->p->knode_driver)) {                   //判断是否已经绑定
247         printk(KERN_WARNING "%s: device %s already bound/n",
248             __func__, kobject_name(&dev->kobj));
249         return;
250     }
251     pr_debug("driver: '%s': %s: bound to device '%s'/n", dev_name(dev),
252          __func__, dev->driver->name);
253     klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);  //将设备添加
254 //到driver的链表
255     if (dev->bus)
256         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
257                          BUS_NOTIFY_BOUND_DRIVER, dev);
258 }
259 //all end

 

  总结一下,driver的注册,主要涉及将自身挂接到bus的driver链表,并将匹配到的设备加入自己的device链表,并且将匹配到的device的driver成员初始化为该driver,私有属性的driver节点也挂到driver的设备链表下,其中匹配函数是利用利用bus的match函数,该函数通常判断如果driver有id表,就查表匹配,如果没有就用driver和device名字匹配。当匹配成功后如果自动初始化标志允许则调用初始化函数probe,bus的probe优先级始终高于driver的。另外注意一点driver是没有总的起始端点的,driver不是可具体描述的事物。

   由于篇幅比较长,device的分析放到下一篇《linux设备模型之bus,device,driver分析<二>》   ^_^!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux设备模型之bus,device,driver分析一 的相关文章

  • 面试题创作0009,请问Linux kernel中的spinlock_t 是如何实现互斥访问同一数据的?

    面试题创作0007 请问Linux kernel中的spinlock t 是如何实现互斥访问同一数据的 在单核多线程 多核多线程 多cpu多线程中 spinlock t实现互斥的机制有区别么 分别是什么呢 进一步列举一些使用spinlock
  • 【Linux内核设计与实现】Linux内核简介

    之前在读 APUE 的时候 更多的是从上层去了解如何使用Linux系统的API 那个时候就十分喜欢Linux的设计 觉得一切都很奇妙 最近有些迷茫 不知道自己以后更加具体的技术方向在哪 所以最近广泛阅读了很多方面的书 C 方面 服务端网络编
  • 内核调试方法 三

    目录 大海里的鱼有很多 而我们需要的是鱼钩一只 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 第一部分 基础知识 总纲 内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 原理理解的陷阱 建立调试环境 发行版的选择和
  • 基础笔记(二):设计模式摘录

    基础笔记 二 设计模式摘录 前言 收录一些自己在开发过程中比较常用的模式 整理出来以便自己复习 毕竟熟才能生巧 才能变通 举一反三 设计模式在大多数灵活性好 可扩展性高 可移植的优秀程序中都有运用 比如界面展现层运用的MVC模式的主要关系就
  • Linux内核:配置内核(一)

    本文译自Linux orgDevynCJohnson的系列文章 Linux内核 本篇链接 http www linux org threads the linux kernel configuring the kernel part 1 4
  • 新手玩转Linux Kernel漏洞之Null Pointer Dereference

    新手玩转Linux Kernel漏洞之Null Pointer Dereference 前言 这是我内核漏洞的入门篇 不是很复杂 希望能给徘徊在门外的小伙伴一点启发 漏洞描述 A NULL pointer dereference occur
  • linux boot 查看module信息

    1 查看内置模块信息 lib modules uname r modules builtin 如 cat lib modules linux4 15 0 modules builtin 或者grep y boot config 4 15 0
  • 本博客全文目录索引

    本专栏博文索引 目录 涵盖 C C STL Data Structure Algorithm TCP IP Linux Interface Driver Kernel Netfilter 和 Projects C C 详解C指针 C 对象模
  • OOM Killer 持续更新中

    虚拟地址空间的概念都门儿清 Linux 认为用户进程调用malloc申请了动态内存后不见得马上就会使用这段内存进行写读操作 Linux使用了拖到最后的分配机制 用时分配机制 但是 即使是用时分配 也不见得总有内存可分配 当进程太多 显得内存
  • linux内核epoll实现分析

    为了更好的分享体验 博客搬迁至极客驿站 欢迎查阅 epoll与select poll的区别 select poll epoll都是IO多路复用的机制 I O多路复用就通过一种机制 可以监视多个描述符 一旦某个描述符就绪 能够通知程序进行相应
  • imx6ull_kernel_移植

    1 文件目录结构 2 顶层Makefile理解 3 kernel启动流程 4 kernel移植 心得 跟着左神一步一步操作 目前没有太多的理解 硬件适配中网络驱动和emmc 8线适配讲解的最多 文件移植部分基本就是拷贝和粘贴 Q 1 如果要
  • linux设备模型之bus,device,driver分析一

    本文系本站原创 欢迎转载 转载请注明出处 http www cnblogs com gdt a20 内核的开发者将总线 设备 驱动这三者用软件思想抽象了出来 巧妙的建立了其间的关系 使之更形象化 结合前面所学的知识 总的来说其三者间的关系为
  • linux源代码.tar.xz解压

    刚开始学习linux内核 在linux内核官网https www kernel org 下载 我下载的版本是 linux 2 6 34 14 tar xz 由于我的linux中没有安装 xz的解压缩软件 需要下载 http download
  • gcov 和 perf 使用的基本套路备忘 ubuntu

    一 源代码 cat helloSeven c include
  • 【buildroot】buildroot使用总结

    文章目录 一 buildroot使用步骤 1 构建图形配置界面 2 配置Target options 3 配置Build options 4 配置Toolchain 5 配置 System configuration 6 配置 Filesy
  • 【linux kernel】linux中断管理—软中断

    linux中断管理 软中断 一 简介 软中断是linux预留给系统中对时间要求最为严苛和最重要的中断下半部使用的 并且 驱动中只有一些对时间极其敏感的模块使用了 例如 块设备和网络子系统 linux系统中定义了几种软中断类型 如下所示 in
  • 面试题创作0001,请解释mmap的细节

    1 请列举Linux的几种ICP工具 2 重解释共享内存的实现原理 3 两个进程A和B共享到的同一页物理内存 如果被A进程勾进CPU的Cache 那么B进程访问这段内存数据时 将会从内存中访问 还是从Cache中访问呢 可以X86为例 或其
  • Android中Log信息的输出方法

    共两篇文章 第一篇讲述了如何在程序中输出Log信息 第二篇详细的分析了Log信息的输出机制 下面是第一篇 转自 http blog 163 com binghaitao 126 blog static 3383532520099309366
  • SELinux深入理解

    1 简介 SELinux带给Linux的主要价值是 提供了一个灵活的 可配置的MAC机制 Security Enhanced Linux SELinux 由以下两部分组成 1 Kernel SELinux模块 kernel security
  • LCD DRM驱动框架分析一

    本文是基于rk3566 rk3568平台 从概念和框架上对LCD DRM驱动框架进行分析 一 DRM Direct Rendering Manager 简介 DRM 是 Linux 目前主流的图形显示框架 相比 FB 架构 DRM 更能适应

随机推荐

  • yolov5的TensorRT部署--warpaffine_cuda核函数

    从0到1实现基于tensorrt的yolo部署教程 http t csdn cn HUn4T 请点击该链接 即可看到全文 本文对于上面的案例 将预处理使用cuda核函数进行加速 一 cuda核函数的基本概念 1 1 CUDA C基础 核函数
  • Redis入门(一)

    1 简介 Redis 是完全开源的 遵守 BSD 协议 是一个高性能的 key value 数据库 Redis 与其他 key value 缓存产品有以下三个特点 Redis支持数据的持久化 可以将内存中的数据保存在磁盘中 重启的时候可以再
  • 计算机组成原理实验二 存储系统预习报告

    实验一 静态RAM 一 实验目的 掌握静态随机存储器 RAM 工作特性及数据的读写方法 基于信号时序图 了解读写静态随机存储器的原理 二 实验预习 1 阅读实验指导书 然后回答问题 实验所用的静态存储器由一片 6116 2K 8bit 构成
  • 离散信号的Matlab表示

    对任意离散序列x k 需用2个向量来表示 一个表示k的取值范围 另一个表示序列的值 例如序列x k 2 1 1 1 3 0 2 可用Matlab表示为 k 2 4 x 2 1 1 1 3 0 2 若序列从0开始 则只用一个向量x就可表示序列
  • 前端开发: 微信小程序 (文字,链接)生成二维码

    首先最主要的还是通过weapp qrcode js 靠这个轮子就可以了 GitHub yingye weapp qrcode weapp qrcode js 在 微信小程序 中 快速生成二维码https github com yingye
  • 人工智能和机器学习

    机器学习 1 什么是机器学习 在进行特定的编程的情况下 给与计算机学习能力的领域 机器学习是从数据中自动分析获得模型 并利用模型对未知数据进行预测 2 机器学习与人工智能 2 1人工智能发展的三个阶段 1980年代是正式形成时期 1990
  • Java接口详解

    http hi baidu com cxgfhfiupuanour item 370967f74ecbe9cca835a2b4 对初学者来说 接口不是很好理解 现将某高手的一篇文章贴出来 共大家分享 我们来看一个类 class A priv
  • 华为UOS欧拉版 K3S+Rancher 安装完全版

    文章目录 K3S服务 happy path安装过程 1 准备工作 1 1 修改网卡名称为eth0 1 2 切换yum源 1 3 关闭防火墙以及selinux 1 4 修改主机名 并修改hosts 1 5 在UOS 基于华为欧拉 上安装doc
  • vue动画之轮播图

  • Vue自定义组件——封装一个简单的可拖拽的弹出框 可拖拽的Dialog

    首先明确需要传入组件的属性 Props dialogVisible Number 非0打开 allowDrag Boolean 是否可以拖拽 noFoot Boolean 是否显示按钮行 submit Function 点击提交按钮的回调
  • Tomcat 环境变量

    到tomcat官方站点 http www apache org dist jakarta tomcat 4 下载tomcat jakarta tomcat 4 1 30 exe 下载之后安装 比如安装在D Tomcat下 安装完之后 设置环
  • AsyncResult 类的使用

    AsyncResult 类封装异步委托上的异步操作的结果 与异步委托一起使用 从该委托的 BeginInvoke 方法返回的 IAsyncResult 可以强制转换为 AsyncResult AsyncResult 具有 AsyncDele
  • 24、【C++】C++11新特性:Lamda表达式/可变参数模板

    一 Lamda表达式 Lamda表达式是C 11中引入的一项新技术 利用Lamda表达式可以编写内嵌的匿名函数 用以替换独立函数或者函数对象 并且使得代码更可读 是一种匿名函数 即没有函数名的函数 Lamda函数的语法定义如下 captur
  • 过河 【状态压缩DP】+【完整的数论推导过程】

    题目链接 题意 很多人以为青蛙是要跳到石头上 一个个往后跳 问最少需要的石头数量 其实不然 题目给的样例的确也是有些坑了 青蛙每次都有跳的距离范围 题目求的是最少会跳到的石头 青蛙可以在水中起跳 它要尽可能的避开石头 也就是问抵达终点时最少
  • git冲突(git权威指南笔记)

    快进式推送 要推送的本地版本库的提交是建立在远程版本库相应分支的现有提交基础上的 即远程版本库相应分支的最新提交是本地版本库最新提交的祖先提交 查看本地版本库的最新提交及历史提交 git rev list HEAD 查看远程版本库的引用对应
  • MATLAB角谱传播实现!FFT2光斑实际尺寸是多少?

    作者B站ID 大洋咩咩咩咩 投稿有对该代码的讲解视频 角谱传播 如果有那个地方有疑问可以去视频中寻找答案 文章及代码解决三个问题 夫琅禾费衍射 理想凸透镜成像 做FFT2后 仿真光斑的实际尺寸确定 光场在自由空间传播一定距离z后的光斑什么样
  • 求数组中的第三大数

  • java将时间按月分段

    java将时间按月分段 返回分段的数组 按照月份分割一段时间 param startTime 开始时间戳 毫秒 param endTime 结束时间戳 毫秒 public static List
  • 关于Element-ui中Table表格无法显示的问题及解决

    Element ui中Table表格无法显示 1 准备工作 2 引用Element ui官方文档中的Table表格代码 3 启动端口 并在浏览器访问 Element ui表格不生效问题 原因是 Element ui中Table表格无法显示
  • linux设备模型之bus,device,driver分析一

    本文系本站原创 欢迎转载 转载请注明出处 http www cnblogs com gdt a20 内核的开发者将总线 设备 驱动这三者用软件思想抽象了出来 巧妙的建立了其间的关系 使之更形象化 结合前面所学的知识 总的来说其三者间的关系为