Por que (não) segmentação?


42

Estou estudando sistemas operacionais e a arquitetura x86 e, enquanto lia sobre segmentação e paginação, naturalmente fiquei curioso para saber como os sistemas operacionais modernos lidam com o gerenciamento de memória. Pelo que encontrei, o Linux e a maioria dos outros sistemas operacionais basicamente evitam a segmentação em favor da paginação. Algumas das razões para isso que encontrei foram simplicidade e portabilidade.

Quais usos práticos existem para a segmentação (x86 ou não) e sempre veremos sistemas operacionais robustos usando-o ou eles continuarão a favorecer um sistema baseado em paginação.

Agora eu sei que essa é uma pergunta carregada, mas estou curioso para saber como a segmentação seria tratada com os sistemas operacionais desenvolvidos recentemente. Faz tanto sentido favorecer a paginação que ninguém considerará uma abordagem mais "segmentada"? Se sim, por quê?


E quando digo segmentação 'shun', estou sugerindo que o Linux a usa apenas na medida do necessário. Apenas 4 segmentos para código de usuário e núcleo / segmentos de dados. Ao ler a documentação da Intel, tive a sensação de que a segmentação foi projetada com soluções mais robustas em mente. Por outro lado, foi-me dito em muitas ocasiões o quão complicado o x86 pode ser.


Achei essa anedota interessante depois de ter sido vinculada ao 'anúncio' original do Linux Torvald para Linux. Ele disse isso alguns posts depois:

Simplesmente, eu diria que é impossível portar. É principalmente em C, mas a maioria das pessoas não chamaria o que eu escrevo C. Ele usa todos os recursos imagináveis ​​do 386 que eu pude encontrar, pois também foi um projeto para me ensinar sobre o 386. Como já mencionado, ele usa um MMU , para paginação (ainda não em disco) e segmentação. É a segmentação que o torna REALMENTE dependente de 386 (cada tarefa possui um segmento de 64Mb para código e dados - máx. 64 tarefas em 4Gb. Qualquer pessoa que precise de mais de 64Mb / cookies resistentes a tarefas).

Acho que minha própria experiência com o x86 me levou a fazer essa pergunta. Linus não tinha o StackOverflow, então ele apenas o implementou para experimentá-lo.


Que livro você leu?

1
Eu estou lendo vários livros. Comecei a me perguntar isso enquanto lia o manual de programação de sistemas da Intel (vol 3), mas li um pouco sobre o gerenciamento de memória do Linux em "Entendendo o kernel do Linux" e outras fontes online.
Sr. Shickadance

Em particular, eu estava lendo a seção Tabelas de descritores locais e fiquei curioso sobre como os sistemas operacionais as usavam.
Shickadance

1
O OpenBSD combina segmentação x86 e paginação para obter simulação de bits NX (recurso de segurança para proibir a execução de páginas de dados). O PaX também pode ser usado.

Não sei quase nada sobre o assunto. Eu apenas digitei uma pergunta de pesquisa para ver respostas para reclamações sobre todos os sistemas operacionais usados ​​atualmente. Observando as reclamações, a maioria das pessoas usa PCs e tablets agora para algumas tarefas específicas. Então, por que não alocar mais uso de memória para executar essas tarefas mais rapidamente, em vez de fornecer todo o lixo periférico que está executando o acesso a ela.

Respostas:


31

Com a segmentação, seria possível, por exemplo, colocar cada objeto alocado dinamicamente (malloc) em seu próprio segmento de memória. O hardware verificaria os limites do segmento automaticamente e toda a classe de bugs de segurança (saturação de buffer) seria eliminada.

Além disso, como todas as compensações de segmento começam em zero, todo o código compilado seria automaticamente independente da posição. Chamar para outra DLL resumia-se a uma chamada remota com deslocamento constante (dependendo da função chamada). Isso simplificaria bastante os vinculadores e carregadores.

Com 4 anéis de proteção, é possível criar um controle de acesso mais refinado (com paginação, você tem apenas 2 níveis de proteção: usuário e supervisor) e kernels de SO mais robustos. Por exemplo, apenas o anel 0 tem acesso total ao hardware. Ao separar o kernel do sistema operacional principal e os drivers de dispositivo nos anéis 0 e 1, você pode criar um sistema operacional de microkernel mais robusto e muito rápido, onde a maioria das verificações de acesso relevantes seria feita pelo HW. (Os drivers de dispositivo podem obter acesso ao hardware através do bitmap de acesso de E / S no TSS.)

No entanto .. x86 é um pouco limitado. Possui apenas 4 registros de segmento de dados "gratuitos"; recarregá-los é bastante caro e é possível acessar simultaneamente apenas 8192 segmentos. (Supondo que você queira maximizar o número de objetos acessíveis, o GDT retém apenas descritores do sistema e descritores LDT.)

Agora, com a segmentação no modo de 64 bits é descrita como "herdada" e as verificações de limite de hardware são feitas apenas em circunstâncias limitadas. IMHO, um grande erro. Na verdade, não culpo a Intel, principalmente culpo os desenvolvedores, a maioria dos quais pensava que a segmentação era "muito complicada" e almejava espaço de endereço simples. Eu também culpo os escritores de SO que não tinham imaginação para usar bem a segmentação. (AFAIK, OS / 2 foi o único sistema operacional que fez pleno uso dos recursos de segmentação.)


1
É por isso que deixei isso aberto. Não há certeza de ser um pouco diferente assume a questão ...
Mr. Shickadance

1
@zvrba: Que explicação excelente !!! Thanx por isso. Agora tenho uma dúvida: você não acha que a INTEL poderia ter ganho o grande prêmio, tornando os segmentos não sobrepostos e com capacidade de 4 GB com a ajuda da paginação? Quero dizer, como eu entendi, "a segmentação com paginação" só é capaz de endereçar um máximo de 4 GB de espaço de endereço de memória virtual. E isso é 'amendoim' !!! Imagine ser capaz de ter um código, pilha, segmentos de dados de até 4 GB cada e sem sobreposição ou sobreposição conforme necessário! E isso teria sido um grande sucesso na época, sem a necessidade de uma arquitetura completa de 64 bits nos dias de hoje.
fante

1
Explicação fantástica de por que a segmentação é boa. É uma pena que tenha caído no esquecimento. Aqui está uma elaboração com mais detalhes para quem está curioso para saber mais.
GDP2 12/11/19

1
Não é de admirar que eu amei o OS / 2! Que triste perda de uma tecnologia verdadeiramente valiosa, graças à ignorância e ao marketing.
ylluminate

Qualquer pessoa que pense que a segmentação é uma boa ideia não deve ter idade suficiente para lembrar o quão terrível é a segmentação. Isso é horrível. Praticamente todo o código C já escrito espera um espaço de endereço simples. É conveniente poder olhar para um ponteiro e apenas ver seu endereço, sem ter que ir à base do segmento, supondo que isso seja possível, o que não ocorre na segmentação em modo protegido x86, a menos que o kernel permita que você veja de alguma forma, provavelmente com uma chamada de sistema muito cara. Não é possível trocar com segmentos, a menos que você troque segmentos inteiros. A paginação é muito, muito superior.
doug65536

25

A resposta curta é que a segmentação é um hack, usado para fazer um processador com uma capacidade limitada de endereçar a memória exceder esses limites.

No caso do 8086, havia 20 linhas de endereço no chip, o que significa que ele podia acessar fisicamente 1Mb de memória. No entanto, a arquitetura interna foi baseada em endereçamento de 16 bits, provavelmente devido ao desejo de manter a consistência com o 8080. Portanto, o conjunto de instruções incluía registradores de segmento que seriam combinados com os índices de 16 bits para permitir o endereçamento de 1Mb de memória completo . O 80286 estendeu esse modelo com uma verdadeira MMU, para oferecer suporte à proteção baseada em segmento e ao endereçamento de mais memória (iirc, 16Mb).

No caso do PDP-11, os modelos posteriores do processador forneceram uma segmentação nos espaços de Instrução e Dados, novamente para suportar as limitações de um espaço de endereço de 16 bits.

O problema com a segmentação é simples: seu programa deve solucionar explicitamente as limitações da arquitetura. No caso do 8086, isso significava que o maior bloco contíguo de memória que você podia acessar era 64k. se você precisasse acessar mais do que isso, teria que alterar seus registros de segmento. O que significava, para um programador C, que você precisava dizer ao compilador C que tipo de ponteiros ele deveria gerar.

Era muito mais fácil programar o MC68k, que tinha uma arquitetura interna de 32 bits e um espaço de endereço físico de 24 bits.


5
Ok, isso tudo faz sentido. No entanto, lendo os documentos da Intel, seria provável que segmentos pudessem realmente ser usados ​​para maior proteção no nível de hardware contra bugs de programas. Especificamente a seção 3.2.3 do Guia de programação de sistemas - existem vantagens no modelo multissegmento? Seria correto dizer que o Linux usa o modelo plano protegido? (seção 3.2.2)
Sr. Shickadance

3
Faz muito tempo desde que prestei atenção aos detalhes da arquitetura de memória Intel, mas não acho que a arquitetura segmentada forneça maior proteção ao hardware. A única proteção real que uma MMU pode oferecer é separar código e dados, evitando ataques de saturação de buffer. E acredito que seja controlável sem segmentos, por meio de atributos no nível da página. Teoricamente, você pode restringir o acesso a objetos criando um segmento separado para cada um, mas não acho isso razoável.

1
Obrigado, você trouxe de volta todas as memórias reprimidas do processamento de imagens na memória segmentada - isso vai significar mais terapia!
Martin Beckett

10
Você entendeu completamente a segmentação. Em 8086, poderia ter sido um hack; 80286 introduziu o modo protegido onde era crucial para a proteção; em 80386, foi ainda mais ampliado e os segmentos podem ser maiores que 64kB, ainda com o benefício de verificações de hardware. (BTW, 80286 não tinha uma MMU.)
zvrba

2
Em 1985, quando o 386 foi introduzido, um espaço de endereço de 4 GiB era considerado enorme. Lembre-se de que um disco rígido de 20 MiB era bastante grande na época e ainda não era incomum que os sistemas viessem apenas com unidades de disquete. O FDD de 3,5 "foi introduzido em 1983, com uma capacidade formatada de 360 ​​KB. (1,44 MB de 3,54 FDDs foram disponibilizados em 1986). Para erros experimentais, todo mundo na época pensava em um espaço de endereço de 32 bits, como agora de 64 bits: fisicamente acessível, mas tão grande que é praticamente infinito.
precisa

15

Para 80x86, existem 4 opções - "nada", apenas segmentação, somente paginação e segmentação e paginação.

Para "nada" (sem segmentação ou paginação), você acaba com uma maneira fácil de proteger um processo de si mesmo, uma maneira fácil de proteger processos um do outro, nenhuma maneira de lidar com coisas como fragmentação do espaço de endereço físico, nenhuma maneira de evitar a posição código independente, etc. Apesar de todos esses problemas, pode (em teoria) ser útil em algumas situações (por exemplo, dispositivo incorporado que executa apenas um aplicativo ou talvez algo que use JIT e virtualize tudo de qualquer maneira).

Apenas para segmentação; quase resolve o problema "proteja um processo de si mesmo", mas são necessárias muitas soluções alternativas para torná-lo utilizável quando um processo deseja usar mais de 8192 segmentos (assumindo um LDT por processo), o que o torna principalmente quebrado. Você quase resolve o problema de "proteger processos uns dos outros"; mas diferentes partes de software em execução no mesmo nível de privilégio podem carregar / usar os segmentos uns dos outros (há maneiras de contornar isso - modificar entradas GDT durante transferências de controle e / ou usar LDTs). Também resolve principalmente o problema do "código independente de posição" (pode causar um problema de "código dependente de segmento", mas isso é muito menos significativo). Ele não faz nada pelo problema "fragmentação do espaço de endereço físico".

Apenas para paginação; ele não resolve muito o problema "proteja um processo de si mesmo" (mas vamos ser honestos aqui, isso é realmente apenas um problema para código de depuração / teste escrito em linguagens inseguras, e existem ferramentas muito mais poderosas como o valgrind). Ele resolve completamente o problema "proteger processos um do outro", resolve completamente o problema "código independente de posição" e resolve completamente o problema "fragmentação do espaço de endereço físico". Como um bônus adicional, ele abre algumas técnicas muito poderosas que não são nem de longe tão práticas sem paginação; incluindo coisas como "copiar na gravação", arquivos mapeados na memória, manipulação eficiente do espaço de troca, etc.

Agora, você pensaria que o uso da segmentação e da paginação traria os benefícios de ambos; e, em teoria, pode, exceto que o único benefício que você ganha com a segmentação (que não é melhor com a paginação) é uma solução para o problema "proteger um processo de si mesmo", com o qual ninguém realmente se importa. Na prática, o que você obtém são as complexidades de ambos e a sobrecarga de ambos, para muito pouco benefício.

É por isso que quase todos os SOs projetados para 80x86 não usam segmentação para gerenciamento de memória (eles o utilizam para itens como armazenamento por CPU e por tarefa, mas isso é principalmente apenas por conveniência, para evitar o consumo de um registro de uso geral mais útil para esses coisas).

É claro que os fabricantes de CPU não são tolos - eles não vão gastar tempo e dinheiro otimizando algo que eles sabem que ninguém usa (eles vão otimizar algo que quase todo mundo usa). Por esse motivo, os fabricantes de CPU não otimizam a segmentação, o que torna a segmentação mais lenta do que poderia ser, o que faz com que os desenvolvedores de SO desejem evitá-la ainda mais. Principalmente, eles mantinham apenas a segmentação para compatibilidade com versões anteriores (o que é importante).

Eventualmente, a AMD projetou o modo longo. Não havia um código antigo / existente de 64 bits para se preocupar, então (para o código de 64 bits) a AMD se livrou do máximo de segmentação possível. Isso deu aos desenvolvedores do sistema operacional mais um motivo (nenhuma maneira fácil de portar código projetado para segmentação para 64 bits) para continuar evitando a segmentação.


13

Estou bastante surpreso que, desde que essa pergunta foi publicada, ninguém mencionou as origens das arquiteturas de memória segmentada e o verdadeiro poder que elas podem pagar.

O sistema original que inventou ou refinou em forma útil todos os recursos que envolvem o design e o uso de sistemas de memória virtual paginada segmentada (junto com multiprocessamento simétrico e sistemas de arquivos hierárquicos) foi o Multics (e também o site Multicians ). A memória segmentada permite que o Multics ofereça uma visão para o usuário de que tudo está na memória (virtual) e permite o nível final de compartilhamento de tudode forma direta (ou seja, diretamente endereçável na memória). O sistema de arquivos se torna simplesmente um mapa para todos os segmentos na memória. Quando usada adequadamente de maneira sistemática (como no Multics), a memória segmentada libera o usuário de muitos encargos de gerenciamento do armazenamento secundário, compartilhamento de dados e comunicação entre processos. Outras respostas fizeram algumas afirmações claras de que a memória segmentada é mais difícil de usar, mas isso simplesmente não é verdade, e Multics provou isso com um sucesso retumbante décadas atrás.

A Intel criou uma versão hobbled de memória segmentada, a 80286, que, embora seja bastante poderosa, suas limitações impediram que ela fosse usada para algo realmente útil. O 80386 aprimorou essas limitações, mas as forças do mercado na época praticamente impediram o sucesso de qualquer sistema que pudesse realmente tirar proveito dessas melhorias. Nos anos seguintes, parece que muitas pessoas aprenderam a ignorar as lições do passado.

A Intel também tentou desde o início criar um super-micro mais capaz, chamado iAPX 432, que ultrapassaria em muito qualquer outra coisa na época, e tinha uma arquitetura de memória segmentada e outros recursos fortemente orientados para a programação orientada a objetos. A implementação original era muito lenta e não foram feitas mais tentativas para corrigi-la.

Uma discussão mais detalhada de como o Multics usou a segmentação e a paginação pode ser encontrada no artigo de Paul Green sobre a memória virtual da Multics - Tutorial e Reflexões


1
Ótimas informações e argumentos excelentes. Thanx pelos links, eles não têm preço !!!
fante

1
Obrigado por criar um link para o Multics e pela resposta muito informativa! Claramente, a segmentação foi superior em muitos aspectos ao que fazemos agora.
GDP2 12/12/19

1
Sua resposta é uma verdadeira jóia em bruto. Muito obrigado por compartilhar essas idéias que perdemos. Sinto-me ansioso por ver um retorno à segmentação através do desenvolvimento de um sistema operacional adequado que possa solicitar o refinamento do hardware. Verdadeiramente, muitos problemas poderiam ser melhorados com essa abordagem! Até soa como se pudéssemos obter verdadeiras linguagens OOP em níveis de desempenho muito mais altos e no bare metal com segmentação.
ylluminate

6

A segmentação era um hack / solução alternativa para permitir que até 1 MB de memória fosse endereçado por um processador de 16 bits - normalmente, apenas 64 K de memória estariam acessíveis.

Quando os processadores de 32 bits surgiram, era possível endereçar até 4 GB de memória com um modelo de memória plana e não havia mais necessidade de segmentação - Os registradores de segmento foram redefinidos como seletores para o GDT / paginação no modo protegido (embora você possa têm o modo protegido de 16 bits).

Além disso, um modo de memória plana é muito mais conveniente para compiladores - você pode escrever programas segmentados de 16 bits em C , mas é um pouco complicado. Um modelo de memória plana torna tudo mais simples.


Há muito a ser dito sobre a 'proteção' fornecida pela segmentação quando podemos apenas usar a paginação?
Shickadance

1
@Sr. A Segmentação Shickadance não fornece nenhum tipo de proteção de memória - Para proteção de memória, você precisa do modo protegido, onde pode proteger a memória usando o GDT ou a paginação.
11117 Justin

5

Algumas arquiteturas (como ARM) não suportam segmentos de memória. Se o Linux dependesse da origem dos segmentos, não poderia ter sido portado para essas arquiteturas com muita facilidade.

Olhando para o quadro mais amplo, a falha dos segmentos de memória tem a ver com a popularidade contínua de C e aritmética de ponteiros. O desenvolvimento em C é mais prático em uma arquitetura com memória plana; e se você quiser memória plana, escolha paginação de memória.

Houve um tempo na virada dos anos 80, quando a Intel, como organização, antecipava a popularidade futura do Ada e de outras linguagens de programação de nível superior. É basicamente daí que algumas de suas falhas mais espetaculares, como a terrível segmentação de memória APX432 e 286, surgiram. Com os 386, capitularam para programadores de memória plana; paginação e um TLB foram adicionados e os segmentos foram redimensionados para 4 GB. E então a AMD basicamente removeu segmentos com x86_64, tornando a base reg um dont-care / implied-0 (exceto fs? Para TLS, eu acho?)

Dito isto, as vantagens dos segmentos de memória são óbvias - alternar espaços de endereço sem precisar repovoar um TLB. Talvez um dia alguém crie uma CPU com desempenho competitivo que suporte segmentação, podemos programar um sistema operacional orientado a segmentação para ela e os programadores podem criar Ada / Pascal / D / Rust / outro idioma que não exija plano programas de memória para ele.


1

A segmentação é um enorme fardo para os desenvolvedores de aplicativos. Foi daí que surgiu o grande impulso para acabar com a segmentação.

Curiosamente, muitas vezes me pergunto quanto melhor o i86 poderia ser se a Intel distribuísse todo o suporte legado para esses modos antigos. Aqui, melhor implicaria menos energia e talvez operação mais rápida.

Eu acho que alguém poderia argumentar que a Intel azedou o leite com segmentos de 16 bits, levando a uma revolta dos desenvolvedores. Mas, convenhamos, um espaço de endereço de 64k não é nada especialmente quando você olha para o aplicativo moderno. No final, eles tiveram que fazer alguma coisa, porque a concorrência poderia e efetivamente comercializava com eficiência os problemas de espaço de endereço do i86.


1

A segmentação leva a traduções e trocas de página mais lentas

Por esses motivos, a segmentação foi amplamente descartada no x86-64.

A principal diferença entre eles é que:

  • paginação divide a memória em pedaços de tamanho fixo
  • A segmentação permite larguras diferentes para cada pedaço

Embora possa parecer mais inteligente ter larguras de segmento configuráveis, à medida que você aumenta o tamanho da memória de um processo, a fragmentação é inevitável, por exemplo:

|   | process 1 |       | process 2 |                        |
     -----------         -----------
0                                                            max

acabará se tornando à medida que o processo 1 cresce:

|   | process 1        || process 2 |                        |
     ------------------  -------------
0                                                            max

até que uma divisão seja inevitável:

|   | process 1 part 1 || process 2 |   | process 1 part 2 | |
     ------------------  -----------     ------------------
0                                                            max

Neste ponto:

  • a única maneira de traduzir páginas é fazer pesquisas binárias em todas as páginas do processo 1, o que requer um log inaceitável (n)
  • uma troca do processo 1 parte 1 pode ser enorme, pois esse segmento pode ser enorme

No entanto, com páginas de tamanho fixo:

  • toda tradução de 32 bits faz apenas 2 leituras de memória: passeio de diretório e tabela de páginas
  • cada troca é um 4KiB aceitável

Pedaços de memória de tamanho fixo são simplesmente mais gerenciáveis ​​e dominam o design atual do sistema operacional.

Consulte também: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.