本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
C语言 module_init函数与initcall案例详解
phpren · 336浏览 · 发布于2021-08-25 +关注

这篇文章主要介绍了C语言 module_init函数与initcall案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

module_init这个函数对做驱动的人来说肯定很熟悉,这篇文章用来跟一下这个函数的实现。

在include/linux/init.h里面有module_init的定义,自然,因为一个module可以在内核启动时自动加载进内核,也可以由我们手动在需要时加载进内核,基于这种场景,内核使用了MODULE这个宏,见代码:

#ifndef MODULE
#ifndef __ASSEMBLY__

...

#define __define_initcall(level,fn,id) \
    static initcall_t __initcall_##fn##id __attribute_used__ \
    __attribute__((__section__(".initcall" level ".init"))) = fn

#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)
#define __initcall(fn) device_initcall(fn)
#define module_init(x)    __initcall(x);
#else /* MODULE */

...

#define module_init(initfn)                    \
    static inline initcall_t __inittest(void)        \
    { return initfn; }                    \
    int init_module(void) __attribute__((alias(#initfn)));...


现在我们先看下第一种情况,即把module配置为m的情况,即else分支的代码。当我们使用make menuconfig来配置内核时,将某个module配置为m时,MODULE这个宏就被定义了,而当配置为y时,则没有定义,具体的实现在kernel的根Makefile(-DMODULE)里。

先看下initcall_t的定义:

typedef int (*initcall_t)(void);

当我们调用insmod将module加载进内核时,会去找init_module作为入口地址,即是我们的initfn, 这样module就被加载了。它是一个接收参数为void, 返回值为int类型的函数指针。这样就明白了,其实前两句话只是做了一个检测,当你传进来的函数指针的参数和返回值与initcall_t不一致时,就会有告警。
重点在第三句,是使用alias将initfn变名为init_module,我们知道,kernel 2.4版本之前都是用init_module来加载模块的。这样做应该是为了不用修改load module的那块代码吧。

取nvme.ko为例,我们可以通过objdump -t nvme.ko 查看该模块的符号表,发现init_module和nvme_init指向同一个偏移量。如下:

现在看第二种情况,即我们选择将模块编进内核,让它随内核启动而加载。

这种情况下module_init最终会调用__define_initcall宏,这个宏的作用就是将我们的初始化函数放在".initcall" level ".init"中。

在这里是.initcall6.init, 它的位置可以在Vmlinux.lds.h里面找到:

#define INITCALLS                            \
      *(.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)

 

而INITCALL可以在vmlinux.lds.S里面找到:


.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) {
      __init_begin = .;
    _sinittext = .;
    *(.init.text)
   _einittext = .;
  }
  .init.data : AT(ADDR(.init.data) - LOAD_OFFSET) { *(.init.data) }
  . = ALIGN(16);
  .init.setup : AT(ADDR(.init.setup) - LOAD_OFFSET) {
      __setup_start = .;
    *(.init.setup)
      __setup_end = .;
   }
  .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {
      __initcall_start = .;
    INITCALLS
     __initcall_end = .;
  }
  .con_initcall.init : AT(ADDR(.con_initcall.init) - LOAD_OFFSET) {
      __con_initcall_start = .;
    *(.con_initcall.init)
      __con_initcall_end = .;
  }

上面贴出来的代码是系统启动时存放初始化数据的地方,执行完成后不再需要,会被释放掉。根据上面的内存布局,可以列出初始化宏和内存的对应关系:

_init_begin              -------------------
                        |  .init.text       | ---- __init
                        |-------------------|
                        |  .init.data       | ---- __initdata
_setup_start       |-------------------|
                        |  .init.setup      | ---- __setup_param
__initcall_start   |-------------------|
                        |  .initcall1.init  | ---- core_initcall
                        |-------------------|
                        |  .initcall2.init  | ---- postcore_initcall
                        |-------------------|
                        |  .initcall3.init  | ---- arch_initcall
                        |-------------------|
                        |  .initcall4.init  | ---- subsys_initcall
                        |-------------------|
                        |  .initcall5.init  | ---- fs_initcall
                        |-------------------|
                        |  .initcall6.init  | ---- device_initcall
                        |-------------------|
                        |  .initcall7.init  | ---- late_initcall
__initcall_end    |-------------------|
                        |                   |
                        |    ... ... ...    |
                        |                   |
__init_end              -------------------

而各个initcall被调用的地方在kernel_init-》do_basic_setup-》do_initcalls里面:

static void __init do_initcalls(void)
{
    initcall_t *call;
    int count = preempt_count();
    for (call = __initcall_start; call < __initcall_end; call++) {
        ktime_t t0, t1, delta;
        char *msg = NULL;
        char msgbuf[40];
        int result;
        if (initcall_debug) {
            printk("Calling initcall 0x%p", *call);
            print_fn_descriptor_symbol(": %s()",
                    (unsigned long) *call);
            printk("\n");
            t0 = ktime_get();
        }
        result = (*call)();
...

}



c

相关推荐

PHP实现部分字符隐藏

沙雕mars · 1325浏览 · 2019-04-28 09:47:56
Java中ArrayList和LinkedList区别

kenrry1992 · 908浏览 · 2019-05-08 21:14:54
Tomcat 下载及安装配置

manongba · 970浏览 · 2019-05-13 21:03:56
JAVA变量介绍

manongba · 962浏览 · 2019-05-13 21:05:52
什么是SpringBoot

iamitnan · 1086浏览 · 2019-05-14 22:20:36
加载中

0评论

评论
我从小喜欢编程,一直在学习中,从未停止,未来也是如此!
小鸟云服务器
扫码进入手机网页