"Writing Great Code"에 따르면 거의 모든 운영 체제 런타임 메모리는 다음 영역으로 구성됩니다.
OS|스택|힙|리터럴|정적|스토리지/BSS
[주소를 추가하여]
사용자 공간 프로세스는 다양한 유형의 데이터 개체에 대해 더 높은 메모리 영역을 사용합니다.
커널 공간 프로세스에는 다양한 유형의 데이터 개체도 있습니다. 이러한 개체는 사용자 공간 메모리 영역(스택, 힙 등)을 공유합니까, 아니면 운영 체제 영역(힙, 스택 등)에 별도의 하위 섹션이 있습니까? 그렇다면 어떤 순서로 나열되어 있습니까? 감사해요,
답변1
뭔가 순서가 잘못됐네요. 운영 체제는 메모리 위에 위치하며, 32비트 커널에서는 일반적으로 3GB 표시( 0xC0000000 ) 위에 있고, 64비트 커널에서는 0x8000000000000000 IIRC의 중간점에 있습니다.
스택과 힙의 위치는 무작위로 지정됩니다. 메인 프로그램에는 텍스트/데이터/BSS 세그먼트의 순서에 대한 실제 규칙이 없으며 각 동적 라이브러리에는 이러한 세그먼트의 자체 세트가 있으므로 그 중 많은 부분이 메모리에 분산되어 있습니다.
공룡이 지구를 지배했을 때(20여년 전) 프로그램 주소 공간은 선형(구멍 없음)이었고 순서는 텍스트, 데이터, bss, 스택, 힙이었습니다. 그 당시에는 동적 라이브러리나 스레드가 없었습니다. 이 모든 것은 가상 메모리에 따라 변경됩니다.
커널 프로세스는 주소 공간의 커널 부분 내에 완전히 포함됩니다. 사용자 부분은 무시됩니다. 이를 통해 커널은 커널 스레드 간의 컨텍스트 전환 속도를 높일 수 있습니다. 모든 프로세스가 페이지 테이블의 동일한 커널 부분을 공유하기 때문에 페이지 테이블을 업데이트할 필요가 없기 때문입니다.
답변2
"거의 모든 운영 체제"에는 해당되지 않습니다. 표시되는 메모리 영역의 유형은 매우 일반적이지만 특정 순서로 표시되어야 할 이유는 없으며 특정 유형에 대해 여러 메모리 영역이 있을 수 있습니다.
cat /proc/$pid/maps
Linux에서는 실행 중인 셸을 보거나 프로세스 자체 매핑을 보는 등 프로세스 ID로 프로세스의 주소 공간을 볼 수 있습니다 . 주문하다$pid
cat /proc/$$/maps
cat
cat /proc/self/maps
cat
pmap
약간 더 나은 출력을 생성합니다.
08048000-08054000 r-xp 00000000 08:01 828061 /bin/cat
08054000-08055000 r--p 0000b000 08:01 828061 /bin/cat
08055000-08056000 rw-p 0000c000 08:01 828061 /bin/cat
08c7f000-08ca0000 rw-p 00000000 00:00 0 [heap]
b755a000-b7599000 r--p 00000000 08:01 273200 /usr/lib/locale/en_US.utf8/LC_CTYPE
b7599000-b759a000 rw-p 00000000 00:00 0
b759a000-b76ed000 r-xp 00000000 08:01 269273 /lib/tls/i686/cmov/libc-2.11.1.so
b76ed000-b76ee000 ---p 00153000 08:01 269273 /lib/tls/i686/cmov/libc-2.11.1.so
b76ee000-b76f0000 r--p 00153000 08:01 269273 /lib/tls/i686/cmov/libc-2.11.1.so
b76f0000-b76f1000 rw-p 00155000 08:01 269273 /lib/tls/i686/cmov/libc-2.11.1.so
b76f1000-b76f4000 rw-p 00000000 00:00 0
b770b000-b7712000 r--s 00000000 08:01 271618 /usr/lib/gconv/gconv-modules.cache
b7712000-b7714000 rw-p 00000000 00:00 0
b7714000-b7715000 r-xp 00000000 00:00 0 [vdso]
b7715000-b7730000 r-xp 00000000 08:01 263049 /lib/ld-2.11.1.so
b7730000-b7731000 r--p 0001a000 08:01 263049 /lib/ld-2.11.1.so
b7731000-b7732000 rw-p 0001b000 08:01 263049 /lib/ld-2.11.1.so
bfbec000-bfc01000 rw-p 00000000 00:00 0 [stack]
실행 파일에서 코드와 읽기-쓰기 데이터(텍스트 및 BSS)를 볼 수 있고, 힙, 메모리 매핑된 파일, 더 많은 읽기-쓰기 데이터, 코드, 읽기 전용 데이터, 읽기 전용 데이터를 볼 수 있습니다. 공유 라이브러리(다시 텍스트 및 BSS)에서 데이터 쓰기, 더 많은 데이터 읽기 및 쓰기, 또 다른 공유 라이브러리(보다 정확하게는 동적 링커) 및 마지막으로 유일한 스레드의 스택입니다.
커널 코드는 자체 주소 범위를 사용합니다. 많은 플랫폼에서 Linux는 커널 주소 공간의 상위 부분(보통 상위 1GB)을 사용합니다. 이상적으로 이 공간은 커널 코드, 커널 데이터, 시스템 메모리(RAM) 및 각 메모리 매핑 장치를 매핑하기에 충분합니다. 오늘날의 일반적인 32비트 PC에서는 이것이 불가능하며 커널 해커에게만 흥미로운 변형이 필요합니다.
커널 코드가 시스템 호출을 처리할 때 이상적으로는(위의 트위스트가 적용되지 않은 경우) 프로세스의 메모리가 동일한 주소에 매핑됩니다. 이를 통해 프로세스는 데이터를 커널에 전달할 수 있으며 커널은 포인터에서 직접 읽을 수 있습니다. 그러나 이는 어쨌든 포인터를 검증해야 하기 때문에 큰 이득은 아닙니다(따라서 프로세스는 프로세스가 액세스할 수 없는 메모리에서 데이터를 읽도록 커널을 속일 수 없습니다).
Linux 커널 공간 내의 메모리 영역은 매우 복잡합니다. 여러 가지 다른 메모리 풀이 있습니다. 주요 차이점은 메모리가 어디서 나오는지가 아니라 메모리를 공유하는 대상이 누구인지입니다. 그 내용이 궁금하다면 확인해 보세요.LDD3.
답변3
대답은 아니지만 더 많은 공간이 필요한 참고 사항입니다.
나는 논리 주소 레이아웃에 대한 귀하의 개념이 단순히 올바르지 않다고 생각합니다.
이 프로그램을 컴파일하고 실행하여 사용자 모드 프로세스의 주소를 볼 수 있습니다.
#include <stdio.h>
long global_initialized = 119234;
long global_uninitialized;
extern int _end, _edata, _etext;
int
main(int ac, char **av)
{
long local;
printf("main at 0x%lx\n", main);
printf("ac at 0x%lx\n", &ac);
printf("av at 0x%lx\n", &av);
printf("av has 0x%lx\n", av);
printf("initialized global at 0x%lx\n", &global_initialized);
printf("global at 0x%lx\n", &global_uninitialized);
printf("local at 0x%lx\n", &local);
printf("_end at 0x%lx\n", &_end);
printf("_edata at 0x%lx\n", &_edata);
printf("_etext at 0x%lx\n", &_etext);
return 0;
}
제가 실행 중인 Red Hat Enterprise Server에는 readelf
커널이 (논리적으로) 실행 파일을 로드할 위치를 나타내는 데 사용할 수 있는 가 있습니다.
readelf -S where
where
의 출력과 동일한 주소 지정 정보를 많이 보여줍니다 .
나는 readelf
Linux 커널(/boot/vmlinuz 또는 이와 유사한)에서 작업하는 것이 쉽지 않다고 생각하며 커널은 기본적으로 자체 주소 공간에서 0x80000000에서 시작한다고 생각합니다. 사용자 주소를 사용하지만 사용자 영역 프로세스에 매핑되지 않습니다. 상태 스택 상단 위는 0x7fffffff(x86, 32비트 주소 지정)에 있습니다.