编写过设备驱动就会经常碰到module_init这个宏来定义驱动入口函数。这个宏定义了一个函数指针指向我们的驱动入口函数,等到上电的时候就将这些一个个的函数指针拿出来调用,那么各个驱动得到加载。特别的是:这些函数指针是存放在linux kernel本体的某个段里。这是通过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镜像
- 了解如果在合适时候使用这些数据结构
在这我将定义一个数据结构,放入自定义的段里面,然后在驱动加载的时候,取出这个数据结构里面的数据,打印出来。
1.在vmlinux.lds增加自定义段
.my_section :{
my_section_begin = .;
*(.my_section)
my_section_end = .;
}
在vmlinux.lds中间找个位置,定义一个.my_section段,并且使用my_section_begin 和my_section_end 记录这个段的起始地址和结束地址。
2.定义一个宏修饰数据结构放在.my_section
#define my_section __attribute__((__section__(".my_section")))
my_section 给这个修饰符取一个常用明显的名字,常见于kernel的用法. attribute (( section ("xxx")))的语法可以参考gnu相关文档。
意思是使用my_section 修饰的数据结构都会被放到.my_section这个段里面
3.声明段起始地址和结束地址
extern const struct person my_section_begin[],my_section_end[];
extern表示my_section_begin,my_section_end已经在链接脚本里面定义了,使用一个同名数组名表示这个地址,这个段里面存放的是一个个的自定义数据结构struct person。
4.修饰自定义数据结构
struct person{
int age;
char *name;
};
struct person my_section lzy ={
18,
"liangzhengyi",
};
经过my_section修饰,lzy 这个实例会被放到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;
}
//内核模块退出函数
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");//许可证