Organização do espaço de endereço lógico do kernel do Linux

8

De acordo com o "Write Great Code" em quase todos os tempos de execução do SO, a memória é organizada nas seguintes regiões:

OS | Stack | Heap | Text | Static | Storage/BSS

[In increasing address fashion]

O processo de espaço do usuário usa uma região de memória mais alta para seus diferentes tipos de objetos de dados.

O processo de espaço do kernel também possui diferentes tipos de objetos de dados. Esses objetos compartilham as regiões de memória do espaço do usuário (pilha, heap etc) ou possuem sub-seções separadas (heap, pilha, etc.) localizadas na região do sistema operacional.E, em caso afirmativo, qual é a ordem em que são organizadas? . Obrigado,

    
por gkt 13.07.2011 / 16:11

3 respostas

5

Está errado sobre a encomenda. O sistema operacional está localizado na parte superior da memória, que geralmente está acima da marca de 3 GB (0xC0000000) no kernel de 32 bits, e no kernel de 64 bits é o ponto intermediário de 0x8000000000000000 IIRC.

O local da pilha e do heap é aleatório. Não existe uma regra real sobre a ordenação dos segmentos text / data / bss dentro do programa principal, e cada biblioteca dinâmica possui seu próprio conjunto, portanto, há muitos deles espalhados por toda a memória.

Quando os dinossauros dominavam a Terra (há mais de 20 anos), o espaço de endereços do programa era linear (sem buracos) e a ordem era texto, dados, bss, pilha, pilha. Também não havia bibliotecas dinâmicas ou encadeamento na época. Tudo isso mudou com a memória virtual.

Os processos do kernel estão totalmente contidos na parte do kernel do espaço de endereço; a parte do usuário é ignorada. Isso permite que o kernel acelere a troca de contexto entre os encadeamentos do kernel, pois não é necessário atualizar as tabelas de páginas, pois todos os processos compartilham a mesma parte do kernel das tabelas de páginas.

    
por 13.07.2011 / 20:11
4

Isso não é verdade em "quase todos os sistemas operacionais". Os tipos de memória representados são bastante típicos, mas não há razão para que eles devam estar em uma ordem específica, e pode haver mais de uma peça de um determinado tipo.

No Linux, você pode ver o espaço de endereço de um processo com cat /proc/$pid/maps , em que $pid é o ID do processo, por exemplo, cat /proc/$$/maps para ver o shell do qual você está executando cat ou cat /proc/self/maps para ver os mapeamentos do próprio cat . O comando pmap produz resultados ligeiramente mais agradáveis.

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]

Você pode ver o código e os dados de leitura / gravação (texto e BSS) do executável, em seguida, o heap, depois um arquivo mapeado na memória e, em seguida, um pouco mais de dados de leitura e gravação e dados de somente leitura. e ler / gravar dados de uma biblioteca compartilhada (texto e BSS novamente), mais dados de leitura e gravação, outra biblioteca compartilhada (mais precisamente, o vinculador dinâmico) e, finalmente, a pilha do único encadeamento.

O código do kernel usa seus próprios intervalos de endereços. Em muitas plataformas, o Linux usa a parte superior do espaço de endereço do kernel, geralmente o 1GB superior. Idealmente, esse espaço seria suficiente para mapear o código do kernel, os dados do kernel e a memória do sistema (RAM) e todos os dispositivos mapeados na memória. Em PCs de 32 bits típicos de hoje, isso não é possível, o que requer contorções que são de interesse apenas dos hackers do kernel.

Enquanto o código do kernel está lidando com uma chamada de sistema, idealmente (quando as contorções acima mencionadas não estão no lugar), a memória do processo é mapeada nos mesmos endereços. Isso permite que os processos passem dados para o kernel, e o kernel pode ler diretamente do ponteiro. No entanto, não é um grande ganho, já que os ponteiros precisam ser validados de qualquer maneira (para que o processo não consiga enganar o kernel para ler da memória que o processo não deveria ter acesso).

As zonas de memória dentro do espaço do kernel do Linux são bastante complexas. Existem vários pools de memória diferentes, e as principais distinções não são sobre onde a memória vem, mas sim com quem ela é compartilhada. Se você está curioso sobre eles, comece com LDD3 .

    
por 22.07.2011 / 22:53
1

Não é uma resposta, mas uma informação que precisa de mais espaço.

Eu não acho que sua concepção de layout de endereço lógico está correta.

Você pode compilar e executar este programa para ver o que um processo de usuário tem para endereços:

#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;
}

O Red Hat Enterprise Server que eu tenho rodando, tem readelf , que pode ser usado para dizer onde o kernel iria (logicamente) carregar um arquivo executável:

readelf -S where

Mostra-me muitas das mesmas informações de endereçamento que a saída de where fornece.

Eu não acho que readelf funcionará facilmente em um kernel Linux (/ boot / vmlinuz ou algo assim), e eu acho que o kernel por padrão começa em 0x80000000 em seu próprio espaço de endereço: ele não está mapeado em um processo userland, apesar de usar um endereço acima do topo da pilha userland em 0x7fffffff (x86, endereçamento de 32 bits).

    
por 13.07.2011 / 18:07