在应用程序编程中,
static
变量存储在.BSS
部分。 与不同的是,局部变量不会在函数返回时分配或类似于全局变量。
在 Linux 内核模块中,函数可以在 Linux 中使用
__init
属性进行标记,并且此类函数的 .text
中的代码将被删除,一旦被执行。
假设有一个带有
__init
的函数并且有 static
局部变量;那些 static
局部变量是否会与 .BSS
部分中的函数一起从 .text
部分中分配?
是的,当然,在 static
函数中声明
__init
变量可能没有任何用处 (如果找到的话请提及),但我想了解幕后的情况。
如果某物被定义为
static
,则它必须在关联单元的整个生命周期内保持活动状态。唯一发生的事情是,您将无法在声明该对象的函数之外的任何地方通过其原始名称来引用该对象。因此,如果您不在其他地方保存对该对象的引用,该对象仍然可用,但您将无法访问它(这可能被视为内存泄漏)。
谈论内核代码时唯一的例外是
__initdata
宏,它将把变量放在特定的部分(.init.data
)中,并在初始化完成时丢弃它们(就像.init
一样)。在这种情况下,即使定义了变量也会消失 static
,并且引用将变得无效。如果您仅需要某种复杂(且大型)的结构用于初始化目的,并且您希望在 init 函数中使用它后将其丢弃以节省空间,那么这非常有用。
这是一个工作示例:
// SPDX-License-Identifier: GPL-3.0
#include <linux/init.h> // module_{init,exit}()
#include <linux/module.h> // THIS_MODULE, MODULE_VERSION, ...
#include <linux/kernel.h> // printk(), pr_*()
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
static unsigned *global_ptr;
static unsigned global_var;
static int global_initdata_var __initdata;
static int __init my_init(void)
{
static unsigned local_var = 123;
static unsigned local_initdata_var __initdata = 456;
global_ptr = &local_var;
global_var = local_initdata_var;
return global_initdata_var;
}
static void __exit my_exit(void)
{
pr_info("%u %u\n", *global_ptr , global_var);
}
module_init(my_init);
module_exit(my_exit);
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("Test module.");
MODULE_AUTHOR("Marco Bonelli");
MODULE_LICENSE("GPL");
编译上述内容后,用
.ko
查看 objdump
文件显示:
local_var
在 .data
部分的 init 函数内定义(因为它被显式初始化为 123
)。local_initdata_var
和 global_initdata_var
位于 .init.data
部分(因为 __initdata
),无论它们是在哪里定义的。.bss
部分中的全局非显式初始化变量。/ # objdump -j .init.data -j .data -j .bss -D static_init.ko
static_init.ko: file format elf64-littleaarch64
Disassembly of section .data:
0000000000000000 <local_var.20642>:
0: 0000007b .word 0x0000007b
Disassembly of section .init.data:
0000000000000000 <local_initdata_var.20643>:
0: 000001c8 .word 0x000001c8
0000000000000004 <global_initdata_var>:
4: 00000000 .word 0x00000000
Disassembly of section .bss:
0000000000000000 <global_ptr>:
...
0000000000000008 <global_var>:
8: 00000000 .word 0x00000000
插入/移除模块的结果:
/ # insmod static_init.ko
/ # cat /proc/kallsyms | grep static_init
ffff800011ad9768 b static_init_done.7337
ffff800008ca0000 t $x [static_init]
ffff800008ca0000 t my_exit [static_init]
ffff800008ca2000 d $d [static_init]
ffff800008ca2000 d local_var.20642 [static_init]
ffff800008ca2348 b $d [static_init]
ffff800008ca2348 b global_ptr [static_init]
ffff800008ca2350 b global_var [static_init]
ffff800008ca1028 r $d [static_init]
ffff800008ca2040 d $d [static_init]
ffff800008ca1040 r $d [static_init]
ffff800008ca1040 r _note_6 [static_init]
ffff800008ca2040 d __this_module [static_init]
ffff800008ca0000 t cleanup_module [static_init]
/ # rmmod static_init
[ 12.155152] static_init: 123 456
/ #
如您所见,即使插入模块后,
local_var
仍然存在,并被标记为d
(本地数据符号),而用__initdata
定义的变量(无论在哪里)不再存在。