From a5ec58bba3c0a1e995b69cca9cfbdd3480dff654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Torres?= Date: Fri, 16 Aug 2024 10:56:03 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20definici=C3=B3n=20de=20t=C3=A9rminos=20e?= =?UTF-8?q?n=20secciones?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/C02-tipos_de_sistemas.adoc | 15 ++++++- content/C04-componentes.adoc | 6 +++ content/C05-servicios.adoc | 1 + content/C06-api.adoc | 4 ++ content/C07-modo_dual.adoc | 1 + content/C08-estructura.adoc | 9 ++-- content/C09-procesos.adoc | 2 + content/C10-ipc.adoc | 1 + content/C12-hilos.adoc | 15 ++++--- "content/C13-sincronizaci\303\263n.adoc" | 11 ++++- "content/C14-planificaci\303\263n.adoc" | 34 ++++++++++++--- content/C15-memoria_principal.adoc | 10 ++++- "content/C16-paginaci\303\263n.adoc" | 4 ++ content/C17-memoria_virtual.adoc | 42 +++++++++++++++---- content/C19-sistema_de_archivos.adoc | 17 +++++++- ...mantaci\303\263n_sistema_de_archivos.adoc" | 22 ++++++++-- 16 files changed, 162 insertions(+), 32 deletions(-) diff --git a/content/C02-tipos_de_sistemas.adoc b/content/C02-tipos_de_sistemas.adoc index 1e71c0e..218ea18 100644 --- a/content/C02-tipos_de_sistemas.adoc +++ b/content/C02-tipos_de_sistemas.adoc @@ -25,7 +25,9 @@ La mayor diferencia entre los superordenadores y los _mainframes_<>). === Sistemas de tiempo compartido + (((sistema, tiempo compartido))) Los sistemas multiprogramados ofrecían un uso más eficiente de la CPU, pero no eran capaces de proporcionar interacción directa con los usuarios. Los programadores seguían teniendo que entregar los trabajos al operador y espera a que este les devolviera los resultados. @@ -183,6 +187,7 @@ Al igual que lo son los _mainframes_ modernos y muchos sistemas operativos actua ==== == Sistemas de escritorio + (((sistema, escritorio))) En la década de los 70 del siglo pasado también aparecieron las primeras CPU en microprocesadores y con estas llegaron las *microcomputadoras* o *microordenadores*. Las primeras *((microcomputadora))s* no incluían teclado ni monitor y se programaban usando interruptores y ledes ubicados en el frontal de la unidad. @@ -227,7 +232,9 @@ Para más información, véase el <<_criterios_de_planificación>>. Son muchos los ejemplos de sistemas operativos en esta categoría. Van desde CP/M —lanzado en 1977— hasta los actuales GNU/Linux, Microsoft Windows y Apple macOS, pasando por MS-DOS, IBM OS/2 y todas las versiones anteriores de Microsoft Windows (véase el <>). == Sistemas de mano -(((sistema, de mano)))(((sistema, móvil))) + +(((sistema, de mano))) +(((sistema, móvil))) Con el nombre genérico de **sistemas de mano** —del inglés _handheld_— hacemos referencia a las _tablets_, _smartphones_, lectores de libros electrónicos y otro sistemas móviles y portátiles. Los desarrolladores de aplicaciones y sistemas de mano deben enfrentarse a diversos desafíos, originados por el tamaño limitado de los dispositivos y la alimentación mediante el uso de baterías. Debido a esas limitaciones, muchos sistemas de mano tienen poca cantidad de memoria, procesadores lentos —en comparación con sus equivalentes de escritorio— y pantallas más pequeñas. @@ -235,6 +242,7 @@ Debido a esas limitaciones, muchos sistemas de mano tienen poca cantidad de memo En el diseño del sistema operativo suele primar la facilidad de uso y buscar un buen equilibrio entre rendimiento y tiempo de vida de la batería. == Sistemas multiprocesador + (((sistema, multiprocesador))) Un *sistema multiprocesador* es aquel ordenador hay procesadores interconectados que comparten el bus del sistema, el reloj y, en ocasiones la memoria, y los periféricos. @@ -278,6 +286,7 @@ Por lo que, aunque sobre el papel esta arquitectura ofrecía gran rendimiento, a ==== == Sistemas distribuidos + (((sistema, distribuido))) En la actualidad es común el uso de redes para interconectar ordenadores individuales —por ejemplo Internet o la red de área local de una oficina— cada uno equipado con su procesador, su memoria, sus dispositivos de almacenamiento, su fuente de alimentación, etc. En las redes de ordenadores los procesadores de dichos ordenadores se comunican con otros procesadores a través de líneas de comunicación, como: redes Ethernet, líneas telefónicas o wifi. @@ -338,6 +347,7 @@ Para más información, véase el http://www.cs.vu.nl/pub/amoeba/[sitio web de A ==== == Sistemas en clúster + (((sistema, clúster))) Como los sistemas distribuidos, los *sistemas en clúster* interconectar ordenadores individuales. Sin embargo, generalmente se acepta que los *sistemas en clúster* comparten el almacenamiento y estén conectados por medio de una red local, condiciones que no tienen por qué darse en los sistemas distribuidos. @@ -360,6 +370,7 @@ También es muy utilizado en servidores de Internet —como servidores web, corr En estos casos el balanceador de carga realiza su trabajo repartiendo las conexiones de los usuarios entre los servidores del clúster. == Sistemas de tiempo real + (((sistema, tiempo real))) Los **sistemas de ((tiempo real))** se utilizan cuando existen requerimientos estrictos de tiempo en la ejecución de ciertas tareas o en el procesamiento de flujos de datos. diff --git a/content/C04-componentes.adoc b/content/C04-componentes.adoc index ca1d587..c229b50 100644 --- a/content/C04-componentes.adoc +++ b/content/C04-componentes.adoc @@ -15,6 +15,7 @@ Lo que veremos en este capítulo es un esquema de los más comunes a la mayoría [[sect-componente-gestión-de-procesos]] == Gestión de procesos + (((gestión, procesos))) La gestión de los procesos es un elemento central de todo sistema operativo: @@ -61,6 +62,7 @@ El componente de gestión de procesos es el responsable de las siguientes activi * Proporcionar mecanismos para el tratamiento de interbloqueos. == Gestión de la memoria principal + (((gestión, memoria))) La memoria principal es un recurso fundamental para las operaciones de cualquier sistema operativo moderno. Esto es así porque generalmente es el único almacenamiento al que la CPU tiene acceso directo. @@ -81,6 +83,7 @@ El componente de gestión de la memoria debe asumir las siguientes responsabilid * Asignar y liberar espacio de la memoria principal según sea necesario. == Gestión del sistema de E/S + (((gestión, sistema de E/S))) El *sistema de E/S*(((sistema, de E/S))) hace de interfaz con el hardware, oculta las peculiaridades del hardware al resto del sistema. @@ -193,6 +196,7 @@ Cuando una aplicación termina de enviar el trabajo, el archivo correspondiente Así no hay acceso simultáneo al dispositivo por parte de varios procesos, mientras que estos pueden entregar el trabajo y continuar con su trabajo sin esperar a que la impresora esté disponible. == Gestión del almacenamiento secundario + (((gestión, almacenamiento secundario))) Dentro de los dispositivos de E/S, los dedicados al almacenamiento secundario —como discos duros, memorias USB o lectores de DVD-ROM— merecen un tratamiento especial. @@ -211,6 +215,7 @@ El gestor del almacenamiento secundario es el responsable de: * Planificar el acceso a los dispositivos, de tal forma que se ordenen las operaciones de forma eficiente. == Gestión del sistema de archivos + (((gestión, sistema de archivos))) Los ordenadores pueden almacenar información en diferentes tipos de medios físicos —por ejemplo en discos duros magnéticos, CD/DVD-ROM, memorias USB o SSD— cada uno de los cuales tiene características propias. El acceso a cada tipo de medio es controlado por un dispositivo —por ejemplo el controlador de disco o la unidad de DVD-ROM— que también tiene características propias. @@ -243,6 +248,7 @@ El sistema de archivos utiliza al gestor del almacenamiento secundario y al sist * Hacer copias de seguridad de los archivos en sistemas de almacenamiento estables y seguros. == Gestión de red + (((gestión, red))) El componente de red se responsabiliza de la comunicación con otros sistemas interconectados mediante una red de ordenadores —por ejemplo, en Internet o en la red de área local de una oficina—. diff --git a/content/C05-servicios.adoc b/content/C05-servicios.adoc index 3c080bc..fdf620b 100644 --- a/content/C05-servicios.adoc +++ b/content/C05-servicios.adoc @@ -50,6 +50,7 @@ Por eficiencia y protección un usuario, normalmente los procesos no puede tener Por ejemplo, puede haber errores del hardware —como fallos de energía o errores en la memoria— en la E/S —como errores de paridad o falta de papel en la impresora— y en los programas de usuario —como desbordamientos aritméticos o accesos ilegales a la memoria—. == Interfaz de usuario + (((interfaz, de usuario))) La *interfaz de usuario* es un servicio fundamental para todos los sistemas diseñados para que los usuarios interactúen con ellos directamente, por lo que nos vamos a detener un poco más en él. diff --git a/content/C06-api.adoc b/content/C06-api.adoc index 5748c13..c390be0 100644 --- a/content/C06-api.adoc +++ b/content/C06-api.adoc @@ -12,6 +12,7 @@ Un sistema operativo proporciona un entorno controlado para la ejecución de pro Dicho entorno debe proporcionar ciertos servicios que pueden ser accedidos por los programas a través de una *interfaz de programación de aplicaciones* o *((API))* (_Application Programming Interface_). == Interfaces de programación de aplicaciones + (((interfaz, programación de aplicaciones))) Algunas de las API disponibles para los desarrolladores de aplicaciones son Windows API y POSIX. @@ -119,6 +120,7 @@ Las definiciones por defecto incluyen: POSIX.1-2008, ISO C99 y algunas funcional Activa `_DEFAULT_SOURCE` y extensiones específicas de los sistemas GNU. == Llamadas al sistema + (((llamada al sistema))) Para un programa, acceder a los servicios del sistema operativo no es tan sencillo como invocar una función. Para invocar una función, un programa necesita conocer la dirección en la memoria del punto de entrada de dicha función —es decir, la ubicación de su primera instrucción—. @@ -206,6 +208,7 @@ En cualquier caso, sea cual sea el método utilizado, el sistema operativo es re A fin de cuentas, una de las funciones del sistema operativo es el control de dichos procesos. == Librería del sistema + (((librería, del sistema))) Las *llamadas al sistema* proporcionan una interfaz con la que los procesos pueden invocar los servicios que el sistema operativo ofrece. El problema es que como se hacen mediante instrucciones en lenguaje ensamblador (véase el <>) no son demasiado cómodas de utilizar. @@ -235,6 +238,7 @@ Por lo tanto, la invocación de las funciones de la librería del sistema se rea ==== == Librería estándar + (((librería, estándar))) Lenguajes distintos de C y {cpp} pueden tener difícil usar las funciones de la librería del sistema. Pero de alguna forma deben poder hacerlo, porque sus programadores necesitan acceso a los servicios que ofrece el sistema operativo. diff --git a/content/C07-modo_dual.adoc b/content/C07-modo_dual.adoc index 9e979de..b2865ef 100644 --- a/content/C07-modo_dual.adoc +++ b/content/C07-modo_dual.adoc @@ -173,6 +173,7 @@ Por ejemplo, un programa mal desarrollado que entra en un bucle infinito, del qu Obviamente, las instrucciones que pueden modificar el contenido del temporizador son instrucciones privilegiadas. == Máquinas virtuales + (((máquina virtual))) Utilizando las técnicas comentadas anteriormente, el sistema operativo crea a los procesos la ilusión de que se ejecutan en su propio procesador y memoria principal, aunque realmente los estén compartiendo con otros procesos. Obviamente, los procesos saben que hay un sistema operativo que los supervisa, porque no pueden acceder directamente al hardware, sino que deben solicitar los distintos recursos a través de las llamadas al sistema. diff --git a/content/C08-estructura.adoc b/content/C08-estructura.adoc index 347765c..0c66929 100644 --- a/content/C08-estructura.adoc +++ b/content/C08-estructura.adoc @@ -12,8 +12,9 @@ Ya hemos discutido anteriormente acerca de los componentes más comunes en un si En esta sección comentaremos cómo se clasifican los distintos sistemas operativos según la organización e interconexión de sus componentes. == Estructura sencilla -(((estructura, sencilla))) + ==== +(((estructura, sencilla))) Los sistemas con *estructura sencilla* se caracterizan por: * No tener una estructura bien definida. @@ -50,8 +51,9 @@ Tanto MS-DOS como UNIX eran originalmente sistemas pequeños y simples, limitado Lo cierto es que con mejor soporte del hardware se puede dividir el sistema operativo en piezas más pequeñas y apropiadas que las del MS-DOS y el UNIX original. == Estructura en capas -(((estructura, en capas))) + ==== +(((estructura, en capas))) Los sistemas con *estructura en capas* se caracterizan por: * La funcionalidad se divide en capas, de tal forma que una capa solo utiliza funciones y servicios de la capa inmediatamente inferior y lo hace a través de una interfaz bien definida. @@ -98,8 +100,9 @@ Esto limita mucho las ventajas de esta técnica porque no permite compartimentar ==== == Microkernel -(((estructura, microkernel)))(((microkernel))) ==== +(((estructura, microkernel))) +(((microkernel))) Los sistemas con *estructura microkernel* se caracterizan por: * Eliminar todos los componentes no esenciales del núcleo e implementarlos como procesos de usuario. diff --git a/content/C09-procesos.adoc b/content/C09-procesos.adoc index b520ff9..e5bd874 100644 --- a/content/C09-procesos.adoc +++ b/content/C09-procesos.adoc @@ -115,6 +115,7 @@ Como en el caso del estado *nuevo*, este estado existe porque terminar un proces El diagrama de estados de los procesos, con las transiciones posibles entre ellos, se muestra en la <>. == Bloque de control de proceso + (((bloque, de control, proceso))) El *bloque de control de proceso* o *((PCB))* (_Process Control Block_) es una estructura de datos que representa a cada proceso en el sistema operativo y que guarda información sobre su estado de actividad actual. @@ -148,6 +149,7 @@ Aquí se incluye la cantidad de CPU usada, límites de tiempo en el uso de la CP Incluye la lista de dispositivos de E/S reservados por el proceso, la lista de archivos abiertos, etc. == Colas de planificación + (((cola, planificación))) En los sistemas operativos hay diferentes *colas de planificación* para los procesos en distintos *estados*. diff --git a/content/C10-ipc.adoc b/content/C10-ipc.adoc index 151c203..85dd98c 100644 --- a/content/C10-ipc.adoc +++ b/content/C10-ipc.adoc @@ -249,6 +249,7 @@ Los sistemas que optan por esta solución suelen disponer de algún mecanismo pa La elección puede ser aleatoria, mediante algún algoritmo, por ejemplo, por turnos o el siguiente proceso en obtener la CPU, a criterio del planificador de la CPU. == Buffering + (((buffering))) Los mensajes intercambiados por enlace de comunicación se almacenan en una cola temporal, a la espera de ser enviados o, tras recibirlos, a la espera de que los reclame el proceso. diff --git a/content/C12-hilos.adoc b/content/C12-hilos.adoc index f712583..5fef7b7 100755 --- a/content/C12-hilos.adoc +++ b/content/C12-hilos.adoc @@ -107,8 +107,8 @@ Otros lenguajes proporcionan un *librería de hilos* dentro de su *librería est El soporte de hilos en un sistema operativo se puede proporcionar a *nivel de usuario* o a *nivel de núcleo*. === Hilos a nivel de usuario -(((hilo, usuario))) +(((hilo, usuario))) El soporte de *hilos a nivel de usuario* se implementan mediante un *librería de hilos* en el espacio de usuario, junto al código del programa y los datos del proceso, sin requerir ningún soporte especial por parte del núcleo del sistema operativo. Por tanto, como se puede observar en el lado derecho de la <>, estos hilos existen desde el punto de vista del proceso y del programa que ejecuta, pero no para el sistema operativo. El planificador de la CPU asigna tiempo de CPU a los procesos, mientras que la *librería de hilos* de cada proceso reparte el tiempo de ejecución del proceso entre los diferentes hilos de este. @@ -120,8 +120,8 @@ image::{chapdir}/comparación_soporte_de_hilos.svg[] Como el código y los datos de la librería residen en el espacio de usuario, invocar una función de la misma se reduce a una simple llamada a una función, evitando el coste de hacer llamadas al sistema. === Hilos a nivel de núcleo -(((hilo, núcleo))) +(((hilo, núcleo))) Se habla de *hilos a nivel de núcleo*, cuando el núcleo del sistema es el encargado de ofrecer el soporte multihilo. El código y los datos de la *librería de hilos*, mediante la cual los programadores pueden solicitar la creación y gestión de los hilos, reside en el espacio del núcleo. Por tanto, para invocar una función de la *librería de hilos* es necesario hacer una llamada al sistema. @@ -151,8 +151,9 @@ Aquí entendemos por *hilos de núcleo* a estas secuencias de instrucciones tal Mientras que el concepto de *hilos de núcleo* corresponde con como las ve el núcleo del sistema. === Muchos a uno (N:1) -(((muchos a uno)))(((modelo, muchos a uno))) +(((muchos a uno))) +(((modelo, muchos a uno))) En el modelo *muchos a uno* muchos *hilos de usuario* son mapeados en *un único hilo de núcleo*. El planificador de la CPU en el núcleo reparte el tiempo de CPU entre los diferentes *hilos de núcleo* en el sistema, mientras que la *librería de hilos* en cada proceso reparte el tiempo de ejecución del *hilo de núcleo* del proceso entre múltiples *hilos de usuario* (véase la <>). @@ -241,8 +242,9 @@ En Java 1.1 era el único modelo soportado —ya que los hilos se implementaban Otras implementaciones de este modelo son las https://docs.microsoft.com/en-us/windows/win32/procthread/fibers[fibras] y https://docs.microsoft.com/en-us/windows/win32/procthread/user-mode-scheduling[UMS] de Windows API, {stackless_python} y http://www.gnu.org/software/pth/[GNU Portable Threads]. === Uno a uno (1:1) -(((uno a uno)))(((modelo, uno a uno))) +(((uno a uno))) +(((modelo, uno a uno))) En el modelo *muchos a uno* un *hilo de usuario* se mapea en *un único hilo de núcleo*. Por lo general, este modelo corresponde al caso de los sistemas que solo soportan *hilos a nivel de núcleo*. @@ -279,8 +281,9 @@ El modelo *uno a uno* se utiliza en la mayor parte de los sistemas operativos mu Linux, Microsoft Windows —desde Windows 95— {solaris} 9 y superiores, macOS y la familia de UNIX BSD; son ejemplos de sistemas operativos que utilizan el modelo *uno a uno*. === Muchos a muchos (M:N) -(((muchos a muchos)))(((modelo, muchos a muchos))) +(((muchos a muchos))) +(((modelo, muchos a muchos))) En teoría debería ser posible aprovechar lo mejor de los dos modelos anteriores con una *librería de hilos* en el núcleo, para crear *hilos de núcleo*, y otra en el espacio de usuario, para crear *hilos de usuario*. Así los desarrolladores pueden utilizar la *librería de hilos* en el espacio de usuario para crear tantos hilos como quieran y que estos se ejecuten sobre los *hilos de núcleo* de su proceso. @@ -645,6 +648,7 @@ int return_code = pthread_setcanceltypr( Se pueden cambiar entre estado y tipo de cancelación en cualquier momento, según lo que encaje mejor con las características de las distintas partes del código. ==== Cancelación asíncrona + (((cancelación, asíncrona))) Por los motivos comentados anteriormente, no es recomendable la *cancelación asíncrona*, a menos que estemos muy seguros de que no puede causar problemas. Uno de los pocos casos con los que es compatible es en bucles 100% dedicados a ejecutar cálculos en la CPU, como el siguiente: @@ -668,6 +672,7 @@ El estándar POSIX solo indica que las funciones {pthread_setcancelstate} y {pth En general, no se puede llamar a otras funciones de la librería del sistema de forma segura en un hilo cancelable asíncronamente. ==== Cancelación en diferido + (((cancelación, en diferido))) Por tanto, la *cancelación en diferido* es la mejor alternativa. Con este tipo de cancelación, la terminación del hilo ocurre en puntos concretos del código. diff --git "a/content/C13-sincronizaci\303\263n.adoc" "b/content/C13-sincronizaci\303\263n.adoc" index e98fdeb..351e7b2 100644 --- "a/content/C13-sincronizaci\303\263n.adoc" +++ "b/content/C13-sincronizaci\303\263n.adoc" @@ -206,6 +206,7 @@ Así, el sistema operativo no puede tomar el control y asignar otro hilo a la CP Indudablemente esta solución no es práctica en sistema multiprocesador, donde hay varios procesadores ejecutándose a la vez. === Instrucciones atómicas + (((instrucción, atómica))) Las CPU modernas disponen de instrucciones para comparar y modificar el contenido de una variable o intercambiar el contenido de dos variables, de forma *((atómica))*. El término *atómico* hace referencia a que las operaciones se ejecutan como una unidad ininterrumpible. @@ -278,6 +279,7 @@ La importancia de estas instrucciones está en que pueden ser utilizadas por el Por ejemplo, *semáforos* o *_mutex_*. == Semáforos + (((semáforo))) La exclusión mutua en las secciones críticas se asegura utilizando adecuadamente una serie de recursos que para ese fin proporciona el sistema operativo. Estos recursos utilizan internamente instrucciones y otras características de la CPU, incluidas por los diseñadores para resolver este tipo de problemas, que hemos comentado anteriormente. @@ -604,6 +606,7 @@ pthread_mutex_unlock( &args->mutex ); En {threads_sync_counter_cpp} y {threads_sync_factorial_cpp} se puede ver ejemplos equivalentes pero usando {cpp_thread} y {cpp_mutex}, de la librería estándar de {cpp}. == Variables de condición + (((variable de condición))) En la solución que dimos al *problema del productor-consumidor* usando *semáforos* (véase el <<_ejemplos_del_uso_de_semáforos>>) empleamos *semáforos* para implementar las esperas del productor y el consumidor cuando el vector está lleno o vacío, respectivamente. Lamentablemente, los _mutex_ no se pueden usar de la misma manera para señalar eventos. @@ -919,7 +922,9 @@ Todas estas cuestiones sobre la sincronización no solo afectan al código que e A la hora de decidir utilizar una librería en un programa multihilo es necesario que tengamos en cuenta los conceptos de *reentrante* y *seguridad de hilos*. === Funciones reentrantes -(((reentrante)))(((función, reentrante))) + +(((reentrante))) +(((función, reentrante))) Una función es *reentrante* puede ser interrumpida en medio de su ejecución y, mientras espera, volver a ser llamada con total seguridad. Obviamente las funciones recursivas deben ser reentrantes para poder llamarse a sí mismas una y otra vez con seguridad. @@ -935,7 +940,9 @@ Si lo hiciera solo puede hacerlo mediante operaciones *leer-modificar-escribir* Como hemos mencionado anteriormente, los *manejadores de señal* deben ser funciones *reentrantes*. === Seguridad en hilos -(((seguridad, en hilos)))(((función, segura en hilos))) + +(((seguridad, en hilos))) +(((función, segura en hilos))) Una función es *segura en hilos* o *((thread-safe))* si al manipular estructuras compartidas de datos lo hace de tal manera que se garantiza la ejecución segura de la misma por múltiples hilos al mismo tiempo. Obviamente estamos hablando de un problema de secciones críticas, por lo que las funciones lo resuelven sincronizando el acceso a estos datos mediante el uso de *semáforos*, *_mutex_* u otros recursos similares ofrecidos por el sistema operativo. diff --git "a/content/C14-planificaci\303\263n.adoc" "b/content/C14-planificaci\303\263n.adoc" index 956de5e..7798256 100644 --- "a/content/C14-planificaci\303\263n.adoc" +++ "b/content/C14-planificaci\303\263n.adoc" @@ -22,6 +22,7 @@ Así que todo lo que comentemos a partir de ahora sobre la planificación de pro En cualquier caso, sea cual sea el algoritmo de planificación utilizado, este debe ser muy rápido, ya que es ejecutado con mucha frecuencia —aproximadamente una vez cada 100 milisegundos—. == Planificación expropiativa + (((planificación, de CPU, expropiativa))) El planificador deben ser invocado necesariamente en los siguientes casos, dado que en ellos la CPU queda libre y es conveniente aprovecharla planificando otro proceso, en lugar de dejarla desocupada: @@ -93,6 +94,7 @@ A continuación presentamos los criterios más comunes. Los algoritmos de planificación son mejores cuanto mayor es su valor para los siguientes criterios. ==== Uso de CPU + (((uso de CPU))) Un buen planificador debería mantener la CPU lo más ocupada posible. El *uso de CPU* es la proporción de tiempo que se usa la CPU en un periodo de tiempo determinado. @@ -104,6 +106,7 @@ Se suele indicar en tanto por ciento. ++++ ==== Tasa de procesamiento + (((tasa, procesamiento))) Cuando la CPU está ocupada es porque el trabajo se está haciendo. Por tanto, una buena medida del volumen de trabajo realizado puede ser el número de tareas o procesos terminados por unidad de tiempo. @@ -204,6 +207,7 @@ Lo haremos considerando que cada proceso tiene una única ráfaga de CPU. Sin embargo, no debemos olvidar que para ser precisos necesitaríamos utilizar muchos más procesos, donde cada uno estuviera compuesto de una secuencia de miles de ráfagas alternativas de CPU y de E/S. === Planificación FCFS + (((planificación, de CPU, FCFS))) En la planificación *((FCFS))* (_First Come, First Served_) o *primero que llega, primero servido* la cola es FIFO: @@ -373,6 +377,7 @@ Esto nos permite llegar a la conclusión de que en cierto orden de llegada la ma Esto reduce la utilización de la CPU y de los dispositivos de E/S por debajo de lo que sería posible, si los procesos más cortos se ejecutasen primero. === Planificación SJF + (((planificación, de CPU, SJF))) La planificación *((SJF))* (_Shortest-Job First_) o *primero el más corto*, consiste en: @@ -531,6 +536,7 @@ En otro caso, dado que tanto _{alpha}_ como 1 - _{alpha}_ son menores de 1, cada El problema es que todos estos cálculos consumen tiempo de CPU, cuando el planificador debe ser lo más rápido posible, dado que se ejecuta con mucha frecuencia. === Planificación SRTF + (((planificación, de CPU, SRTF))) El algoritmo *SJF* es *cooperativo*, pero se puede implementar de forma *expropiativa*, en cuyo caso se llama *((SRTF))* (Shortest-Remaing-Time First). La diferencia está en lo que ocurre cuando un nuevo proceso llega a la *cola de preparados*. @@ -612,6 +618,7 @@ Sin embargo, debemos tener en cuenta que —aunque no lo estemos considerando en Los algoritmos expropiativos también suelen ofrecer mejores tiempos de respuesta, puesto que un proceso que llega a la *cola de preparados* puede ser asignado a la CPU sin esperar a que el proceso que se ejecuta en ella actualmente termine su ráfaga de CPU. === Planificación con prioridades + (((planificación, de CPU, prioridades))) En la *planificación con prioridades* se asocia una prioridad a cada proceso, de tal forma que el de prioridad más alta es asignado a la CPU. En caso de igual prioridad, se utiliza *FCFS*. @@ -791,7 +798,9 @@ De esta manera los proceso de baja prioridad tarde o temprano tendrán una oport Una vez se les asigna la CPU, se restablece su prioridad al valor original. === Planificación RR -(((planificación, de CPU, RR)))(((planificación, de CPU, Round-Robin))) + +(((planificación, de CPU, RR))) +(((planificación, de CPU, Round-Robin))) El algoritmo *((RR))* (_Round-Robin_) es similar al *FCFS*, pero añadiendo la expropiación para conmutar entre procesos cuando llevan cierta cantidad de tiempo ejecutándose en la CPU. Este algoritmo requiere los siguientes elementos: @@ -914,6 +923,7 @@ Esto hace que se desaprovechen los dispositivos de E/S y genera un incremento de Para evitarlo se puede optar por un *planificador de colas multinivel* —para resolver el problema combinando el algoritmo *RR* con otro que priorice adecuadamente los procesos limitados por la E/S (véase el <<_planificación_con_colas_multinivel>>)— o por la *planificación equitativa* que veremos a continuación. === Planificación equitativa + (((planificación, de CPU, equitativa))) Hasta el momento hemos hablado de planificadores que se centran en cuál es el proceso más importante para ejecutarlo a continuación. Sin embargo, otra opción, desde el punto de vista de la planificación, es dividir directamente el tiempo de CPU entre los procesos. @@ -1064,8 +1074,8 @@ t_{i} = \text{ceil}\left(\frac{g}{n}\right)M de forma que se reparten las stem:[g] porciones entre los stem:[n] procesos. ==== Planificación equitativa ponderada -(((planificación, de CPU, equitativa ponderada))) +(((planificación, de CPU, equitativa ponderada))) Al igual que en los algoritmos anteriores de planificación, en ocasiones puede ser interesante priorizar unos procesos frente a otros, tanto por motivos ajenos al sistema operativo como por motivos internos. Por ejemplo, se puede querer favorecer a los procesos limitados por la E/S para mejorar la eficiencia del sistema, tal y como comentamos en el apartado <<_ciclo_de_ráfagas_de_cpu_y_de_es>>. @@ -1235,6 +1245,7 @@ Una vez resuelto el problema, los *tiempos de espera y ejecución promedio* corr |=== === Planificación con colas multinivel + (((planificación, de CPU, multinivel))) Los diseñadores recurren a la *planificación de colas multinivel* cuando quieren combinar las características de varios algoritmos. @@ -1268,6 +1279,7 @@ Sin embargo, en este tipo de *colas multinivel* la asignación de los procesos a Mientras que hoy en día es común que los procesos se muevan entre colas según las características del proceso. === Planificación con colas multinivel realimentadas + (((planificación, de CPU, multinivel realimentada))) Para aumentar la flexibilidad de la planificación con colas multinivel se puede permitir a los procesos pasar de una cola a otra. Así se pueden clasificar en colas distintas, procesos con diferentes tiempos de ráfaga de CPU. @@ -1505,7 +1517,10 @@ En el <<_sistemas_de_tiempo_real>> discutimos la importancia de los sistemas de A continuación, describiremos las funcionalidades necesarias para soportar la ejecución de procesos en tiempo real dentro de un sistema operativo de propósito general. === Tiempo real estricto -(((sistema, tiempo real, estricto)))(((tiempo real, estricto)))(((hard real-time))) + +(((sistema, tiempo real, estricto))) +(((tiempo real, estricto))) +(((hard real-time))) Los sistemas de *tiempo real estricto* son necesarios para realizar tareas críticas que deben ser completadas dentro de unos márgenes de tiempo preestablecidos. Generalmente las tareas son entregadas al sistema operativo junto con una declaración de las restricciones de tiempo —periodicidad y límite de tiempo— y la cantidad de tiempo que necesitan para ejecutarse. @@ -1516,7 +1531,10 @@ Esto es imposible en sistemas con almacenamiento secundario o memoria virtual, y Por tanto, el *tiempo real estricto* no es compatible con los sistemas operativos de propósito general, como los sistemas operativos de escritorio modernos. === Tiempo real flexible -(((sistema, tiempo real, flexible)))(((tiempo real, flexible)))(((soft real-time))) + +(((sistema, tiempo real, flexible))) +(((tiempo real, flexible))) +(((soft real-time))) La ejecución de procesos de *tiempo real flexible* es menos restrictiva. Tan solo requiere que los procesos críticos reciban mayor prioridad que los que no lo son. Esto puede generar excesos en la cantidad de recursos asignados a los procesos de tiempo real, así como inanición y grandes retrasos en la ejecución del resto de los procesos, pero es compatible con los sistemas de propósito general. @@ -1620,14 +1638,18 @@ Permite que cada núcleo de procesador que está presente físicamente, el siste Al margen de estas cuestiones, según el tipo de procesamiento, existen diversas posibilidades a la hora de enfrentar el problema de la planificación en un sistema multiprocesador (véase el <<_sistemas_multiprocesador>>). === Multiprocesamiento asimétrico -(((sistema, multiprocesamiento, asimétrico)))(((multiprocesamiento, asimétrico))) + +(((sistema, multiprocesamiento, asimétrico))) +(((multiprocesamiento, asimétrico))) Cuando utilizamos *multiprocesamiento asimétrico* todas las decisiones de planificación, procesamiento de E/S y otras actividades son gestionadas por el núcleo del sistema ejecutándose en un único procesador: el *servidor* o *maestro*. El resto de procesadores se limitan a ejecutar código de usuario, que les es asignado por ese procesador *maestro*. Este esquema es sencillo, puesto que evita la necesidad de compartir estructuras de datos entre el código que se ejecuta en los diferentes procesadores. === Multiprocesamiento simétrico -(((sistema, multiprocesamiento, simétrico)))(((multiprocesamiento, simétrico))) + +(((sistema, multiprocesamiento, simétrico))) +(((multiprocesamiento, simétrico))) Cuando utilizamos *multiprocesamiento simétrico* o *SMP*, cada procesador ejecuta su propia copia del núcleo del sistema operativo y se autoplanifica mediante su propio planificador de CPU. En estos sistemas nos podemos encontrar con varias alternativas. diff --git a/content/C15-memoria_principal.adoc b/content/C15-memoria_principal.adoc index 14357dc..db4266b 100644 --- a/content/C15-memoria_principal.adoc +++ b/content/C15-memoria_principal.adoc @@ -383,6 +383,7 @@ Esto permite reducir el consumo de memoria principal compartiendo las regiones d Esto aumenta el tiempo de carga de las librerías y solo permite que compartan memoria física partes de la librería que sigan siendo iguales tras la reubicación de las direcciones. === Librerías compartidas + (((librería, compartida))) Habitualmente las librerías incluyen información acerca de su versión. Esta información puede ser utilizada para evitar que los programas se ejecuten con versiones incompatibles de las mismas, o para permitir que haya más de una versión de cada librería en el sistema. @@ -458,6 +459,7 @@ En la *asignación contigua de memoria*(((asignación, contigua, memoria))) a ca Esto se puede hacer mediante *particionado fijo* o *particionado dinámico* === Particionado fijo + (((particionado, fijo))) El *particionado fijo* la memoria se divide en varias secciones de tamaño fijo, cada una de las cuales contiene un proceso. Cuando un proceso termina, se carga uno nuevo de la cola de entrada en la partición libre. @@ -468,6 +470,7 @@ image::{chapdir}/particionado_fijo.svg[] Este método fue utilizado originalmente por el {ibmos360}, pero ya no se utiliza === Particionado dinámico + (((particionado, dinámico))) El *particionado dinámico* es una generalización del anterior: @@ -505,11 +508,13 @@ Para evaluar qué estrategia es la mejor, se han realizado algunas simulaciones * El *primer ajuste* es normalmente más rápido que el *mejor ajuste*. == Fragmentación + (((fragmentación))) Las estrategias de asignación de espacio de almacenamiento generalmente sufren de problemas de *fragmentación*. Vamos a comentar brevemente cómo afecta la *fragmentación* a la *asignación contigua de memoria*. === Fragmentación externa + (((fragmentación, externa))) La *fragmentación externa* ocurre cuando hay suficiente espacio libre para satisfacer una petición, pero el espacio no es contiguo. Es decir, el espacio de almacenamiento está fraccionado en un gran número de huecos de pequeño tamaño: @@ -530,6 +535,7 @@ Existen dos técnicas complementarias que utilizan esta solución: la paginació (véase el <>) y la https://es.wikipedia.org/wiki/Segmentaci%C3%B3n_de_memoria[segmentación]. === Fragmentación interna + (((fragmentación, interna))) La *fragmentación interna* se origina por la diferencia entre el espacio solicitado y el espacio finalmente asignado. @@ -542,7 +548,9 @@ Esto hace que, en general, se asigne más memoria de la que realmente se ha soli A esto se lo denomina *fragmentación interna*. == Intercambio -(((intercambio)))(((swapping))) + +(((intercambio))) +(((swapping))) Un proceso debe estar en la memoria para ser ejecutado, pero en algunos sistemas operativos un proceso puede ser sacado de la memoria y copiado a un almacenamiento de respaldo de forma temporal —generalmente un dispositivo de almacenamiento secundario, como un disco— y en algún momento volver a ser traído a la memoria para continuar su ejecución. Al procedimiento descrito se lo denomina *intercambio* o *_swapping_*. diff --git "a/content/C16-paginaci\303\263n.adoc" "b/content/C16-paginaci\303\263n.adoc" index c83bdef..d6b0545 100644 --- "a/content/C16-paginaci\303\263n.adoc" +++ "b/content/C16-paginaci\303\263n.adoc" @@ -263,6 +263,7 @@ La protección de las páginas se consigue mediante unos bits que indican las op Normalmente, estos bits son almacenados en cada una de las entradas de la *tabla de páginas*. === Bits de protección + (((bit, protección))) Los *bits de protección* pueden ser: @@ -281,6 +282,7 @@ Si no lo es, se genera una excepción de violación de protección de memoria, d Normalmente, el sistema operativo responde a dicha excepción terminando el proceso que la generó. === Bit de válido + (((bit, válido))) Además de los *bits de protección* comentados, se suele añadir a cada entrada un *bit de válido*: @@ -336,6 +338,7 @@ En cualquier caso, las *páginas* de la región no ocupada, forman parte del esp La falta de *marco* es indicada por el sistema operativo utilizando el *bit de válido* para denegar el acceso. == Páginas compartidas + (((página, compartida))) Una de las ventajas importantes de la paginación, es la posibilidad de compartir *páginas* entre procesos. Para conseguir esto, basta con que las *páginas compartidas* de los distintos procesos tengan asignadas un mismo *marco*. @@ -345,6 +348,7 @@ También permite compartir las *páginas* de código de una librería compartida Compartir *páginas* no solo permite ahorrar memoria, pues en los sistemas operativos modernos, la comunicación entre procesos mediante memoria compartida (véase el <>), se implementa mediante *páginas compartidas*. == Paginación jerárquica + (((paginación, jerárquica))) Al método básico de paginación, se lo conoce como *tabla de páginas lineal*(((tabla, páginas, lineal))). Sin embargo, las CPU comúnmente, utilizan otras técnicas a la hora de estructurar la *tabla de páginas*. diff --git a/content/C17-memoria_virtual.adoc b/content/C17-memoria_virtual.adoc index eb74a02..b92afca 100644 --- a/content/C17-memoria_virtual.adoc +++ b/content/C17-memoria_virtual.adoc @@ -247,6 +247,7 @@ Este último método parece conseguir un buen compromiso entre el tamaño del es Por eso se utiliza en la mayor parte de los sistemas operativos modernos. == Copy-on-write + (((copy-on-write))) El *_copy-on-write_* o *copia durante la escritura* permite la creación rápida de nuevos procesos, minimizando la cantidad de páginas que deben ser asignadas a estos. @@ -300,6 +301,7 @@ En previsión de esto, el sistema mantiene un marco completamente a 0 y durante De esta forma, se ahorra memoria física y tiempo de CPU, puesto que realmente no se asigna un marco de memoria física y se pone a 0 su contenido hasta el momento en el que el proceso intenta escribir en una página de memoria previamente reservada. == Archivos mapeados en memoria + (((archivo, mapeado en memoria))) Los *archivos mapeados en memoria* permiten acceder a un archivo como parte del espacio de direcciones virtuales de un proceso. Algunas de las características de esta técnica son: @@ -422,11 +424,13 @@ En general, para implementar la paginación bajo demanda necesitamos: Obviamente, estos algoritmos deben ser escogidos de forma que mantengan la *tasa de fallos de página* lo más baja posible para perjudicar en lo mínimo el rendimiento del sistema. === Algoritmos de reemplazo de páginas + (((algoritmo, reemplazo))) Hay muchos *algoritmos de reemplazo de página*. La cuestión es cómo seleccionar uno en particular, sabiendo que debe tener la menor tasa posible de *fallos de página*. ==== Trazas de referencias + (((traza de referencias))) Podemos evaluar el algoritmo utilizando una secuencia de referencias a memoria y calculando el número de *fallos de página*. A dicha secuencia de referencias se la denomina *trazas de referencias*. @@ -452,7 +456,9 @@ con páginas de 100 bytes, podemos reducirla a: Indudablemente, para determinar el número de *fallos de página* también debemos conocer el número de marcos disponibles para el proceso. ==== Reemplazo FIFO -(((algoritmo, reemplazo, FIFO)))(((reemplazo, FIFO))) + +(((algoritmo, reemplazo, FIFO))) +(((reemplazo, FIFO))) El *algoritmo de reemplazo de páginas ((FIFO))* es el más sencillo. Funciona de la siguiente manera: @@ -508,7 +514,9 @@ La <> muestra la curva de *fallos de página* frente Como se puede apreciar, el número de *fallos de página* con cuatro marcos es superior al número de fallos con tres marcos, aunque era de esperar que el número de fallos de páginas decrementara al aumentar el número de marcos. ==== Reemplazo óptimo -(((algoritmo, reemplazo, óptimo)))(((reemplazo, óptimo))) + +(((algoritmo, reemplazo, óptimo))) +(((reemplazo, óptimo))) Como resultado del descubrimiento de la *anormalidad de Belady* se comenzó a buscar un algoritmo de reemplazo de página óptimo. Es decir: @@ -534,7 +542,9 @@ Desafortunadamente, el *algoritmo de reemplazo óptimo* es difícil de implement Por lo que solo se usa en estudios comparativos, con el fin de saber cuánto se aproxima al óptimo un algoritmo de reemplazo determinado. ==== Reemplazo LRU -(((algoritmo, reemplazo, LRU)))(((reemplazo, LRU))) + +(((algoritmo, reemplazo, LRU))) +(((reemplazo, LRU))) El *algoritmo de reemplazo de página ((LRU))* (_Least Recently Used_) es una aproximación del óptimo. La hipótesis es que si una página no ha sido usada durante un gran periodo de tiempo, entonces probablemente tampoco será utilizada en el futuro; por lo que reemplazando la página que hace más tiempo que no se usa, nos estaríamos aproximando al algoritmo óptimo. Vamos a ilustrarlo con un ejemplo: @@ -607,7 +617,9 @@ Con el *bit de referencia* no podemos saber con exactitud el instante, pero sí Utilizándolo, podemos implementar diversas aproximaciones al algoritmo LRU. ===== Reemplazo NRU -(((algoritmo, reemplazo, NRU)))(((reemplazo, NRU))) + +(((algoritmo, reemplazo, NRU))) +(((reemplazo, NRU))) En el *algoritmo de reemplazo de página ((NRU))* se utiliza el *bit de referencia* de la siguiente manera: . En intervalos regulares de tiempo, todos los *bits de referencia* son puestos a cero por el sistema operativo. @@ -705,7 +717,12 @@ En un caso extremo, el número de *bits de referencia adicionales* podría ser c A este algoritmo se lo conoce como el *algoritmo de la segunda oportunidad*. ===== Algoritmo de la segunda oportunidad -(((algoritmo, reemplazo, reloj)))(((algoritmo, reloj)))(((algoritmo, reemplazo, segunda oportunidad)))(((reemplazo, reloj)))(((reemplazo, segunda oportunidad))) + +(((algoritmo, reemplazo, reloj))) +(((algoritmo, reloj))) +(((algoritmo, reemplazo, segunda oportunidad))) +(((reemplazo, reloj))) +(((reemplazo, segunda oportunidad))) El *algoritmo de reemplazo de la segunda oportunidad* o del *reloj* es un *algoritmo de reemplazo de página FIFO*, pero donde una página es seleccionada considerando el *bit de referencia*: . Cuando es necesario seleccionar una víctima para reemplazo se extrae una página de la cola FIFO. @@ -821,7 +838,11 @@ Debido a que esas páginas han sido utilizadas intensamente, tiene un contador d La solución es utilizar el *algoritmo LRU aproximado con bits de referencia adicionales* (véase <<_con_bits_de_referencia_adicionales>>) porque tiene un coste muy similar a este y prioriza las usadas más recientemente sobre las que fueron usadas con mucha frecuencia en el pasado. ===== Reemplazo LFU -(((algoritmo, reemplazo, NFU)))(((algoritmo, reemplazo, LFU)))(((reemplazo, NFU)))(((reemplazo, LFU))) + +(((algoritmo, reemplazo, NFU))) +(((algoritmo, reemplazo, LFU))) +(((reemplazo, NFU))) +(((reemplazo, LFU))) En el *algoritmo de reemplazo de página ((LFU))* (_Least Frequently Used_) o *((NFU))* (_Not Frequently Used_) se escoge la página con el contador más bajo. Esto es así, puesto que suponemos que las páginas menos referenciadas son las que no se están utilizando de forma más activa. @@ -869,6 +890,7 @@ h|F h|F h|F h|F h| h| h| h| h|F h|F h|F h| Por extraña que parezca esta política, suele ser más eficiente que el *LRU* cuando se utiliza en las aplicaciones de almacenamiento de datos, porque algunas las páginas se utilizan intensamente durante breves periodos de tiempo, pero están un tiempo sin utilizarse. === Algoritmos de buffering de páginas + (((algoritmo, buffering de páginas))) Existen otros procedimientos que pueden ser utilizados, junto con alguno de los *algoritmos de reemplazo* comentados, con el objetivo de mejorar su eficiencia. Estos procedimientos se agrupan dentro de lo que se denomina *algoritmos de _buffering_ de páginas*. @@ -910,6 +932,7 @@ Por tanto: Generalmente, el *reemplazo global* proporciona mayor rendimiento, por lo que es el método más utilizado. == Asignación de marcos de página + (((algoritmo, asignación de marcos))) La cuestión que queda por resolver es cómo repartir los marcos de memoria física libre entre los diferentes procesos, con el fin de cubrir las necesidades de reemplazo de cada uno de ellos. Posibles soluciones a esto serían: repartir la memoria por igual entre todos los procesos o hacerlo en proporción a la cantidad de memoria virtual que utilizan. @@ -931,8 +954,8 @@ En general, si se va reduciendo el número de marcos asignados a un proceso, muc Cuando eso ocurre se dice que el proceso está *hiperpaginando*. == Hiperpaginación -(((hiperpaginación))) +(((hiperpaginación))) Como hemos comentado, la *hiperpaginación* se produce cuando un proceso no dispone de suficientes marcos como para alojar todas las páginas que necesita con frecuencia, por lo que la *tasa de fallos de página* se vuelve muy elevada. Se dice que un proceso sufre de *hiperpaginación* cuando gasta más tiempo paginando que ejecutándose. @@ -987,6 +1010,7 @@ Sin embargo, un algoritmo de *reemplazo local* no evita completamente que un pro El uso intensivo del *dispositivo de intercambio*, por parte del proceso *hiperpagina*, puede afectar al rendimiento del sistema al aumentar el *tiempo de acceso efectivo* al disco. === Modelo del conjunto de trabajo + (((modelo, conjunto de trabajo))) Para entender el modelo de conjunto de trabajo es necesario comenzar definiendo el *modelo de localidad*(((modelo, localidad))). El *modelo de localidad* establece que: @@ -1068,8 +1092,8 @@ Ya hemos comentado que las principales decisiones que deben ser tomadas en el di Sin embargo, hay otras consideraciones que deben ser tenidas en cuenta. === Prepaginado -(((prepaginado))) -El *prepaginado* es una técnica que consiste en cargar múltiples páginas junto con la página demandada en cada *fallo de página*. + +El *((prepaginado))* es una técnica que consiste en cargar múltiples páginas junto con la página demandada en cada *fallo de página*. Esas otras páginas se escogen especulativamente bajo la hipótesis de que van a ser necesitadas por el proceso en un corto espacio de tiempo. De manera que si la predicción es acertada, la *tasa de fallos de página* se reduce significativamente. diff --git a/content/C19-sistema_de_archivos.adoc b/content/C19-sistema_de_archivos.adoc index 280e4f9..8922b66 100644 --- a/content/C19-sistema_de_archivos.adoc +++ b/content/C19-sistema_de_archivos.adoc @@ -22,6 +22,7 @@ En la <> se muestra un ejemplo típico de la Cada nivel utiliza las funciones de los niveles inferiores y proporciona nuevas funciones a los niveles superiores. === Control de E/S + (((control de E/S))) En el nivel más bajo, accediendo directamente a los dispositivos de almacenamiento, se encuentra el *control de E/S*. @@ -36,6 +37,7 @@ Lo más común es que su tamaño sea de 512 bytes. ==== === Sistema básico de archivos + (((sistema de archivos, básico))) El *sistema básico de archivos* se encarga de enviar comandos genéricos al controlador de dispositivo apropiado, con el fin de leer y escribir bloques físicos en el disco. Cada bloque físico se identifica mediante su dirección de disco numérica. @@ -51,6 +53,7 @@ Antes de este método, se usaba el de cabeza-cilindro-sector (CHS), pero tenía ==== === Módulo de organización de archivos + (((módulo de organización de archivos))) El *módulo de organización de archivos* tiene conocimiento de los archivos y se encarga de traducir las direcciones lógicas de los bloques en los archivos —es decir, posición del bloque dentro del archivo, siendo 0 la dirección del primer bloque— en las direcciones físicas de bloque —por ejemplo, cilindro, cabeza y sector del bloque correspondiente en el dispositivo de almacenamiento— que serán enviadas al *sistema básico de archivos* para que realice las transferencias solicitadas. @@ -87,12 +90,16 @@ Estas estructuras varían dependiendo del sistema operativo y del sistema de arc Sin embargo, a continuación intentaremos describir brevemente las estructuras en disco más comunes. === Bloque de control de arranque -(((bloque, de control, arranque)))(((bloque, inicio)))(((sector, arranque))) + +(((bloque, de control, arranque))) +(((bloque, inicio))) +(((sector, arranque))) En todo sistema de archivos suele haber un *bloque de control de arranque* —también llamado *bloque de inicio* o *sector de arranque*— que suele ocupar el primer bloque de cada volumen y que contiene la información necesaria para iniciar un sistema operativo a partir de dicho volumen. Este bloque puede estar vacío, si el volumen no contiene un sistema operativo. === Bloque de control de volumen + (((bloque, de control, volumen))) El *bloque de control de volumen* contiene todos los detalles acerca del volumen. Por ejemplo, el número máximo de bloques, el tamaño de los bloques, el número de bloques libres y punteros a los mismos; así como un contador de bloques de información *FCB* ocupados y punteros a estos. @@ -101,6 +108,7 @@ A esta estructura se la denomina *((superbloque))*, en los sistemas de archivos Mientras que en {ntfs} esta información se almacena en la *tabla maestra de archivos* o *((MFT))* (_Master File Table_). === Bloque de control de archivo + (((bloque, de control, archivo))) Todo sistema de archivos tiene un *bloque de control de archivo* o *((FCB))* (_File Control Block_) por archivo, en que se almacenan numerosos detalles sobre cada uno de los archivos Por ejemplo, los permisos, el propietario, el tamaño y la ubicación de los bloques de datos, entre otros. @@ -139,6 +147,7 @@ La *tabla de archivos abiertos* contiene, para cada archivo, un puntero a la ent Por ejemplo, si el proceso lo ha abierto para lectura o escritura o la posición del puntero que indica la siguiente posición a leer o escribir. == Montaje de sistemas de archivos + (((montaje))) Un sistema de archivos debe *montarse* para que sus archivos sean accesibles a los procesos del sistema. El proceso de montaje incluye los siguientes pasos: @@ -390,6 +399,7 @@ Normalmente, cada entrada de directorio incluye un bit donde se indica si dicha Esto se hace así porque, generalmente, los directorios no son más que archivos con un formato interno especial; por lo que el sistema debe saber si la entrada apunta a un directorio para interpretar correctamente los datos del directorio. ==== Directorio de trabajo actual + (((directorio, trabajo))) Comúnmente, en el *PCB* de cada proceso se guarda cuál es su *directorio de trabajo* actual. De esta forma, cuando se hace referencia a un archivo en una llamada al sistema usando solo su nombre, se le busca en el *directorio de trabajo* del proceso. @@ -414,6 +424,7 @@ Ese proceso hereda el *directorio de trabajo* actual de *Bash*. ==== ==== Nombre de ruta + (((nombre de ruta))) Los *nombres de ruta* es la forma en la que se indica la ubicación de un archivo o directorio en el árbol de directorios. @@ -637,6 +648,7 @@ Esos permisos pueden ser gestionados utilizando los comandos {cmd_setfacl} y {cm En general, se suele asignar a la *ACL* más prioridad que a la *ACL condensada*, pues la primera tiene una granularidad más fina y no se crea de forma predeterminada. === Semántica de coherencia + (((semántica, coherencia))) La *semántica de coherencia* especifica cuándo las modificaciones que un proceso realiza en los archivos serán observables por los otros procesos. Por tanto, es importante tenerla en cuenta cuando esperamos que varios procesos utilicen los mismos archivos al mismo tiempo. @@ -644,6 +656,7 @@ Por tanto, es importante tenerla en cuenta cuando esperamos que varios procesos A continuación vamos a comentar algunos ejemplos de tipos *semántica de coherencia*. ==== Semántica POSIX + (((semántica, POSIX))) Los sistemas de archivos de los sistemas operativos POSIX utilizan la siguiente *semántica de coherencia*: @@ -658,6 +671,7 @@ Por ejemplo, un proceso que haga *read* sobre un archivo podría quedar en esper La competición por acceder a esta imagen única provoca retrasos en los procesos debido a estos bloqueos. ==== Semántica de sesión + (((semántica, sesión))) El https://es.wikipedia.org/wiki/Andrew_File_System[sistema de archivos Andrew] (AFS) es un sistema de archivos en red —o sistema de archivos distribuido— es decir, sirve para compartir archivos en una red de ordenadores y usarlos como si estuvieran almacenados localmente. @@ -685,6 +699,7 @@ A cambio hay que tener cuidado con el hecho de que un proceso puede estar leyend Si un proceso necesita acceder a los datos que escribe otro proceso, ambos deben sincronizarse explícitamente abriendo y cerrando el archivo. ==== Semántica de archivos compartidos inmutables + (((semántica, archivos compartidos inmutables))) En esta semántica, cuando un archivo es declarado como compartido por su creador, ya no puede ser modificado. diff --git "a/content/C20-implemantaci\303\263n_sistema_de_archivos.adoc" "b/content/C20-implemantaci\303\263n_sistema_de_archivos.adoc" index b4f735c..8a0192d 100644 --- "a/content/C20-implemantaci\303\263n_sistema_de_archivos.adoc" +++ "b/content/C20-implemantaci\303\263n_sistema_de_archivos.adoc" @@ -108,6 +108,7 @@ El siguiente problema es cómo asignar el espacio disponible en el disco a los a Como la unidad mínima de asignación de espacio a un archivo es el bloque, la fragmentación interna suele ser un problema común a todos los métodos que veremos a continuación. === Asignación contigua + (((asignación, contigua, disco))) La *asignación contigua* requiere que cada archivo ocupe un conjunto contiguo de bloques en el disco. Esto es muy eficiente, puesto que el acceso a todos los datos de un archivo requiere un movimiento mínimo del cabezal del disco. @@ -152,6 +153,7 @@ El motivo es que cuantos más bloques contiguos sean asignados a un archivo, men En {ext4} el espacio se asigna a los archivos en *extensiones* de hasta 128 MiB, compuestas por bloques, generalmente, de 4 KiB. === Asignación enlazada + (((asignación, enlazada))) En la *asignación enlazada* cada archivo es una lista enlazada de bloques de disco, pudiendo estos bloques estar dispersos por todo el disco: @@ -240,6 +242,7 @@ Una de las ventajas de este esquema es que mejora el tiempo de los accesos aleat **** === Asignación indexada + (((asignación, indexada))) El mecanismo de *asignación indexada* agrupa todos los punteros de la asignación enlazada en una única ubicación: el *bloque de índices*(((bloque, índices))). Así se resuelve la falta de eficiencia de la asignación enlazada convencional —en ausencia de FAT— cuando se realizan accesos aleatorios. @@ -372,6 +375,7 @@ Es decir, se utilizan estructuras de datos y procedimientos comunes para separar La implementación de un sistema de archivos está compuesta de tres niveles fundamentales: la *interfaz del sistema de archivos*, el *sistema de archivos virtual* y, finalmente, la implementación real del sistema de archivos. === Interfaz del sistema de archivos + (((interfaz, sistema de archivos))) El primer nivel es la *interfaz del sistema de archivos*, a la que acceden los desarrolladores a través de las llamadas al sistema. En sistemas POSIX, estamos hablando de las llamadas {linux_open}, {linux_read}, {linux_write} y {linux_close}, entre otras. @@ -380,6 +384,7 @@ Y de los descriptores de archivos con los que se identifican los archivos abiert Esta interfaz es la misma sea cual sea el sistema de archivos al que se esté intentando acceder. === Sistema de archivos virtual + (((sistema de archivos, virtual))) El segundo nivel es la interfaz del *sistema de archivos virtual* o *((VFS))* (_Virtual File System_). Este nivel es utilizado por el anterior para atender las peticiones realizadas. @@ -441,6 +446,7 @@ Al considerar todo el tiempo necesario para atender la petición, a más *tiempo En los dispositivos de almacenamiento basados en memorias de estado sólido (véase el <<_memorias_de_estado_sólido>>) el tiempo de acceso viene determinado por las características de la memoria —entre otros factores— lo que hace que las diferencias entre accesos secuenciales y accesos aleatorios sean mucho menos significativas. === Cola de E/S al disco + (((cola, E/S al disco))) Cuando se solicita una operación de E/S sobre el almacenamiento, si la controladora y la unidad de disco están desocupadas, el sistema operativo puede atender la petición sobre la marcha. Pero si están ocupadas, el sistema operativo almacena la solicitud en una cola de peticiones pendientes. @@ -450,6 +456,7 @@ La cuestión es ¿cuál es el orden adecuado para escoger la peticiones de E/S d // TODO: Problemas de planificación de disco. === Planificación FCFS + (((planificación, de disco, FCFS))) En la planificación *((FCFS))* (_First Come, First Served_) o *primero que llega, primero servido* la cola de E/S al disco es FIFO. Es decir, que las solicitudes se atienden en orden de llegada. @@ -464,6 +471,7 @@ Se suele utilizar en los discos basados en memorias de estado sólido, donde reo ==== === Planificación SSTF + (((planificación, de disco, SSTF))) En la planificación *((SSTF))* (_Sortest_ _Seek Time First_) o algoritmo de *tiempo de búsqueda más corto*, de toda cola se selecciona la solicitud con el menor *tiempo de búsqueda* desde la posición actual de la cabeza. Este algoritmo de planificación primero da servicio a las solicitudes cercanas a la posición actual de la cabeza, antes de alejarse para dar servicio a otras solicitudes, dado que el *tiempo de búsqueda* se incrementa a medida que lo hace el número de cilindros que es necesario recorrer para llegar a una posición dada. @@ -471,7 +479,9 @@ Este algoritmo de planificación primero da servicio a las solicitudes cercanas Aun así, la solución no es óptima, dado que puede provocar inanición de algunas solicitudes, si van llegando constantemente nuevas solicitudes sobre regiones cercanas a donde está actualmente la cabeza del disco. === Planificación SCAN y C-SCAN -(((planificación, de disco, SCAN)))(((planificación, de disco, C-SCAN))) + +(((planificación, de disco, SCAN))) +(((planificación, de disco, C-SCAN))) En la planificación *((SCAN))*, algoritmo de *exploración* o del *ascensor*, el brazo del disco comienza en un extremo del disco y se mueve hacia el otro, atendiendo solicitudes a medida que pasa por cada cilindro, hasta llegar al otro extremo del disco. En el otro extremo, la dirección de movimiento de la cabeza se invierte para recorrer el disco en sentido inverso, repitiendo el proceso. @@ -482,7 +492,9 @@ A la variante del *SCAN* que cuando llega a un extremo vuelve al inicio, para vo Con esta variante el tiempo que tiene que esperar una solicitud para ser atendida es más uniforme que con el algoritmo *SCAN* original. === Planificación LOOK y C-LOOK -(((planificación, de disco, LOOK)))(((planificación, de disco, C-LOOK))) + +(((planificación, de disco, LOOK))) +(((planificación, de disco, C-LOOK))) En teoría los algoritmos *SCAN* y *C-SCAN* hacen que el brazo recorra los cilindros del primero al último, pero normalmente no se suelen implementar así. Por lo general, cuando en el recorrido del brazo, tras atender una solicitud, se descubre que ya no hay más solicitudes siguiendo la misma dirección, el brazo invierte la dirección sin llegar hasta el extremo del disco. @@ -495,7 +507,10 @@ Linux utilizó *C-LOOK*, bajo el nombre de *_elevator_*, como planificador de E/ ==== === Planificación N-Step-SCAN, N-Step-LOOK y F-SCAN -(((planificación, de disco, N-Step-SCAN)))(((planificación, de disco, N-Step-LOOK)))(((planificación, de disco, F-SCAN))) + +(((planificación, de disco, N-Step-SCAN))) +(((planificación, de disco, N-Step-LOOK))) +(((planificación, de disco, F-SCAN))) Los algoritmos *((N-Step-SCAN))* y *((N-Step-LOOK))* son variantes de los algoritmos *SCAN* y *LOOK*, respectivamente; donde se limita a _N_ el número de solicitudes que se atenderán en cada barrido del brazo del disco. Estos algoritmos funcionan de la siguiente manera: @@ -512,6 +527,7 @@ El término *rigidez del brazo* hace referencia a cuando hay un flujo continuo d Como *F-SCAN*, *N-Step-SCAN* y *N-Step-LOOK* separan las solicitudes en dos colas —haciendo que las nuevas tengan que esperar— el brazo siempre continúa su barrido hacia el extremo del disco. === Planificación CFQ + (((planificación, de disco, CFQ))) El planificador *((CFQ))* (_Completely Fair Queuing_) se diseñó para compartir de forma equitativa el *ancho de banda* entre todos los procesos que solicitan acceso al disco. Fue utilizado por defecto en muchas distribuciones de Linux hasta la versión 5.0 del núcleo y funciona de la siguiente manera: