Расширение макроса__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)
Связывание кажется довольно сложной задачей:)