Linux中THIS_MODULE宏定义详解

一直都在耿耿于怀,这个THIS_MODULE到底是个什么玩意,linux内核中无处不在的东西。今天上网搜了一下,算是基本明白了。网上牛人写的已经比较详细,另外目前暂时没有时间往更深层次分析,所以直接贴过来得了。。。

转帖网址:

http://blog.csdn.net/a954423389/archive/2010/12/27/6101369.aspx

源码位置:

@ kernel/module.c

@ include/linux/module.h

结构体struct module在内核中代表一个内核模块,通过insmod(实际执行init_module系统调用)把自己编写的内核模块插入内核时,模块便与一个 struct module结构体相关联,并成为内核的一部分。下面是结构体struct module的完整定义,接下来会逐个解释:

struct module

{

enum module_state state;

struct list_head list;

char name[MODULE_NAME_LEN];

struct module_kobject mkobj;

struct module_param_attrs *param_attrs;

const char *version;

const char *srcversion;

const struct kernel_symbol *syms;

unsigned int num_syms;

const unsigned long *crcs;

const struct kernel_symbol *gpl_syms;

unsigned int num_gpl_syms;

const unsigned long *gpl_crcs;

unsigned int num_exentries;

const struct exception_table_entry *extable;

int (*init)(void);

void *module_init;

void *module_core;

unsigned long init_size, core_size;

unsigned long init_text_size, core_text_size;

struct mod_arch_specific arch;

int unsafe;

int license_gplok;

#ifdef CONFIG_MODULE_UNLOAD

struct module_ref ref[NR_CPUS];

struct list_head modules_which_use_me;

struct task_struct *waiter;

void (*exit)(void);

#endif

#ifdef CONFIG_KALLSYMS

Elf_Sym *symtab;

unsigned long num_symtab;

char *strtab;

struct module_sect_attrs *sect_attrs;

#endif

void *percpu;

char *args;

};

我们插入一个内核模块,一般会使用工具insmod,该工具实际上调用了系统调用init_module,在该系统调用函数中,首先调用 load_module,把用户空间传入的整个内核模块文件创建成一个内核模块,返回一个struct module结构体。内核中便以这个结构体代表这个内核模块。

state是模块当前的状态。它是一个枚举型变量,可取的值为:MODULE_STATE_LIVE,MODULE_STATE_COMING,MODULE_STATE_GOING。分别表示模块当前正常使用中(存活状态),模块当前正在被加载,模块当前正在被卸载。load_module函数中完成模块的部分创建工作后,把状态置为MODULE_STATE_COMING,sys_init_module函数中完成模块的全部初始化工作后(包括把模块加入全局的模块列表,调用模块本身的初始化函数),把模块状态置为MODULE_STATE_LIVE,最后,使用rmmod工具卸载模块时,会调用系统调用delete_module,会把模块的状态置为MODULE_STATE_GOING。这是模块内部维护的一个状态。

list是作为一个列表的成员,所有的内核模块都被维护在一个全局链表中,链表头是一个全局变量struct module *modules。任何一个新创建的模块,都会被加入到这个链表的头部,通过modules->next即可引用到。

name是模块的名字,一般会拿模块文件的文件名作为模块名。它是这个模块的一个标识。

另外,还要介绍一下宏THIS_MODULE,它的定义如下是#define THIS_MODULE (&__this_module),__this_module是一个struct module变量,代表当前模块,跟current有几分相似。可以通过THIS_MODULE宏来引用模块的struct module结构,试试下面的模块:

#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void)

{

unsigned int cpu = get_cpu();

struct module *mod;

printk(KERN_ALERT "this module: %p==%p/n", &__this_module, THIS_MODULE );

printk(KERN_ALERT "module state: %d/n", THIS_MODULE->state );

printk(KERN_ALERT "module name: %s/n", THIS_MODULE->name );

list_for_each_entry(mod, *(&THIS_MODULE->list.prev), list )

printk(KERN_ALERT "module name: %s/n", mod->name );

return 0;

}

static void hello_exit(void)

{

printk(KERN_ALERT "module state: %d/n", THIS_MODULE->state );

}

module_init(hello_init);

module_exit(hello_exit);

owner是一个struct module *类型的结构体指针,现在告诉你的是每个struct module结构体在内核里都代表了一个内核模块,就像十七大里的每个代表都代表了一批人,至于代表了什么人,选他们的人才知道,同样,每个struct module结构体代表了什么模块,对它进行初始化的模块才知道。当然,初始化这个结构不是写驱动的人该做的事,是在刚才略过的那个从insmod或 modprobe到你驱动的xxx_init函数的曲折过程中做的事。insmod命令执行后,会调用kernel/module.c里的一个系统调用init_module,它会调用load_module函数,将用户空间传入的整个内核模块文件创建成一个内核模块,并返回一个struct module结构体,从此,内核中便以这个结构体代表这个内核模块。

再看看THIS_MODULE宏是什么意思,它在include/linux/module.h里的定义是

85 #define THIS_MODULE (&__this_module)

是一个struct module变量,代表当前模块,与那个著名的current有几分相似,可以通过THIS_MODULE宏来引用模块的struct module结构,比如使用THIS_MODULE->state可以获得当前模块的状态。现在你应该明白为啥在那个岁月里,你需要毫不犹豫毫不迟疑的将struct usb_driver结构里的owner设置为THIS_MODULE了吧,这个owner指针指向的就是你的模块自己。那现在owner咋就说没就没了那?这个说来可就话长了,咱就长话短说吧。不知道那个时候你有没有忘记过初始化owner,反正是很多人都会忘记,大家都把注意力集中到probe、 disconnect等等需要动脑子的角色上面了,这个不需要动脑子,只需要花个几秒钟指定一下的owner反倒常常被忽视,这个就是容易得到的往往不去珍惜,不容易得到的往往日日思量着去争取。于是在2006年的春节前夕,在咱们都无心工作无心学习等着过春节的时候,Greg坚守一线,去掉了 owner,于是千千万万个写usb驱动的人再也不用去时刻谨记初始化owner了。咱们是不用设置owner了,可core里不能不设置,struct usb_driver结构里不是没有owner了么,可它里面嵌的那个struct device_driver结构里还有啊,设置了它就可以了。于是Greg同时又增加了usb_register_driver()这么一层,usb_register()可以通过将参数指定为THIS_MODULE去调用它,所有的事情都挪到它里面去做。反正usb_register() 也是内联的,并不会增加调用的开销。

其余相关网址,可供参考:

http://hi.baidu.com/_huangshuijing/blog/item/253f9a9516183f0c7bf480c5.html

http://www.embedu.org/Column/Column92.htm

以下均针对于内核2.6.18

在module.h 中 THIS_MODULE的定义如下:

extern struct module __this_module;
#define THIS_MODULE (&__this_module)

即是保存了__this_module这个对象的地址,那这个__this_module在哪里定义呢?这就要从module的编译说起啦,如果编译过模块就会发现,会生成*.mod.c这样的一个文件,打开这个文件,就会发现,类似下面的定义:

struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
 .name = KBUILD_MODNAME,
 .init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
 .exit = cleanup_module,
#endif
};

这个文件是调用modpost生成的,modpost的main中有这样一段代码:

    for (mod = modules; mod; mod = mod->next) {
        if (mod->skip)
            continue;

        buf.pos = 0;

        add_header(&buf, mod);
        add_versions(&buf, mod);
        add_depends(&buf, mod, modules);
        add_moddevtable(&buf, mod);
        add_srcversion(&buf, mod);

        sprintf(fname, "%s.mod.c", mod->name);
        write_if_changed(&buf, fname);
    }

其中的add_header就偷偷添加了__this_module 的定义

static void add_header(struct buffer *b, struct module *mod)
{
    buf_printf(b, "#include <linux/module.h>\n");
    buf_printf(b, "#include <linux/vermagic.h>\n");
    buf_printf(b, "#include <linux/compiler.h>\n");
    buf_printf(b, "\n");
    buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
    buf_printf(b, "\n");
    buf_printf(b, "struct module __this_module\n");
    buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n");
    buf_printf(b, " .name = KBUILD_MODNAME,\n");
    if (mod->has_init)
        buf_printf(b, " .init = init_module,\n");
    if (mod->has_cleanup)
        buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n"
                  " .exit = cleanup_module,\n"
                  "#endif\n");
    buf_printf(b, "};\n");
}

时间: 2024-10-07 17:55:37

Linux中THIS_MODULE宏定义详解的相关文章

VC中预处理指令与宏定义详解

刚接触到MFC编程的人往往会被MFC 向导生成的各种宏定义和预处理指令所吓倒,但是预处理和宏定义又是C语言的一个强大工具.使用它们可以进行简单的源代码控制,版本控制,预警或者完成一些特殊的功能. 一个经典的例子 使用预处理与宏定义最经典的例子莫过于加在一个头文件中以避免头文件被两次编译.试想这种的情况,有一个文件headerfile.h 它被包含在headerfile1.h中,同时在headerfile2.h 中也被包含了,现在有一个CPP文件,implement.cpp 包含了headerfi

(转)linux 中特殊符号用法详解

linux 中特殊符号用法详解 原文:https://www.cnblogs.com/lidabo/p/4323979.html # 井号 (comments)#管理员  $普通用户 脚本中 #!/bin/bash   #!/bin/sh井号也常出现在一行的开头,或者位于完整指令之后,这类情况表示符号后面的是注解文字,不会被执行. # This line is comments.echo "a = $a" # a = 0由于这个特性,当临时不想执行某行指令时,只需在该行开头加上 # 就

[转]C++宏定义详解

一.#define的基本用法     #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质,总是在此处产生一些困惑,在编程时误用该命令,使得程序的运行与预期的目的不一致,或者在读别人写的程序时,把运行结果理解错误,这对 C语言的学习很不利. 1 #define命令剖析 1.1   #define的概念     #define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符

C++宏定义详解

一.#define的基本用法     #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质,总是在此处产生一些困惑,在编程时误用该命令,使得程序的运行与预期的目的不一致,或者在读别人写的程序时,把运行结果理解错误,这对 C语言的学习很不利. 1 #define命令剖析 1.1   #define的概念     #define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符

c语言宏定义详解

1,防止一个头文件被重复包含 #ifndef COMDEF_H #define COMDEF_H //头文件内容 #endif 2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数 差异,方便移植. typedef  unsigned char      boolean;     /* Boolean value type. */ typedef  unsigned long int  uint32;      /* Unsigned 32 bit value */ type

linux 中特殊符号用法详解

# 井号 (comments)#管理员  $普通用户 脚本中 #!/bin/bash   #!/bin/sh井号也常出现在一行的开头,或者位于完整指令之后,这类情况表示符号后面的是注解文字,不会被执行. # This line is comments.echo "a = $a" # a = 0由于这个特性,当临时不想执行某行指令时,只需在该行开头加上 # 就行了.这常用在撰写过程中.#echo "a = $a" # a = 0如果被用在指令中,或者引号双引号括住的话

linux中pipe和dup2详解

1.什么是管道 管道是半双工的,数据只能向一个方向流动:需要双方通信时,需要建立起两个管道: 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程): 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在于内存中.数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出.写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据. 2.管道的创建 int pipe(i

linux中FTP服务搭建详解--1.匿名与权限

实验环境:在redhat6.5中安装配置FTP服务,并使用一台win7系统访问验证. 理论: FTP连接方式     支持两种连接模式:主动模式(Port)和被动模式(Pasv),这两种模式都是针对数据链路进行的,与控制链路无关. 主动模式工作过程: 1.客户端从自己的一个任意端口(N > 1024)和FTP服务器的21端口建立控制链路2.然后客户端发出Port指令告诉服务器连接自己的N+1端口来建立一条数据通道3.当FTP服务器接到这一指令时,会使用20端口连接用户在Port指令中指定的端口号

【转】linux 中dd命令使用详解

原文网址:http://xiaozhuang.blog.51cto.com/4396589/850657 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://xiaozhuang.blog.51cto.com/4396589/850657 使用权限: 所有使用者dd 这个指令在 manual 里的定义是 convert and copy a file 使用方式: dd [option] 如果你想要在线看 manual, 可以试试: