module_init的内核调用顺序

现在以module_init为例分析initcall在内核中的调用顺序

在头文件init.h中,有如下定义:

#define module_init(x)     __initcall(x);

#define __initcall(fn) device_initcall(fn)

继续看:

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

#define __define_initcall(level,fn,id) \

       static initcall_t __initcall_##fn##id __used \

       __attribute__((__section__(".initcall" level ".init"))) = fn

最终我们看到的是module_init的真身:__define_initcall(level,fn,id),仔细推敲这个真身,知道这是个宏,它把传给module_init的函数名组装成以__initcall为前缀的、以6为后缀的函数名,并把这个函数定义到代码段.initcall6.init里面。

在代码段.initcall6.init里面?这函数躲在这里干嘛,啥时候才轮得到它出头啊!找到有此字符串的文件vmlinux.lds.h,相关代码如下所示:

#define INITCALLS                                           \

       *(.initcallearly.init)                                        \

       VMLINUX_SYMBOL(__early_initcall_end) = .;                  \

     *(.initcall0.init)                                       \

     *(.initcall0s.init)                                     \

     *(.initcall1.init)                                       \

     *(.initcall1s.init)                                     \

     *(.initcall2.init)                                       \

     *(.initcall2s.init)                                     \

     *(.initcall3.init)                                       \

     *(.initcall3s.init)                                     \

     *(.initcall4.init)                                       \

     *(.initcall4s.init)                                     \

     *(.initcall5.init)                                       \

     *(.initcall5s.init)                                     \

       *(.initcallrootfs.init)                                      \

     *(.initcall6.init)                                       \

     *(.initcall6s.init)                                     \

     *(.initcall7.init)                                       \

     *(.initcall7s.init)

在vmlinux.lds.S里面

__initcall_start = .;

                     INITCALLS

              __initcall_end = .;

              __con_initcall_start = .;

                     *(.con_initcall.init)

              __con_initcall_end = .;

              __security_initcall_start = .;

                     *(.security_initcall.init)

              __security_initcall_end = .;

言归正传,INITCALLS在__initcall_start = .与__initcall_end = .之间,表示INITCALLS宏内涵的相关段代码顺序存放在这里,module_init所代表段的镶嵌其中

#define pure_initcall(fn)          __define_initcall("0",fn,0)

 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

至此我们应该明白了各个initcall是如何来的以及在代码存储空间上是怎么组织的了吧,下面来看看内核是什么时候调用它的。

内核启动流程如下所示

Main()àlinux_main()àstart_uml()àstart_kernel_proc()àstart_kernelàrest_inità kernel_inità do_basic_setupà do_initcalls

do_initcalls的代码如下所示

static void __init do_initcalls(void)

{

       initcall_t *call;

 

       for (call = __early_initcall_end; call < __initcall_end; call++)

              do_one_initcall(*call);

 

       /* Make sure there is no pending stuff from the initcall sequence */

       flush_scheduled_work();

}

上面的代码中,__early_initcall_end在INITCALLS内定义,__initcall_end在文件vmlinux.lds.S中定义,他们代表的是一些初始化函数的指针数组起始与结束地址,执行函数do_initcalls时,包含在这各指针数组里面的函数顺序的被调用以执行一些必要的初始化工作。至此,明白的各initcall的执行时刻了吧。

`module_init()` 是Linux内核中用于注册初始化函数的宏,该函数在内核模块被加载时调用。`dma_init()` 是在 `module_init()` 中指定的初始化函数,它是在内核模块加载时执行的入口点。 如果你想在 `dma_init()` 函数中调用设备,通常需要执行以下几个步骤: 1. 获取设备资源:在 `dma_init()` 中,首先需要通过平台设备或者总线驱动框架获取到设备的资源,这可能包括I/O端口、内存映射、中断号等。 2. 初始化DMA相关结构:根据获取的设备资源,初始化DMA传输相关的数据结构,比如分配并初始化DMA描述符、设置DMA传输参数等。 3. 注册设备:如果需要将设备添加到内核的设备模型中,可能还需要调用 `device_register()` 函数来注册设备。 4. 配置和启用DMA:配置DMA引擎,设置传输方向、缓冲区地址、传输大小等参数,并启动DMA传输。 以下是一个简化的示例代码,展示了如何在 `module_init()` 中调用设备: ```c #include <linux/module.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> static int dma_init(void) { struct platform_device *pdev; struct resource *res; int ret; // 假设你已经通过某种方式得到了平台设备的指针 pdev pdev = platform_device_register_simple("my_dma_device", -1, NULL, 0); if (!pdev) { printk(KERN_ERR "Failed to register platform device\n"); return -ENODEV; } // 获取设备资源,假设DMA相关的资源是第一个资源 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { printk(KERN_ERR "Failed to get memory resource for DMA\n"); ret = -ENODEV; goto err_remove_device; } // 其他初始化代码,包括DMA内存分配、配置等 // 如果需要注册设备到内核设备模型 ret = device_register(&pdev->dev); if (ret) { printk(KERN_ERR "Failed to register device\n"); goto err_remove_device; } // 配置和启用DMA引擎 return 0; err_remove_device: platform_device_unregister(pdev); return ret; } module_init(dma_init); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple DMA initialization example"); ``` 在上述代码中,我们通过 `platform_device_register_simple` 注册了一个简单的平台设备,然后通过 `platform_get_resource` 获取了设备的资源。接着,根据需要执行了设备注册和其他初始化操作。请注意,实际代码会根据具体的硬件和需求有所不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值