programing

__read_macro, __init, __macro에 대한 좋은 설명

codeshow 2023. 8. 30. 22:19
반응형

__read_macro, __init, __macro에 대한 좋은 설명

__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 <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

, 이제 그없이것제이▁the이없▁now▁without것그제.__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

따라서 본질적으로 하위 섹션만 생성됩니다. 화려하지는 않습니다.

Objdump 분해도 차이가 없습니다.

그래서 그것들에 대한 저의 마지막 결론은, 링커의 일이 특별한 이름으로 표시된 데이터 섹션을 위해 무언가를 한다는 것입니다.리눅스 커널은 이러한 것들을 달성하기 위해 일종의 맞춤형 링커 스크립트를 사용한다고 생각합니다.

에 대한 것 중 하나.__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-sections_Denys_Vlasenko.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)

링크하는 것은 하기에 꽤 복잡한 것 같습니다 :)

GCC 속성은 언어 자체의 사양을 벗어난 명령을 컴파일러에 제공하는 일반적인 메커니즘입니다.

매크로가 나열하는 일반적인 기능은 다음과 같은 특성을 사용하는 것입니다.

section가 특정 합니다.예를 들어, 선언은 다음과 같습니다.

extern void foobar (void) __attribute__ ((section ("bar")));

막대 섹션에 foobar 함수를 배치합니다.

섹션에 무언가를 넣는다는 것은 무엇을 의미할까요?..text기계어의 경우, 가한컴코경우의드퓨터능실,.data데이터의 경우, "-쓰기"입니다..rodata데이터의 경우 읽기전데의경우터이.bss 등0으로 초기화된 데이터의 경우 등.규약의 이며, 수 .__attribute__ ((section))파일

당신의 예에서 당신은 그것을 추측할 수 있습니다..data..read_mostly는 의하섹션다니의 입니다..data; 대분읽데의터경우이을부▁for..init.text는 프로그램이 초기화될 때 실행되는 텍스트(기계 코드) 섹션입니다.

이 리눅스에 할 때 할 것인지 입니다. 사용자 공간이 요청할 때exec합니다..data페이지로 ..rodata 전용, 읽기전으로용,,.text실행 전용 등으로아마도.init.text프로그램이 시작되기 전에 실행될 것입니다. 커널이나 프로그램의 시작 지점에 배치된 사용자 공간 코드에 의해 실행될 수 있습니다(나는 후자라고 추측합니다).

를 gcc와 함께 .-S섹션 지시사항을 포함하는 어셈블리 코드를 출력하는 옵션입니다. 섹션 하지 않고 다 섹 지 션 사 사 지 어 리 를 있 수 니 습 다 할 실 행 블 셈 않 하 고 용 나 거 을 항 사 하 용objdump또는 16진수 덤프를 사용하여 결과 개체 파일이 어떻게 다른지 확인할 수도 마찬가지입니다.

제가 알기로는 이 매크로들은 커널에서만 사용하는 것으로 알고 있습니다.이론적으로, 그것들은 사용자 공간에 적용될 수 있지만, 저는 이것이 사실이라고 생각하지 않습니다.이들은 모두 서로 다른 효과를 위해 유사한 변수와 코드를 함께 그룹화합니다.

in/in/in-message

커널을 설정하는 데 많은 코드가 필요합니다. 사용자 공간이 실행되기 전에 이러한 코드가 발생합니다.즉, init 태스크가 실행되기 전입니다.대부분의 경우 이 코드는 다시 사용되지 않습니다.따라서 부팅 후 스왑 불가능한 RAM을 사용하는 것은 낭비입니다.익숙한 커널 메시지 Release init memory는 다음의 결과입니다.init부분.일부 드라이버는 모듈로 구성될 수 있습니다.이러한 경우에는 종료됩니다.그러나 커널에 컴파일된 경우 를 종료할 필요는 없습니다(종료될 수 있음).이 섹션은 이 유형의 코드/데이터를 그룹화하는 또 다른 섹션입니다.

추운/더운

캐시 라인은 크기가 고정되어 있습니다.동일한 유형의 데이터/기능을 캐시에 넣어 캐시를 최대화할 수 있습니다.이 아이디어는 자주 사용되는 코드가 나란히 갈 수 있다는 것입니다.캐시가 4개의 명령어인 경우 하나의 핫 루틴의 끝은 다음 핫 루틴의 시작과 병합되어야 합니다.마찬가지로 거의 사용되지 않는 코드를 함께 보관하는 것이 좋습니다. 캐시에 코드가 들어가지 않기를 바랍니다.

읽기_쓰기

여기서의 아이디어는 핫과 유사합니다. 데이터와의 차이는 값을 업데이트할 수 있습니다.이렇게 하면 전체 캐시 라인이 더러워지고 메인 RAM에 다시 써야 합니다.이는 다중 CPU 일관성과 캐시 라인이 오래된 경우에 필요합니다.CPU 캐시 버전과 기본 메모리 간의 차이가 변경되지 않은 경우 삭제할 때 아무 작업도 수행할 필요가 없습니다.이것은 RAM 버스를 최적화하여 다른 중요한 일들이 일어날 수 있도록 합니다.

이러한 항목은 커널 전용입니다.사용자 공간에 대해서도 유사한 트릭을 구현할 수 있습니다.이는 사용 중인 로더에 따라 다르며, 종종 사용 인 libc에 따라 다릅니다.

언급URL : https://stackoverflow.com/questions/11505681/good-explanation-of-read-mostly-init-exit-macros

반응형