編寫過設(shè)備驅(qū)動(dòng)就會(huì)經(jīng)常碰到module_init這個(gè)宏來定義驅(qū)動(dòng)入口函數(shù)。這個(gè)宏定義了一個(gè)函數(shù)指針指向我們的驅(qū)動(dòng)入口函數(shù),等到上電的時(shí)候就將這些一個(gè)個(gè)的函數(shù)指針拿出來調(diào)用,那么各個(gè)驅(qū)動(dòng)得到加載。特別的是:這些函數(shù)指針是存放在linux kernel本體的某個(gè)段里。這是通過gnu 的__attribute__來修飾的。
實(shí)際上,kernel里面有非常多的段,這些段的起始地址和結(jié)束地址都能被源碼里得到,因此學(xué)會(huì)看鏈接腳本和引用里面的段再或者是自定義段對于理解kernel的源碼大有益處。
鏈接腳本
任何一個(gè)可執(zhí)行程序是被鏈接腳本將一個(gè)個(gè)的.o鏈接起來的。kernel也不例外,kernel的每個(gè)架構(gòu)都有一個(gè)默認(rèn)的鏈接腳本路徑,如:arch/arm/kernel/vmlinux.lds
.init.arch.info : {
__arch_info_begin = .;
*(.arch.info.init)//機(jī)器信息段
__arch_info_end = .;
}
部分段的形式如上,__arch_info_begin 和__arch_info_end 就能被源碼引用,得到這個(gè)地址范圍的數(shù)據(jù)。怎么把數(shù)據(jù)放入這些段:使用attribute修飾你想要放入這個(gè)段的數(shù)據(jù)結(jié)構(gòu) 。
對于kernel的段組成有非常多,按需查看源碼即可。
實(shí)戰(zhàn)
目的:
了解自定義數(shù)據(jù)結(jié)構(gòu)通過鏈接腳本如何放入kernel鏡像
- 了解自定義數(shù)據(jù)結(jié)構(gòu)通過鏈接腳本如何放入kernel鏡像
- 了解如果在合適時(shí)候使用這些數(shù)據(jù)結(jié)構(gòu)
在這我將定義一個(gè)數(shù)據(jù)結(jié)構(gòu),放入自定義的段里面,然后在驅(qū)動(dòng)加載的時(shí)候,取出這個(gè)數(shù)據(jù)結(jié)構(gòu)里面的數(shù)據(jù),打印出來。
1.在vmlinux.lds增加自定義段
.my_section :{
my_section_begin = .;
*(.my_section)
my_section_end = .;
}
在vmlinux.lds中間找個(gè)位置,定義一個(gè).my_section段,并且使用my_section_begin 和my_section_end 記錄這個(gè)段的起始地址和結(jié)束地址。
2.定義一個(gè)宏修飾數(shù)據(jù)結(jié)構(gòu)放在.my_section
#define my_section __attribute__((__section__(".my_section")))
my_section 給這個(gè)修飾符取一個(gè)常用明顯的名字,常見于kernel的用法. attribute (( section ("xxx")))的語法可以參考gnu相關(guān)文檔。
意思是使用my_section 修飾的數(shù)據(jù)結(jié)構(gòu)都會(huì)被放到.my_section這個(gè)段里面
3.聲明段起始地址和結(jié)束地址
extern const struct person my_section_begin[],my_section_end[];
extern表示my_section_begin,my_section_end已經(jīng)在鏈接腳本里面定義了,使用一個(gè)同名數(shù)組名表示這個(gè)地址,這個(gè)段里面存放的是一個(gè)個(gè)的自定義數(shù)據(jù)結(jié)構(gòu)struct person。
4.修飾自定義數(shù)據(jù)結(jié)構(gòu)
struct person{
int age;
char *name;
};
struct person my_section lzy ={
18,
"liangzhengyi",
};
經(jīng)過my_section修飾,lzy 這個(gè)實(shí)例會(huì)被放到vmlinux.lds定義的段里面。
5.完整代碼
#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;
}
//內(nèi)核模塊退出函數(shù)
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");//許可證