【问题标题】:What happens to local static identifiers in __init function?__init 函数中的本地静态标识符会发生什么?
【发布时间】:2020-10-27 16:18:55
【问题描述】:

在应用程序编程中,static 存储在.BSS 部分。与局部变量不同,它们不会在函数返回或类似于全局变量的情况下被释放。


函数可以在 Linux 中使用__init 属性进行标记,并且这些函数的.text 中的代码将被删除,一旦它们被执行。


假设有一个带有__initstatic 局部变量的函数;那些static 本地的会从.BSS 部分连同.text 部分的功能一起被取消分配吗?

是的,当然在__init 函数中声明static 变量可能没有任何用处(如果你找到了,请提及),但我想了解幕后的情况。

【问题讨论】:

  • 它们绝对没有用。局部静态变量用于在调用函数之间记住一些东西,但如果函数只能被调用一次,那就没有意义了。我想编译器可以优化它以将它们视为自动变量。
  • 但是如果他们不做那个优化,我希望变量像任何其他局部静态变量一样被处理,并根据它们是否为零存储在 BSS 或 DATA 中-初始化或显式初始化。我认为这些部分不允许释放。
  • 如果执行一次、两次或没有执行一次,它们将被完全独立地删除。删除与执行的事实是正交的。

标签: linux gcc linux-kernel static linux-device-driver


【解决方案1】:

如果某个东西被定义为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");

编译上述显示后用objdump查看.ko文件:

  • 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(无论在哪里)定义的变量不再存在。

【讨论】:

  • 将变量声明为static unsigned static_var __initdata = 123; 会将其放入.init.data 部分,允许在加载模块后将其丢弃。当然,ref 变量将不再指向任何有效的地方,因此my_exit 函数中的pr_info("%u\n", *ref); 调用将崩溃。
  • 很高兴知道,我不知道__initdata。我会在上面更新,谢谢。
猜你喜欢
  • 1970-01-01
  • 2010-12-31
  • 2015-04-11
  • 2015-03-31
  • 1970-01-01
  • 1970-01-01
  • 2014-04-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多