A interface de programação do Linux mostra o layout de um espaço de endereço virtual de um processo. Cada região do diagrama é um segmento?
De Entendendo o kernel do Linux ,
está correto que o seguinte significa que a unidade de segmentação na MMU mapeia os segmentos e as compensações nos segmentos para o endereço de memória virtual e a unidade de paginação mapeia o endereço de memória virtual para o endereço de memória física?
A Unidade de Gerenciamento de Memória (MMU) transforma um endereço lógico em um endereço linear por meio de um circuito de hardware chamado unidade de segmentação; subseqüentemente, um segundo circuito de hardware chamado unidade de paginação transforma o endereço linear em um endereço físico (veja a Figura 2-1).
Então, por que diz que o Linux não usa segmentação, mas apenas paginação?
A segmentação foi incluída nos microprocessadores 80x86 para incentivar os programadores a dividir seus aplicativos em entidades logicamente relacionadas, como sub-rotinas ou áreas de dados globais e locais. No entanto, o Linux usa a segmentação de uma maneira muito limitada. De fato, a segmentação e a paginação são um pouco redundantes, porque ambas podem ser usadas para separar os espaços de endereços físicos dos processos: a segmentação pode atribuir um espaço de endereço linear diferente para cada processo, enquanto a paginação pode mapear o mesmo espaço de endereço linear em diferentes espaços de endereços físicos . O Linux prefere a paginação à segmentação pelos seguintes motivos:
• O gerenciamento de memória é mais simples quando todos os processos usam os mesmos valores de registro de segmento - ou seja, quando compartilham o mesmo conjunto de endereços lineares.
• Um dos objetivos de design do Linux é a portabilidade para uma ampla variedade de arquiteturas; As arquiteturas RISC, em particular, têm suporte limitado à segmentação.
A versão 2.6 do Linux usa a segmentação somente quando exigida pela arquitetura 80x86.
Respostas:
A arquitetura x86-64 não usa a segmentação no modo longo (modo de 64 bits).
Quatro dos registradores de segmento: CS, SS, DS e ES são forçados a 0 e o limite a 2 ^ 64.
https://en.wikipedia.org/wiki/X86_memory_segmentation#Later_developments
Não é mais possível para o sistema operacional limitar quais intervalos dos "endereços lineares" estão disponíveis. Portanto, ele não pode usar a segmentação para proteção de memória; deve confiar inteiramente na paginação.
Não se preocupe com os detalhes das CPUs x86 que só se aplicariam quando executados nos modos de 32 bits herdados. O Linux para os modos de 32 bits não é tão usado. Pode até ser considerado "em um estado de negligência benigna por vários anos". Veja Suporte de 32 bits x86 no Fedora [LWN.net, 2017].
(Ocorre que o Linux de 32 bits também não usa segmentação. Mas você não precisa confiar em mim, basta ignorá-lo :-).
fonte
mov eax, [fs:rdi + 16]
). O kernel usa o GS (depoisswapgs
) para encontrar a pilha de kernel do processo atual nosyscall
ponto de entrada. Mas sim, a segmentação não é usada como parte do principal mecanismo de gerenciamento / proteção de memória do SO.Como o x86 possui segmentos, não é possível não usá-los. Mas os endereços base
cs
(segmento de código) eds
(segmento de dados) estão definidos como zero, portanto, a segmentação não é realmente usada. Uma exceção são dados locais de encadeamento, um dos segmentos normalmente não utilizados registra pontos para encadear dados locais. Mas isso é principalmente para evitar a reserva de um dos registros de uso geral para esta tarefa.Não diz que o Linux não usa segmentação no x86, pois isso não seria possível. Você já destacou uma parte, o Linux usa a segmentação de uma maneira muito limitada . A segunda parte é que o Linux usa segmentação somente quando exigido pela arquitetura 80x86
Você já citou os motivos, a paginação é mais fácil e mais portátil.
fonte
Não.
Enquanto o sistema de segmentação (no modo protegido de 32 bits em um x86) foi projetado para oferecer suporte a segmentos separados de código, dados e pilha, na prática, todos os segmentos estão definidos na mesma área de memória. Ou seja, eles começam em 0 e terminam no final da memória (*) . Isso torna os endereços lógicos e lineares iguais.
Isso é chamado de modelo de memória "plano" e é um pouco mais simples que o modelo em que você tem segmentos distintos e, em seguida, ponteiros dentro deles. Em particular, um modelo segmentado requer ponteiros mais longos, pois o seletor de segmentos deve ser incluído além do ponteiro de deslocamento. (Seletor de segmento de 16 bits + deslocamento de 32 bits para um total de ponteiro de 48 bits; versus apenas um ponteiro plano de 32 bits.)
O modo longo de 64 bits realmente não suporta segmentação que não seja o modelo de memória plana.
Se você programasse no modo protegido de 16 bits no 286, teria mais necessidade de segmentos, pois o espaço de endereço é de 24 bits, mas os ponteiros são de apenas 16 bits.
(* Observe que não me lembro como o Linux de 32 bits lida com a separação do kernel / espaço do usuário. A segmentação permitiria isso através da definição dos limites dos segmentos do espaço do usuário para que eles não incluíssem o espaço do kernel. A paginação permite isso, pois fornece um nível de proteção por página.)
O x86 ainda possui os segmentos e você não pode desativá-los. Eles são usados o menos possível. No modo protegido de 32 bits, os segmentos precisam ser configurados para o modelo plano e, mesmo no modo de 64 bits, eles ainda existem.
fonte
wrfsbase
é ilegal no modo protegido / compatível, somente no modo longo, portanto, em um espaço de usuário do kernel de 32 bits, não é possível definir o FS como alto.O Linux x86 / 32 não usa segmentação no sentido de inicializar todos os segmentos no mesmo endereço e limite linear. A arquitetura x86 exige que o programa tenha segmentos: o código pode ser executado apenas a partir do segmento de código, a pilha pode ser localizada apenas no segmento de pilha, os dados podem ser manipulados apenas em um dos segmentos de dados. O Linux ignora esse mecanismo definindo todos os segmentos da mesma maneira (com exceções que seu livro não menciona de qualquer maneira), para que o mesmo endereço lógico seja válido em qualquer segmento. Isso é de fato equivalente a não ter segmentos.
fonte
Estes são 2 usos quase totalmente diferentes da palavra "segmento"
Os usos têm uma origem comum: se você estivesse usando um modelo de memória segmentada (especialmente sem memória virtual paginada), poderá ter dados e endereços BSS relativos à base do segmento DS, pilha relativa à base SS e código relativo à Endereço base do CS.
Portanto, vários programas diferentes podem ser carregados em endereços lineares diferentes ou até movidos após a inicialização, sem alterar os desvios de 16 ou 32 bits em relação às bases do segmento.
Mas então você precisa saber a qual segmento um ponteiro é relativo, para ter "ponteiros distantes" e assim por diante. (Os programas x86 reais de 16 bits geralmente não precisavam acessar seu código como dados, portanto, poderia usar um segmento de código de 64k em algum lugar e talvez outro bloco de 64k com DS = SS, com a pilha crescendo de compensações altas e dados em ou um pequeno modelo de código com todas as bases de segmentos iguais).
Como a segmentação x86 interage com a paginação
O mapeamento de endereço no modo 32/64 bits é:
as tabelas de páginas (armazenadas em cache pelo TLB) são mapeadas linearmente para o endereço físico 32 (modo herdado), 36 (PAE herdado) ou 52 bits (x86-64). ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ).
Esta etapa é opcional: a paginação deve ser ativada durante a inicialização, definindo um pouco em um registro de controle. Sem paginação, endereços lineares são endereços físicos.
Observe que a segmentação não permite que você use mais de 32 ou 64 bits de espaço de endereço virtual em um único processo (ou thread) , porque o espaço de endereço simples (linear) em que tudo é mapeado tem apenas o mesmo número de bits que as compensações. (Esse não era o caso do x86 de 16 bits, onde a segmentação era realmente útil para usar mais de 64k de memória, com registros e deslocamentos principalmente de 16 bits.)
A CPU armazena em cache os descritores de segmento carregados do GDT (ou LDT), incluindo a base do segmento. Quando você desreferencia um ponteiro, dependendo do registro em que ele está, o padrão é DS ou SS como o segmento. O valor do registro (ponteiro) é tratado como um deslocamento da base do segmento.
Como a base do segmento é normalmente zero, as CPUs fazem isso em casos especiais. Ou a partir de outra perspectiva, se você faz tem uma base de segmento não-zero, cargas têm latência extra porque o caso "especial" (normal) de contornar adicionando o endereço de base não se aplica.
Como o Linux configura os registros do segmento x86:
A base e o limite de CS / DS / ES / SS são todos 0 / -1 no modo de 32 e 64 bits. Isso é chamado de modelo de memória plana porque todos os ponteiros apontam para o mesmo espaço de endereço.
(Os arquitetos de CPU AMD neutralizaram a segmentação ao impor um modelo de memória plana para o modo de 64 bits porque os sistemas operacionais principais não o usavam de qualquer maneira, exceto pela proteção sem execução, que era fornecida de uma maneira muito melhor paginando com o PAE ou x86- Formato de tabela de 64 páginas.)
TLS (Thread Local Storage): FS e GS não são fixos na base = 0 no modo longo. (Eles eram novos no 386 e não eram usados implicitamente por nenhuma instrução, nem mesmo
rep
pelas instruções de cadeia de caracteres que usam ES). x86-64 Linux define o endereço base do FS para cada thread como o endereço do bloco TLS.por exemplo,
mov eax, [fs: 16]
carrega um valor de 32 bits de 16 bytes no bloco TLS para este encadeamento.o descritor do segmento CS escolhe em que modo a CPU está (modo protegido de 16/32/64 bits / modo longo). O Linux usa uma única entrada GDT para todos os processos de espaço do usuário de 64 bits e outra entrada GDT para todos os processos de espaço do usuário de 32 bits. (Para que a CPU funcione corretamente, o DS / ES também deve ser definido como entradas válidas, assim como o SS). Ele também escolhe o nível de privilégio (kernel (anel 0) vs. usuário (anel 3)), portanto, mesmo ao retornar ao espaço do usuário de 64 bits, o kernel ainda precisa providenciar a alteração do CS, usando
iret
ou emsysret
vez de um normal instruções de pular ou reter.No x86-64, o
syscall
ponto de entrada usaswapgs
para alternar o GS do GS do espaço do usuário para o kernel, que ele usa para encontrar a pilha de kernel desse encadeamento. (Um caso especializado de armazenamento local de encadeamento). Asyscall
instrução não altera o ponteiro da pilha para apontar para a pilha do kernel; ainda está apontando para a pilha do usuário quando o kernel atinge o ponto de entrada 1 .O DS / ES / SS também deve ser definido como descritores de segmento válidos para que a CPU funcione no modo protegido / modo longo, mesmo que a base / limite desses descritores seja ignorada no modo longo.
Então, basicamente, a segmentação x86 é usada para TLS e para os itens obrigatórios do osdev x86 que o hardware exige que você faça.
Nota de rodapé 1: Histórico de diversão: há arquivos de listas de discussão entre desenvolvedores do kernel e arquitetos da AMD alguns anos antes do lançamento do AMD64 silicon, resultando em ajustes no design,
syscall
para que ele fosse utilizável. Veja os links nesta resposta para obter detalhes.fonte