La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

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,

Presentaciones similares


Presentación del tema: "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,"— Transcripción de la presentación:

1

2 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

3 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. Gestión de la memoria 3

4 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. Gestión de la memoria 4

5 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

6 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

7 Memoria virtual 7

8 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. Memoria virtual 8

9 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

10 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? Modelo abstracto de memoria virtual 10

11 Modelo abstracto de memoria virtual 11

12 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

13 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

14 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. Control de acceso 14

15 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. Control de acceso 15

16 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. Control de acceso 16

17 _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. Control de acceso 17

18 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

19 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). Cachés 19

20 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

21 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. Tabla de páginas en Linux 21

22 Tabla de páginas en Linux 22

23 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

24 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

25 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

26 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

27 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

28 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

29 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

30 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

31 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

32 Estructuras de datos El fichero define los tipos utilizados para representar las entradas en las tablas de páginas. Estas son las siguientes: 32

33 Estructuras de datos Además existen varias macros. Estas son las siguientes: 33

34 Estructuras de datos El fichero 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: 34

35 Estructuras de datos Las constantes siguientes, declaradas en el archivo definen el estado de una página. Estas son las siguientes: 35

36 Estructuras de datos La estructura free_area_struct, declarada en el fichero fuente define el formato de los descriptores de la lista de grupos. 36

37 Estructuras de datos La estructura free_area_struct, declarada en el fichero fuente define el formato de los descriptores de la lista de grupos. 37

38 Función do_page_fault ( ) Esta función se llama cuando ocurre un fallo de página 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

39 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

40 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

41 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 esp) 7017goto 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

42 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

43 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 */ 7035write++; 7036break; /* De aquí no pasan los case 2 y 3*/ 43

44 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; 7057return; 44

45 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

46 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

47 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 */ 7128die(Ops, regs, error_code); 7129 do_exit(SIGKILL); 47

48 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

49 Función handle_mm_fault ( ) 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 int handle_mm_fault(struct task_struct *tsk, struct vm_area_struct * vma, 32727unsigned long address, int write_access){ pgd_t *pgd; pmd_t *pmd; /* Se obtiene un puntero a la entrada correspondiente a address en la tab. de pág. global para el proceso */ 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). */ pmd = pmd_alloc(pgd, address); 49

50 Función handle_mm_fault /* Si la entrada en la tabla intermedia (un puntero a una tabla de páginas) no es nula */ 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 */ pte_t * pte = pte_alloc(pmd, address); 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 */ if (handle_pte_fault(tsk, vma, address, 32738write_access, pte)) { return 1; } } } return 0; /* Se retorna 0 en caso de error */ } 50

51 Macro pgd_offset ( ) 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) */ #define pgd_offset(mm, address) \ ((mm)->pgd + ((address) >> PGDIR_SHIFT)) 51

52 Función pmd_alloc ( ) 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 */ extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address) { return (pmd_t *) pgd; } 52

53 Función pte_alloc ( ) 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 extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address) { /* Se extrae de address el desplazamiento dentro de la tabla de páginas: desde el bit 22 al 12 */ address = (address >> (PAGE_SHIFT-2)) & *(PTRS_PER_PTE - 1); 53

54 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 */ if (pmd_none(*pmd)) goto getnew; /* Si la entrada de la tabla de páginas intermedia es incorrecta, se trata un error */ if (pmd_bad(*pmd)) 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) */ return (pte_t *) (pmd_page(*pmd) + address); 54

55 Función pte_alloc /* Se obtiene una página para tabla de páginas. */ 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. */ unsigned long page =(unsigned long)get_pte_fast(); /* Si no se encuentra ninguna página libre en la caché, la carga mediante la función get_pte_slow. */ if (!page) return get_pte_slow(pmd, address); 55

56 Función pte_alloc /* Si get_pte_fast encontró una página libre se inserta en la entrada de la tabla intermedia. */ 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) */ return (pte_t *) (page + address); } /* Se ha detectado un error, se imprime un mensaje */ fix: __bad_pte(pmd); return NULL; } 56

57 Función handle_pte_fault ( ) Esta función lee la página solicitada o realiza la llamada para realizar una COW static inline int handle_pte_fault( struct task_struct *tsk, struct vm_area_struct * vma, unsigned long address, int write_access, pte_t * pte) { pte_t entry; entry = *pte; /* entrada de la tabla de páginas */ 57

58 Función handle_pte_fault /* Si la página no está en memoria */ 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)*/ 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 */ return do_no_page(tsk, vma, 32703address, write_access, pte); /* La página no está en memoria, pero ya había sido mapeada, por tanto, se lee del fichero de swap */ return do_swap_page(tsk, vma, address, pte, 32705entry, write_access); } 58

59 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 */ entry = pte_mkyoung(entry); set_pte(pte, entry); /* Se actualiza la tlb */ flush_tlb_page(vma, address); 59

60 Función handle_pte_fault /* Si se quiere escribir */ 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 */ if (!pte_write(entry)) /* Si la página es de sólo-lectura hay que hacer una COW*/ 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) */ entry = pte_mkdirty(entry); set_pte(pte, entry); /* Se actualiza la TLB */ flush_tlb_page(vma, address); } return 1; } 60

61 Función do_wp_page ( ) Esta función es la encargada de realizar la copia en escritura (Copia-On-Write) static int do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long address, pte_t *page_table) { pte_t pte; unsigned long old_page, new_page; struct page * page_map; /*entrada en la tabla de páginas*/ pte = *page_table; /* Se obtiene una nueva página de memoria libre, que se utilizará para hacer la COW */ new_page = __get_free_page(GFP_USER); 61

62 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 */ if (pte_val(*page_table) != pte_val(pte)) 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 */ if (!pte_present(pte)) goto end_wp_page; 62

63 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 */ if (pte_write(pte)) 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*/ 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 */ if (MAP_NR(old_page) >= max_mapnr) goto bad_wp_page; 63

64 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 */ tsk->min_flt++; /* Se obtiene el descriptor de la página en memoria (estructura que describe la página) */ page_map = mem_map + MAP_NR(old_page); /* La copia se puede evitar si: - Nosotros somos el único usuario (count=1) - Hay otro usuario, pero éste es el swap cache */ 64

65 Función do_wp_page 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é */ if (!PageSwapCache(page_map)) 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 */ if (swap_count(page_map->offset) != 1) break; /* Si definitivamente el otro usuario era la caché pues se borra de esta porque la página va a ser modificada */ delete_from_swap_cache(page_map); /* FallThrough */ 65

66 Función do_wp_page 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 */ 32445set_pte(page_table, pte_mkdirty(pte_mkwrite(pte))); /* Se actualiza la TLB */ flush_tlb_page(vma, address); 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 */ if (new_page) free_page(new_page); return 1; } 66

67 Función do_wp_page /* Hay que hacer la COW. Si la nueva página no se creó-> error */ if (!new_page) return 0; /* Si la página fue reservada se incrementa el número de páginas residentes del proceso */ if (PageReserved(mem_map + MAP_NR(old_page))) vma->vm_mm->rss; /* Se copia el contenido de la página original en la página reservada (con un memcpy) */ copy_cow_page(old_page, new_page); 67

68 Función do_wp_page /* Se marca la página como dirty y writable y coloca el resto de protecciones indicadas en su VMA */ set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot)))); /* Se decrementa el contador de usuarios de la antigua página ya que ahora este proceso tiene su propia copia */ free_page(old_page); return 1;

69 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 */ bad_wp_page: printk("do_wp_page: bogus page at address " "%08lx (%08lx)\n", address, old_page); 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 */ if (new_page) free_page(new_page); return 0; } 69

70 Función try_to_swap_out ( ) 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 static int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma, unsigned long address, pte_t * page_table, int gfp_mask) { pte_t pte; unsigned long entry; unsigned long page; struct page * page_map; pte = *page_table; /*entrada en la tabla de pág.*/ 70

71 Función try_to_swap_out /* Si la página no está en memoria, no se puede liberar */ if (!pte_present(pte)) return 0; /* Se extrae la dirección de la página física */ 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*/ if (MAP_NR(page) >= max_mapnr) return 0; /* Se extrae el descriptor de la página física */ page_map = mem_map + MAP_NR(page); 71

72 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*/ if (PageReserved(page_map) || PageLocked(page_map) || ((gfp_mask & __GFP_DMA) && !PageDMA(page_map))) 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 */ if (pte_young(pte)) { /* Se hace más vieja a la página */ set_pte(page_table, pte_mkold(pte)); set_bit(PG_referenced, &page_map->flags); return 0; } 72

73 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é */ if (PageSwapCache(page_map)) { entry = page_map->offset; swap_duplicate(entry); /* Se actualiza page_table a entry*/ set_pte(page_table, __pte(entry)); drop_pte: /* Se decrementa el número de páginas residentes del proceso, puesto que dejará de estar en memoria */ vma->vm_mm->rss--; 73

74 Función try_to_swap_out /* Decrementa el número de referencias a la página y se libera si llega a cero */ __free_page(page_map); return 0; } /* 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 */ if (!pte_dirty(pte)) { pte_clear(page_table); goto drop_pte; } /* Si no se puede hacer una operación de E/S no se hace el swap. Recursos ocupados */ if (!(gfp_mask & __GFP_IO)) return 0; 74

75 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 */ if (vma->vm_ops && vma->vm_ops->swapout) { pid_t pid = tsk->pid; /* Se limpia la entrada en la tabla de páginas */ pte_clear(page_table); /* Se decrementa el numero de paginas residentes en memoria del proceso */ vma->vm_mm->rss--; /* Si hay un error en su operación de swapout se mata al proceso enviándole la señal SIGBUS */ if (vma->vm_ops->swapout(vma, page_map)) kill_proc(pid, SIGBUS, 1); 75

76 Función try_to_swap_out /* Se decrementa el número de referencias a la página */ __free_page(page_map); return 1; } /* Se obtiene una nueva entrada en la caché de swap */ entry = get_swap_page(); if (!entry) return 0; /* No queda espacio en el swap */ /* Se decrementa el número de páginas residentes del proceso en memoria */ vma->vm_mm->rss--; /* Se incrementa el numero de páginas del proceso que se han guardado en la zona de intercambio */ tsk->nswap++; 76

77 Función try_to_swap_out /* Se actualiza page_table a entry indicando ahora que la página está en la caché de swap */ 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é */ swap_duplicate(entry); /* Se asocia la página (struct page) a la entrada de la caché */ add_to_swap_cache(page_map, entry); /* Se realiza la copia al área de swap asíncronamente */ rw_swap_page(WRITE, entry, (char *) page, 0); /* Se decrementa el número de referencias a la página */ __free_page(page_map); return 1; } 77

78 FIN


Descargar ppt "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,"

Presentaciones similares


Anuncios Google