解密module_init,module_exit

本文深入解析了Linux内核模块初始化宏module_init的工作原理,包括其在MODULE宏定义和未定义情况下的展开方式,以及如何通过这些宏实现模块在内核启动过程中的正确初始化。同时解释了模块初始化函数在不同场景下的调用时机。

http://blog.163.com/xinbuqianjin@126/blog/static/167563447201010221231507/


include/linux/init.h


在Linux底下写过driver模块的对这个宏一定不会陌生。module_init宏在MODULE宏有没有定义的情况下展开的内容是不同的,如果这个宏没有定义,基本上表明阁下的模块是要编译进内核的(obj-y)。
1.在MODULE没有定义这种情况下,module_init定义如下:
#define module_init(x) __initcall(x);
因为
#define __initcall(fn)                            device_initcall(fn)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
所以,module_init(x)最终展开为:
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
更直白点,假设阁下driver所对应的模块的初始化函数为int gpio_init(void),那么module_init(gpio_init)实际上等于:
static initcall_t  __initcall_gpio_init_6 __used __attribute__((__section__(".initcall6.init"))) = gpio_init;
就是声明一类型为initcall_t(typedef int (*initcall_t)(void))函数指针类型的变量__initcall_gpio_init_6并将gpio_init赋值与它。
这里的函数指针变量声明比较特殊的地方在于,将这个变量放在了一名为".initcall6.init"节中。接下来结合vmlinux.lds中的
.initcall.init : AT(ADDR(.initcall.init) - (0xc0000000 -0x00000000)) {
   __initcall_start = .;
   *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
   __initcall_end = .;
   }
以及do_initcalls:
static void __init do_initcalls(void)
{
initcall_t *call;
for (call = __initcall_start; call < __initcall_end; call++)
do_one_initcall(*call);
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
那么就不难理解阁下模块中的module_init中的初始化函数何时被调用了:在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。

 












2.在MODULE被定义的情况下(大部分可动态加载的driver模块都属于此, obj-m),module_init定义如下:
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
这段宏定义关键点是后面一句,通过alias将initfn变名为init_module。前面那个__inittest的定义其实是种技巧,用来对initfn进行某种静态的类型检查,如果阁下将模块初始化函数定义成,比如,void gpio_init(void)或者是int gpio_init(int),那么在编译时都会有类似下面的warning:
GPIO/fsl-gpio.c: In function '__inittest':
GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type
通过module_init将模块初始化函数统一别名为init_module,这样以后insmod时候,在系统内部会调用sys_init_module()去找到init_module函数的入口地址。

如果objdump -t gpio.ko,就会发现init_module和gpio_init位于相同的地址偏移处。简言之,这种情况下模块的初始化函数在insmod时候被调用。


===================add by voldemort ==========================

//insmod 命令 内部使用系统调用 sys_init_module() ,根据insmod 的参数 *.ko 找到*.ko 中的init_module 

==================

MODULE 被定义的情况下,module_exit 与module_init 同理,别名为cleanup_module 


#define module_exit(exitfn)\
static inline exitcall_t __exittest(void)\
{ return exitfn; }\
void cleanup_module(void) __attribute__((alias(#exitfn)));

rmmod 会调用sys_delete_module系统调用,根据rmmod 的参数,调用cleanup_module函数


疑问:MODULE 是在哪里定义的

`crypto_akcipher_decrypt` 是 Linux 内核中用于异步非对称加密算法(如 RSA、ECC 等)解密操作的函数。 ### 功能介绍 `crypto_akcipher_decrypt` 函数主要用于对使用非对称加密算法加密的数据进行解密。它支持异步操作,允许在进行解密操作时不会阻塞当前线程,提高系统的并发性能。该函数通常与 `crypto_akcipher` 相关的结构体和函数一起使用,用于处理非对称加密密钥和加密数据。 ### 函数原型 ```c int crypto_akcipher_decrypt(struct crypto_async_request *req, struct akcipher_request *areq); ``` - `req`:指向 `crypto_async_request` 结构体的指针,该结构体包含了异步请求的通用信息,如回调函数、错误码等。 - `areq`:指向 `akcipher_request` 结构体的指针,该结构体包含了非对称加密解密请求的特定信息,如加密数据、解密密钥等。 ### 返回值 - 成功时返回 0。 - 失败时返回一个负的错误码。 ### 使用示例 以下是一个简单的使用 `crypto_akcipher_decrypt` 进行 RSA 解密的示例代码: ```c #include <linux/module.h> #include <linux/crypto.h> #include <linux/err.h> #include <linux/scatterlist.h> static int __init akcipher_decrypt_example_init(void) { struct crypto_akcipher *tfm; struct akcipher_request *areq; struct crypto_async_request req; struct scatterlist sg; int err; // 加载 RSA 算法 tfm = crypto_alloc_akcipher("rsa", 0, 0); if (IS_ERR(tfm)) { err = PTR_ERR(tfm); printk(KERN_ERR "Failed to allocate akcipher: %d\n", err); return err; } // 分配 akcipher 请求 areq = akcipher_request_alloc(tfm, GFP_KERNEL); if (!areq) { err = -ENOMEM; printk(KERN_ERR "Failed to allocate akcipher request: %d\n", err); crypto_free_akcipher(tfm); return err; } // 初始化异步请求 req.complete = NULL; req.data = NULL; req.tfm = crypto_akcipher_tfm(tfm); // 初始化 scatterlist sg_init_one(&sg, "encrypted_data", sizeof("encrypted_data")); // 设置 akcipher 请求 akcipher_request_set_crypt(areq, &sg, &sg, sizeof("encrypted_data"), NULL); akcipher_request_set_callback(areq, CRYPTO_TFM_REQ_MAY_BACKLOG, req.complete, req.data); // 进行解密操作 err = crypto_akcipher_decrypt(&req, areq); if (err) { printk(KERN_ERR "Decryption failed: %d\n", err); } else { printk(KERN_INFO "Decryption succeeded\n"); } // 释放资源 akcipher_request_free(areq); crypto_free_akcipher(tfm); return err; } static void __exit akcipher_decrypt_example_exit(void) { printk(KERN_INFO "Example module unloaded\n"); } module_init(akcipher_decrypt_example_init); module_exit(akcipher_decrypt_example_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Example of using crypto_akcipher_decrypt"); ``` ### 代码解释 1. **加载算法**:使用 `crypto_alloc_akcipher` 函数加载 RSA 算法。 2. **分配请求**:使用 `akcipher_request_alloc` 函数分配 `akcipher_request` 结构体。 3. **初始化请求**:初始化 `crypto_async_request` 和 `akcipher_request` 结构体。 4. **设置数据**:使用 `sg_init_one` 初始化 `scatterlist`,并使用 `akcipher_request_set_crypt` 设置加密数据。 5. **进行解密**:调用 `crypto_akcipher_decrypt` 函数进行解密操作。 6. **释放资源**:使用 `akcipher_request_free` 和 `crypto_free_akcipher` 释放分配的资源。 ### 注意事项 - 示例代码仅为演示目的,实际使用中需要根据具体情况进行修改。 - 非对称加密解密操作通常需要正确设置密钥,示例代码中未包含密钥设置部分。 - 异步操作需要正确处理回调函数,示例代码中回调函数设置为 `NULL`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值