本来我的工作主要集中于嵌入式Linux这一块,关于RTOS,虽然之前也有用UcosII做过一两个项目并量产,但并不是主要发力点,感觉相对与Linux来说,RTOS太过“easy”,能研究的东西并不多。
最新闲来看到Rtthread发展挺不错,文档更新也比较多,也出了专用的IDE,加之“中美大战”,“国产”、“爱国”等情怀也被炒至高点,因此动了心思来研究研究。
因为之前用过ucos,所以研究路线即定为:改用rtthread实现之前ucos项目的所有功能。
经过一周的时间,项目基本移植完成并正常使用,此过程,收益还挺多,也总结了一些观点
1. rtthread兼容性还是不错的,支持范围也挺广,Cortex M,A,R都有,还有RISC-V,尤其是R系列支持,让人感觉非常好
2. 编码风格及设计思想,与Linux相近,尤其是设备驱动的思想,熟悉linux的话来学习rtthread应该会很似曾相识
3. 实现了很多POSIX API,比如Socket,pthread等,甚至有与Linux相近的设备驱动,个人感觉有点不伦不类,既是一个RTOS,就应该做RTOS专业的事情(微小,实时,精妙),搞得太过复杂不一定是好事。当然它的目的肯定是为了应用层统一和跨平台。
4. 至于内核的实现,比如中断处理,任务调度,通讯机制等等,基本是常用实现手段
5. shell控制台有点意思,比较方便查看相关信息
回到正题,在研究过程中,自动初始化这一机制比较有特色,当然还有动态库以及模块的加载。
网上有很多关于自动初始化机制的使用以及一些实现原理的分析,在此不多描述,其核心思想是把函数加入特定段,配合特定标示符号,在系统加载过程中,捕获特定标示,进而实现函数调用。
此文主要分析编译过程:
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0x80100000
Start of program headers: 52 (bytes into file)
Start of section headers: 817480 (bytes into file)
Flags: 0x5000200, Version5 EABI, soft-float ABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
Number of section headers: 22
Section header string table index: 21
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 80100000 010000 02ff3c 00 AX 0 0 32
[ 2] .init PROGBITS 8012ff3c 03ff3c 000004 00 AX 0 0 4
[ 3] .fini PROGBITS 8012ff40 03ff40 000004 00 AX 0 0 4
[ 4] .rodata PROGBITS 8012ff48 03ff48 003f35 00 A 0 0 8
[ 5] .ARM.exidx ARM_EXIDX 80133e80 043e80 000008 00 AL 1 0 4
[ 6] .l1_page_table NOBITS 80134000 043e88 004000 00 WA 0 0 1
[ 7] .data PROGBITS 80138000 048000 001398 00 WA 0 0 8
[ 8] .bss NOBITS 8013c000 049398 008cd8 00 WA 0 0 16384
[ 9] .comment PROGBITS 00000000 049398 00006e 01 MS 0 0 1
[10] .ARM.attributes ARM_ATTRIBUTES 00000000 049406 00002d 00 0 0 1
[11] .debug_info PROGBITS 00000000 049433 0425e0 00 0 0 1
[12] .debug_abbrev PROGBITS 00000000 08ba13 0087ae 00 0 0 1
[13] .debug_loc PROGBITS 00000000 0941c1 009650 00 0 0 1
[14] .debug_aranges PROGBITS 00000000 09d818 000750 00 0 0 8
[15] .debug_line PROGBITS 00000000 09df68 00e3dd 00 0 0 1
[16] .debug_str PROGBITS 00000000 0ac345 007eb6 01 MS 0 0 1
[17] .debug_frame PROGBITS 00000000 0b41fc 006ad8 00 0 0 4
[18] .debug_ranges PROGBITS 00000000 0bacd8 000148 00 0 0 8
[19] .symtab SYMTAB 00000000 0bae20 007840 10 20 1038 4
[20] .strtab STRTAB 00000000 0c2660 00520f 00 0 0 1
[21] .shstrtab STRTAB 00000000 0c786f 0000d9 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
没有什么特别,都是常用的一些段
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SECTIONS
{
. = 0x80100000;
__text_start = .;
.text :
{
*(.vectors)
*(.text)
*(.text.*)
/* section information for finsh shell */
. = ALIGN(4);
__fsymtab_start = .;
KEEP(*(FSymTab))
__fsymtab_end = .;
. = ALIGN(4);
__vsymtab_start = .;
KEEP(*(VSymTab))
__vsymtab_end = .;
. = ALIGN(4);
/* section information for modules */
. = ALIGN(4);
__rtmsymtab_start = .;
KEEP(*(RTMSymTab))
__rtmsymtab_end = .;
/* section information for initialization */
. = ALIGN(4);
__rt_init_start = .;
KEEP(*(SORT(.rti_fn*)))
__rt_init_end = .;
} =0
__text_end = .;
__rodata_start = .;
.rodata : { *(.rodata) *(.rodata.*) }
__rodata_end = .;
. = ALIGN(4);
。。。。。。
. = ALIGN(8);
__bss_start = .;
.bss :
{
*(.bss)
*(.bss.*)
*(COMMON)
. = ALIGN(4);
}
. = ALIGN(4);
__bss_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) }
_end = .;
}
注释已经非常明确: /* section information for initialization */,及各组件的初始化,位于text段,由此可见模块以及shell也位于此段。
以board.c的rt_hw_timer_init函数为例,确认一下过程:
首先使用:
INIT_BOARD_EXPORT(rt_hw_timer_init);
其实现为:
/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
针对GCC:
#define SECTION(x) __attribute__((section(x)))
很明确rt_hw_timer_init 在.rti_fn段,包含在text段
objdump -t -j .text rtthread-imx6.elf
gwind@gwind-P5820T:/media/gwind/gcode/opensource/rt-thread/bsp/imx6ul$ objdump -t -j .text rtthread-imx6.elf | grep rt_hw
801001cc l F .text 0000002c rt_hw_timer_isr
80100334 l F .text 00000034 rt_hw_uart_isr
8012a108 l .text 00000000 rt_hw_context_switch_interrupt_do
80112028 g F .text 00000038 rt_hw_trap_swi
80111cb4 g F .text 0000022c rt_hw_stack_init
80111648 g F .text 00000020 rt_hw_interrupt_get_irq
80111bcc g F .text 00000034 rt_hw_set_domain_register
801103bc g F .text 00000058 rt_hw_cpu_shutdown
80112304 g .text 00000000 rt_hw_cpu_dcache_disable
80112098 g F .text 00000038 rt_hw_trap_dabt
8012ff20 g O .text 00000004 __rt_init_rt_hw_timer_init
80112244 g .text 00000000 rt_hw_cpu_icache_enable
8011220c g .text 00000000 rt_hw_context_switch_to
801118d0 g F .text 00000244 rt_hw_cpu_dump_page_table
80111ee0 g F .text 00000110 rt_hw_show_register
80122468 g F .text 00000114 rt_hw_serial_register
8012a07c g .text 00000000 rt_hw_context_switch_interrupt
80111724 g F .text 000001ac rt_hw_cpu_dump_page_table_2nd
80112190 g F .text 00000068 rt_hw_trap_fiq
80100648 g F .text 0000007c rt_hw_uart_init
8010030c g F .text 00000028 rt_hw_board_init
801120d0 g F .text 00000038 rt_hw_trap_resv
80111c00 g F .text 0000006c rt_hw_init_mmu_table
80112108 g F .text 00000088 rt_hw_trap_irq
80111590 g F .text 00000068 rt_hw_interrupt_init
80111690 g F .text 00000094 rt_hw_interrupt_install
8012ff24 g O .text 00000004 __rt_init_rt_hw_uart_init
80112320 g .text 00000000 rt_hw_cpu_icache_disable
80111ff0 g F .text 00000038 rt_hw_trap_undef
80111668 g F .text 00000028 rt_hw_interrupt_ack
80111c6c g F .text 00000048 rt_hw_mmu_init
8010bcb8 w F .text 00000020 rt_hw_console_output
801121f8 g .text 00000000 rt_hw_interrupt_disable
801115f8 g F .text 00000028 rt_hw_interrupt_mask
8012a0a8 g .text 00000000 rt_hw_context_switch_exit
801001f8 g F .text 00000114 rt_hw_timer_init
80111b14 g F .text 000000b8 rt_hw_mmu_setmtt
80112204 g .text 00000000 rt_hw_interrupt_enable
80111620 g F .text 00000028 rt_hw_interrupt_umask
80112234 g .text 00000000 rt_hw_cpu_dcache_enable
80111570 g F .text 00000020 rt_hw_vector_init
8012257c g F .text 00000420 rt_hw_serial_isr
8012a058 g .text 00000000 rt_hw_context_switch
80112060 g F .text 00000038 rt_hw_trap_pabt
可见结果: 801001f8 g F .text 00000114 rt_hw_timer_init
对比编译的MAP信息:
.text 0x00000000801001cc 0x168 build/drivers/board.o
0x00000000801001f8 rt_hw_timer_init
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
static int rti_board_start(void)
{
return 0;
}
INIT_EXPORT(rti_board_start, "0.end");
static int rti_board_end(void)
{
return 0;
}
INIT_EXPORT(rti_board_end, "1.end");
其段信息:
gwind@gwind-P5820T:/media/gwind/gcode/opensource/rt-thread/bsp/imx6ul$ objdump -t -j .text rtthread-imx6.elf | grep __rt_init_
8012ff2c g O .text 00000004 __rt_init_dfs_init
8012ff18 g .text 00000000 __rt_init_start
8012ff20 g O .text 00000004 __rt_init_rt_hw_timer_init
8012ff3c g .text 00000000 __rt_init_end
8012ff38 g O .text 00000004 __rt_init_rti_end
8012ff18 g O .text 00000004 __rt_init_rti_start
8012ff24 g O .text 00000004 __rt_init_rt_hw_uart_init
8012ff28 g O .text 00000004 __rt_init_rti_board_end
8012ff30 g O .text 00000004 __rt_init_libc_system_init
8012ff34 g O .text 00000004 __rt_init_finsh_system_init
8012ff1c g O .text 00000004 __rt_init_rti_board_start
总结:
rt-thread目前装机量也有好几亿了,使用上也非常方便,提供的组件也很丰富,值得学习及深入研究。