initcall机制在应用程序中的使用

来源:互联网 发布:linux nginx php 403 编辑:程序博客网 时间:2024/06/05 03:23

首先尊重一下网上的朋友,转载了他的一些东西,自己也加入了自己的东西。转载部分的网址:http://blog.csdn.net/jccz_zys/article/details/1633963


1、前言

initcall机制原理
    在linux初始化的过程中,内核采用了一种initcall的机制,它利用gcc的扩展功能以及ld的连接控制脚本实现了在内核初始化的过程中通过简单的循环就实现了相关驱动的初始化。当然这是内核中使用的一种机制。最大的好处是可以一个简单的循环便可遍历各个函数,对于有共性的代码构造作用很好。如果你在开发中遇到,一类相似的功能比如接收命令处理命令这种,怎么可以很方便的遍历每个函数呢?最原始的方法就是先判断命令再调用处理函数进行处理。你想想多少个if else if 代码看起来好难看,多的话,这个函数得多长啊。现在有了这个initcall机制就方便多了,把结构体定义嵌入到链接控制脚本中,然后就可以很方便的遍历了。

2、代码.c
#include <stdio.h>
/*把函数的指针地址注入到ld控制脚本中*/
typedef int (*initcall_t)(void); /*定义函数指针*/
extern initcall_t __initcall_start, __initcall_end; /*申明外部变量,在ld的脚本文件中定义*/
#define __init_call     __attribute__ ((used, __section__("function_ptrs")))
#define __initcall(fn) static initcall_t __initcall_##fn __init_call = fn 
#define module_init(x) __initcall(x);
/*上述宏定义名为"__initcall_函数名"的函数指针,且将函数指针放在function_ptrs节
 这个函数指针,指向fn(fn函数则放在code_segment节中)*/
#define __init __attribute__((__section__ ("code_segment")))
  /*函数放在code_segment节*/
/*把结构体变量注入到ld链接脚本中,采用gcc的扩展功能*/
typedef struct DRIVIER_TYPE
{
unsigned char szDriverType[3];
int (*ProcDriver)(const unsigned char *pucData);
}stDriverType;
#define __attribute_section__(S) __attribute__((section(#S)))
#define Init_Driver(DriverType) \
static stDriverType Object_##DriverType\
__attribute_used__ __attribute_section__(.module.rodata) = \
{ \
.szDriverType = #DriverType,\
.ProcDriver = ProcDriv##DriverType,\
};
static int ProcDriv1(const unsigned char *pucData)
{
printf ("----ProcDriv1---------\n");
 
       return 0;
}
static int ProcDriv2(const unsigned char *pucData)
{
printf ("----ProcDriv2---------\n");
       return 0;
}
static int ProcDriv3(const unsigned char *pucData)
{
printf ("----ProcDriv3---------\n");
       return 0;
}
Init_Driver(1);
Init_Driver(2);
Init_Driver(3);
static int __init my_init1 (void)
{
       printf ("my_init () #1\n");
       return 0;
}
static int __init my_init2 (void)
{
       printf ("my_init () #2\n");
       return 0;
}
module_init (my_init1);/*定义要被调用的函数指针并放到指定的节中*/
module_init (my_init2);
 
void do_initcalls(void)
{
     initcall_t *call_p; //定义函数指针变量
     call_p = &__initcall_start;/*获取节首址*/
     do {
            fprintf (stderr, "call_p: %p\n", call_p);
            (*call_p)();
             ++call_p;/*32位机器上,函数指针占4bytes,增加一次就是指针便宜4bytes*/
       } while (call_p < &__initcall_end);
}
extern stDriverType cmd_array_start, cmd_array_end;
void do_whileProc(void)
{
stDriverType *pstProcCmd;
unsigned char  acData[10] = {0};
pstProcCmd = &cmd_array_start;/*获取节首址*/
do {
printf("%s, %d, szDriverType = %s\n", __FUNCTION__, __LINE__, pstProcCmd->szDriverType);
pstProcCmd->ProcDriver(acData);//执行函数
pstProcCmd++;
 } while (pstProcCmd < &cmd_array_end);
}
int main (void)
{
      fprintf (stderr, "in main()\n");
      do_initcalls(); /*调用*/
do_whileProc();
       return 0;
}
3
、导出默认的连接控制脚本文件:保存为linker.lds
       通过命令gcc -Wl,--verbose可以获得默认的连接控制脚本, 即选择 "=======..."之间的文本,保存为gcc_linker.lds文件。
先不贴出来,等会把加的东西全贴出来
4、在gcc_linker.lds文件中增加本例需要控制的语句:
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
              "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("/usr/i686-redhat-linux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); . = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
  .note.gnu.build-id : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rel.dyn        :
    {
      *(.rel.init)
      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
      *(.rel.fini)
      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
      *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
      *(.rel.ctors)
      *(.rel.dtors)
      *(.rel.got)
      *(.rel.sharable_data .rel.sharable_data.* .rel.gnu.linkonce.shrd.*)
      *(.rel.sharable_bss .rel.sharable_bss.* .rel.gnu.linkonce.shrb.*)
      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
      *(.rel.ifunc)
    }
  .rel.plt        :
    {
      *(.rel.plt)
      PROVIDE_HIDDEN (__rel_iplt_start = .);
      *(.rel.iplt)
      PROVIDE_HIDDEN (__rel_iplt_end = .);
    }
  .init           :
  {
    KEEP (*(.init))
  } =0x90909090
  .plt            : { *(.plt) *(.iplt) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely)
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
  } =0x90909090
  .fini           :
  {
    KEEP (*(.fini))
  } =0x90909090
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  .rodata         :/*这个是给定义的结构体使用的,cmd_array_start 是起始地址,cmd_array_end 是结束地址,中间是程序放入的结构体信息*/
  {
PROVIDE (cmd_array_start = .);
*(.module.rodata)
PROVIDE (cmd_array_end = .);
*(.rodata .rodata.* .gnu.linkonce.r.*)
  }

  .eh_frame_hdr : { *(.eh_frame_hdr) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
  /* Thread Local Storage sections  */
  .tdata          : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
  .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array     :
  {
     PROVIDE_HIDDEN (__init_array_start = .);
     KEEP (*(SORT(.init_array.*)))
     KEEP (*(.init_array))
     PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array     :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(.fini_array))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE_HIDDEN (__fini_array_end = .);
  }
  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
  .dynamic        : { *(.dynamic) }
  .got            : { *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (12, .);
  .got.plt        : { *(.got.plt)  *(.igot.plt) }
  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  /* Sharable data sections.  */
  .sharable_data   : ALIGN(CONSTANT (MAXPAGESIZE))
  {
    PROVIDE_HIDDEN (__sharable_data_start = .);
    *(.sharable_data .sharable_data.* .gnu.linkonce.shrd.*)
    /* Align here to ensure that the sharable data section ends at the
       page boundary.  */
    . = ALIGN(. != 0 ? CONSTANT (MAXPAGESIZE) : 1);
    PROVIDE_HIDDEN (__sharable_data_end = .);
  }
  _edata = .; PROVIDE (edata = .);
    /*定义__initcall_start符号为当前位置,即.代表当前位置*/
     __initcall_start = .;
     function_ptrs : { *(function_ptrs) }
     __initcall_end = .;
     /*上述3行代码代表function_ptrs节位于__initcall_start和__initcall_end之间*/
     code_segment : { *(code_segment) }

  __bss_start = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 32 / 8 : 1);
  }
  /* Sharable bss sections  */
  .sharable_bss   : ALIGN(CONSTANT (MAXPAGESIZE))
  {
    PROVIDE_HIDDEN (__sharable_bss_start = .);
    *(.dynsharablebss)
    *(.sharable_bss .sharable_bss.* .gnu.linkonce.shrb.*)
    *(SHARABLE_COMMON)
    /* Align here to ensure that the sharable bss section ends at the
       page boundary.  */
    . = ALIGN(. != 0 ? CONSTANT (MAXPAGESIZE) : 1);
    PROVIDE_HIDDEN (__sharable_bss_end = .);
  }
  . = ALIGN(32 / 8);
  . = ALIGN(32 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}
3、编译程序
gcc -Tgcc_linker.lds -o initcall initcall.c
 其中:
-T选项告诉ld要用的连接控制脚本文件,做为链接程序的依据。格式如下:
              -T commandfile 或
              --script=commandfile

4、执行程序
./initcall 
in main()
call_p: 0x80498b4
my_init () #1
call_p: 0x80498b8
my_init () #2
do_whileProc, 90, szDriverType = 1
----ProcDriv1---------
do_whileProc, 90, szDriverType = 2
----ProcDriv2---------
do_whileProc, 90, szDriverType = 3
----ProcDriv3---------

5、总结
do_whileProc, 90, szDriverType = 3
----ProcDriv3---------
主要关注几个点:
一、rodata 是存放C中的字符串和#define定义的常量,
data:数据段(datasegment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
结构体的定义放在了rodata段,转载的那个放在了数据段,个人感觉,更应该放在转载的这个数据段里,因为一开始就已经初始化了的。不过它也能用,呵呵!
二、__attribute_used__ __attribute_section__(.module.rodata)的功能,它是核心,把函数都嵌入到了ld脚本中,后面的程序就可以方便使用了。
三、宏定义的巧妙使用,方便我们的代码归类注入。

0 0