хорошее объяснение __чтение _в основном, __инициализация, __макросы выхода

Расширение макроса__read_mostly:

#define __read_mostly __attribute__((__section__(".data..read_mostly"))

Это изcache.h

__init:

#define __init          __section(.init.text) __cold notrace

изinit.h

__exit:

#define __exit          __section(.exit.text) __exitused __cold notrace

После поиска в сети я не нашел хорошего объяснения что там происходит.

Дополнительный вопрос :Я слышал о различной "магии компоновщика" занят в разработке ядра. Любая информация относительно этого будет замечательно.

У меня есть некоторые идеи об этих макросах о том, что они делают . Вроде __initдолжно указывать, что код функции можно удалить после инициализации. __read_mostlyуказывает на то, что данные записываются редко, и тем самым сводит к минимуму промахи в кэше. Но я понятия не имею Как они это делают . Я имею в виду, что это gccрасширения. Так что теоретически их можно продемонстрировать с помощью небольшого пользовательского кода c.

ОБНОВЛЕНИЕ 1:

Я попытался протестировать __section__с произвольным именем раздела. тестовый код:

#include 

#define __read_mostly __attribute__((__section__("MY_DATA")))

struct ro {
    char a;
    int b;
    char * c;
};

struct ro my_ro  __read_mostly = {
   .a = 'a',
   .b = 3,
   .c = NULL,
};


int main(int argc, char **argv) {
    printf("hello");
    printf("my ro %c %d %p \n", my_ro.a, my_ro.b, my_ro.c);
    return 0;
}

Теперь с __read_mostlyсгенерированным ассемблерным кодом:

   .file   "ro.c"
.globl my_ro
   .section    MY_DATA,"aw",@progbits
   .align 16
   .type   my_ro, @object
   .size   my_ro, 16
my_ro:
   .byte   97
   .zero   3
   .long   3
   .quad   0
   .section   .rodata
.LC0:
   .string "hello"
.LC1:
   .string "my ro %c %d %p \n"
   .text
.globl main
   .type   main, @function
main:
.LFB0:
   .cfi_startproc
    pushq   %rbp
   .cfi_def_cfa_offset 16
   .cfi_offset 6, -16
    movq    %rsp, %rbp
   .cfi_def_cfa_register 6
    pushq   %rbx
    subq    $24, %rsp
    movl    %edi, -20(%rbp)
    movq    %rsi, -32(%rbp)
    movl    $.LC0, %eax
    movq    %rax, %rdi
    movl    $0, %eax
   .cfi_offset 3, -24
    call    printf
    movq    my_ro+8(%rip), %rcx
    movl    my_ro+4(%rip), %edx
    movzbl  my_ro(%rip), %eax
    movsbl  %al, %ebx
    movl    $.LC1, %eax
    movl    %ebx, %esi
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    addq    $24, %rsp
    popq    %rbx
    leave
   .cfi_def_cfa 7, 8
    ret
   .cfi_endproc
.LFE0:
   .size   main,.-main
   .ident  "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)"
   .section   .note.GNU-stack,"",@progbits

Теперь без макроса __read_mostlyассемблерный код остается примерно таким же.

это разница

--- rm.S    2012-07-17 16:17:05.795771270 +0600
+++ rw.S    2012-07-17 16:19:08.633895693 +0600
@@ -1,6 +1,6 @@
   .file   "ro.c"
.globl my_ro
-  .section    MY_DATA,"aw",@progbits
+  .data
   .align 16
   .type   my_ro, @object
   .size   my_ro, 16

Таким образом, создается только подраздел a, ничего особенного.

Даже разборка objdump не показывает никакой разницы.

Итак, мой окончательный вывод о них: работа компоновщика сделать что-то для раздела данных, помеченного специальным именем. Я думаю, что ядро ​​​​linux использует какой-то пользовательский скрипт компоновщика , который позволяет достичь этих целей.

Одна из особенностей __read_mostlyзаключается в том, что данные, которые были помещены туда, могут быть сгруппированы и управляться таким образом, чтобы можно было уменьшить промахи кэша.

Кто-то из lkml отправил патч для удаления __read_mostly. Что породило оживленную дискуссию о достоинствах и недостатках __read_mostly.

вот ссылка:https://lkml.org/lkml/2007/12/13/477

Я опубликую дальнейшие обновления на __initи __exit.

ОБНОВЛЕНИЕ 2

Эти макросы __init, __exitи __read_mostlyпомещают содержимое данных(в случае__read_mostlyтекст(в случаях __initи__exit)помещаются в настраиваемые именованные разделы. Эти разделы используются компоновщиком. Теперь, когда компоновщик не используется по умолчанию по разным причинам, для достижения целей этих макросов используется скрипт компоновщика .

Можно найти информацию о том, как можно использовать пользовательский скрипт компоновщика для устранения неработающего кода (. код, на который ссылается компоновщик, но который никогда не выполнялся ). Этот вопрос имеет очень большое значение во встроенных сценариях. В этом документе обсуждается, как можно настроить скрипт компоновщика для удаления мертвого кода :. elinux.org/images/2/2d/ELC2010 -gc -разделы _Денис _Власенко.pdf

В случае ядра исходный скрипт компоновщика можно найти include/asm-generic/vmlinux.lds.h. Это не окончательный сценарий. Это своего рода отправная точка , скрипт компоновщика дополнительно модифицируется для разных платформ.

Беглый взгляд на этот файл позволяет сразу найти интересующие части:

#define READ_MOSTLY_DATA(align)                     \
   . = ALIGN(align);                       \
    *(.data..read_mostly)                       \
   . = ALIGN(align);

Кажется, этот раздел использует раздел «.data..readmostly».

Также вы можете найти __initи __exitсвязанные команды компоновщика:

#define INIT_TEXT                           \
    *(.init.text)                           \
    DEV_DISCARD(init.text)                      \
    CPU_DISCARD(init.text)                      \
    MEM_DISCARD(init.text)

#define EXIT_TEXT                           \
    *(.exit.text)                           \
    DEV_DISCARD(exit.text)                      \
    CPU_DISCARD(exit.text)                      \
    MEM_DISCARD(exit.text)

Связывание кажется довольно сложной задачей:)

35
задан Aftnix 17 July 2012 в 14:15
поделиться