本來我的工作主要集中於嵌入式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)
沒有什麼特別,都是常用的一些段
再查看鏈接腳本imx6.lds,以imx6ul爲例
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段
下面,確認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
最後,初始化調用,標識爲__rti_init_rti_board_end
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目前裝機量也有好幾億了,使用上也非常方便,提供的組件也很豐富,值得學習及深入研究。