¿Por qué (no) segmentación?

39

Estoy estudiando los sistemas operativos y la arquitectura x86, y mientras leía sobre segmentación y paginación, naturalmente sentía curiosidad por saber cómo los sistemas operativos modernos manejan la administración de la memoria. Por lo que encontré, Linux y la mayoría de los otros sistemas operativos esencialmente rechazan la segmentación en favor de la paginación. Algunas de las razones de esto que encontré fueron la simplicidad y la portabilidad.

¿Qué usos prácticos hay para la segmentación (x86 o de otro tipo) y veremos cómo los sistemas operativos robustos lo utilizan o seguirán favoreciendo un sistema basado en paginación?

Ahora sé que esta es una pregunta cargada, pero tengo curiosidad por saber cómo se manejaría la segmentación con los sistemas operativos desarrollados recientemente. ¿Tiene tanto sentido favorecer la paginación que nadie considerará un enfoque más "segmentado"? Si es así, ¿por qué?

Y cuando digo segmentación 'shun' estoy dando a entender que Linux solo lo usa en la medida en que tiene que hacerlo. Solo 4 segmentos para usuario y código de kernel / segmentos de datos. Mientras leía la documentación de Intel, tuve la sensación de que la segmentación se había diseñado pensando en soluciones más sólidas. Una vez más, en muchas ocasiones me dijeron lo complicado que puede ser el x86.

Encontré esta anécdota interesante después de haber sido vinculado al "anuncio" original de Linux Torvald para Linux. Dijo esto unos pocos mensajes más tarde:

  

Simplemente, yo diría que portar es imposible. Es mayormente en C, pero la mayoría   la gente no llamaría a lo que escribo C. Utiliza todas las características concebibles   de los 386 que pude encontrar, ya que también era un proyecto para enseñarme sobre el   386. Como ya se mencionó, utiliza una MMU, tanto para la paginación (no para el disco   todavía) y la segmentación. Es la segmentación que lo hace REALMENTE 386   dependiente (cada tarea tiene un segmento de 64 Mb para código y datos: máx. 64 tareas)   en 4gb. Cualquiera que necesite más de 64Mb / tarea: cookies difíciles.

Supongo que mi propia experimentación con x86 me llevó a hacer esta pregunta. Linus no tenía StackOverflow, así que solo lo implementó para probarlo.

    
pregunta Mr. Shickadance 10.08.2011 - 16:20

8 respuestas

29

Con la segmentación, sería posible, por ejemplo, colocar cada objeto asignado dinámicamente (malloc) en su propio segmento de memoria. El hardware verificará los límites de los segmentos automáticamente y se eliminará toda la clase de errores de seguridad (sobrecargas de búfer).

Además, dado que todas las compensaciones de segmento comienzan en cero, todo el código compilado sería automáticamente independiente de la posición. Llamar a otra DLL se reduciría a una llamada lejana con un desplazamiento constante (dependiendo de la función llamada). Esto simplificaría enormemente los enlazadores y cargadores.

Con 4 anillos de protección, es posible diseñar un control de acceso más detallado (con paginación solo tiene 2 niveles de protección: usuario y supervisor) y núcleos de sistema operativo más robustos. Por ejemplo, solo el anillo 0 tiene acceso completo al hardware. Al separar el núcleo del sistema operativo y los controladores de dispositivos en los anillos 0 y 1, se podría crear un sistema operativo de micro-kernel más robusto y muy rápido donde HW realizaría la mayoría de las verificaciones de acceso relevantes. (Los controladores de dispositivos podrían obtener acceso al hardware a través del mapa de bits de acceso de E / S en el TSS).

Sin embargo ... x86 es un poco limitado. Tiene solo 4 registros de segmento de datos "libres"; volver a cargarlos es bastante costoso, y es posible acceder simultáneamente a 8192 segmentos. (Suponiendo que desea maximizar el número de objetos accesibles, el GDT solo contiene descriptores de sistema y descriptores de LDT).

Ahora, con el modo de 64 bits, la segmentación se describe como "heredada" y las comprobaciones de límite de hardware se realizan solo en circunstancias limitadas. En mi humilde opinión, un gran error. En realidad no culpo a Intel, yo culpo principalmente a los desarrolladores, la mayoría de los cuales pensaron que la segmentación era "demasiado complicada" y deseaba un espacio de direcciones plano. También culpo a los escritores de sistemas operativos que carecían de imaginación para dar buen uso a la segmentación. (AFAIK, OS / 2 fue el único sistema operativo que hizo uso completo de las funciones de segmentación).

    
respondido por el zvrba 10.08.2011 - 22:42
22

La respuesta corta es que la segmentación es un truco, utilizado para hacer que un procesador con una capacidad limitada para abordar la memoria exceda esos límites.

En el caso del 8086, había 20 líneas de dirección en el chip, lo que significa que podría acceder físicamente a 1Mb de memoria. Sin embargo, la arquitectura interna se basó en un direccionamiento de 16 bits, probablemente debido al deseo de mantener la coherencia con el 8080. Por lo tanto, el conjunto de instrucciones incluyó registros de segmentos que se combinarían con los índices de 16 bits para permitir el direccionamiento de la memoria completa de 1Mb. . El 80286 extendió este modelo con una MMU verdadera, para admitir la protección basada en segmentos y el direccionamiento de más memoria (iirc, 16Mb).

En el caso del PDP-11, los modelos posteriores del procesador proporcionaron una segmentación en los espacios de Instrucción y Datos, nuevamente para admitir las limitaciones de un espacio de direcciones de 16 bits.

El problema con la segmentación es simple: su programa debe solucionar explícitamente las limitaciones de la arquitectura. En el caso del 8086, esto significaba que el mayor bloque de memoria contiguo al que se podía acceder era de 64k. si necesita acceder a más que eso, tendrá que cambiar los registros de su segmento. Lo que significaba, para un programador de C, que tenía que decirle al compilador de C qué tipo de punteros debería generar.

Fue mucho más fácil programar el MC68k, que tenía una arquitectura interna de 32 bits y un espacio de direcciones físicas de 24 bits.

    
respondido por el parsifal 10.08.2011 - 16:35
13

Para 80x86 hay 4 opciones: "nada", solo segmentación, solo paginación y segmentación y paginación.

Para "nada" (sin segmentación o paginación), no tiene una manera fácil de proteger un proceso de sí mismo, no es una manera fácil de protegerse entre sí, no hay manera de manejar cosas como la fragmentación del espacio de direcciones físicas, de ninguna manera para evitar el código independiente de la posición, etc. A pesar de todos estos problemas, podría (en teoría) ser útil en algunas situaciones (por ejemplo, un dispositivo integrado que solo ejecuta una aplicación; o tal vez algo que use JIT y virtualice todo de todos modos).

Solo para segmentación; casi resuelve el problema de "proteger un proceso de sí mismo", pero requiere muchos arreglos para que sea utilizable cuando un proceso quiere usar más de 8192 segmentos (asumiendo un LDT por proceso), lo que lo hace prácticamente incompleto. Casi resuelves el problema de "proteger los procesos unos de otros"; pero diferentes partes del software que se ejecutan en el mismo nivel de privilegio pueden cargar / usar los segmentos de cada uno (hay formas de solucionarlo: modificar las entradas de GDT durante las transferencias de control y / o usar LDT). También soluciona principalmente el problema del "código independiente de la posición" (puede causar un problema del "código dependiente del segmento", pero eso es mucho menos significativo). No hace nada por el problema de la "fragmentación del espacio de direcciones físicas".

Sólo para paginación; no resuelve mucho el problema de "proteger un proceso de sí mismo" (pero seamos honestos aquí, esto es realmente un problema para el código de depuración / prueba escrito en lenguajes no seguros, y hay herramientas mucho más poderosas como valgrind de todos modos). Resuelve por completo el problema de "proteger los procesos entre sí", resuelve por completo el problema de "código independiente de la posición" y resuelve por completo el problema de la "fragmentación física del espacio de direcciones". Como un bono adicional, abre algunas técnicas muy poderosas que no son tan prácticas como las de la paginación; incluyendo cosas como "copiar en escritura", archivos asignados en memoria, manejo eficiente del espacio de intercambio, etc.

Ahora pensaría que usar tanto la segmentación como la paginación le darían los beneficios de ambos; y en teoría puede, excepto que el único beneficio que obtiene de la segmentación (que no se hace mejor con la paginación) es una solución al problema de "proteger un proceso de sí mismo" que a nadie le importa realmente. En la práctica, lo que obtiene es la complejidad de ambos y la sobrecarga de ambos, con muy poco beneficio.

Esta es la razón por la que casi todos los sistemas operativos diseñados para 80x86 no utilizan la segmentación para la administración de la memoria (sí lo utilizan para cosas como el almacenamiento por CPU y por tarea, pero eso se debe principalmente a la conveniencia para evitar consumir un propósito general más útil registrarse para estas cosas).

Por supuesto, los fabricantes de CPU no son tontos: no van a gastar tiempo y dinero optimizando algo que saben que nadie usa (van a optimizar algo que casi todos usan). Por esta razón, los fabricantes de CPU no optimizan la segmentación, lo que hace que la segmentación sea más lenta de lo que podría ser, lo que hace que los desarrolladores de sistemas operativos deseen evitarlo aún más. La mayoría de ellos solo mantuvieron la segmentación por compatibilidad con versiones anteriores (lo cual es importante).

Eventualmente, AMD diseñó el modo largo. No había ningún código de 64 bits antiguo / existente de qué preocuparse, por lo que (para el código de 64 bits) AMD se deshizo de la mayor segmentación posible. Esto dio a los desarrolladores de sistemas operativos una razón más (no es una manera fácil de codificar el código diseñado para la segmentación a 64 bits) para continuar evitando la segmentación.

    
respondido por el Brendan 20.07.2013 - 07:49
12

Estoy bastante sorprendido de que todo el tiempo desde que se publicó esta pregunta, nadie haya mencionado los orígenes de las arquitecturas de memoria segmentada y el verdadero poder que pueden permitirse.

El sistema original que inventó, o perfeccionó en forma útil, todas las características que rodean el diseño y el uso de sistemas de memoria virtual segmentados segmentados (junto con el multiproceso simétrico y los sistemas de archivos jerárquicos) era Multics (y vea también el sitio Multicians ). La memoria segmentada permite que Multics ofrezca una vista al usuario de que todo está en la memoria (virtual), y permite el máximo nivel de uso compartido de todo en forma directa (es decir, directamente direccionable en memoria). El sistema de archivos se convierte simplemente en un mapa de todos los segmentos en la memoria. Cuando se usa correctamente de manera sistemática (como en Multics), la memoria segmentada libera al usuario de las muchas cargas de administrar el almacenamiento secundario, compartir datos y las comunicaciones entre procesos. Otras respuestas han hecho algunas afirmaciones de que la memoria segmentada es más difícil de usar, pero esto simplemente no es cierto, y Multics lo demostró con un éxito rotundo hace décadas.

Intel creó una versión de 80286 de memoria segmentada que, aunque es bastante potente, sus limitaciones impidieron que se usara para algo realmente útil. El 80386 mejoró estas limitaciones, pero las fuerzas del mercado en ese momento impidieron el éxito de cualquier sistema que realmente pudiera aprovechar estas mejoras. En los años en que parece que mucha gente ha aprendido a ignorar las lecciones del pasado.

Intel también intentó desde el principio construir un súper micro más capaz llamado iAPX 432 que hubiera superado cualquier cosa más en ese momento, y tenía una arquitectura de memoria segmentada y otras características fuertemente orientadas a la programación orientada a objetos. Sin embargo, la implementación original fue demasiado lenta y no se hicieron más intentos para corregirla.

Una discusión más detallada sobre cómo Multics usó la segmentación y la paginación se puede encontrar en el documento de Paul Green Multics Memoria virtual - Tutorial y Reflexiones

    
respondido por el Greg A. Woods 02.11.2015 - 03:36
5

La segmentación fue un truco / solución alternativa para permitir que un procesador de 16 bits direccionara hasta 1 MB de memoria; normalmente solo se habría podido acceder a 64 K de memoria.

Cuando los procesadores de 32 bits aparecían, podía abordar hasta 4 GB de memoria con un modelo de memoria plana y ya no había necesidad de segmentación. Los registros de segmentos se volvieron a utilizar como selectores para el GDT / paginación en modo protegido ( aunque puede tener modo protegido de 16 bits).

También un modo de memoria plana es mucho más conveniente para los compiladores: puede escribir Programas segmentados de 16 bits en C , pero es un poco engorroso. Un modelo de memoria plana hace que todo sea más simple.

    
respondido por el Justin 10.08.2011 - 16:46
3

Algunas arquitecturas (como ARM) no admiten segmentos de memoria en absoluto. Si Linux hubiera dependido de la fuente de los segmentos, no podría haber sido portado a esas arquitecturas muy fácilmente.

Mirando la imagen más amplia, la falla de los segmentos de memoria tiene que ver con la continua popularidad de C y la aritmética de punteros. El desarrollo de C es más práctico en una arquitectura con memoria plana; y si desea una memoria plana, elige la paginación de memoria.

Hubo un momento en torno al cambio de los 80 cuando Intel, como organización, anticipaba la popularidad futura de Ada y otros lenguajes de programación de nivel superior. Aquí es básicamente de donde provienen algunas de sus fallas más espectaculares, como la terrible segmentación de memoria APX432 y 286. Con el 386 capitularon ante programadores de memoria plana; se agregó paginación y un TLB y los segmentos se redimensionaron a 4GB. Y luego AMD básicamente eliminó los segmentos con x86_64 al hacer que la base de registros sea un no-cuidado / implícito-0 (a excepción de fs? Para TLS, creo?)

Habiendo dicho eso, las ventajas de los segmentos de memoria son obvias: cambiar los espacios de direcciones sin tener que volver a llenar un TLB. Tal vez algún día alguien haga una CPU con rendimiento competitivo que admita la segmentación, podemos programar un sistema operativo orientado a la segmentación, y los programadores pueden hacer Ada / Pascal / D / Rust / another-langage-that-not-require-flat - Programas de memoria para ello.

    
respondido por el Ted Middleton 15.02.2016 - 19:14
1

La segmentación es una gran carga para los desarrolladores de aplicaciones. Aquí es donde surgió el gran impulso para eliminar la segmentación.

Es interesante que a menudo me pregunto cuánto mejor sería i86 si Intel eliminara todo el soporte heredado para estos modos antiguos. Aquí, mejor implicaría una menor potencia y quizás una operación más rápida.

Supongo que se podría argumentar que Intel agrió la leche con segmentos de 16 bits que llevaron a una especie de revuelta de desarrolladores. Pero seamos sinceros, un espacio de direcciones de 64k no es nada, especialmente cuando se mira una aplicación moderna. Al final, tuvieron que hacer algo porque la competencia podía e hizo un mercado eficaz contra los problemas de espacio de direcciones de i86.

    
respondido por el Dave 14.08.2011 - 19:22
1

La segmentación hace que las traducciones y el intercambio de páginas sean más lentos

Por esas razones, la segmentación se redujo en gran medida en x86-64.

La principal diferencia entre ellos es que:

  • la paginación divide la memoria en trozos de tamaño fijo
  • la segmentación permite diferentes anchos para cada fragmento

Si bien puede parecer más inteligente tener anchos de segmento configurables, a medida que aumenta el tamaño de la memoria para un proceso, la fragmentación es inevitable, por ejemplo:

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

se convertirá eventualmente a medida que crezca el proceso 1:

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

hasta que una división sea inevitable:

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

En este punto:

  • la única forma de traducir páginas es hacer búsquedas binarias en todas las páginas del proceso 1, lo que requiere un registro (n) inaceptable
  • un intercambio fuera del proceso 1 parte 1 podría ser enorme, ya que ese segmento podría ser enorme

Sin embargo, con páginas de tamaño fijo:

  • cada traducción de 32 bits hace solo 2 lecturas de memoria: recorrido por el directorio y la tabla de páginas
  • cada canje es un 4KiB aceptable

Los trozos de memoria de tamaño fijo son simplemente más manejables, y han dominado el diseño actual del sistema operativo.

Vea también: enlace

    

Lea otras preguntas en las etiquetas