Макрорасширение __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
расширения. Поэтому теоретически их можно продемонстрировать с помощью небольшого кода пользователя userland c.
ОБНОВЛЕНИЕ 1:
Я попытался проверить __section__
на произвольное имя раздела. тестовый код:
#include <stdio.h>
#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
код сборки остается более или менее одинаковым.
это diff
--- 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
Таким образом, по существу создается только один подраздел, ничего необычного.
Даже objdump disasmbly не показывает никакой разницы.
Итак, мой окончательный вывод о них, его компоновщик делают что-то для раздела данных, отмеченного специальным именем. Я думаю, что ядро linux использует какой-то пользовательский компоновщик script, чтобы достичь этих целей.
Одна из вещей в __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
) помещаются в пользовательские именованные разделы. Эти разделы используются компоновщиком. Теперь, поскольку компоновщик не используется по умолчанию по различным причинам, для достижения целей этих макросов используется A linker script.
Можно узнать, как можно использовать собственный компоновщик script для устранения мертвого кода (код, связанный с компоновщиком, но никогда не исполняемый). Эта проблема имеет очень важное значение во встроенных сценариях. В этом документе обсуждается, как компоновщик script может быть настроен для удаления мертвого кода: elinux.org/images/2/2d/ELC2010-gc-sections_Denys_Vlasenko.pdf
В случае ядра исходный компоновщик script можно найти include/asm-generic/vmlinux.lds.h
. Это не окончательный script. Это своего рода начальная точка, компоновщик script дополнительно модифицируется для разных платформ.
Быстрый просмотр этого файла может сразу найти интересующие вас части:
#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)
Связывание выглядит довольно сложной задачей:)