今天在开发的过程中需要把两个类似产品的代码进行合并,减少代码的维护量,同时可以减少物料的维护成本。
     两个产品只是一个通信用结构体内的元素有些区别,结构体相同元素的便宜地址发生了变化,同时增加了一些元素。
     在设计之初应该考虑到升级的问题,应该做到通信地址兼容,不改变原来的通信地址,但是由于前人已设计好了,且工程庞大,不易再修改。
   
 在此基础上想到了,让程序动态的去动态的确定使用哪个结构体去使用,正好,这个结构体一个固定的便宜量中有一个产品ID能够用来区分是哪一种产品。这个恰好是反射机制,在此记录学习一下反射机制

<>反射机制

   
 计算机中的反射,是在运行的时候来自我检查,并对内部成员进行操作。就是说这个变量的类型可以动态的改变,在运行的时候确定它的作用,很多高级编程语言中有这些机制:Python,lua,c#,java都自带有这个机制。下面以lua作为例子
local AllTypes = { Type1 = 1, Type2 = 2, Type3 = 3, } local typeClsHash = {}
typeClsHash[AllTypes.Type1] = Cls1 typeClsHash[AllTypes.Type2] = Cls2
typeClsHash[AllTypes.Type3] = Cls3 local theType = AllTypes.Type2 local cls =
typeClsHash[theType] local instance = cls:new() instance:doSth()
因为没有指定变量的类型,所以可以把任意类型赋值给他,在运行的时候检测当前的类型。

<>c实现反射机制

     高级语言的反射机制,简单来说就是可以通过字符串型获取对应的类或者函数。但是c语言没有这个机制,需要自己动手来实现。

<>基础形式,c语言结构化编程基础实现

1)声明
typedef void (*callback)(void); typedef struct { const char *name; callback
fn; }callback_t; void f0(); void f1(); callback_t callbacks[] = { { "cmd0",
f0}, {"cmd1", f1}, } void f0() { } void f1() { }
2)调用
void do_callback(const char *name) { size_t i; for (i = 0; i <
sizeof(callbacks) / sizeof(callbacks[0]); i++) { if (!strcmp(callbacks[i].name,
name)) { return callbacks[i].fn(); } } }
这种方式的不便之处在于,当需要映射的函数因分散在不同文件时,每增加一个新的映射都需要修改这个数组,以及头文件。

<>利用自定义段

gcc支持通过使用__attribute__((section())),将函数、变量放到指定的数据段中。
也就是说,可以让编译器帮我们完成1中向数组添加成员的动作。
借助此机制,回调函数可以在任意文件申明,不需要修改其他文件。

     内核驱动初始化函数的遍历执行就采用了这种方法。
下面是init/main.c对于初始化函数的遍历调用
extern initcall_t __initcall_start[]; extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[]; extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[]; extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[]; extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[]; extern initcall_t __initcall_end[];
static initcall_t *initcall_levels[] __initdata = { __initcall0_start,
__initcall1_start, __initcall2_start, __initcall3_start, __initcall4_start,
__initcall5_start, __initcall6_start, __initcall7_start, __initcall_end, };
//这些值都是在lds链接脚本中得到的数据 /* Keep these in sync with initcalls in
include/linux/init.h */ static char *initcall_level_names[] __initdata = {
"early", "core", "postcore", "arch", "subsys", "fs", "device", "late", };
static void __init do_initcall_level(int level) { extern const struct
kernel_param __start___param[], __stop___param[]; initcall_t *fn;
strcpy(static_command_line, saved_command_line);
parse_args(initcall_level_names[level], static_command_line, __start___param,
__stop___param - __start___param, level, level, &repair_env_string); for (fn =
initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(*fn); } static void __init do_initcalls(void) { int level; for
(level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level); }
配合arch/xxx/kernel/vmlinux.lds
. = ALIGN(4096); /* Init code and data */ __init_begin = .; . = ALIGN(4096);
.init.text : AT(ADDR(.init.text) - 0) { _sinittext = .; *(.init.text)
*(.cpuinit.text) *(.meminit.text) _einittext = .; } .init.data :
AT(ADDR(.init.data) - 0) { *(.init.data) *(.cpuinit.data) *(.meminit.data)
*(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . = ALIGN(32);
__dtb_start = .; *(.dtb.init.rodata) __dtb_end = .; . = ALIGN(16);
__setup_start = .; *(.init.setup) __setup_end = .; __initcall_start = .;
*(.initcallearly.init) __initcall0_start = .; *(.initcall0.init)
*(.initcall0s.init) __initcall1_start = .; *(.initcall1.init)
*(.initcall1s.init) __initcall2_start = .; *(.initcall2.init)
*(.initcall2s.init) __initcall3_start = .; *(.initcall3.init)
*(.initcall3s.init) __initcall4_start = .; *(.initcall4.init)
*(.initcall4s.init) __initcall5_start = .; *(.initcall5.init)
*(.initcall5s.init) __initcallrootfs_start = .; *(.initcallrootfs.init)
*(.initcallrootfss.init) __initcall6_start = .; *(.initcall6.init)
*(.initcall6s.init) __initcall7_start = .; *(.initcall7.init)
*(.initcall7s.init) __initcall_end = .; __con_initcall_start = .;
*(.con_initcall.init) __con_initcall_end = .; __security_initcall_start = .;
*(.security_initcall.init) __security_initcall_end = .; }
利用链接脚本可以得到每段的起始地址,然后在一段中遍历执行每一个初始化函数

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信