Transcript address

Gestión de la memoria
El subsistema de gestión de memoria es una de las
partes más importantes del sistema operativo.
Desde los inicios de la informática, ha existido la
necesidad de utilizar más memoria de la que se dispone
físicamente en el sistema.
Entre las diversas estrategias desarrolladas para
resolver este problema, la de mayor éxito ha sido la
memoria virtual.
2
Gestión de la memoria
La memoria virtual hace que parezca que el
sistema dispone de más memoria de la que realmente
tiene, compartiéndola entre los distintos procesos
conforme la necesitan.
El subsistema de gestión de memoria ofrece:
• Espacio de direcciones grande: El sistema operativo
hace que el sistema parezca tener una gran cantidad de
memoria.
• Protección: Cada proceso del sistema tiene su propio
espacio de direcciones virtuales.
3
Gestión de la memoria
• Proyección en Memoria (Memory Mapping): La
proyección de memoria se utiliza para asignar un fichero
sobre el espacio de direcciones de un proceso.
• Asignación Equitativa de Memoria Física: El subsistema
de gestión de memoria permite que cada proceso del
sistema se ejecute con una cantidad de memoria justa de
toda la memoria física disponible
• Memoria virtual compartida: Hay veces que es
necesario que varios procesos compartan memoria. El
subsistema de memoria realiza esta tarea con gran
facilidad.
4
Memoria virtual
Un proceso no utiliza todo el código y los datos
contenidos en su memoria virtual dentro de un periodo de
tiempo determinado.
No sería deseable cargar todo su código y datos en
la memoria física donde podría terminar sin usarse.
Para solventar este problema, Linux usa una
técnica llamada Paginación Por Demanda que sólo copia
la memoria virtual de un proceso en la memoria física del
sistema cuando el proceso trata de utilizarla.
5
Memoria virtual
Linux necesita gestionar todas estas áreas de
memoria virtual.
El contenido de la memoria virtual de cada
proceso se describe mediante una estructura mm_struct
a la cual se apunta desde la estructura task_struct del
proceso.
La estructura mm_struct contiene punteros a
una lista de estructuras vm_area_struct, cada una de
las cuales representa un área de memoria virtual dentro
del proceso.
6
Memoria virtual
7
Memoria virtual
Cuando un proceso reserva memoria virtual, en
realidad Linux no reserva memoria física para el proceso.
Lo que hace es describir la memoria virtual
creando una nueva estructura vm_area_struct. Ésta
se une a la lista de memoria virtual del proceso.
Cuando el proceso intenta acceder a una dirección
virtual dentro de la nueva región de memoria, el sistema
emitirá un fallo de página (al no encontrar la entrada en la
tabla de páginas del proceso).
La tarea de Linux es cargar la página deseada a
partir de la información contenida en dicha región.
8
Modelo abstracto de memoria virtual
Cada una de estas páginas tiene asociado un único
número; el número de marco de página (PFN: Page Frame
Number).
En este modelo de paginación (suponiendo una
arquitectura de 32 bits como x86), una dirección virtual
está compuesta por dos partes: un número de página
virtual (20 bits) y un desplazamiento (12 bits).
Cada vez que el procesador encuentra una
dirección virtual ha de extraer el desplazamiento y el
número de marco de página.
9
Modelo abstracto de memoria virtual
Una tabla de páginas teórica contiene la siguiente
información:
• Flag de Válido: indica si la entrada de la tabla de
páginas es válida o no.
• El número de marco de página físico: indica la página
física a la que está asociada.
•Información de control de acceso: Describe cómo se
puede utilizar la página. ¿Se puede leer? ¿Contiene código
ejecutable?
10
Modelo abstracto de memoria virtual
11
Intercambio (swapping)
Si un proceso necesita cargar una página en
memoria física y no hay ninguna página física libre, el
sistema operativo tiene que crear espacio para la nueva
página eliminando alguna otra página de memoria física.
Si la página a descartar no se ha modificado, no es
necesario guardarla. Simplemente, se puede desechar.
Si ha sido modificada, su contenido debe
preservarse para accesos posteriores. Esta página ha de
guardarse en un fichero de swap.
12
Memoria compartida
Con los mecanismos de memoria virtual se puede
conseguir fácilmente que varios procesos compartan
memoria.
Todos los accesos a memoria se realizan a través
de las tablas de páginas y cada proceso tiene su propia
tabla de páginas.
Para que 2 procesos compartan una misma página
de memoria física, simplemente, debe aparecer el número
de marco de esa página física en sus tablas de páginas.
13
Control de acceso
Las entradas de la tabla de páginas también
contienen información relativa al control de acceso.
Además de la información almacenada en las
entradas de la tabla de páginas, también existe
información de acceso en los descriptores de las regiones
de memoria virtual (VMA).
Se puede utilizar fácilmente la información de
control de acceso para comprobar que el proceso no está
accediendo a memoria de forma inapropiada.
14
Control de acceso
En un procesador x86 una entrada de la tabla de
páginas contiene, además de la base de la dirección de una
página, algunos flags que indican entre otras cosas que
operaciones se pueden aplicar sobre la página.
En una arquitectura de 32 bits con tamaño de
página fijo de 4Kbytes (como en x86) sólo se necesitan
los 20 bits de mayor peso de los 32 bits que componen
una dirección para identificar la base de una página.
Por tanto los 12 bits restantes se aprovechan para
almacenar información importante acerca de la página.
15
Control de acceso
A continuación se muestran los flags más
importantes que se almacenan en la tabla de páginas para
cada entrada:
• _PAGE_PRESENT: Activo si la página está físicamente
en memoria.
• _PAGE_RW: 0 si la página es sólo de lectura y 1 si es de
lectura-escritura (no existen páginas sólo de escritura).
•_PAGE_USER: Activo si la página pertenece al espacio
de usuario e inactivo si pertenece al núcleo.
16
Control de acceso
• _PAGE_WT: Indica la política de caché de la página.
- writethrought (0): Actualiza inmediatamente los
datos escritos en la caché en memoria principal.
- writeback (1): Sólo se actualizan los cambios
escritos en la memoria caché en la memoria principal
cuando la línea de caché va a ser sustituida.
• _PAGE_ACCESSED: Activo si la página ha sido
accedida recientemente.
• _PAGE_DIRTY: Activo si la página ha sido modificada.
17
Cachés
Linux emplea varias cachés para la gestión de la
memoria. Estas cachés son las siguientes:
• Buffer Cache: Contiene buffers de datos que son
utilizados por los manejadores de dispositivos de bloques.
Estos buffers son de tamaño fijo y contienen información
que ha sido transferida desde un dispositivo de bloques.
• Cache de Páginas: Se utiliza para acelerar el acceso a
imágenes de ejecutables y datos en disco. Se utiliza para
guardar el contenido lógico de un fichero.
18
Cachés
• Cache de Intercambio: Es una caché que contiene
páginas del fichero de intercambio.
• Caches Hardware: Son una o varias cachés
normalmente implementadas en el propio procesador (L1
y L2) y una caché con entradas de tablas de páginas
llamada TLB (Translation Lookaside Buffer).
El procesador guarda en la TLB las entradas de tablas
de páginas accedidas más recientemente, evitando leer
siempre la tabla de páginas de memoria cada vez que se
realiza un acceso a memoria (para datos o instrucciones).
19
Tabla de páginas en Linux
Linux supone 3 niveles de tablas de páginas: las
entradas de los 2 primeros niveles apuntan a tablas de
páginas y las del último a páginas de memoria (cada nivel
contiene el PFN de las páginas del siguiente nivel).
Para traducir una dirección virtual a una física, el
procesador tiene que tomar el contenido de cada uno de
los campos de las tablas de páginas, convertirlos en
desplazamientos de la página física que contiene la tabla
de páginas y leer el número de marco de página del
siguiente nivel de la tabla de páginas.
20
Tabla de páginas en Linux
Esta operación se repite tres veces hasta que se
encuentra el número de la página física que contiene la
dirección virtual.
Ahora el último campo de la dirección virtual se
utiliza para encontrar el dato dentro de la página
(desplazamiento).
A continuación se muestra una figura que ilustra lo
comentado.
21
Tabla de páginas en Linux
22
Paginación por demanda
Cuando un proceso accede a una dirección virtual
que no tiene una entrada válida en la tabla de páginas, el
procesador informará sobre el fallo de página a Linux.
El fallo de página indica la dirección virtual donde
se produjo el fallo de página y el tipo de acceso que lo
causó.
Linux debe encontrar la vm_area_struct que
representa el área de memoria donde sucedió el fallo de
página.
23
Paginación por demanda
Si no hay ninguna estructura vm_area_struct
para la dirección virtual que produjo el fallo de página
entonces el proceso ha accedido a una dirección de
memoria virtual ilegal.
Linux enviará al proceso la señal SIGSEGV, y si
el proceso no ha instalado un manejador de señales para
esta señal entonces morirá.
Lo siguiente que hace Linux es comprobar el tipo
de fallo de página producido y los tipos de acceso
permitidos para el área de memoria virtual en cuestión.
24
Paginación por demanda
Si el proceso ha intentado acceder a la memoria de
forma ilegal también se le señalará un error de memoria.
Una vez que Linux ha determinado que el fallo de
página es legal, tiene que tratarlo.
Linux ha de diferenciar entre páginas que están en
un fichero de intercambio y aquellas que son parte de una
imagen ejecutable localizadas en algún lugar del disco
(nunca han estado en memoria física).
Esto lo hace utilizando la entrada en la tabla de
páginas de la página que causó el fallo.
25
Paginación por demanda
Si la entrada de la tabla de páginas es inválida
pero tiene información, el fallo de página se debe a que la
página está en esos momentos en un fichero de
intercambio.
Si no contiene información habrá que buscar en el
mapa de memoria de la región de memoria virtual a la que
pertenece para extraer la dirección en memoria
secundaria.
Cuando la página necesitada es cargada en
memoria física, las tablas de páginas del proceso y la TLB
son actualizadas.
26
Intercambiando y liberando páginas
Cuando queda poca memoria física, el subsistema
de gestión de memoria de Linux tiene que intentar liberar
páginas físicas.
Este trabajo es realizado por el demonio de
intercambio del núcleo (kswapd).
Si hay suficientes páginas libres, el demonio de
intercambio se vuelve a dormir hasta que el temporizador
vuelva a expirar otra vez, en caso contrario el demonio
intentará reducir el número de páginas ocupadas.
27
Intercambiando y liberando páginas
El demonio puede reducir el número de páginas
físicas ocupadas en memoria de tres formas distintas.
Estas son las siguientes:
• Reduciendo el tamaño de la caché de páginas y el buffer
cache: eliminar páginas de ficheros proyectados en
memoria y buffers de dispositivos de bloques.
• Enviando a disco páginas compartidas: Habrá que
modificar todas las entradas de las tablas de páginas de
todos los procesos afectados.
28
Intercambiando y liberando páginas
• Enviando a disco o descartando páginas: Se eligen las
mejores candidatas a enviar a disco. Esto es, liberando las
páginas físicas en base a su antigüedad (campo age)
Las páginas se envian a disco sólo si los datos que
contienen no se pueden recuperar de otra forma (“dirty”).
Las páginas bloqueadas en memoria no se pueden
intercambiar ni descartar.
La entrada en la tabla de páginas se establece a
inválida y se inserta información acerca de donde está la
página en el fichero de intercambio.
29
Copy-on-Write
Cuando se invoca a la primitiva fork, Linux no
duplica las páginas de memoria que son necesarias para el
nuevo proceso.
Lo que se hace es apuntar las entradas de la tabla
de páginas del nuevo proceso a las páginas del proceso
padre.
Cuando alguna de las páginas es modificada por
alguno de los procesos, entonces el núcleo pasa a realizar
la duplicación de dicha página.
30
Copy-on-Write
La forma de llevar a cabo este proceso consiste en
establecer los permisos de estas páginas a sólo-lectura
pero sabiendo que dichas páginas se pueden modificar
(información almacenada en las regiones de memoria
virtual del proceso: vma modificable).
Cuando ocurre una violación de acceso a estas
páginas es cuando se realiza la duplicación propiamente
dicha.
31
Estructuras de datos
El fichero <asm/page.h> define los tipos utilizados
para representar las entradas en las tablas de páginas.
Estas son las siguientes:
Tipo
typedef unsigned long pte_t;
typedef unsigned long pmd_t;
typedef unsigned long pgd_t;
typedef unsigned long pgprot_t;
Descripción
Tipo de una entrada de la tabla de páginas
Tipo de una entrada de la tabla de intermedia
Tipo de una entrada de la tabla de global
Tipo utilizado para expresar las protecciones de memoria
32
Estructuras de datos
Además existen varias macros. Estas son las siguientes:
Macro
#define pte_val (x) (x)
#define __pte
(x) (x)
#define pmd_val (x) (x)
#define __pmd (x) (x)
#define pgd_val (x) (x)
#define __pgd
(x) (x)
#define pgprot_val (x) (x)
#define __pgprot (x) (x)
Descripción
Devuelve el contenido de una entrada de la tabla de páginas
Devuelve el contenido de una entrada de la tabla intermedia
Devuelve el contenido de una entrada de la tabla global
Devuelve la protección de memoria
33
Estructuras de datos
El fichero <linux/mm.h> define el formato de la
estructura mem_map_t (struct page). Esta estructura se
utiliza para mantener la información acerca de cada
página de memoria física. Los campos relevantes de está
estructura son los siguientes:
Tipo
struct page *
struct page *
struct inode *
unsigned long
struct page *
atomic_t
unsigned
unsigned
unsigned
struct buffer_head*
Campo
next
prev
inode
offset
next_hash
count
flags
dirty
age
buffers
Descripción
Puntero a la página libre siguiente.
Puntero a la página libre anterior.
Descriptor del i-nodo correspondiente al contenido de la página.
Desplazamiento de la página dentro de i-nodo.
Dirección del descriptor de la página siguiente en una lista hash.
Número de referencias a la página.
Estado de la página.
Booleano que indica si el contenido de la página se ha modificado.
Contador utilizado para seleccionar las páginas a descartar de la memoria.
Dirección del primer descriptor de memoria intermedia cuyo contenido se
sitúa en la página.
34
Estructuras de datos
Las constantes siguientes, declaradas en el archivo
<linux/mm.h> definen el estado de una página. Estas son
las siguientes:
Constante
PG_locked
PG_error
PG_referenced
PG_update
PG_free_after
PG_decr_after
PG_DMA
PG_reserved
Descripción
La página está bloqueada en memoria.
Se ha producido un error en la carga de la página en memoria.
La página ha sido accedida.
El contenido de la página está actualizado.
La página debe liberarse tras el fin de la entrada/salida en curso.
El número de referencias a la página debe decrementarse tras el fin de la e/s en curso.
La página es utilizable para una transferencia DMA (en x86, está contenida en los
primeros 16MB)
La página está reservada para un uso futuro, no es posible acceder a ella.
35
Estructuras de datos
La estructura free_area_struct, declarada en el
fichero fuente <mm/page_alloc.c> define el formato de
los descriptores de la lista de grupos.
Tipo
Campo
struct page * next
struct page * prev
struct int *
map
Descripción
Puntero a la primera página contenida en el primer
grupo de páginas.
No utilizado.
Puntero a una tabla de bits: cada bit indica si el grupo
correspondiente está asignado o disponible.
36
Estructuras de datos
La estructura free_area_struct, declarada en el
fichero fuente <mm/page_alloc.c> define el formato de
los descriptores de la lista de grupos.
Tipo
Campo
struct page * next
struct page * prev
struct int *
map
Descripción
Puntero a la primera página contenida en el primer
grupo de páginas.
No utilizado.
Puntero a una tabla de bits: cada bit indica si el grupo
correspondiente está asignado o disponible.
37
Función do_page_fault (<arch/i386/mm/fault.c>)
Esta función se llama cuando ocurre un fallo de
página.
6980 asmlinkage void do_page_fault(struct pt_regs *regs,
6981 unsigned long error_code)
6982 {
6983
struct task_struct *tsk;
6984
struct mm_struct *mm;
6985
struct vm_area_struct * vma;
6986
unsigned long address;
6987
unsigned long page;
6988
unsigned long fixup;
6989
int write; /* Nos indica si se fue a escribir */
/* se obtiene la dirección que provocó el fallo de página
Esto sólo es válido en x86, que utiliza el registro cr2 */
6992
__asm__("movl %%cr2,%0":"=r" (address));
38
Función do_page_fault
/* Tomamos la información de gestión de la memoria del
proceso actual desde la tabla de procesos */
6994
tsk = current;
6995
mm = tsk->mm;
6996
/* Si fue una interrupción o no fue en el contexto de
usuario (fue el núcleo) no se tomará en cuenta el fallo */
6999
if (in_interrupt() || mm == &init_mm)
7000
goto no_context;
7001
/* Se extrae el segmento de memoria virtual que contiene
la dirección o puede contenerla: su final > address */
7004
vma = find_vma(mm, address);
39
Función do_page_fault
/* Si no se ha encontrado ningún segmento --> bad_area */
7005
if (!vma)
7006
goto bad_area;
/* Si la dirección pertenece al rango de direcciones del
proceso  good_area */
7007
if (vma->vm_start <= address)
7008
goto good_area;
/* Si la dirección no está contenida en la vma, pero esta
crece hacia abajo (p.e. la pila), la dirección se toma
como válida */
7009
if (!(vma->vm_flags & VM_GROWSDOWN))
7010
goto bad_area;
40
Función do_page_fault
/* Si el fallo de página es en modo usuario se comprueba
que el proceso no se ha salido de la pila. */
7011
if (error_code & 4) {
/*El bit 2 de error_code indica si se está en modo usuario
+32: existen instrucciones como “pusha” que realizan un
post-incremento más tarde (32 bits por dirección) */
7016
if (address + 32 < regs->esp)
7017
goto bad_area;
7018
}
/* Intenta expandir el segmento para contener la nueva
dirección. Si falla  bad_area */
7019
if (expand_stack(vma, address))
7020
goto bad_area;
41
Función do_page_fault
/* En este punto se tiene una vm_area buena para el acceso
realizado. Por tanto, podemos tratar el fallo de
página */
7023 good_area:
7024
write = 0;
/* Bit 0 de error_code: 0 la página no estaba en memoria
1: la página estaba en memoria pero se violaron
sus protecciones de acceso
Bit 1 de error_code: 0 para una lectura y 1 para una
escritura */
7025
switch (error_code & 3) {
7026
default: /* case 3 */
/* La página está en memoria y se intentó escribir sobre
ella [11]. Al entrar en este este caso, también se entra
por el caso 2. */
42
Función do_page_fault
/* No se hace nada salvo imprimir un error si el núcleo se
compiló para depurar y este fue quien provocó el fallo. */
7027
#ifdef TEST_VERIFY_AREA
7028
if (regs->cs == KERNEL_CS)
7029
printk("WP fault at %08lx\n", regs->eip);
7031
#endif
7031
/* fall through */
7032
case 2: /* write, not present */
/* Si se intentó escribir y el segmento es de sólo-lectura
[10] --> bad_area */
7033
if (!(vma->vm_flags & VM_WRITE))
7034
goto bad_area;
/* Si el segmento es de lectura-escritura se continua y
write = 1<-- Se intentó escribir */
7035
write++;
7036
break; /* De aquí no pasan los case 2 y 3*/
43
Función do_page_fault
/* Se está intentando leer un segmento sin permisos de
lectura [01] (violación de las protecciones de acceso). */
7037
case 1: /* read, present */
7038
goto bad_area;
/* Se va a leer una página que no está en memoria. [00]*/
7039
case 0: /* read, not present */
Si no hay permisos de lectura o de ejecución --> bad_area,
en otro caso, fallo de página */
7040
if(!(vma->vm_flags &
(VM_READ|VM_EXEC) ))
7041
goto bad_area;
7042
}
/* Se intenta cargar la página o hacer el COW. */
7047
if (!handle_mm_fault(tsk, vma, address, write))
7048
goto do_sigbus;
7057
return;
44
Función do_page_fault
/* Fallo de protección por un intento de escribir una
dirección protegida o por intento de acceder a una
dirección que no pertenece al proceso */
7062 bad_area:
/* Si el fallo lo provocó un proceso de usuario, se le
envía una señal SIGSEGV (“segmentation fault”) */
7066
if (error_code & 4) {
7067
tsk->tss.cr2 = address;
7068
tsk->tss.error_code = error_code;
7069
tsk->tss.trap_no = 14;
7070
force_sig(SIGSEGV, tsk);
7071
return;
7072
}
45
Función do_page_fault
/* Se llega a este punto si es un problema del núcleo:
Aún no se ha dado el caso (sin contar el de la NOTA)*/
7086 no_context:
/* ¿Está
preparado
el núcleo
para manipular esta
excepción? */
7088
if((fixup=search_exception_table(regs->eip))!=0) {
/* El núcleo puede tratar la excepción y se carga el
contador de programa con la nueva dirección */
7089
regs->eip = fixup;
7090
return;
7091
}
7092
/* NOTA: Hay un caso especial: en el arranque, el núcleo
provoca un error para comprobar las protecciones de
escritura de la MMU y lo trata adecuadamente en esta zona
de la función, sin necesidad de capturar la excepción. */
46
Función do_page_fault
/* Si no se trata del test de la MMU */
/* El núcleo no sabe como tratar la excepción y muestra
posibles errores */
7109
if (address < PAGE_SIZE)
7110
printk(KERN_ALERT "Unable to handle kernel"
7111
"NULL pointer dereference");
7112
else
7113
printk(KERN_ALERT "Unable to handle kernel"
7114
"paging request");
/* Se provoca la detención del sistema */
7128
die(“Ops”, regs, error_code);
7129
do_exit(SIGKILL);
47
Función do_page_fault
/* Si no se pudo cargar la página se envía una señal
SIGBUS al proceso. */
7134 do_sigbus:
/* Se actualiza el estado de la tarea y se le envía una
señal SIGBUS al proceso */
7139
tsk->tss.cr2 = address;
7140
tsk->tss.error_code = error_code;
7141
tsk->tss.trap_no = 14;
7142
force_sig(SIGBUS, tsk);
/* Si la tarea era el núcleo, se vuelve a atrás para
intentar tratar la excepción o detener la ejecución del
sistema. (bit 2 de error_code==0 en modo núcleo) */
7145
if (!(error_code & 4))
7146
goto no_context;
7147 }
48
Función handle_mm_fault (<mm/memory.c>)
Esta función obtiene la dirección de la página a la
que se ha accedido y realiza la llamada para cargar la
página en memoria física o para hacer una COW.
32725 int handle_mm_fault(struct task_struct *tsk,
32726
struct vm_area_struct * vma,
32727
unsigned long address, int write_access){
32729
pgd_t *pgd;
32730
pmd_t *pmd;
32731
/* Se obtiene un puntero a la entrada correspondiente a
address en la tab. de pág. global para el proceso */
32732
pgd = pgd_offset(vma->vm_mm, address);
/* Se obtiene un puntero a la entrada correspondiente a
address en la t. de pág. Intermedia (en x86 coinciden). */
32733
pmd = pmd_alloc(pgd, address);
49
Función handle_mm_fault
/* Si la entrada en la tabla intermedia (un puntero a una
tabla de páginas) no es nula */
32734
if (pmd) {
/* Se trata de localizar la página que contiene a address
en la tabla de páginas a partir de la tabla intermedia */
32735
pte_t * pte = pte_alloc(pmd, address);
32736
if (pte) {
/* Si se localiza la página, se intenta cargar la página
si no está en memoria y si lo está se establecen los flags
adecuados realizando una COW en caso necesario */
32737
if (handle_pte_fault(tsk, vma, address,
32738
write_access, pte)) {
32740
return 1;
32741
}
32742
}
32743
}
32744
return 0; /* Se retorna 0 en caso de error */
32745 }
50
Macro pgd_offset (<include/asm-i386/ pgtable.h>)
Esta macro obtiene un puntero a la entrada de la
tabla de páginas global (o directorio) en cuya
correspondiente tabla de páginas intermedia se encuentra
la tabla de páginas donde se encuentra address.
Se obtiene a partir de su base y desplazamiento
dentro de la misma (primeros bits de address).
/* Para ello suma la base de la tabla de páginas global a
los bits (22) de mayor peso de address (que contienen el
desplazamiento dentro de dicha tabla) */
11284 #define pgd_offset(mm, address) \
11285
((mm)->pgd + ((address) >> PGDIR_SHIFT))
51
Función pmd_alloc (<include/asm-i386/ pgtable.h>)
Esta función obtiene un puntero a la entrada de la
tabla intermedia en cuya correspondiente tabla de páginas
se encuentra address.
/* En x86 no existe tabla intermedia (PMD) por eso se
devuelve la dirección calculada para la tabla global PGD.
Linux supone que la PMD es de tamaño 1 y que por tanto,
está incluida en la PGD. Con esto se consigue abstraer el
código de Linux de una arquitectura específica */
11454 extern inline pmd_t * pmd_alloc(pgd_t * pgd,
11455
unsigned long address)
11456 {
11457
return (pmd_t *) pgd;
11458 }
52
Función pte_alloc (<include/asm-i386/ pgtable.h>)
Esta función obtiene un puntero a la entrada de la
tabla de páginas asociada a address. Si no existe la tabla
de páginas, la crea.
11422 extern inline pte_t * pte_alloc(pmd_t * pmd,
11423
unsigned long address)
11424 {
/* Se extrae de address el desplazamiento dentro de la
tabla de páginas: desde el bit 22 al 12 */
11425
address = (address >> (PAGE_SHIFT-2)) &
11426
4*(PTRS_PER_PTE - 1);
53
Función pte_alloc
/* Si el valor de la entrada es 0 entonces no existe tabla
de páginas. Hay que crear una nueva tabla de páginas -->
getnew */
11428
if (pmd_none(*pmd))
11429
goto getnew;
/* Si la entrada de la tabla de páginas intermedia es
incorrecta, se trata un error */
11430
if (pmd_bad(*pmd))
11431
goto fix;
/* Si la página apuntada por la entrada de la tabla de
páginas intermedia es válida y está en memoria, se
devuelve su dirección:
(Dir. base de la PMD) + (desplazamiento dentro de ella) */
11432
return (pte_t *) (pmd_page(*pmd) + address);
54
Función pte_alloc
/* Se obtiene una página para tabla de páginas. */
11433 getnew:
/* Al buscar una página libre se mira primero en la caché
con get_pte_fast. El núcleo tiene una caché páginas de
tablas
de
páginas
recientemente
liberadas
llamada
pte_quitcklist. */
11435
unsigned long page =(unsigned long)get_pte_fast();
11436
/* Si no se encuentra ninguna página libre en la caché, la
carga mediante la función get_pte_slow. */
11437
if (!page)
11438
return get_pte_slow(pmd, address);
55
Función pte_alloc
/* Si get_pte_fast encontró una página libre se inserta en
la entrada de la tabla intermedia. */
11439
pmd_val(*pmd) = _PAGE_TABLE + __pa(page);
/* Se devuelve la dirección de la entrada de la tabla de
páginas (dirección de la página + desplazamiento) */
11440
return (pte_t *) (page + address);
11441
}
/* Se ha detectado un error, se imprime un mensaje */
11442 fix:
11443
__bad_pte(pmd);
11444
return NULL;
11445 }
56
Función handle_pte_fault (<mm/memory.c>)
Esta función lee la página solicitada o realiza la
llamada para realizar una COW.
32690 static inline int handle_pte_fault(
32691
struct task_struct *tsk,
32692
struct vm_area_struct * vma, unsigned long
address, int write_access, pte_t * pte)
32694 {
32695
pte_t entry;
32698
entry = *pte; /* entrada de la tabla de páginas */
57
Función handle_pte_fault
/* Si la página no está en memoria */
32700
if (!pte_present(entry)) {
/* Si además la página nunca ha estado en memoria física
(no tiene asignada una entrada en una tabla de páginas)*/
32701
if (pte_none(entry))
/* Se realiza un mapeo de la página leyéndola e insertando
su entrada correspondiente en la tabla de páginas */
32702
return do_no_page(tsk, vma,
32703
address, write_access, pte);
/* La página no está en memoria, pero ya había sido
mapeada, por tanto, se lee del fichero de swap */
32704
return do_swap_page(tsk, vma, address, pte,
32705
entry, write_access);
32706
}
58
Función handle_pte_fault
/* Si la página está en memoria, se concluye que hubo una
violación de las protecciones de la página. */
/* Se marca la página como accedida en entry y en pte */
32708
entry = pte_mkyoung(entry);
32709
set_pte(pte, entry);
/* Se actualiza la tlb */
32710
flush_tlb_page(vma, address);
59
Función handle_pte_fault
/* Si se quiere escribir */
32711
if (write_access) {
/* Si se intenta escribir en una página protegida contra
escritura hay que hacer una COW ya que el segmento al que
pertenecía no estaba protegido contra escritura */
32712
if (!pte_write(entry))
/* Si la página es de sólo-lectura hay que hacer una COW*/
32713
return do_wp_page(tsk, vma,
address, pte);
/* Se está intentando escribir en una página que lo
permite, y se marca como escrita (bit dirty=1) */
32715
entry = pte_mkdirty(entry);
32716
set_pte(pte, entry);
/* Se actualiza la TLB */
32717
flush_tlb_page(vma, address);
32718
}
32720
return 1;
32721 }
60
Función do_wp_page (<mm/memory.c>)
Esta función es la encargada de realizar la copia en
escritura (Copia-On-Write)
32401 static int do_wp_page(struct task_struct * tsk,
32402
struct vm_area_struct * vma, unsigned long
address, pte_t *page_table)
32404 {
32405
pte_t pte;
32406
unsigned long old_page, new_page;
32407
struct page * page_map;
32408 /*entrada en la tabla de páginas*/
32409
pte = *page_table;
/* Se obtiene una nueva página de memoria libre, que se
utilizará para hacer la COW */
32410
new_page = __get_free_page(GFP_USER);
61
Función do_wp_page
/* Esta función no es atómica, por lo tanto, se comprueba
que tras solicitar la nueva página, esta no se ha copiado
ya. Esto se realiza con las siguientes preguntas: */
/* ¿Ya está la entrada correspondiente a la página en la
tabla de páginas? La nueva entrada en la tabla de páginas
y la antigua ya no deberían coincidir */
32413
if (pte_val(*page_table) != pte_val(pte))
32414
goto end_wp_page;
/* ¿La página no está en memoria? Antes estaba -> Es una
nueva página que puede ser que no se haya duplicado */
32415
if (!pte_present(pte))
32416
goto end_wp_page;
62
Función do_wp_page
/* ¿La página ya tiene el permiso de escritura activado?
Antes no lo tenía y nosotros no lo hemos hecho */
32417
if (pte_write(pte))
32418
goto end_wp_page;
/* Se obtiene la dirección de la página física en memoria
pte=entrada en la tabla de páginas,
old_page=dirección de la página en memoria*/
32419
old_page = pte_page(pte);
/* Si se ha mapeado la página a un número mayor que el
número de páginas físicas, se ha producido un error */
32420
if (MAP_NR(old_page) >= max_mapnr)
32421
goto bad_wp_page;
63
Función do_wp_page
/* Se incrementa el número de fallos de página “minor”,
aquellos que se resuelven sin acceder a disco. Una COW no
necesita acceder a disco */
32422
tsk->min_flt++;
/* Se obtiene el descriptor de la página
(estructura que describe la página) */
32423
page_map = mem_map + MAP_NR(old_page);
en
memoria
/* La copia se puede evitar si:
- Nosotros somos el único usuario (count=1)
- Hay otro usuario, pero éste es el “swap cache” */
64
Función do_wp_page
32432
32433
switch (&page_map->count) {
case 2: /* hay 2 usuarios */
/* Si no está en la caché de swap entonces hay que hacer
la COW ya que el otro usuario no es la caché */
32434
if (!PageSwapCache(page_map))
32435
break;
/* Si hay más de un usuario haciendo uso de la copia en la
caché de swap entonces hay que hacer la COW (+ de 1
usuario */
32436
if (swap_count(page_map->offset) != 1)
32437
break;
/* Si definitivamente el otro usuario era la caché pues se
borra de esta porque la página va a ser modificada */
32438
delete_from_swap_cache(page_map);
32439
/* FallThrough */
65
Función do_wp_page
32440
case 1: /* Si sólo había un usuario real (también
puede venir del case 2 entonces... */
/* Se marca la página como “dirty” y “writable” */
32445
set_pte(page_table,
pte_mkdirty(pte_mkwrite(pte)));
/* Se actualiza la TLB */
32446
flush_tlb_page(vma, address);
32447 end_wp_page:
/* Si se había reservado una página, se libera porque no
es necesaria porque la página sólo tiene un usuario. Luego
se retorna de la función */
32448
if (new_page)
32449
free_page(new_page);
32450
return 1;
32451
}
66
Función do_wp_page
/* Hay que hacer la COW. Si la nueva página no se creó->
error */
32454
if (!new_page)
32455
return 0;
32456
/* Si la página fue reservada se incrementa el número de
páginas residentes del proceso */
32457
if (PageReserved(mem_map + MAP_NR(old_page)))
32458
++vma->vm_mm->rss;
/*
Se copia el contenido de la página original en la
página reservada (con un memcpy) */
32459
copy_cow_page(old_page, new_page);
67
Función do_wp_page
/* Se marca la página como “dirty” y “writable” y coloca
el resto de protecciones indicadas en su VMA */
32463
set_pte(page_table,
32464
pte_mkwrite(pte_mkdirty(mk_pte(new_page,
32465
vma->vm_page_prot))));
/* Se
página
32466
32468
32469
decrementa el contador de usuarios de la antigua
ya que ahora este proceso tiene su propia copia */
free_page(old_page);
return 1;
68
Función do_wp_page
/* Si se ha mapeado la página a un número mayor que el
número de páginas físicas, se ha producido un error, se
muestra información y se mata el proceso que provocó el
fallo */
32470 bad_wp_page:
32471
printk("do_wp_page: bogus page at address "
32472
"%08lx (%08lx)\n", address, old_page);
32473
send_sig(SIGKILL, tsk, 1);
/* Si se había reservado una página para realizar la COW,
se libera, puesto que no se va a utilizar */
32474
if (new_page)
32475
free_page(new_page);
32476
return 0;
32477 }
69
Función try_to_swap_out (<mm/vmscan.c>)
Esta función se llama para intentar liberar una
página siempre que se pueda (no esté bloqueada ni
reservada). Esta función se llama periódicamente a partir
del proceso kswapd.
38863 static int try_to_swap_out(struct task_struct * tsk,
38864
struct vm_area_struct* vma, unsigned long
address, pte_t * page_table, int gfp_mask)
38866 {
38867
pte_t pte;
38868
unsigned long entry;
38869
unsigned long page;
38870
struct page * page_map;
38871
38872
pte = *page_table; /*entrada en la tabla de pág.*/
70
Función try_to_swap_out
/* Si la página no está en memoria, no se puede liberar */
38873
if (!pte_present(pte))
38874
return 0;
/* Se extrae la dirección de la página física */
38875
page = pte_page(pte);
/* Si la página es mayor que el número de páginas físicas
de memoria presentes en el sistema, se devuelve un error*/
38876
if (MAP_NR(page) >= max_mapnr)
38877
return 0;
/* Se extrae el descriptor de la página física */
38879
page_map = mem_map + MAP_NR(page);
71
Función try_to_swap_out
/* Se devuelve error si la página está Reservada,
Bloqueada o está siendo accedida en una operación de DMA*/
38880
if (PageReserved(page_map)
38881
|| PageLocked(page_map)
38882
|| ((gfp_mask & __GFP_DMA) && !PageDMA(page_map)))
38883
return 0;
/* Si la página es ‘joven’ no es buena idea liberarla
porque se viola el Principio de Localidad Temporal, por
tanto, se marca como ‘antigua’, para que un intento de
intercambio futuro si se puede realizar */
38885
if (pte_young(pte)) {
/* Se hace más vieja a la página */
38888
set_pte(page_table, pte_mkold(pte));
38889
set_bit(PG_referenced, &page_map->flags);
38890
return 0;
38891
}
72
Función try_to_swap_out
/* Si la página ya está en la “swap cache”, se incrementa
el número de referencias a ella en la swap caché */
38899
if (PageSwapCache(page_map)) {
38900
entry = page_map->offset;
38901
swap_duplicate(entry);
/* Se actualiza page_table a entry*/
38902
set_pte(page_table, __pte(entry));
38903 drop_pte:
/* Se decrementa el número de páginas residentes del
proceso, puesto que dejará de estar en memoria */
38904
vma->vm_mm->rss--;
73
Función try_to_swap_out
/* Decrementa el número de referencias a la página y se
libera si llega a cero */
38906
__free_page(page_map);
38907
return 0;
38908
}
/* Si la página no ha sido modificada (se puede recuperar
del disco), se borra la entrada de la tabla de páginas y
se libera */
38920
if (!pte_dirty(pte)) {
38921
pte_clear(page_table);
38922
goto drop_pte;
38923
}
/* Si no se puede hacer una operación de E/S no se hace el
swap. Recursos ocupados */
38927
if (!(gfp_mask & __GFP_IO))
38928
return 0;
74
Función try_to_swap_out
/* En este punto se sabe que la página está a dirty, por
tanto se ha de guardar a disco */
/* Si el segmento tiene implementada su propia operación
de swapout se invoca */
38945
if (vma->vm_ops && vma->vm_ops->swapout) {
38946
pid_t pid = tsk->pid;
/* Se limpia la entrada en la tabla de páginas */
38947
pte_clear(page_table);
/* Se decrementa el numero de paginas residentes en
memoria del proceso */
38949
vma->vm_mm->rss--;
/* Si hay un error en su operación de swapout se mata al
proceso enviándole la señal SIGBUS */
38951
if (vma->vm_ops->swapout(vma, page_map))
38952
kill_proc(pid, SIGBUS, 1);
75
Función try_to_swap_out
/* Se decrementa el número de referencias a la página */
38953
__free_page(page_map);
38954
return 1;
38955
}
/* Se obtiene una nueva entrada en la caché de swap */
38961
entry = get_swap_page();
38962
if (!entry)
38963
return 0; /* No queda espacio en el swap */
/* Se decrementa el número de páginas residentes del
proceso en memoria */
38965
vma->vm_mm->rss--;
/* Se incrementa el numero de páginas del proceso que se
han guardado en la zona de intercambio */
38966
tsk->nswap++;
76
Función try_to_swap_out
/* Se actualiza page_table a entry indicando ahora que la
página está en la caché de swap */
38967
set_pte(page_table, __pte(entry));
/* Se verifica la entrada entry en la caché de swap (si es
correcta) y se incrementa el número de referencias que hay
apuntando a esa entrada en la swap caché */
38970
swap_duplicate(entry);
/* Se
caché
38971
/* Se
38977
/* Se
38979
38980
38981
asocia la página (struct page) a la entrada de la
*/
add_to_swap_cache(page_map, entry);
realiza la copia al área de swap asíncronamente */
rw_swap_page(WRITE, entry, (char *) page, 0);
decrementa el número de referencias a la página */
__free_page(page_map);
return 1;
}
77
FIN