我们在编写一个模块的时候,常常使用一些宏定义来标识模块的作者,版本,以及相关信息的描述,如:MODULE_AUTHOR
、MODULE_DESCRIPTION
、MODULE_LICENSE
、MODULE_ALIAS
等,那么这些宏是如何进行管理的呢?
小处诚实非小事!
今天带着大家,我们来深入分析一下这些宏定义,其内部的奥秘!
在上面提到的MODULE_AUTHOR
、MODULE_DESCRIPTION
、MODULE_LICENSE
、MODULE_ALIAS
,这些宏定义,查看其声明,我们发现:
/* For userspace: you can also call me... */
#define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)
/* Soft module dependencies. See man modprobe.d for details.
* Example: MODULE_SOFTDEP("pre: module-foo module-bar post: module-baz")
/*
* The following license idents are currently accepted as indicating free
* software modules
*
* "GPL" [GNU Public License v2 or later]
* "GPL v2" [GNU Public License v2]
* "GPL and additional rights" [GNU Public License v2 rights and more]
* "Dual BSD/GPL" [GNU Public License v2
* or BSD license choice]
* "Dual MIT/GPL" [GNU Public License v2
* or MIT license choice]
* "Dual MPL/GPL" [GNU Public License v2
* or Mozilla license choice]
*
* The following other idents are available
*
* "Proprietary" [Non free products]
*
* There are dual licensed components, but when running with Linux it is the
* GPL that is relevant so this is a non issue. Similarly LGPL linked with GPL
* is a GPL combined work.
*
* This exists for several reasons
* 1. So modinfo can show license info for users wanting to vet their setup
* is free
* 2. So the community can ignore bug reports including proprietary modules
* 3. So vendors can do likewise based on their own policies
*/
#define MODULE_LICENSE(_license) MODULE_INFO(license, _license)
/*
* Author(s), use "Name " or just "Name", for multiple
* authors use multiple MODULE_AUTHOR() statements/lines.
*/
#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
/* What your module does. */
#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
无论是MODULE_LICENSE
、MODULE_AUTHOR
、还是MODULE_DESCRIPTION
,其最终都调用了MODULE_INFO
的宏定义,并将对应的字符串参数传入进去。
那么关键就在于MODULE_INFO
这个宏定义了!
查看MODULE_INFO
的定义,写成了我们看不懂的形式,下面我们来逐步分析
// include/linux/module.h
/* Generic info of form tag = "info" */
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
// include/linux/moduleparam.h
#define __MODULE_INFO(tag, name, info) \
static const char __UNIQUE_ID(name)[] \
__used __attribute__((section(".modinfo"), unused, aligned(1))) \
= __stringify(tag) "=" info
// include/linux/compiler-gcc.h
#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
// include/linux/compiler_types.h
#define ___PASTE(a,b) a##b
#define __PASTE(a,b) ___PASTE(a,b)
上面是MODULE_INFO
的完整定义,其目的是为了生成唯一的字符串。
下面我们举个例子:
MODULE_AUTHOR(donge)
// #define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
MODULE_INFO(author, donge)
// #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
__MODULE_INFO(author, author, donge)
//#define __MODULE_INFO(tag, name, info) \
//static const char __UNIQUE_ID(name)[] \
// __used __attribute__((section(".modinfo"), unused, aligned(1))) \
// = __stringify(tag) "=" info
static const char __UNIQUE_ID(author)[] \
__used __attribute__((section(".modinfo"), unused, aligned(1))) \
= __stringify(author) "=" donge
// #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
static const char __PASTE(__PASTE(__UNIQUE_ID_, author), __COUNTER__)[] \
__used __attribute__((section(".modinfo"), unused, aligned(1))) \
= __stringify(author) "=" donge
// #define ___PASTE(a,b) a##b
// #define __PASTE(a,b) ___PASTE(a,b)
static const char __UNIQUE_ID_author__COUNTER__[] \
__used __attribute__((section(".modinfo"), unused, aligned(1))) \
= __stringify(author) "=" donge
// __COUNTER__会被展开为一个从0开始的整数,每次调用都会加一
static const char __UNIQUE_ID_author0[] \
__used __attribute__((section(".modinfo"), unused, aligned(1))) \
= __stringify(author) "=" donge
// #define __stringify_1(x...) #x
// #define __stringify(x...) __stringify_1(x)
static const char __UNIQUE_ID_author0[] \
__used __attribute__((section(".modinfo"), unused, aligned(1))) \
= "author=donge"
现在我们对这些宏定义了如指掌了吧,下面来分析一下部分属性的含义
aligned
:指定变量或结构域的起始地址对齐(以字节为单位)
used
:通知编译器在目标文件中保留一个静态函数,即使它没有被引用。
attribute__((used))
:被标记在目标文件中,以避免链接器删除未使用的节。
__attribute__((section(".modinfo")))
:将被修饰的变量或函数编译到特定的一块内存空间的位置
__attribute__((used))
:描述静态变量
unused
:表明该函数或者变量可能不使用,避免编译器产生警告信息。
#和##
:我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.
我们通过MODULE_XXXX
设置的信息,怎么去查看呢?通过lsmod xxx.ko
命令
[r8-common-vrf dong@ubuntu ~/WorkSpace/arobot_buildroot/output/r8-common-vrf/build/linux-refs_remotes_origin_r8_develop/drivers/char]$ modinfo arobot-rp-r8.ko
filename: /home/dong/WorkSpace/arobot_buildroot/output/r8-common-vrf/build/linux-refs_remotes_origin_r8_develop/drivers/char/arobot-rp-r8.ko
description: Arobot processor B driver
author: Amicro
license: GPL
alias: of:N*T*Carobot,r8-rpC*
alias: of:N*T*Carobot,r8-rp
depends:
intree: Y
name: arobot_rp_r8
vermagic: 4.19.123 SMP preempt mod_unload ARMv7 p2v8
OK,上文较为详细介绍了
Kernel
内部MODULE_INFO
的详细实现,还有一些重要的知识点没有展开介绍,例如:__attribute__((section(".xxx")))
的作用,后续再起一篇文章介绍。