good explanation of __read_mostly, __init, __exit macros
http://stackoverflow.com/questions/11505681/good-explanation-of-read-mostly-init-exit-macros
The macro expansion of __read_mostly
:
#define __read_mostly __attribute__((__section__(".data..read_mostly"))
This one is from cache.h
__init
:
#define __init __section(.init.text) __cold notrace
from init.h
__exit
:
#define __exit __section(.exit.text) __exitused __cold notrace
After searching through net i have not found any good explanation ofwhat is happening there.
Additonal question : I have heard about various "linker magic"employed in kernel development. Any informationregarding this will be wonderful.
I have some ideas about these macros about what they do. Like
__init
supposed to indicate that the function code can be removed after initialization.
__read_mostly
is for indicating that the data is seldom written and by this it minimizes cache misses. But i have not idea about
How they do it. I mean they are gcc
extensions. So in theory they can be demonstrated by small userland c code.
UPDATE 1:
I have tried to test the __section__
with arbitrary section name. the test code :
#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;
}
Now with __read_mostly
the generated assembly code :
.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
Now without the __read_mostly
macro the assembly code remains more or less the same.
this is the 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
So essentially only the a subsection is created, nothing fancy.
Even the objdump disassmbly does not show any difference.
So my final conclusion about them, its the linker's job do something for data section marked with a special name. I think linux kernel uses some kind of custom linker script do achieve these things.
One of the thing about __read_mostly
, data which were put there can be grouped and managed in a way so that cache misses can be reduced.
Someone at lkml submitted a patch to remove __read_mostly
. Which spawned a fascinated discussion on the merits and demerits of
__read_mostly
.
here is the link : https://lkml.org/lkml/2007/12/13/477
I will post further update on __init
and __exit
.
UPDATE 2
These macros __init
, __exit
and __read_mostly
put the contents of
data(in case of __read_mostly
) and text(in cases of
__init
and __exit
) are put into custom named sections. These sections are utilized by the linker. Now as linker is not used as its default behaviour for various reasons, A
linker script is employed to achieve the purposes of these macros.
A background may be found how a custom linker script can be used to eliminate dead code(code which is linked to by linker but never executed). This issue is of very high importance in embedded scenarios. This document discusses how a linker script can be fine tuned to remove dead code : elinux.org/images/2/2d/ELC2010-gc-sections_Denys_Vlasenko.pdf
In case kernel the initial linker script can be found include/asm-generic/vmlinux.lds.h
. This is not the final script. This is kind of
starting point, the linker script is further modified for different platforms.
A quick look at this file the portions of interest can immediately found:
#define READ_MOSTLY_DATA(align) \
. = ALIGN(align); \
*(.data..read_mostly) \
. = ALIGN(align);
It seems this section is using the ".data..readmostly" section.
Also you can find __init
and __exit
section related linker commands :
#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)
Linking seems pretty complex thing to do :)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通