linux中各种 **init_call 定义

 

@ linux2.6.*/include/linux/init.h

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

#definepure_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 __exitcall(fn) \
 static exitcall_t __exitcall_##fn __exit_call = fn

#define console_initcall(fn) \
 static initcall_t __initcall_##fn \
 __used __section(.con_initcall.init) = fn

#define security_initcall(fn) \
 static initcall_t __initcall_##fn \
 __used __section(.security_initcall.init) = fn

#define module_init(x) __initcall(x);

#define module_exit(x) __exitcall(x);

 

可以发现 *init_call(fn)最终是由 _define_initcall(level,fn)宏定义生成的,

但是最终的还是调用了

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

 

 

如果您driver所对应的模块的初始化函数为

int XXX_init(void),那么module_init(XXX_init)实际上等于:
static initcall_t  __initcall_XXX_init_6 __used __attribute__((__section__(".initcall6.init"))) =XXX_init;
就是声明一类型为

initcall_t(typedef int (*initcall_t)(void))

函数指针类型的变量 __initcall_XXX_init_6,并将XXX_init 赋值与它。
这里的函数指针变量声明比较特殊的地方在于,将这个变量放在了一名为".initcall6.init"节中。接下来结合vmlinux.lds中的
.initcall.init : AT(ADDR(.initcall.init) - (0xc0000000 -0x00000000)) {
    __initcall_start = .;
    *(.initcallearly.init) __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)
    __initcall_end = .;
  }

@linux2.6.*/init/main.c      do_initcalls()


static void __init do_initcalls(void)
{
        initcall_t *call;

        for (call = __initcall_start; call < __initcall_end; call++)
                do_one_initcall(*call);

        /* Make sure there is no pending stuff from the initcall sequence */
        flush_scheduled_work();
}
那么模块中的module_init中的初始化函数何时被调用了。

在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls() --》。。。。

 

1。

linux核心代码的时候看到/init/main.c 里面的do_initcalls函数
static void __init do_initcalls(void)
{
  initcall_t *call;

  call = &__initcall_start;
  do {
   (*call)();
   call++;
  } while (call < &__initcall_end);

  /* Make sure there is no pending stuff from the initcall sequence */
  flush_scheduled_tasks();
}
当时就很诧异,在用source insight 查看的时候,没有发现__initcall_start的定义,
然后就很网站搜索,(估计是我搜索水平太菜),没有发现有相关的文章,于是在相关的linux
论坛提问,有人指引我看看/arch/i386/vmlinux.lds
lds是什么?晕菜,不清楚,查了资料得知是ld script.看来的研究ld了

2。

察看/arch/i386/vmlinux.lds,发现一段代码
 __initcall_start = .;
 .initcall.init : { *(.initcall.init) }
 __initcall_end = .;
 
跟我找的东西相关
使用info ld,察看相关资料,(最终发现太麻烦,到网上找了一个ld.pdf).发现有这么一
段介绍关于c++地联结构造器的使用,和这段用法相同
其含义时,是让__initcall_start指向代码节.initcall.init的节首,而__initcall_end
指向.initcall.init的节尾。
那么第一段代码从程序逻辑上得到了解释。

3。
因为do_initcalls所作的是系统中有关于选择的驱动部分的初始化工作,那么具体是这些
函数指针数据怎样放到了.initcall.init节。
想起来了,还没有使用grep哈哈,在
grep -rn .initcall.init *
发现在include/linux/init.h:83:有这样一个定义
#define __init_call   __attribute__ ((unused,__section__ (".initcall.init")))
娃哈哈哈
终于让我发现了
然后又发现了
#define __initcall(fn) \
static initcall_t __initcall_##fn __init_call = fn

4。
问题是什么是__attribute__??,查找man gcc,找到关于__attribute__的定义
`section ("section-name")'
   Normally, the compiler places the code it generates in the `text'
   section. Sometimes, however, you need additional sections, or you
   need certain particular functions to appear in special sections.
   The `section' attribute specifies that a function lives in a
   particular section. For example, the declaration:

     extern void foobar (void) __attribute__ ((section ("bar")));

   puts the function `foobar' in the `bar' section.

   Some file formats do not support arbitrary sections so the
   `section' attribute is not available on all platforms. If you
   need to map the entire contents of a module to a particular
   section, consider using the facilities of the linker instead.
  
他的意思就是使它建造一个在.initcall.init节的指向初始函数的指针

5。
问题是##是什么意思?
查阅gcc的man page得知,她是用在可变参数使用宏定义的时候的
在这里也就是建立一个变量名称为所指向的函数的名称,并且前面加上
__initcall_.

6。
然后看看成果
在/include/linux/init.c中有发现
#define module_init(x) __initcall(x);
看看很多驱动中都有类似
module_init(usb_init);
module_exit(usb_exit);
的代码

 

 参考

http://hi.baidu.com/duiguangcz/blog/item/b863c52833feaae4e7cd40f2.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用`regmap(I2C_SMBUS_BLOCK_PROC_CALL)`函数的实例: ```c #include <linux/i2c.h> #include <linux/regmap.h> /* 假设我们已经获取了一个i2c_client结构体指针,称为client */ /* 定义一个regmap_config结构体 */ static const struct regmap_config my_regmap_config = { .name = "my_regmap", .reg_bits = 8, .val_bits = 8, .max_register = 0xff, }; /* 初始化一个regmap结构体 */ static struct regmap *my_regmap_init(struct i2c_client *client) { struct regmap *map; int ret; /* 分配一个regmap结构体 */ map = devm_regmap_init_i2c(client, &my_regmap_config); if (IS_ERR(map)) { pr_err("Failed to allocate regmap: %ld\n", PTR_ERR(map)); return NULL; } /* 设置I2C_SMBUS_BLOCK_PROC_CALL读写函数 */ ret = regmap_set_i2c_bus(map, client->adapter, client->addr, I2C_SMBUS_BLOCK_PROC_CALL); if (ret) { pr_err("Failed to set I2C bus for regmap: %d\n", ret); regmap_exit(map); return NULL; } return map; } /* 在驱动程序使用regmap */ static int my_driver_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct regmap *map; u8 val; /* 初始化一个regmap结构体 */ map = my_regmap_init(client); if (!map) return -ENODEV; /* 使用regmap进行读写操作 */ regmap_write(map, 0x10, 0x55); regmap_read(map, 0x20, &val); return 0; } /* 在驱动程序使用regmap */ static int my_driver_remove(struct i2c_client *client) { /* 在驱动程序卸载时,释放regmap结构体 */ regmap_exit(dev_get_regmap(client, NULL)); return 0; } /* 定义一个i2c_driver结构体 */ static const struct i2c_device_id my_driver_id[] = { { "my_device", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, my_driver_id); static struct i2c_driver my_driver = { .driver = { .name = "my_driver", }, .probe = my_driver_probe, .remove = my_driver_remove, .id_table = my_driver_id, }; /* 注册i2c_driver */ module_i2c_driver(my_driver); ``` 在上面的代码,我们定义了一个`regmap_config`结构体,并在其指定了需要使用的寄存器位数、值位数和最大寄存器地址。然后,我们使用`devm_regmap_init_i2c`函数初始化了一个`regmap`结构体,并使用`regmap_set_i2c_bus`函数设置了I2C总线和设备地址以及使用`I2C_SMBUS_BLOCK_PROC_CALL`读写函数。 接着,在驱动程序使用`regmap_write`和`regmap_read`函数来进行寄存器读写操作。 注意:这只是一个简单的示例,实际上使用`regmap`需要更多的代码和配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值