
編寫過設備驅動就會經常碰到module_init這個宏來定義驅動入口函數。這個宏定義了一個函數指針指向我們的驅動入口函數,等到上電的時候就將這些一個個的函數指針拿出來調用,那么各個驅動得到加載。特別的是:這些函數指針是存放在linuxkernel本體的某個段里。這是通過gnu 的__attribute__來修飾的。
實際上,kernel里面有非常多的段,這些段的起始地址和結束地址都能被源碼里得到,因此學會看鏈接腳本和引用里面的段再或者是自定義段對于理解kernel的源碼大有益處。
任何一個可執行程序是被鏈接腳本將一個個的.o鏈接起來的。kernel也不例外,kernel的每個架構都有一個默認的鏈接腳本路徑,如:arch/arm/kernel/vmlinux.lds
【資料圖】
.init.arch.info : { __arch_info_begin = .; *(.arch.info.init)//機器信息段 __arch_info_end = .; }
部分段的形式如上,__arch_info_begin 和__arch_info_end 就能被源碼引用,得到這個地址范圍的數據。怎么把數據放入這些段:使用attribute修飾你想要放入這個段的數據結構 。
對于kernel的段組成有非常多,按需查看源碼即可。
目的:
了解自定義數據結構通過鏈接腳本如何放入kernel鏡像
了解自定義數據結構通過鏈接腳本如何放入kernel鏡像了解如果在合適時候使用這些數據結構在這我將定義一個數據結構,放入自定義的段里面,然后在驅動加載的時候,取出這個數據結構里面的數據,打印出來。
.my_section :{ my_section_begin = .; *(.my_section) my_section_end = .; }
在vmlinux.lds中間找個位置,定義一個.my_section段,并且使用my_section_begin 和my_section_end 記錄這個段的起始地址和結束地址。
#define my_section __attribute__((__section__(".my_section")))
my_section 給這個修飾符取一個常用明顯的名字,常見于kernel的用法. attribute (( section ("xxx")))的語法可以參考gnu相關文檔。
意思是使用my_section 修飾的數據結構都會被放到.my_section這個段里面
extern const struct person my_section_begin[],my_section_end[];
extern表示my_section_begin,my_section_end已經在鏈接腳本里面定義了,使用一個同名數組名表示這個地址,這個段里面存放的是一個個的自定義數據結構struct person。
struct person{ int age; char *name;};struct person my_section lzy ={ 18, "liangzhengyi",};
經過my_section修飾,lzy 這個實例會被放到vmlinux.lds定義的段里面。
#include < linux/kernel.h >#include < linux/module.h >#include < uapi/linux/sched.h >#include < linux/init_task.h >#include < linux/init.h >#include < linux/fdtable.h >#include < linux/fs_struct.h >#include < linux/mm_types.h >#include < linux/list.h >#include < linux/types.h >#define my_section __attribute__((__section__(".my_section")))struct person{ int age; char *name;};struct person my_section lzy ={ 18, "liangzhengyi",};extern const struct person my_section_begin[],my_section_end[];static int __init section_add_init(void){ struct person *addr_begin = my_section_begin; struct person *addr_end = my_section_end; printk("section_add_init\\n"); printk("find section %d %s",addr_begin- >age,addr_begin- >name); printk("my section lenth:%d\\n",(my_section_end-my_section_begin)*sizeof(struct person)); return 0;}//內核模塊退出函數static void __exit section_add_exit(void){ printk("section_add_exit\\n");}module_init(section_add_init);//入口module_exit(section_add_exit);//出口MODULE_LICENSE("GPL");//許可證
在驅動里,我引用段的起始地址得到我的數據結構,并且我利用了這些數據(打印,最后我打印這個段的大小。
需要注意的是,這個驅動代碼需要編譯進kernel,因為里面要用到的變量是屬于kernel的一部分,段就是kernel的一部分。
可以看到,在上電log里面用到了這個數據結構,數據結構大小是8,段的大小也是8,驗證了數據結構存入段里面的空間分布。
標簽: