Res P1 ArqSO (2023)
Res P1 ArqSO (2023)
Res P1 ArqSO (2023)
Objetivos:
Facilidad de uso: Un SO facilita el uso de un computador.
Eficiencia: Un SO permite que los recursos de un sistema de computación se puedan
utilizar de una manera eficiente.
Capacidad para evolucionar: Un SO se debe construir de tal forma que se puedan
desarrollar, probar e introducir nuevas funciones en el sistema sin interferir con su
servicio.
Funciones principales:
SO Máquina Extendida: Se encarga de presentar una interfaz agradable para el programador
u operador por medio de archivos ocultando las peculiaridades del hardware como las
interrupciones, administración de memoria, temporizadores y otras funciones de bajo
nivel.
SO Controlador de Recursos: Consiste en efectuar un reparto ordenado y controlado de los
procesadores memoria dispositivos de e/s, entre diversos programas que compiten por él.
También se mantiene al tanto de quien está usando qué recurso concede solicitudes de
recurso y media entre las solicitudes de los programas y usuarios que entren en
conflicto.
Generaciones:
Primera generación: Tubos de vacío y tableros
En esta generación las computadoras eran enormes, muy lentas, con miles de tubos de
vacío, su función general era de operaciones numéricas simples. Toda la programación se
efectuaba en lenguaje de máquina absoluto, a menudo alambrando tableros de conexión.
Segunda generación: Transistores y sistemas por lotes
Estas máquinas llamadas mainframes se alojaban en grandes habitaciones manejadas por
operadores profesionales. Gracias a los transistores las computadoras se volvieron
fiables para la venta a clientes comerciales. El sistema por lotes fue implementado para
reducir el alto costo.
Tercera generación: Circuitos integrados y multiprogramación
En esta generación las computadoras lograron cumplir dos funciones básicas: manejar
cómputos tanto científicos como comerciales. También fue planteada la idea de familia de
computadoras compatibles, y multiprogramación. Que consistía en dividir la memoria en
varias partes con distintos trabajos en cada una de las partes. El spooling y el sistema
de tiempo compartido también fueron claves en la tercera generación.
Cuarta generación: Computadoras personales
Con el desarrollo de circuitos integrados o chips surgió la era de las computadoras
personales trayendo consigo el desarrollo de interfaz gráfica y SO de red o SO
distribuidos.
Procesos: Es básicamente un programa en ejecución, consta de un programa ejecutable sus,
datos y pila, contador y otros registros. Los procesos son interrumpidos o liberados
periódicamente para dar tiempo de cpu a otros procesos, cuando este se detiene toda su
información se almacena explícitamente en un lugar, para cuando vuelva a ejecutarse su
estado es el mismo en el cual fue interrumpido.
Archivos: Las llamadas al sistema crean, eliminan, abren y cierran archivos. Los archivos
se guardan en directorios donde los archivos que se encuentran en él pueden determinarse
mediante “nombre de ruta de acceso“ desde el directorio raíz.
Estructura jerárquica (anillos): En el sistema de anillos, cada uno tiene una apertura,
conocida como puerta o trampa (trap), por donde pueden entrar las llamadas de las capas
inferiores. De esta forma, las zonas más internas del SO o núcleo del sistema estarán más
protegidas de accesos indeseados desde las capas más externas.
Máquina virtual: Se trata de un tipo de SO que presenta una interfaz a cada proceso
mostrando una máquina que parece idéntica a la máquina real subyacente. Estos SO separan
dos conceptos que suelen estar unidos en el resto de sistemas: la multiprogramación y la
máquina extendida. El objetivo de los SO de máquina virtual es el de integrar distintos
SO dando la sensación de ser varias máquinas diferentes.
Cliente-Servidor (microkernel): Este sistema sirve para toda clase de aplicaciones por
tanto, es de propósito general y cumple con las mismas actividades que los SO
convencionales. El núcleo tiene como misión establecer la comunicación entre los clientes
y los servidores. Los procesos pueden ser tanto servidores como clientes
Interrupciones (IRQ): Una interrupción es un evento producido por el hardware del sistema
que altera la secuencia en la cual el procesador ejecuta las instrucciones. Se genera
cuando es necesario llamar la atención del procesador para realizar alguna acción
importante. Cuando se llevan a cabo se realizan las siguientes operaciones: El control
pasa al SO, el SO almacena el estado completo del proceso interrumpido, se analiza la
interrupción producida, se ejecuta la rutina de servicio, se restaura el proceso
interrumpido, se ejecuta el siguiente proceso que seleccione el planificador.
Clases de interrupciones:
De programa: Generada por alguna condición que se produce como resultado de la ejecución
de una instrucción, tales como un desbordamiento aritmético, una división por cero, un
intento de ejecutar una instrucción de máquina ilegal, y las referencias fuera del
espacio de la memoria permitido para un usuario.
Por temporizador: Generada por un temporizador del procesador. Permite al SO realizar
ciertas funciones de forma regular.
De E/S: Generada por un controlador de E/S para señalar la conclusión normal de una
operación o para indicar diversas condiciones de error.
Por fallo del hardware: Generada por un fallo, como un fallo en el suministro de energía
o un error de paridad en la memoria.
Modos de ejecución:
Modo usuario: permite la ejecución de instrucciones que no afectan a otros
procesos.
Modo kernel: permite la ejecución de todas las instrucciones.
Creación: Inicialización del sistema Llamadas al sistema para crear un proceso (fork +
exec, createProcess). Petición de usuario, lanzamiento de una nueva aplicación desde la
interfaz de usuario. Inicio de un proceso por lotes, sistema de cola de trabajos en
servidores.
Ejecutando: El proceso está actualmente en ejecución. Para este capítulo asumimos que el
computador tiene un único procesador, de forma que sólo un proceso puede estar en este
estado en un instante determinado.
Listo: Un proceso que se prepara para ejecutar cuando tenga oportunidad.
Bloqueado. Un proceso que no puede ejecutar hasta que se cumpla un evento determinado o
se complete una operación E/S.
Nuevo: Un proceso que se acaba de crear y que aún no ha sido admitido en el grupo de
procesos ejecutables por el SO. Típicamente, se trata de un nuevo proceso que no ha sido
cargado en memoria principal, aunque su bloque de control de proceso (BCP) si ha sido
creado.
Saliente: Un proceso que ha sido liberado del grupo de procesos ejecutables por el SO,
debido a que ha sido detenido o que ha sido abortado por alguna razón.
Colas de planificación: El SO usa una serie de colas para planificar los recursos. Estas
pueden ser: cola de trabajos (procesos de almacenamiento secundario esperando memoria
principal), cola de procesos listos (procesos en memoria, listos y esperando su
ejecución), cola de dispositivos (para cada dispositivo hay una cola de procesos
esperando utilizarlo).
Funciones del planificador de procesos: llevar control del estado de cada proceso,
decidir qué proceso usa el procesador y durante cuánto tiempo, para ello emplea un cierto
criterio en base al cual tomar las decisiones, asignar el procesador al proceso, quitar
el procesador al proceso.
Tipos de planificación:
Largo plazo: cuando se crea un proceso se puede decidir alguno de los criterios para su
planificación, como por ejemplo la prioridad o quantum (tiempo máximo que se permite a un
proceso el uso del procesador).
Corto plazo: cada vez que un proceso abandona la CPU, toma la decisión de qué proceso
planificar en función de la política de planificación establecida y del valor de los
parámetros planificados.
Mediano plazo: otras partes del sistema operativo pueden intervenir en la planificación
de forma indirecta (al sacar un proceso de memoria, etc hace que este no sea planificable
Tipos de planificación:
No apropiativo: el proceso en ejecución conserva el uso de la CPU mientras lo desee.
Ejemplo: FCFS, SJF y prioridades.
Apropiativo: el SO puede expulsar a un proceso de la CPU. Ejemplo: prioridades, SRTF y
Round Robin
FCFS (First come, first served): El primer proceso que entró en la cola de procesos
listos es el primero al que se le asigna CPU Se implementa con una cola FIFO (first in,
first out). Es un algoritmo del tipo ejecución hasta terminación. Sensible al orden de
llegada de los procesos.
SJF (Short Job First): Primero el trabajo corto. Se asocia a cada proceso la longitud de
su siguiente ráfaga de CPU. Si CPU disponible se le asigna al proceso de menor longitud
de ráfaga, si hay 2 con igual longitud de ráfaga se usa FCFS. Solamente se puede aplicar
si se conoce de antemano la duración de cada trabajo. Posibilidad de inanición si
continuamente llegan trabajos cortos, los trabajos largos nunca llegan a ejecutarse.
SRTF (Shortest Remaining Time First): Primero el menor tiempo restante. El planificador
siempre escoge el proceso que tiene el menor tiempo de ejecución restante esperado. El
planificador podría expulsar al proceso actual cuando llega un nuevo proceso con menor
ráfaga. El planificador debe tener una estimación del tiempo de proceso para realizar la
función seleccionada, y existe riesgo de inanición para los procesos más largos.
Prioridades: Se asigna una prioridad a cada proceso. El de menor prioridad se ejecuta en
CPU, si hay 2 procesos de igual prioridad se utiliza FCFS. Se asigna números a la
prioridad
Round Robin: Adecuado para implementar tiempo compartido. Corresponde a FCFS con
expropiación. Cada proceso tiene un quantum de tiempo máximo. Si cuando expira el quantum
de tiempo el proceso continúa en CPU, el planificador lo desaloja y lo ingresa al final
de la cola de listos. Un proceso puede abandonar la CPU libremente (si ráfaga de CPU
quantum) o después de interrupción (si ráfaga de CPU quantum).
Particionamiento de la memoria:
La operación principal de la gestión de la memoria es traer los procesos a la memoria
principal para que el procesador los pueda ejecutar. En casi todos los sistemas
multiprogramados modernos, esto implica el uso de un esquema sofisticado denominado
memoria virtual. Dicha memoria se basa en una o ambas de las siguientes técnicas básicas:
segmentación y paginación. Antes de ver éstas técnicas, veremos algunas más sencillas
que no utilizan memoria virtual, ellas son particionamiento, ahora obsoleto ý paginación
simple y segmentación simple.
Particionamiento fijo:
En la mayoría de los esquemas para la gestión de la memoria, se puede asumir que el
sistema operativo ocupa alguna porción fija de la memoria principal y que el resto de la
memoria principal está disponible para múltiples procesos. El esquema más simple para
gestionar la memoria disponible es repartirla en regiones con límites fijos.
Veremos dos alternativas. Una posibilidad consiste en hacer uso de particiones del mismo
tamaño. Cualquier proceso cuyo tamaño sea menor o igual que el tamaño de la partición
puede cargarse en cualquier partición disponible. Si todas las particiones están llenas y
no hay ningún proceso en estado Listo o Ejecutando, el S.O. puede mandar a swap a un
proceso de cualquiera de las particiones y cargar otro proceso. Existen dos dificultades
con el uso de las particiones fijas del mismo tamaño.
Existen dos dificultades con el uso de las particiones fijas del mismo tamaño. La primera
es que un programa podría ser demasiado grande para caber en una partición. En este caso,
el programador debe diseñar el programa con el uso de overlays , de forma que solo se
necesite una porción del programa en memoria principal en un momento dado. Cuando se
necesita un módulo que no está presente, el programa debe cargar dicho módulo en la
partición del programa, superponiendo a cualquier programa o datos que haya allí. Y la
segunda es que la utilización de la memoria principal es extremadamente ineficiente.
Cualquier programa ocupa una partición entera. Por ejemplo, podría haber un programa cuya
longitud sea 2Mb, como tratamos de particiones fijas ocuparía una partición de, en este
caso, 8Mb. Se puede observar que hay espacio interno malgastado, debido a que el bloque
de datos cargado es menor que la partición. Este fenómeno se conoce como fragmentación
interna.
Ambos problemas se pueden mejorar, aunque no resolver, utilizando particionamiento de
tamaños diferentes. Siguiendo este ejemplo, un programa de 16 Mb se puede acomodar sin
overlays Las particiones más pequeñas de 8 Mb permiten que los programas más pequeños se
puedan acomodar disminuyendo la fragmentación interna.
Con particiones del mismo tamaño , la ubicación de los procesos en memoria es trivial. En
cuanto haya una partición disponible, un proceso se carga en dicha partición. Si todas
las particiones se encuentran ocupadas por procesos que no están listos para ejecutar,
uno de ellos es llevado al disco para dejar espacio para un nuevo proceso. ¿Cuál de los
procesos es el que se lleva a disco? Es una decisión del planificador.
Con particionamiento de diferente tamaño, hay dos formas posibles de asignar los procesos
a las particiones. La forma más sencilla es asignar cada proceso a la partición más
pequeña en la cual cabe. En este caso, se necesita una cola de planificación para cada
partición que mantenga los procesos en disco destinados a dicha partición. La ventaja de
esta técnica es que los procesos siempre se asignan de tal forma que se minimiza la
fragmentación interna. Aunque esta técnica parece óptima desde el punto de vista de una
partición individual, no es óptima desde el punto de vista del sistema completo. Por
ejemplo, si se considera un caso en el que no hay procesos con un tamaño entre 12 y 16
Mb, la partición de 16 Mb quedará sin utilizarse. Una técnica óptima sería emplear una
única cola para todos los procesos. En el momento de cargar un proceso a la memoria
principal, se selecciona la partición más pequeña disponible para albergar dicho proceso.
Si todas las particiones están ocupadas, se debe llevar a cabo una decisión para enviar a
swap a algún proceso.
El uso de particiones de distinto tamaño proporciona un grado de flexibilidad frente a
las particiones fijas. Se podría decir que los esquemas de particiones fijas son
relativamente sencillos y requieren un soporte mínimo por parte del sistema operativo.
Sin embargo tiene una serie de desventajas, como que el número de particiones
especificadas en tiempo de generación del sistema limita el número de procesos activos
del sistema.; o como que debido a que los tamaños de las particiones son preestablecidos
en tiempo de generación del sistema, puede ser eficiente en entornos donde el requisito
de almacenamiento principal de los trabajos es conocido de antemano. Pero eso no ocurre
en la mayoría de los casos.
Particionamiento dinámico:
Las particiones son de longitud y número variable. Cuando un proceso llega a la memoria
principal, se le asigna exactamente tanta memoria como memoria como requiera y no más.
Un ejemplo: Inicialmente, la memoria principal está vacía excepto por el SO. Los primeros
3 procesos se cargan justo donde el SO finaliza y ocupando el espacio justo. Esto deja un
hueco al final demasiado pequeño para un cuarto proceso. En algún momento, ninguno de los
procesos que se encuentra en memoria está disponible. El SO lleva el proceso 2 al disco,
lo que deja suficiente espacio para cargar un nuevo proceso. Se carga en memoria
principal el proceso 4, debido a que es más pequeño que el proceso 2, se crea otro
pequeño hueco. Posteriormente se alcanza un punto donde ninguno de los procesos de la
memoria principal está listo, pero el proceso 2, en estado suspendido-listo, está
disponible. Porque no hay espacio suficiente para el proceso 2, el SO lleva a disco el
proceso 1. Se lleva a memoria principal el proceso 2. Como podemos ver, el método
comienza correctamente, pero finalmente lleva a una situación en la cual existen muchos
huecos pequeños en la memoria.
A medida que pasa el tiempo, la memoria se fragmenta cada vez más y la utilización de la
memoria decrementa. Este fenómeno se lo conoce como fragmentación externa, indicando que
la memoria que es externa a todas las particiones se fragmenta de forma incremental. Una
técnica para eliminar la fragmentación externa es la compactación: de vez en cuando, el
SO desplaza los procesos en memoria, de forma que se encuentren contiguos y, de este
modo, toda la memoria libre se encontrará unida en un bloque.
Debido a que la compactación de memoria consume una gran cantidad de tiempo, el diseñador
del S.O. debe ser inteligente a la hora de decidir cómo asignar la memoria a los
procesos. Existen cuatro algoritmos de colocación que pueden considerarse: mejor ajuste
(escoge el bloque más cercano en tamaño a la petición), primer ajuste (comienza a
analizar la memoria desde el principio y escoge el primer bloque disponible que sea
suficientemente grande), siguiente ajuste (comienza a analizar la memoria desde la última
colocación y elige el siguiente bloque disponible que sea suficientemente grande), y peor
ajuste (escoge el mayor bloque de memoria libre para un proceso).
Paginación simple:
Tanto las particiones de tamaño fijo como variable son ineficientes en el uso de la
memoria. Los primeros provocan fragmentación interna y los últimos fragmentación externa.
Supongamos, que la memoria principal se divide en porciones de tamaño fijo relativamente
pequeños, y que cada proceso también se divide en porciones pequeñas del mismo tamaño
fijo. Dichas porciones del procesos, conocidas como páginas , se les asigna porciones
disponibles de memoria, conocidas como marcos o marcos de página.
Supongamos un sistema con 15 marcos del mismo tamaño. El SO mantiene una lista de marcos
libres. El proceso A, almacenado en disco está formado por cuatro páginas. En el momento
de cargar este proceso, el SO encuentra cuatro marcos libres y carga las cuatro páginas
del proceso A. El proceso B formado por tres páginas y el proceso C formado por cuatro,
se cargan a continuación. En un instante el proceso B se suspende y deja la memoria
principal. El sistema necesita traer a un nuevo proceso, el proceso D que está formado
por 5 páginas. Si vemos este ejemplo no hay 5 marcos contiguos
disponibles. ¿Significa que el S.O. no pueda cargar el proceso D? No, porque se puede
utilizar el concepto de dirección lógica.
El SO mantiene una tabla de páginas por cada proceso. La tabla de páginas muestra la
ubicación del marco por cada página del proceso. Dentro del proceso, cada dirección
lógica está formada por un número de página y un desplazamiento dentro de la página. Con
paginación, la traducción de direcciones lógicas a físicas las realiza el hardware del
procesador. Por lo tanto, presentando una dirección lógica (n de página, desplazamiento),
el procesador utiliza la tabla de páginas para producir una dirección física (n de marco,
desplazamiento).
Vemos que la paginación simple, es similar al particionamiento fijo. Las diferencias son
que, con la paginación. Las particiones son bastante pequeñas y un programa podría ocupar
más de una partición y no necesariamente contiguas.
En resumen, con paginación simple la memoria principal se divide en muchos marcos
pequeños de igual tamaño; cada proceso se divide en páginas de igual tamaño; procesos
pequeños requieren menos páginas; procesos mayores requieren más páginas; cuando un
proceso se trae a la memoria, todas sus páginas se cargan en los marcos disponibles, y se
establece una tabla de páginas; y esta técnica resuelve muchos de los problemas
inherentes en el particionamiento.
Por ejemplo, sea un sistema de 12 bits con paginación simple, donde 4 bits están
destinados para cantidad de páginas y 8 bits para desplazamiento. Teniendo un proceso con
7 páginas y la siguiente tabla de páginas, calcular la dirección física a partir de la
relativa 321.
Esquema de paginación simple de 12 bits, de los cuales 4 indican la cantidad máxima de
páginas por proceso (2^4=16). Quedan 8 bits para el desplazamiento máximo dentro de la
página o marco (2^8=256), que corresponde al tamaño. Realizamos una división entera para
obtener el n de página y desplazamiento (321/256=1*256+65) donde el cociente indica el n
de página y el resto el desplazamiento. En este caso n de página es 1 y el desplazamiento
65. Con la tabla de página se obtiene el marco y luego se realiza la siguiente operación:
n de marco * tamaño de marco + desplazamiento (7*256+65=1857) (dirección física
relativa).
Otro ejemplo, en binario.
Segmentación simple:
Un programa de usuario se puede subdividir utilizando segmentación, en la cual el
programa y sus datos asociados se dividen en un número de segmentos. No se requiere que
todos los programas sean de la misma longitud, aunque hay una longitud máxima de
segmento. Al igual que la paginación, una dirección lógica está compuesta por dos partes,
en este caso, un n de segmento y un desplazamiento.
Debido al uso de segmentos de distintos tamaños, la segmentación es similar al
particionamiento dinámico. Aunque la diferencia comparada con éste último, es que con la
segmentación podría ocupar más de una partición, y éstas no necesitan ser contiguas.
Elimina la fragmentación interna, aunque sufre de externa, pero debido a que el proceso
se divide en varias piezas más pequeñas, la fragmentación externa es mucho menor.
También tenemos una tabla de segmentos, pero a diferencia que en paginación, la tabla
contiene una entrada más que corresponde a la longitud
o tamaño del segmento. Al realizar las traducciones de memoria lógica a física, hay que
tener en cuenta que el desplazamiento no supere la longitud máxima del segmento, ya que
en ese caso no sería una dirección válida.
Por ejemplo, sea un sistema de 14 bits con segmentación, donde 4 bits son para segmentos
y 10 bits para el tamaño del segmento y desplazamiento dentro de él. Teniendo un proceso
con 5 segmentos y la siguiente tabla de segmentos, calcular la dirección física a partir
de la relativa (3, 875).
Dirección lógica (3, 875) dónde el n de segmento en binario corresponde al 0011 (primeros
4 bits), revisamos la tabla dada anteriormente y obtenemos la longitud 1111101000
(últimos 10 bits) y la memoria base 00101101111010 (14 bits). Como para obtener la
dirección física es necesario sumar el desplazamiento con la memoria base, entonces
realizamos la suma 00101101111010+1101101011=00111011100101.
Otro ejemplo, en binario.
Memoria virtual:
Comparando la paginación simple y la segmentación simple, tenemos una distinción entre
particionamiento estático y dinámico, además tenemos los fundamentos iniciales de la
gestión de memoria. Las características tanto de la paginación como la segmentación que
son claves para este inicio son: todas las referencias a la memoria dentro de un proceso
se realiza a direcciones lógicas, que se traducen dinámicamente a direcciones físicas
durante la ejecución; y un proceso puede dividirse en varias porciones (páginas o
segmentos) y estas porciones no tiene que estar localizadas en memoria de forma continua
durante la ejecución.
Si las dos características anteriores se dan, entonces es necesario que todas las páginas
o todos los segmentos de un proceso se encuentren en la memoria principal durante la
ejecución. Si la porción (segmento o página) en la que se encuentra la siguiente
instrucción a buscar está y si la porción donde se encuentra la siguiente dirección de
datos que se va a acceder también está, entonces al menos la siguiente instrucción se
podrá ejecutar.
Supongamos que se tiene que traer un nuevo proceso de memoria. El SO comienza trayendo
únicamente una o dos porciones. Que incluye la porción inicial del programa y la porción
inicial de datos sobre la cual acceden las primeras instrucciones. Esta parte del proceso
que se encuentra en memoria principal se denomina conjunto residente del proceso. Cuando
el proceso se está ejecutando, las cosas ocurren de forma suave mientras que todas las
referencias a la memoria se encuentren dentro del conjunto residente. Usando una tabla de
páginas o segmentos, el procesador siempre es capaz de determinar si esto es así.
Si el procesador encuentra una dirección lógica que no se encuentra en la memoria
principal, generará una interrupción indicando un fallo de acceso a la memoria. El SO
coloca al proceso interrumpido en estado bloqueado y toma el control. Para que la
ejecución del proceso pueda reanudarse más adelante, el SO necesita traer a memoria
principal la memoria del proceso que contiene la dirección lógica que ha causado el fallo
de acceso. Para eso, el SO realiza una petición de E/S al disco, luego el SO puede
activar otro proceso para que se ejecute mientras el disco realiza la operación de
lectura. Una vez que la porción solicitada se ha traído a la memoria principal, una nueva
interrupción de E/S se lanza, dando el control al SO que coloca al proceso afectado en
estado listo.
Aunque se puede cuestionar la eficiencia de realizar esta tarea, ya que el proceso
necesita ser interrumpido, veremos dos implicaciones que justifican el uso de este
sistema: pueden mantenerse un mayor número de procesos en memoria principal; y un proceso
puede ser mayor que toda la memoria principal. Debido a que un proceso se ejecuta sólo en
memoria principal, esta memoria se denomina memoria real. El programador o usuario
perciben una memoria potencialmente mucho más grande (la cual se encuentra en disco).
Esta última se denomina memoria virtual.
El término memoria virtual se asocia habitualmente con sistemas que emplean paginación, a
pesar de que la memoria virtual basada en segmentación también se utiliza. Cuando vimos
paginación simple, indicamos que cada proceso dispone de su propia tabla de páginas, y
que todas las páginas se encuentran localizadas en memoria principal. Cada entrada en la
tabla consiste en un número de marco de la correspondiente página. Para la memoria
virtual basada en paginación, también se necesita una tabla de páginas, aunque es un poco
más compleja.
Debido a que sólo algunas páginas se encuentran en memoria principal, se necesita que
cada entrada en la tabla de páginas indique si la correspondiente página está presente
(P) en memoria principal. Además, la entrada de la tabla de páginas incluye un bit de
modificado (M), que indica si los contenidos de la correspondiente página han sido
alterados desde que la página se cargó por última vez en la memoria principal.