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

39

Estou estudando sistemas operacionais e a arquitetura x86, e enquanto estava lendo sobre segmentação e paginação, naturalmente fiquei curioso sobre como os sistemas operacionais modernos lidam com o gerenciamento de memória. Pelo que descobri, o Linux e a maioria dos outros sistemas operacionais essencialmente 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 ou continuarão favorecendo um sistema baseado em paginação.

Agora eu sei que esta é uma pergunta carregada, mas estou curioso sobre como a segmentação seria tratada com sistemas operacionais recém-desenvolvidos. 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 só usa o que for preciso. Apenas 4 segmentos para segmentos de código / dados do usuário e do kernel. Enquanto lia a documentação da Intel, achei que a segmentação foi projetada com soluções mais robustas em mente. Então novamente me disseram em muitas ocasiões quão complicado o x86 pode ser.

Encontrei esta anedota interessante depois de estar ligada ao 'anúncio' original do Linux Torvald para o Linux. Ele disse isso alguns posts depois:

Simply, I'd say that porting is impossible. It's mostly in C, but most people wouldn't call what I write C. It uses every conceivable feature of the 386 I could find, as it was also a project to teach me about the 386. As already mentioned, it uses a MMU, for both paging (not to disk yet) and segmentation. It's the segmentation that makes it REALLY 386 dependent (every task has a 64Mb segment for code & data - max 64 tasks in 4Gb. Anybody who needs more than 64Mb/task - tough cookies).

Acho que minha própria experiência com o x86 me levou a fazer essa pergunta. Linus não tem o StackOverflow, então ele acabou de implementá-lo para testá-lo.

    
por Mr. Shickadance 10.08.2011 / 16:20
fonte

8 respostas

29

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 automaticamente os limites do segmento, e toda a classe de bugs de segurança (saturação de buffer) seria eliminada.

Além disso, como todos os deslocamentos do segmento começam em zero, todo o código compilado seria automaticamente independente da posição. Chamar para outra DLL se resumiria a uma chamada distante com deslocamento constante (dependendo da função chamada). Isso simplificaria muito os linkers e os 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 sistema operacional mais robustos. Por exemplo, somente o anel 0 tem acesso total ao hardware. Ao separar o kernel principal do SO e os drivers de dispositivo nos anéis 0 e 1, você poderia criar um SO de microkernel mais robusto e muito rápido, no qual a maioria das verificações de acesso relevantes seria feita pelo HW. (Os drivers de dispositivo podem obter acesso ao hardware por meio do bitmap de acesso de E / S no TSS.)

No entanto, o x86 é um pouco limitado. Tem apenas 4 registros de segmento de dados "livres"; 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 contém apenas descritores de sistema e descritores de LDT.)

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

    
por 10.08.2011 / 22:42
fonte
22

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 poderia acessar fisicamente 1 MB 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 . O 80286 estendeu esse modelo com uma MMU verdadeira, para suportar proteção baseada em segmento e endereçamento de mais memória (iirc, 16Mb).

No caso do PDP-11, 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 trabalhar explicitamente em torno das limitações da arquitetura. No caso do 8086, isso significava que o maior bloco contíguo de memória que você poderia 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ê tinha que dizer ao compilador C que tipo de ponteiros deveria gerar.

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

    
por 10.08.2011 / 16:35
fonte
13

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

Para "nada" (sem segmentação ou paginação), você acaba sem uma maneira fácil de proteger um processo de si mesmo, nenhuma 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 para evitar código independente de posição, etc. Apesar de todos esses problemas, ele pode (em teoria) ser útil em algumas situações (por exemplo, dispositivo embutido que executa apenas um aplicativo; ou talvez algo que use JIT e virtualize tudo de qualquer maneira).

Apenas para segmentação; ele quase resolve o problema "proteger um processo de si mesmo", mas é preciso muito trabalho para torná-lo utilizável quando um processo deseja usar mais de 8192 segmentos (assumindo um LDT por processo), o que o torna praticamente quebrado. Você quase resolve o problema "proteger processos uns dos outros"; mas diferentes partes de software rodando no mesmo nível de privilégio podem carregar / usar os segmentos uns dos outros (há maneiras de contornar isso - modificando entradas GDT durante transferências de controle e / ou usando LDTs). Ele também resolve principalmente o problema de "código independente de posição" (pode causar um problema de "código dependente de segmento", mas isso é muito menos significativo). Não faz nada para o problema da "fragmentação do espaço de endereço físico".

Apenas para pager; ele não resolve muito o problema "proteger um processo de si mesmo" (mas vamos ser honestos aqui, isso é realmente um problema para depuração / teste de código escrito em linguagens inseguras, e existem ferramentas muito mais poderosas como valgrind de qualquer maneira). Ele resolve completamente o problema "proteger processos uns dos outros", resolve completamente o problema "código independente de posição" e resolve completamente o problema de "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 perto tão práticas sem paginação; incluindo coisas como "copiar na gravação", arquivos mapeados na memória, manuseio eficiente do espaço de troca, etc.

Agora, você acha que usar segmentação e paginação oferece 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 por paginação) é uma solução para o problema de "proteger um processo de si mesmo" com o qual ninguém realmente se importa. Na prática, o que você consegue é a complexidade 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 usam para coisas como armazenamento por CPU e por tarefa, mas isso é principalmente por conveniência para evitar o uso de um propósito geral mais útil registrar para essas coisas).

É claro que os fabricantes de CPU não são tolos - eles não gastam tempo e dinheiro otimizando algo que eles sabem que ninguém usa (eles vão otimizar algo que quase todo mundo usa em vez disso). 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 sistemas operacionais queiram evitá-la ainda mais. Principalmente eles só mantinham segmentação para compatibilidade com versões anteriores (o que é importante).

Eventualmente, a AMD projetou o modo longo. Não havia nenhum código antigo / existente de 64 bits para se preocupar, então (para o código de 64 bits), a AMD se livrou da segmentação que pôde. Isso deu aos desenvolvedores de SO uma outra razão (não é um caminho fácil para o código de porta projetado para segmentação para 64 bits) para continuar evitando a segmentação.

    
por 20.07.2013 / 07:49
fonte
12

Estou um tanto atordoado que, desde o momento em que essa pergunta foi postada, ninguém mencionou as origens das arquiteturas de memória segmentadas e o verdadeiro poder que elas podem pagar.

O sistema original que inventou, ou refinou em forma útil, todas as características que envolvem o design e o uso de sistemas de memória virtual paginados segmentados (juntamente com sistemas de arquivos hierárquicos e multiprocessamento simétricos) foi Multics (e veja também o site Multicians ). A memória segmentada permite que o Multics ofereça uma visão ao usuário de que tudo está na memória (virtual), e permite o último nível de compartilhamento de tudo de forma direta (isto é, diretamente) endereçável na memória). O sistema de arquivos torna-se simplesmente um mapa para todos os segmentos da memória. Quando usada adequadamente de maneira sistemática (como em Multics), a memória segmentada libera o usuário das muitas dificuldades de gerenciamento de armazenamento secundário, compartilhamento de dados e comunicação entre processos. Outras respostas fizeram algumas alegações de que a memória segmentada é mais difícil de usar, mas isso simplesmente não é verdade, e a Multics provou isso com um sucesso retumbante décadas atrás.

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

A Intel também tentou criar um supermicro mais capaz, chamado iAPX 432 , que superaria em muito qualquer coisa mais na época, e tinha uma arquitetura de memória segmentada e outros recursos strongmente orientados para a programação orientada a objetos. A implementação original foi lenta demais, e nenhuma outra tentativa foi feita para consertá-lo.

Uma discussão mais detalhada de como as Multics usam segmentação e paginação pode ser encontrada no artigo de Paul Green Multics Memória Virtual - Tutorial e Reflexões

    
por 02.11.2015 / 03:36
fonte
5

A segmentação foi um hack / workaround para permitir que até 1MB de memória fosse endereçada por um processador de 16 bits - normalmente apenas 64K de memória seriam acessíveis.

Quando os processadores de 32 bits apareciam, era possível endereçar até 4 GB de memória com um modelo de memória simples e não havia mais necessidade de segmentação - os registros de segmento eram redefinidos como seletores para o GDT / paging no modo protegido ( embora você possa ter o modo protegido de 16 bits).

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

    
por 10.08.2011 / 16:46
fonte
3

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

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

Houve uma época em torno da virada dos anos 80, quando a Intel, como organização, antecipava a popularidade futura da Ada e de outras linguagens de programação de nível superior. Isto é basicamente onde algumas de suas falhas mais espetaculares, como a horrível APX432 e 286 segmentação de memória, vieram. Com o 386, eles capitularam para programadores de memória simples; paginação e um TLB foi adicionado e os segmentos foram redimensionados para 4 GB. E, em seguida, a AMD basicamente removeu segmentos com x86_64, tornando a base reg um dont-care / implied-0 (exceto para fs? Para TLS, eu acho?)

Tendo dito isso, as vantagens dos segmentos de memória são óbvias - comutação de espaços de endereço sem ter que preencher novamente um TLB. Talvez algum dia alguém possa fazer uma CPU com desempenho competitivo que suporte segmentação, podemos programar um sistema operacional segmentado para ela, e programadores podem fazer Ada / Pascal / D / Rust / outra linguagem que não requer programas de memória para isso.

    
por 15.02.2016 / 19:14
fonte
1

A segmentação é um fardo enorme para os desenvolvedores de aplicativos. Este é o lugar onde o grande impulso veio para afastar a segmentação.

Curiosamente, eu me pergunto o quanto melhor a i86 poderia ser se a Intel distribuísse todo o suporte legado para esses modos antigos. Aqui, melhor, implicaria menor potência 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 espécie de revolta dos desenvolvedores. Mas vamos enfrentá-lo um espaço de 64k endereço não é nada, especialmente quando você olha para o aplicativo moderno. No final, eles tiveram que fazer alguma coisa porque a concorrência podia e efetivamente comercializava contra as questões de espaço de endereços da i86.

    
por 14.08.2011 / 19:22
fonte
1

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

Por essas razões, a segmentação foi largamente descartada em x86-64.

A principal diferença entre eles é que:

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

Embora possa parecer mais inteligente ter larguras de segmentos 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

eventualmente se tornará conforme o processo 1 crescer:

|   | 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 momento:

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

Com páginas de tamanho fixo, no entanto:

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

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

Veja também: link

    
por 11.06.2017 / 17:07
fonte