字符设备驱动编程①

字符设备驱动编程①



前言

前面有学习过模块化编程,及其字符设备的编写,现在我们深入学习一下字符设备驱动编程,了解字符设备编程所用函数之间的调用关系。


静态加载与动态加载概念

静态加载:将驱动代码直接编译进内核,内核在启动过程中就会自动加载内核;
动态加载:将驱动代码单独编译成.ko格式的文件,再用insmod命令在需要的时候加载内核,在不需要驱动的时候用rmmod命令卸载驱动。静态加载一般用于基础功能的驱动,反正都是迟早是要加载的,编译进内核效率更高;动态加载一般用于扩展功能的驱动,这个设备可能
在加载驱动的时候我们使用:

module_init();

在卸载驱动的时候我们使用:

module_exit();

在我们使用insmod加载驱动的时候就是间接调用module_init(),使用rmmod卸载驱动的时候间接调用module_exit()。为什么是间接调用呢?insmod和rmmod其实并不能识别module_init和module_exit,它们只能识别init_module和cleanup_module。

静态加载

在内核中如果是静态编译,函数调用关系如下:
在这里插入图片描述
根据上面的调用关系,可以知道module_init(x)和module_exit(x)的展开结果:
展开前:

module_init(x);

展开后:

static initcall_t __initcall_x6 __used \
    __attribute__((__section__(".initcall6.init"))) = x;

展开前:

module_exit(x);

展开后:

static exitcall_t __exitcall_x __used __attribute__ ((__section__(#.exitcall.exit))) = x

动态加载

在我们使用insmod加载驱动的时候就是间接调用module_init(),使用rmmod卸载驱动的时候间接调用module_exit()。为什么是间接调用呢?insmod和rmmod其实并不能识别module_init和module_exit,它们只能识别init_module和cleanup_module。

/* Each module must use one module_init(). */
#define module_init(initfn)					\
	static inline initcall_t __inittest(void)		\
	{ return initfn; }					\
	int init_module(void) __attribute__((alias(#initfn)));

/* This is only required if you want to be unloadable. */
#define module_exit(exitfn)					\
	static inline exitcall_t __exittest(void)		\
	{ return exitfn; }					\
	void cleanup_module(void) __attribute__((alias(#exitfn)));

驱动导出

linux内核采用的是模块化的形式管理内核代码。内核中每个模块之间是相互独立的,也就是说A模块的全局变量和函数,B模块是无法访问的。若B模块想要使用A模块中的已有符号,那么必须将A模块中的符号做符号导出,导出到模块符号表中,然后B模块可以使用A模块导出的符号。我们常常使用下面宏来实现驱动导出。

EXPORT_SYMBOL(函数名);

EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用。内核中的定义如下:

/* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec)				\
	extern typeof(sym) sym;					\
	__CRC_SYMBOL(sym, sec)					\
	static const char __kstrtab_##sym			\
	__attribute__((section("__ksymtab_strings"), aligned(1))) \
	= VMLINUX_SYMBOL_STR(sym);				\
	extern const struct kernel_symbol __ksymtab_##sym;	\
	__visible const struct kernel_symbol __ksymtab_##sym	\
	__used							\
	__attribute__((section("___ksymtab" sec "+" #sym), unused))	\
	= { (unsigned long)&sym, __kstrtab_##sym }

#define EXPORT_SYMBOL(sym)					\
	__EXPORT_SYMBOL(sym, "")

我们只需要会使用即可(使用EXPORT_SYMBOL()导出的符号,都将在ksymtab节中放置一个与之关联的结构体)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦梨甜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值