Chinaunix首页 | 论坛 | 博客
  • 博客访问: 58441
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 60
  • 用 户 组: 普通用户
  • 注册时间: 2014-10-12 21:41
个人简介

路人甲

文章分类

全部博文(5)

文章存档

2015年(5)

我的朋友

分类: LINUX

2015-08-27 11:28:10

系统中存在很多设备,每个设备在kernel中都有对应的驱动,那么这些驱动初始化设备以及关闭设备的顺序是怎样的呢?本文试图解答这个问题。

1 驱动probe/remove执行时机

probe的执行有两个时机,一是设备创建时,二是驱动注册时;remove相对也有两个执行时机,一是设备注销时,二是驱动注销时。

设备创建时:

无论是通过platform_ device _register,还是通过dts创建设备,最终会有以下流程

device_add

    -> bus_probe_device

        -> device_attach

            -> bus_for_each_drv(dev->bus, NULL, dev, __device_attach) //遍历bus所有驱动

                -> __device_attach //如果match,则启动probe

                    -> driver_probe_device

                        -> really_probe

                            -> drv->probe

驱动注册时:

driver_register

    -> bus_add_driver

        -> driver_attach

            -> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) //遍历bus所有驱动

                -> __driver_attach //如果match,则启动probe

                    -> driver_probe_device

                        -> really_probe

                            -> drv->probe

设备注销时:

device_del

    -> bus_remove_device

        -> device_release_driver

            -> __device_release_driver

                -> drv->remove

驱动注销时:

driver_unregister

    -> bus_remove_driver

        -> driver_detach //遍历所有该驱动控制的设备

            -> __device_release_driver

                -> drv->remove

2 驱动初始化顺序

    从上文可以看出,设备和驱动,任意一个注册时都会调用probe,但调用的前提是另一个已经存在。所以有很多组合场景,不同场景顺序可以各不相同。这里先列出我们希望解答的场景:所有驱动都静态编译在vmlinx而非动态加载的模块,设备都在dts中定义,只讨论相同级别的同一个bus下的设备驱动初始化顺序

    在这种场景下,设备的创建在bus驱动的初始化过程中,解析dts节点,创建所有的设备,而此时这些设备的驱动都还没有加载。

    内核驱动的常规做法是在module_init时注册驱动,还有很多封装module_init的宏定义,比如module_platform_drivermodule_i2c_driver等等,它们的核心思想就是把设备注册和module_init封装到一起,简化调用。

    此时驱动的初始化顺序,就是驱动模块的加载顺序。那么驱动的加载顺序是由什么决定的呢?首先看module_init的定义,如上假设的情况下所有模块静态编译,而非模块化编译,module_init定义如下:

#define module_init(x)     __initcall(x);

#define __initcall(fn) device_initcall(fn)

 

#define core_initcall(fn)            __define_initcall(fn, 1)

#define core_initcall_sync(fn)          __define_initcall(fn, 1s)

#define postcore_initcall(fn)             __define_initcall(fn, 2)

#define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)

#define arch_initcall(fn)            __define_initcall(fn, 3)

#define arch_initcall_sync(fn)          __define_initcall(fn, 3s)

#define subsys_initcall(fn)                 __define_initcall(fn, 4)

#define subsys_initcall_sync(fn)      __define_initcall(fn, 4s)

#define fs_initcall(fn)                          __define_initcall(fn, 5)

#define fs_initcall_sync(fn)               __define_initcall(fn, 5s)

#define rootfs_initcall(fn)                  __define_initcall(fn, rootfs)

#define device_initcall(fn)                 __define_initcall(fn, 6)

#define device_initcall_sync(fn)       __define_initcall(fn, 6s)

#define late_initcall(fn)             __define_initcall(fn, 7)

#define late_initcall_sync(fn)           __define_initcall(fn, 7s)

         这些initcall被赋予了不同级别,其中device_initcall的级别为6,相对较低,在系统启动较晚调用,调用流程如下:

start_kernel

    -> rest_init

        -> kernel_init

            -> kernel_init_freeable

                -> do_basic_setup

                    -> do_initcalls //遍历各个优先级

                        -> do_initcall_level //遍历相同优先级各个函数

                            -> do_one_initcall

遍历相同优先级的initcall时,是按照地址从低到高进行的,因此调用顺序就是编译时连接到initcall section的顺序,通过System.map可以查到顺序,例如:

62054:ffffffc000e61c38 T __initcall_start

62055:ffffffc000e61c38 t __initcall_trace_init_flags_sys_exitearly

62057:ffffffc000e61c40 t __initcall_trace_init_flags_sys_enterearly

62058:ffffffc000e61c48 t __initcall_init_hw_perf_eventsearly

62059:ffffffc000e61c50 t __initcall_cpu_suspend_initearly

链接顺序可以通过调整Makefile中的.o文件的先后进行调整。

3 驱动shutdown顺序

         系统关机或重启的过程中,会调用设备驱动的shutdown函数来完成设备的关闭操作,有需要的设备可以在驱动中定义该函数。

其调用流程如下:

kernel_restart

    -> kernel_restart_prepare

        -> device_shutdown //逆向遍历devices_kset->list所有device

            -> dev->driver->shutdown

由此可见,各个驱动shutdown的顺序由设备在链表中的位置决定,后添加的先调用。

设备添加到链表中的流程如下:

device_initialize

device_add

    -> kobject_add

        -> kobj_kset_join

            -> list_add_tail(&kobj->entry, &kobj->kset->list)

由此可见,设备注册时,会把节点添加到devices_kset->list末尾

因此驱动的shutdown顺序是设备注册的逆序,而在通过dts创建设备的系统中,设备的注册顺序是设备节点在dts中的前后顺序。

阅读(4446) | 评论(0) | 转发(0) |
0

上一篇:arm linux中memblock与mem_map的初始化

下一篇:没有了

给主人留下些什么吧!~~