¿Por qué (no) segmentación?


42

Estoy estudiando los sistemas operativos y la arquitectura x86, y mientras leía acerca de la segmentación y la paginación, naturalmente tenía curiosidad sobre 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 evitan la segmentación a favor de la paginación. Algunas de las razones de esto que encontré fueron la simplicidad y la portabilidad.

¿Qué usos prácticos existen para la segmentación (x86 o de otro tipo) y alguna vez veremos sistemas operativos robustos que lo usen o continuará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 recientemente desarrollados. ¿Tiene tanto sentido favorecer la paginación que nadie considerará un enfoque más "segmentado"? Si es así, ¿por qué?


Y cuando digo la segmentación de 'evitar', estoy implicando que Linux solo lo usa en la medida de lo necesario. Solo 4 segmentos para el usuario y el código del núcleo / segmentos de datos. Mientras leía la documentación de Intel tuve la sensación de que la segmentación se diseñó con soluciones más sólidas en mente. Por otra parte, me dijeron en muchas ocasiones lo complicado que puede ser el x86.


Encontré esta anécdota interesante después de estar vinculado al 'anuncio' original de Linux Torvald para Linux. Dijo esto algunas publicaciones más tarde:

Simplemente, diría que portar es imposible. Está principalmente en C, pero la mayoría de las personas no llamarían lo que yo escribo C. Utiliza todas las características imaginables del 386 que pude encontrar, ya que también fue un proyecto para enseñarme sobre el 386. Como ya se mencionó, usa un MMU , tanto para la paginación (todavía no en el disco) como para la segmentación. Es la segmentación lo que lo hace REALMENTE dependiente 386 (cada tarea tiene un segmento de 64Mb para código y datos - máximo 64 tareas en 4Gb. Cualquiera que necesite más de 64Mb / tarea - cookies difíciles).

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


¿Qué libro leíste?

1
Estoy leyendo varios libros. Empecé a preguntarme esto mientras leía el manual de programación de sistemas Intel (vol. 3), pero leí un poco sobre la administración de memoria de Linux en "Comprender el kernel de Linux" y otras fuentes en línea.
Sr. Shickadance

En particular, estaba leyendo la sección sobre Tablas de descriptores locales, y tenía curiosidad por saber cómo los usaban los sistemas operativos.
Sr. Shickadance

1
OpenBSD combina segmentación x86 y paginación para obtener simulación de bits NX (característica de seguridad para prohibir la ejecución de páginas de datos). Puede ser que PaX haya usado esto también.

No sé casi nada sobre el tema. Acabo de escribir una pregunta de búsqueda para ver las respuestas a las quejas sobre todos los sistemas operativos utilizados actualmente. En cuanto a las quejas, la mayoría de las personas usan PC y ahora tabletas para algunas tareas específicas. Entonces, ¿por qué no asignar más uso de memoria para hacer esas tareas más rápido en lugar de dar acceso a toda la basura periférica que está ejecutando?

Respuestas:


31

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

Además, dado que todos los desplazamientos 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 desplazamiento constante (dependiendo de la función llamada). Esto simplificaría enormemente los enlazadores y cargadores.

Con 4 anillos de protección, es posible idear 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 principal y los controladores de dispositivo en los anillos 0 y 1, podría hacer un sistema operativo de microkernel más robusto y muy rápido donde HW realizaría la mayoría de las verificaciones de acceso relevantes. (Los controladores de dispositivo 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"; recargarlos es bastante costoso y es posible acceder simultáneamente solo a 8192 segmentos. (Suponiendo que desea maximizar el número de objetos accesibles, entonces el GDT solo contiene descriptores del sistema y descriptores LDT).

Ahora, con la segmentación en modo de 64 bits se describe como "heredado" 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, culpo principalmente a los desarrolladores, la mayoría de los cuales pensaban que la segmentación era "demasiado complicada" y anhelaba un espacio de direcciones planas. También culpo a los escritores del sistema operativo que carecían de la imaginación para dar un buen uso a la segmentación. (AFAIK, OS / 2 fue el único sistema operativo que hizo uso completo de las características de segmentación).


1
Es por eso que dejé esto abierto. No es seguro que será un poco diferente toma en el tema ...
Sr. Shickadance

1
@zvrba: ¡Qué excelente explicación! Gracias por eso. Ahora tengo una duda: ¿no crees que INTEL podría haber ganado el gran premio al hacer que los segmentos no se superpongan y que tengan 4 GB de capacidad con la ayuda de la paginación? Quiero decir, como entendí, la "segmentación con paginación" solo es capaz de abordar un máximo de 4 GB de espacio de direcciones de memoria virtual. ¡Y eso es 'maní'! ¡Imagínese poder tener un Código, una Pila, segmentos de Datos de hasta 4GB cada uno y sin solaparse o superponerse como lo necesitaría! Y eso habría sido un gran éxito en ese momento, sin tener que pedir una arquitectura completa de 64 bits como hoy en día.
fante

1
Fantástica explicación de por qué la segmentación es buena. Es una lástima que haya quedado en el camino. Aquí hay una elaboración con más detalles para aquellos que tienen curiosidad por aprender más.
GDP2

1
¡No es de extrañar que me encantara OS / 2! Qué triste pérdida de una tecnología verdaderamente valiosa gracias a la ignorancia y al marketing.
iluminar

Cualquiera que piense que la segmentación es una buena idea no debe tener la edad suficiente para recordar cuán horrible es la segmentación. Es horrible. Prácticamente todo el código C jamás escrito espera un espacio de dirección plano. Es conveniente poder mirar un puntero y solo ver su dirección, no tener que ir a profundizar en la base del segmento, suponiendo que sea posible, que no está en segmentación de modo protegido x86, a menos que el núcleo le permita ver de alguna manera, muy probablemente con una llamada al sistema muy costosa. El intercambio no es posible con segmentos, a menos que intercambies segmentos enteros. La paginación es muy, muy superior.
doug65536

25

La respuesta corta es que la segmentación es un truco, que se usa para hacer que un procesador con una capacidad limitada para direccionar 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 podía acceder físicamente a 1Mb de memoria. Sin embargo, la arquitectura interna se basó en el direccionamiento de 16 bits, probablemente debido al deseo de mantener la coherencia con el 8080. Por lo tanto, el conjunto de instrucciones incluía registros de segmentos que se combinarían con los índices de 16 bits para permitir el direccionamiento de 1Mb completo de memoria . El 80286 amplió este modelo con una verdadera MMU, 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 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 evitar explícitamente las limitaciones de la arquitectura. En el caso del 8086, esto significaba que el bloque contiguo de memoria más grande al que podía acceder era 64k. Si necesitara acceder a más que eso, tendría que cambiar sus registros de 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.


55
Ok, todo tiene sentido. Sin embargo, al leer los documentos de Intel, uno se inclinaría a pensar que los segmentos realmente podrían usarse para una mayor protección a nivel de hardware contra errores del programa. Específicamente la sección 3.2.3 de la Guía de programación de sistemas: ¿existen ventajas para el modelo de segmentos múltiples? ¿Sería correcto decir que Linux usa el modelo plano protegido? (sección 3.2.2)
Sr. Shickadance

3
Ha pasado mucho tiempo desde que presté atención a los detalles de la arquitectura de memoria de Intel, pero no creo que la arquitectura segmentada proporcione una mayor protección de hardware. La única protección real que una MMU puede brindarle es separar el código y los datos, evitando ataques de desbordamiento del búfer. Y creo que es controlable sin segmentos, a través de atributos de nivel de página. Teóricamente, podría restringir el acceso a los objetos creando un segmento separado para cada uno, pero no creo que sea razonable.

1
Gracias, ha recuperado todos los recuerdos reprimidos de procesar imágenes en la memoria segmentada. ¡Esto significará más terapia!
Martin Beckett

10
Has entendido completamente la segmentación. En 8086 podría haber sido un hack; 80286 introdujo el modo protegido donde era crucial para la protección; en 80386 se amplió aún más y los segmentos pueden ser mayores de 64kB, aún con el beneficio de las comprobaciones de hardware. (Por cierto, 80286 NO tenía una MMU.)
zvrba

2
En 1985, cuando se introdujo el 386, un espacio de direcciones de 4 GiB se consideraba enorme. Recuerde que un disco duro de 20 MiB era bastante grande en ese momento, y todavía no era del todo raro que los sistemas vengan solo con unidades de disquete. El FDD de 3.5 "se introdujo en 1983, con una capacidad formateada de 360 ​​KB. (1.44 MB Los FDD de 3.5" estuvieron disponibles en 1986). Dentro del error experimental, todos pensaron en un espacio de direcciones de 32 bits como ahora pensamos de 64 bits: físicamente accesible, pero tan grande como para ser prácticamente infinito.
un CVn

15

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) terminará sin una manera fácil de proteger un proceso de sí mismo, no hay una manera fácil de proteger los procesos entre sí, no hay forma de manejar cosas como la fragmentación del espacio de direcciones físicas, no hay forma de evitar la posición código independiente, etc. A pesar de todos estos problemas, podría (en teoría) ser útil en algunas situaciones (por ejemplo, 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 se necesitan muchas soluciones alternativas para que sea utilizable cuando un proceso quiere usar más de 8192 segmentos (suponiendo un LDT por proceso), lo que hace que se rompa. Casi resuelven el problema de "proteger los procesos unos de otros"; pero diferentes piezas de software que se ejecutan en el mismo nivel de privilegio pueden cargar / usar los segmentos de cada uno (hay formas de evitarlo: modificando las entradas de GDT durante las transferencias de control y / o usando LDT). También resuelve principalmente el problema del "código de posición independiente" (puede causar un problema de "código dependiente del segmento", pero eso es mucho menos significativo). No hace nada por el problema de "fragmentación del espacio de direcciones físicas".

Solo para paginación; no resuelve mucho el problema de "proteger un proceso de sí mismo" (pero seamos honestos aquí, esto solo es realmente un problema para depurar / probar código escrito en lenguajes inseguros, y de todos modos hay herramientas mucho más poderosas como valgrind). Resuelve completamente el problema de "proteger los procesos unos de otros", resuelve completamente el problema del "código de posición independiente" y resuelve completamente el problema de "fragmentación del espacio de direcciones físicas". Como una ventaja adicional, abre algunas técnicas muy poderosas que no son tan prácticas sin paginación; incluyendo cosas como "copiar al escribir", archivos mapeados en memoria, manejo eficiente del espacio de intercambio, etc.

Ahora pensaría que usar tanto la segmentación como la paginación le daría 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 mediante 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 obtienes es la complejidad de ambos y la sobrecarga de ambos, para muy poco beneficio.

Esta es la razón por la cual casi todos los sistemas operativos diseñados para 80x86 no usan la segmentación para la administración de la memoria (lo usan para cosas como almacenamiento por CPU y por tarea, pero eso es solo por conveniencia para evitar consumir un registro de propósito general más útil para estos 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 quieran evitarla aún más. En su mayoría, solo mantuvieron la segmentación por compatibilidad con versiones anteriores (lo cual es importante).

Finalmente, AMD diseñó el modo largo. No había que preocuparse por el código antiguo / existente de 64 bits, por lo que (para el código de 64 bits) AMD se deshizo de tanta segmentación como pudieron. Esto les dio a los desarrolladores del sistema operativo otra razón (no es una forma fácil de portar código diseñado para la segmentación a 64 bits) para continuar evitando la segmentación.


13

Estoy bastante sorprendido de que, en todo momento 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 refinó en forma útil, todas las características que rodean el diseño y el uso de sistemas segmentados de memoria virtual paginada (junto con multiprocesamiento simétrico y sistemas de archivos jerárquicos) fue Multics (y vea también el sitio de Multicians ). La memoria segmentada permite a Multics ofrecer al usuario una vista de que todo está en la memoria (virtual), y permite el máximo nivel de intercambio de todoen forma directa (es decir, directamente direccionable en la 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 y 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 cobarde de memoria segmentada, la 80286, que aunque es bastante poderosa, sus limitaciones impidieron que se usara para algo realmente útil. El 80386 mejoró estas limitaciones, pero las fuerzas del mercado en ese momento prácticamente impidieron el éxito de cualquier sistema que realmente pudiera aprovechar estas mejoras. En los años transcurridos desde que parece, demasiadas personas han 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 con creces cualquier otra cosa 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 solucionarlo.

Una discusión más detallada de cómo Multics usó la segmentación y la paginación se puede encontrar en el documento de Paul Green, Multics Virtual Memory - Tutorial and Reflections


1
Gran información y excelentes argumentos. Gracias por los enlaces, no tienen precio!
fante

1
¡Gracias por vincular a Multics y por la respuesta muy informativa! Claramente, la segmentación fue superior en muchos aspectos a lo que hacemos ahora.
GDP2

1
Su respuesta es una verdadera joya en bruto. Muchas gracias por compartir estas ideas que hemos perdido. Me anhela ver un regreso a la segmentación a través del desarrollo de un sistema operativo adecuado que pueda impulsar el refinamiento del hardware. ¡Verdaderamente, muchos problemas podrían mejorarse con este enfoque! Incluso suena como si pudiéramos obtener verdaderos lenguajes OOP en niveles de rendimiento mucho más altos y en el metal desnudo con la segmentación.
iluminar

6

La segmentación fue un truco / solución alternativa para permitir que un procesador de 16 bits direccione hasta 1 MB de memoria; normalmente, solo 64 K de memoria habrían sido accesibles.

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

Además, 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 simplifica todo.


¿Hay mucho que decir sobre la 'protección' proporcionada por la segmentación cuando podemos usar paginación en su lugar?
Sr. Shickadance

1
@Señor. La segmentación de Shickadance no proporciona ningún tipo de protección de la memoria: para la protección de la memoria, necesita un modo protegido donde pueda proteger la memoria utilizando GDT o paginación.
Justin

5

Algunas arquitecturas (como ARM) no admiten segmentos de memoria en absoluto. Si Linux hubiera dependido de la fuente de los segmentos, no podría haberse 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 puntero. El desarrollo de C es más práctico en una arquitectura con memoria plana; y si quieres memoria plana, eliges la paginación de memoria.

Hubo un momento en el cambio de los años 80 cuando Intel, como organización, anticipaba la futura popularidad de Ada y otros lenguajes de programación de alto nivel. Esto es básicamente de donde provienen algunas de sus fallas más espectaculares, como la horrible segmentación de memoria APX432 y 286. Con el 386 capitularon ante programadores de memoria plana; paginación y se agregó un TLB y los segmentos se hicieron redimensionables a 4GB. Y luego, AMD básicamente eliminó segmentos con x86_64 al hacer que el registro base sea un dont-care / implied-0 (¿excepto fs? Para TLS, creo).

Dicho esto, las ventajas de los segmentos de memoria son obvias: cambiar los espacios de direcciones sin tener que repoblar un TLB. Tal vez algún día alguien haga una CPU de rendimiento competitivo que soporte la segmentación, podemos programar un sistema operativo orientado a la segmentación para ella, y los programadores pueden hacer que Ada / Pascal / D / Rust / another-langage-that-does-require-require-flat -programas de memoria para ello.


1

La segmentación es una gran carga para los desarrolladores de aplicaciones. De aquí vino el gran impulso para eliminar la segmentación.

Curiosamente, a menudo me pregunto qué 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 condujeron a una especie de revuelta de desarrolladores. Pero seamos sinceros, un espacio de direcciones de 64k no es nada, especialmente cuando miras una aplicación moderna. Al final tuvieron que hacer algo porque la competencia podía y lo hizo comercializar efectivamente contra los problemas de espacio de direcciones de i86.


1

La segmentación conduce a traducciones e intercambios de páginas más lentos

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

La principal diferencia entre ellos es que:

  • la paginación divide la memoria en fragmentos 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

eventualmente se convertirá a medida que crece 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, que toma un registro inaceptable (n)
  • 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 solo hace 2 lecturas de memoria: anotación de directorio y tabla de páginas
  • cada intercambio 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.

Ver también: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.