__attribute__((section()))构建初始化函数表

作用:

        使用__attribute__((section()))构建初始化函数表后,由模块告知main:“我要初始化“,添加新模块再也不需要在main代码中显式调用模块初始化接口。以此实现main与模块之间的隔离,main不再关心有什么模块,模块的删减也不需要修改main。

      "section"关键字会将被修饰的变量或函数编译到特定的一块位置,不是物理存储器上的特定位置,而是在可执行文件的特定段内。此处,可参考__attribute__之section详解

原理:

        1. 模块通过__attribute__((section("name")))的实现,在编译时把初始化的接口放到name数据段中。

        2.  main在执行初始化时并不需要知道有什么模块需要初始化,只需要把name数据段中的所有初始化接口执行一遍即可。

练习代码:

#include <unistd.h>
#include <stdint.h>
#include <stdio.h>

typedef void (*init_call)(void);

/*
 * These two variables are defined in link script.
 */

extern init_call _init_start;
extern init_call _init_end;

#define _init __attribute__((unused, section(".myinit")))
#define DECLARE_INIT(func) init_call _fn_##func _init = func

static void A_init(void)
{
    write(1, "A_init\n", sizeof("A_init\n"));
}
DECLARE_INIT(A_init);

static void B_init(void)
{
    printf("B_init\n");
}
DECLARE_INIT(B_init);

static void C_init(void)
{
    printf("C_init\n");
}
DECLARE_INIT(C_init);

/*
 * DECLARE_INIT like below:
 *  static init_call _fn_A_init __attribute__((unused, section(".myinit"))) = A_init;
 *  static init_call _fn_C_init __attribute__((unused, section(".myinit"))) = C_init;
 *  static init_call _fn_B_init __attribute__((unused, section(".myinit"))) = B_init;
 */

void do_initcalls(void)
{
    init_call *init_ptr = &_init_start;

    for (; init_ptr < &_init_end; init_ptr++) {
        printf("init address: %p\n", init_ptr);
        (*init_ptr)();
    }
}

int main(void)
{
    do_initcalls();

    return 0;
}

在代码中,我们做了3件事:

  1. 使用__attribute__((section()))定义了宏:DECLARE_INIT,此宏把函数放置到初始化函数表

  2. 使用DELCARE_INIT的宏,声明了3个模块初始化函数:A_init/B_init/C_init

  3. 在main中通过调用do_initcalls函数,依次调用编译时构建的初始化函数。其中,“_init_start”和“_init_end”的变量在链接脚本中定义。

其他细节请参考原文:廖威雄: 利用__attribute__((section()))构建初始化函数表与Linux内核init的实现 

猜你喜欢

转载自blog.csdn.net/sy_123a/article/details/108350306
今日推荐