首页 > 解决方案 > 内核全局变量存储在哪里?

问题描述

据我所知,全局变量(在用户空间程序中)存储在对应的 DATA(和 BSS)段中。但是在内核代码的情况下,它们是如何以及在哪里存储的呢?

标签: variableslinux-kerneloperating-systemkernel

解决方案


例如,让我们看一下 Linux 的全局变量之一。基本上,Linux 是使用 gzip 或其他压缩工具压缩的 ELF 映像。如维基百科(https://en.wikipedia.org/wiki/Vmlinux)所述:

传统上,在创建可引导内核映像时,内核也使用 gzip 进行压缩,或者,从 Linux 2.6.30 开始,[3] 使用 LZMA 或 bzip2,这需要在生成的映像中包含一个非常小的解压缩存根。存根解压缩内核代码,在某些系统上将点打印到控制台以指示进度,然后继续引导过程。稍后添加了对 LZO、[4] xz[5] 和 LZ4[6] 压缩的支持。

extract-vmlinux在 Ubuntu 上,您的 Linux 内核版本附带了一个名为的特殊脚本( https://blog.packagecloud.io/eng/2016/03/08/how-to-extract-and-disassmble-a-linux -kernel-image-vmlinuz/)。Linux ELF 文件是 /boot/vmlinuz。要解压它,您必须使用可安装的 extract-vmlinux 脚本

sudo apt-get install linux-headers-$(uname -r)

该脚本可以安全使用,因为它是一个标准脚本,可以在您的分发包的官方存储库中找到(那里的大多数包都是开源且安全的)。现在使用

sudo /usr/src/kernels/$(uname -r)/scripts/extract-vmlinux /boot/vmlinuz-$(uname -r) > vmlinux

从压缩的 vmlinux 映像中提取 Linux 内核的 ELF 文件到您的主存储库。现在我们可以查看Linux内核的ELF文件。用于readelf -a vmlinux对内容进行摘要。我的输出是这样的:

user@user-System-Product-Name:~$ readelf -a vmlinux
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x1000000
  Start of program headers:          64 (bytes into file)
  Start of section headers:          46137728 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         5
  Size of section headers:           64 (bytes)
  Number of section headers:         35
  Section header string table index: 34

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         ffffffff81000000  00200000
       0000000000e025f7  0000000000000000  AX       0     0     4096
  [ 2] .rodata           PROGBITS         ffffffff82000000  01200000
       00000000004e46f2  0000000000000000  WA       0     0     4096
  [ 3] .pci_fixup        PROGBITS         ffffffff824e4700  016e4700
       00000000000032b0  0000000000000000   A       0     0     16
  [ 4] .tracedata        PROGBITS         ffffffff824e79b0  016e79b0
       0000000000000078  0000000000000000   A       0     0     1
  [ 5] __ksymtab         PROGBITS         ffffffff824e7a28  016e7a28
       0000000000010a34  0000000000000000   A       0     0     4
  [ 6] __ksymtab_gpl     PROGBITS         ffffffff824f845c  016f845c
       00000000000121ec  0000000000000000   A       0     0     4
  [ 7] __kcrctab         PROGBITS         ffffffff8250a648  0170a648
       00000000000058bc  0000000000000000   A       0     0     4
  [ 8] __kcrctab_gpl     PROGBITS         ffffffff8250ff04  0170ff04
       00000000000060a4  0000000000000000   A       0     0     4
  [ 9] __ksymtab_strings PROGBITS         ffffffff82515fa8  01715fa8
       0000000000037bea  0000000000000001 AMS       0     0     1
  [10] __init_rodata     PROGBITS         ffffffff8254dba0  0174dba0
       00000000000002a8  0000000000000000   A       0     0     32
  [11] __param           PROGBITS         ffffffff8254de48  0174de48
       00000000000038e0  0000000000000000   A       0     0     8
  [12] __modver          PROGBITS         ffffffff82551728  01751728
       00000000000005a0  0000000000000000  WA       0     0     8
  [13] __ex_table        PROGBITS         ffffffff82551cd0  01751cd0
       0000000000001af4  0000000000000000   A       0     0     4
  [14] .notes            NOTE             ffffffff825537c4  017537c4
       00000000000001ec  0000000000000000   A       0     0     4
  [15] .data             PROGBITS         ffffffff82600000  01800000
       00000000003657c0  0000000000000000  WA       0     0     8192
  [16] __bug_table       PROGBITS         ffffffff829657c0  01b657c0
       00000000000178d4  0000000000000000  WA       0     0     1
  [17] .vvar             PROGBITS         ffffffff8297e000  01b7e000
       0000000000001000  0000000000000000  WA       0     0     16
  [18] .data..percpu     PROGBITS         0000000000000000  01c00000
       000000000002f000  0000000000000000  WA       0     0     4096
  [19] .init.text        PROGBITS         ffffffff829ae000  01dae000
       00000000000772e5  0000000000000000  AX       0     0     16
  [20] .altinstr_aux     PROGBITS         ffffffff82a252e5  01e252e5
       0000000000002d9b  0000000000000000  AX       0     0     1
  [21] .init.data        PROGBITS         ffffffff82a2a000  01e2a000
       00000000001c74f0  0000000000000000  WA       0     0     8192
  [22] .x86_cpu_dev.init PROGBITS         ffffffff82bf14f0  01ff14f0
       0000000000000028  0000000000000000   A       0     0     8
  [23] .parainstructions PROGBITS         ffffffff82bf1518  01ff1518
       0000000000022b7c  0000000000000000   A       0     0     8
  [24] .altinstructions  PROGBITS         ffffffff82c14098  02014098
       0000000000006660  0000000000000000   A       0     0     1
  [25] .altinstr_replace PROGBITS         ffffffff82c1a6f8  0201a6f8
       0000000000001789  0000000000000000  AX       0     0     1
  [26] .iommu_table      PROGBITS         ffffffff82c1be88  0201be88
       00000000000000f0  0000000000000000   A       0     0     8
  [27] .apicdrivers      PROGBITS         ffffffff82c1bf78  0201bf78
       0000000000000040  0000000000000000  WA       0     0     8
  [28] .exit.text        PROGBITS         ffffffff82c1bfb8  0201bfb8
       0000000000001f4b  0000000000000000  AX       0     0     1
  [29] .smp_locks        PROGBITS         ffffffff82c1e000  0201e000
       000000000000a000  0000000000000000   A       0     0     4
  [30] .data_nosave      PROGBITS         ffffffff82c28000  02028000
       0000000000001000  0000000000000000  WA       0     0     4
  [31] .bss              NOBITS           ffffffff82c29000  02029000
       00000000005d7000  0000000000000000  WA       0     0     4096
  [32] .brk              NOBITS           ffffffff83200000  02029000
       000000000002c000  0000000000000000  WA       0     0     1
  [33] .init.scratch     PROGBITS         ffffffff83400000  02800000
       0000000000400000  0000000000000000  WA       0     0     32
  [34] .shstrtab         STRTAB           0000000000000000  02c00000
       0000000000000180  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000200000 0xffffffff81000000 0x0000000001000000
                 0x00000000015539b0 0x00000000015539b0  R E    0x200000
  LOAD           0x0000000001800000 0xffffffff82600000 0x0000000002600000
                 0x000000000037f000 0x000000000037f000  RW     0x200000
  LOAD           0x0000000001c00000 0x0000000000000000 0x000000000297f000
                 0x000000000002f000 0x000000000002f000  RW     0x200000
  LOAD           0x0000000001dae000 0xffffffff829ae000 0x00000000029ae000
                 0x0000000000e52000 0x0000000000e52000  RWE    0x200000
  NOTE           0x00000000017537c4 0xffffffff825537c4 0x00000000025537c4
                 0x00000000000001ec 0x00000000000001ec         0x4

 Section to Segment mapping:
  Segment Sections...
   00     .text .rodata .pci_fixup .tracedata __ksymtab __ksymtab_gpl __kcrctab __kcrctab_gpl __ksymtab_strings __init_rodata __param __modver __ex_table .notes 
   01     .data __bug_table .vvar 
   02     .data..percpu 
   03     .init.text .altinstr_aux .init.data .x86_cpu_dev.init .parainstructions .altinstructions .altinstr_replacement .iommu_table .apicdrivers .exit.text .smp_locks .data_nosave .bss .brk .init.scratch 
   04     .notes 

There is no dynamic section in this file.

There are no relocations in this file.

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

No version information found in this file.

Displaying notes found in: .notes
  Owner                Data size    Description
  Xen                  0x00000006   Unknown note type: (0x00000006)
   description data: 6c 69 6e 75 78 00 
  Xen                  0x00000004   Unknown note type: (0x00000007)
   description data: 32 2e 36 00 
  Xen                  0x00000008   Unknown note type: (0x00000005)
   description data: 78 65 6e 2d 33 2e 30 00 
  Xen                  0x00000008   Unknown note type: (0x00000003)
   description data: 00 00 00 80 ff ff ff ff 
  Xen                  0x00000008   Unknown note type: (0x0000000f)
   description data: 00 00 00 00 80 00 00 00 
  Xen                  0x00000008   NT_VERSION (version)
   description data: c0 e1 9a 82 ff ff ff ff 
  Xen                  0x00000008   NT_ARCH (architecture)
   description data: 00 20 00 81 ff ff ff ff 
  Xen                  0x00000029   Unknown note type: (0x0000000a)
   description data: 21 77 72 69 74 61 62 6c 65 5f 70 61 67 65 5f 74 61 62 6c 65 73 7c 70 61 65 5f 70 67 64 69 72 5f 61 62 6f 76 65 5f 34 67 62 
  Xen                  0x00000004   Unknown note type: (0x00000011)
   description data: 01 88 00 00 
  Xen                  0x00000004   Unknown note type: (0x00000009)
   description data: 79 65 73 00 
  Xen                  0x00000008   Unknown note type: (0x00000008)
   description data: 67 65 6e 65 72 69 63 00 
  Xen                  0x00000010   Unknown note type: (0x0000000d)
   description data: 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 
  Xen                  0x00000004   Unknown note type: (0x0000000e)
   description data: 01 00 00 00 
  Xen                  0x00000004   Unknown note type: (0x00000010)
   description data: 01 00 00 00 
  Xen                  0x00000008   Unknown note type: (0x0000000c)
   description data: 00 00 00 00 00 80 ff ff 
  Xen                  0x00000008   Unknown note type: (0x00000004)
   description data: 00 00 00 00 00 00 00 00 
  GNU                  0x00000014   NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: c0263a3075bc0a9388365ddf35ab5422da3356a9
  Linux                0x00000001   OPEN
   description data: 00 
  Xen                  0x00000008   Unknown note type: (0x00000012)
   description data: 40 07 00 01 00 00 00 00

该问题的重要部分是以下Program headers部分:

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000200000 0xffffffff81000000 0x0000000001000000
                 0x00000000015539b0 0x00000000015539b0  R E    0x200000
  LOAD           0x0000000001800000 0xffffffff82600000 0x0000000002600000
                 0x000000000037f000 0x000000000037f000  RW     0x200000
  LOAD           0x0000000001c00000 0x0000000000000000 0x000000000297f000
                 0x000000000002f000 0x000000000002f000  RW     0x200000
  LOAD           0x0000000001dae000 0xffffffff829ae000 0x00000000029ae000
                 0x0000000000e52000 0x0000000000e52000  RWE    0x200000
  NOTE           0x00000000017537c4 0xffffffff825537c4 0x00000000025537c4
                 0x00000000000001ec 0x00000000000001ec         0x4

我猜 RW 部分是静态和独立内核映像的数据/bss 段。此内核映像中没有太多 bss 数据(未初始化的数据)。您可以通过查看不同段的文件大小与内存大小来看到这一点。在 bss 数据中,只有大小写入可执行文件。然后加载程序将零分配给这些段。由于我们只有与文件大小相等的 memsize,所以我们这里没有太多 bss。

此外,我们还可以查看内核在解压后加载自身的不同虚拟地址。这对于查找某个全局变量所在的段很有用。

现在,要确定全局变量的加载位置,可以查看 /boot 目录中的 System.map 文件。例如,我们可以在 kernel/sched/core.c 中调用一个随机全局变量,sysctl_sched_rt_period并使用

user@user-System-Product-Name:~$ sudo grep "sysctl_sched_rt_period"  /boot/System.map-$(uname -r)

ffffffff8266eb24 D sysctl_sched_rt_period

地址是这样的ffffffff8266eb24。该地址位于 ffffffff82600000 加载的 RW 段中,因为该段的大小为 37f000 并且因为ffffffff82600000 + 37f000 > ffffffff8266eb24. 可以清楚的看到这个全局变量存放在内核可执行文件的数据段中。


推荐阅读