Análisis de Buffer Cache • Ubicación en el sistema operativo. SYSTEM STRUCTURE Other application programs nroff cpp who a.out Kernel comp cc sh date Hardware as wc Id vi Other ed grep programs application Lower Level File System Algorithms namei alloc free ialloc ifree iget iput bmap buffer allocation algorithms getblk brelse bread breads bwrite Diagrama de bloque del Kernel del Sistema user programs libraries trap User Level Kernel Level system call interface file subsystem buffer cache process inter-process comunication control scheduler subsystem memory management character block device drivers Kernel Level Hardware Level hardware control hardware Process States and Transitions user running 1 sys call or interrupt kernel running return Interrupt, interrupt return 2 schedule process sleep asleep 4 wakeup 3 context switch permissible • Los procesos van a dormir cuando esperan la ocurrencia de algún evento. • Ejemplo : - Parte de un proceso que espera por un recurso : while(buffer locked) sleep( hasta buffer unlocked); set buffer locked; /* lo toma y pone candado */ ...... - Parte de un proceso que libera un recurso de uso exclusivo : ...... set buffer unlocked; wakeup ( a los que esperan por buffer unlocked); ..... Multiple Processes Sleeping on a Lock time Proc A Proc B Proc C Buffer locked Sleeps Buffer locked Sleeps Buffer is unlocked Ready to run Buffer locked Sleeps Wake up all sleeping procs Ready to run Ready to run Runs Buffer unlocked Lock buffer Sleep for arbitrary reason Runs Buffer locked Sleep Ready to run Runs Buffer locked Sleep Wakes up Unlocks Buffer Wake up all sleeping procs Context switch, eventually Ready to run Buffer cache (escondite, oculto) • Estructura en memoria primaria, para minimizar el acceso a discos. • El kernel no lee o escribe directamente a disco. • Se llama también pool de buffers (grupo a se compartido) buffer de proceso pool de buffer disco • Se intenta mantener el mayor tiempo posible "buenos datos" en el cache. • Se posterga la escritura física disco, lo mayor que sea posible. • También se intenta predecir lecturas (precache) para cargar con datos que serán ocupados más adelante. • Se emplear un header y una zona de memoria para los datos. número lógico Estado Puntero a datos identifica unívocamente una porción del disco apunta al buffer propiamente tal (*) } } cola de colisiones en tabla de hash lista de libres • (*) aquí se copia, en memoria, un bloque del disco Estado • Se puede estar locked o unlocked (ocupado o libre). • Datos válidos. • Delayed - write (está marcado como viejo ). • El kernel está leyendo o escribiendo este buffer. • Un proceso está esperando que este buffer quede unlocked. Algorithm for Buffer Allocation Algorithm getblk input: file system number block number output: locked bufffer that can now be used for block {while (buffer not found) { if (block in hash queue) { if (buffer busy ) /* scenario 5 */ { sleep (event buffer becomes free); continue /* back to while loop */ } mark buffer busy /* scenario 1*/ remove buffer from free list; return buffer; } else { /* block not on hash queue */ if (there are no buffers on free list ) /* scenario 4 */ { sleep ( event any buffer become free ); continue; /* back to while loop`*/ } remove buffer from free list; if (buffer marked for delayed write) { /*scenario 3 ( se marca como viejo) */ asynchronous write buffer to disk; continue; /* back to while loop */ } /* scenario 2 -- found a free buffer */ remove buffer from old hash queue; put buffer onto new hash queue; return buffer; } } } /*** Fin del algoritmo ***/ Algorithm for Releasing a Buffer algorithm brelse input: locked buffer output: none { wakeup all procs; event, waiting for any buffer to become free; wakeup all procs; event, waiting for this buffer to become free; raise processor execution level to block interrupts: if (buffer contents valid and buffer not old) enqueue buffer at end of free list else enqueue buffer at beginning of free list lower processor execution level to allow interrupts; unlock (buffer); } Algorithm for Writing a Disk Block algorithm bwrite /* Block write */ input: buffer output: none { initiate disk write; if ( I/O synchronous ) { sleep( event I/O complete ); release buffer (algorithm brelse); } else if ( buffer marked for delayed write) mark buffer to put at head of free list; } • En write asincrónico, se inicia la escritura, pero no se espera que la operación se complete. • En escritura asincrónica, el driver de disco, interrumpe invocando a brelse. Algorithm for Reading a Disk Block algorithm bread /* block read */ input : file system block number output: buffer containing data { get buffer for block (algorithm getblk); if ( buffer data valid) return (buffer): initiate disk read; sleep( event disk read complete ); return ( buffer ); } • El proceso que lee un bloque : llama a bread i) Obtiene a través de getblk un buffer locked ii) En caso que deba leer desde disco, va a dormir. Es despertado por el driver de disco. iii) Debe liberar bloque cuando ya no lo necesita. Algorithm for Block Read Ahead (Pre Cache ) algorithm breada /* block read and read ahead */ input: (1) file system block number for inmediate read (2) file system block number for asynchronous read output: buffer containing data for inmediate read { if ( first block not in cache ) { get buffer for first block (algorithm getblk); if (buffer data not valid ) initiate disk read; } if ( second block not in cache ) { get buffer for second block (algorithm getblk); if (buffer data valid ) release buffer ( algorithm brelse ); else initiate disk read; /* (****) */ } if ( first block was originally in cache ) { read first block (algorithm bread); return buffer; } sleep (event first buffer contains valid data); return (buffer); } • (****) En escritura asincrónica el driver de disco debe liberar el recurso (con brelse) una vez completa la operación. getblk(4) • Scenario 1 in Finding a Buffer : buffer on hash queue a) Search for Block 4 on First Hash Queue hash queue headers blkno 0 mod 4 ........ 28 4 64 blkno 1 mod 4 ........ 17 5 97 blkno 2 mod 4 ........ 98 50 10 blkno 3 mod 4 ........ 3 35 99 freelist header • • • • Está en hash Si está unlocked: lo toma colocando locked. Lo remueve de los libres Retorna buffer locked b) Remove Block 4 from Free List hash queue headers blkno 0 mod 4 ........ 28 4 64 blkno 1 mod 4 ........ 17 5 97 blkno 2 mod 4 ........ 98 50 10 blkno 3 mod 4 ........ 3 35 99 freelist header • Al colocar locked: Impide a otros procesos, que lo requieran, que puedan avanzar en modo kernel. Si intentan accesarlo deben dormir. • Nota : se usa hash abierto. Las colisiones se resuelven en una lista doblemente enlazada circular. (permite pasar elementos a otras colas ) getblk(18) • Second Scenario for Buffer Allocation a) Search for Block 18: Not in Cache hash queue headers blkno 0 mod 4 ........ 28 4 64 blkno 1 mod 4 ........ 17 5 97 blkno 2 mod 4 ........ 98 50 10 blkno 3 mod 4 ........ 3 35 99 freelist header • No está en cache, ya que no está en tabla de hash • Se remueve el primero de lista de libres. • Si no está marcado para escritura postergada, lo marca ocupado (locked). • Lo remueve de antigua posición en tabla de hash (si es necesario) • lo coloca en posición correcta b) Remove First Block from Free List, Assign to 18 hash queue headers blkno 0 mod 4 ........ 28 4 64 blkno 1 mod 4 ........ 17 5 97 blkno 2 mod 4 ........ 98 50 10 blkno 3 mod 4 ........ freelist header 35 99 18 getblk(18) • Third Scenario for Buffer Allocation a) Search for Block 18, Delayed Write Blocks on Free List hash queue headers blkno 0 mod 4 ........ 28 blkno 1 mod 4 ........ 17 blkno 2 mod 4 ........ blkno 3 mod 4 ........ delay freelist header 4 64 5 97 98 50 10 3 35 99 delay • No está en hash, por lo tanto no está en cache. • Hay bloques libres. Remueve los marcados para escritura postergada y los envía a escribir. Luego se los inserta en la cabeza. (**). • Se continua, hasta llegar a uno libre : • Se lo remueve de la lista. • Lo marca ocupado. • Remueve de posición en tabla de hash (si es necesario). • lo coloca en posición correcta b) Writing Blocks 3,5, Reassign 4 to 18 hash queue headers blkno 0 mod 4 ........ 28 blkno 1 mod 4 ........ 17 blkno 2 mod 4 ........ blkno 3 mod 4 ........ writing freelist header 64 5 97 98 50 10 3 35 99 writing 18 • Nota : Lista de libres : se insertan se consumen (cuando son liberados) cabeza 1 2 3 (**) Doble cola : operaciones en ambos extremos Cola simple • Si se usa uno, no puede volver a emplearse hasta que todos los demás hayan sido usados más recientemente. (LRU, least recently used) getblk(18) • Fourth Scenario for Buffer Allocation Search for Block 18, Empty Free List hash queue headers blkno 0 mod 4 ........ 28 4 64 blkno 1 mod 4 ........ 17 5 97 blkno 2 mod 4 ........ 98 50 10 blkno 3 mod 4 ........ 3 35 99 freelist header • No está en hash, por lo tanto no está en cache. • No hay buffer en la lista de libres, por lo tanto va a dormir. • Debe esperar a que otro proceso invoque a brelse. Race for Free Buffer time Process A Process B Cannot find block b on hash queue No buffers on free list Sleeps Cannot find block b on hash queue No buffers on free list Sleeps Somebody frees a buffer: brelse Takes buffer from free list Assign to block b • Contienda por un buffer libre : Luego de despertar, debe volver a buscar, esto soluciona carreras entre procesos para adquirir un bloque. Pero puede producir starvation (inanición) getblk(18) • Fifth Scenario for Buffer Allocation Search for Block 99, Block Busy hash queue headers blkno 0 mod 4 ........ 28 4 64 blkno 1 mod 4 ........ 17 5 97 blkno 2 mod 4 ........ 98 50 10 blkno 3 mod 4 ........ 3 35 99 busy freelist header • Está en hash, pero ocupado. • Luego de despertar debe volver a buscar. Race for a Locked Buffer time Proc A Proc B Proc C Allocate buffer to block b Lock buffer Initiate I/O Sleep until I/O done Find block b on hash queue Buffer locked, sleeps Sleep waiting for any free buffer (scenario 4) I/O done, wake up brelse() : wake up others Get buffer previously assigned to block b reassign buffer to block b buffer does not contain block b start search buffer Sistema de Archivos ( nivel usuario) (nivel biblioteca) • En un nivel abstracto un programador requiere abrir un archivo, por su nombre, antes de escribir o leer en él. • fp = fopen(nombre,modo) nombre lógico, nombre interno, modo : "r", "w", "a" • c=getc(fp) • putc(c,fp) • fclose(fp) • fscan ( fp, control, arg1, arg2, .....); • fprintf ( fp, control, arg1, arg2, .....); • fgets ( línea, MAXLINE, fp ); • fputs ( línea, fp ); • ungetc ( c, fp); • fp es puntero a estructura de tipo FILE : FILE *fopen(), *fp; • se requiere esta declaración en una función que manipule archivos. • Las funciones anteriores, algunas son macros, están definidas en el archivo estándar stdio.h . • Por esta razón, debe plantearse, previo a su uso : #include <stdio.h> • Buscar en : /usr/include/stdio.h Observaciones : • fopen : - Si se abre archivo que no existe, para escribir o agregar al final, se crea si es posible. - Si se abre archivo existente para escritura, se sobreescribe. - Si se abre para lectura un archivo que no existe, se produce un error. También si no se tiene permisos, o si por alguna razón no se puede crear, abrir, leer o escribir se produce error, retorna puntero : NULL (definido en stdio.h) • getc : - Retorna el siguiente caracter, EOF al final del archivo. • stdin, stdout, stderr : - Son punteros a archivos predefinidos (son constantes), normalmente el terminal del usuario. • fclose : - flush ("tirar la cadena") el buffer del usuario. - Pasa zona de memoria del usuario al sistema operativo. - Permite reusar el puntero a archivo. • exit( entero ): - Rutina estándar de biblioteca, que provee un valor de retorno al programa. (error level en DOS) - Por convenio valor 0 implica desarrollo normal, diferente de 0 avisa all ambiente e externo que se produjo situación anormal. - exit llama a fclose para cada archivo abierto, y luego invoca a _exit , que produce término inmediato sin salvar contenido de buffers (flushing) • Ver 7.7 y 7.8 en K.R. cat, fgets, fputs. Implementación Bibiloteca Llamados al sistema • • • • • • • • n_read = read ( fs, buf, n); n_write = write ( fs, buf, n); fd = open (nombre, modo); fd = creat (nombre, permisos); close (fd); unlink ( nombre ); borra del directorio lseek (fd, offset, origen ); fd entero descriptor del archivo, describe con un número un archivo en el sistema. Observaciones : • buf es el buffer de usuario char buf [ BUFSIZE ] ; • En lectura, se retorna número actual de bytes transferidos. Puede ser menor que n ( nº solicitado). Retorno indica fin de archivo. Retorno -1 indica error de algún tipo. • En escritura, se retorna número de bytes transferidos. Si no es igual al solicitado es condición de error. • n puede ser 1 (se denomina "caracter a caracter " o unbuffered ) • n puede ser un bloque de cache (o físico). Este valor está en stdio.h : BUFSIZ • Discusión tamaño buffer : Pike, Kernighan pág. 203. • Ver implementación de copy K.R. 8.2 getchar (unbuffered) K.R. 8.2 getchar (buffered) K.R. 8.2 • open : retorna descriptor de archivo; -1 si no puede abrir . modo : 0 read; 1 write ; 2 read and write • creat : permisos en octal rwx dueño 0755 rx grupo rw 0644 rx otros r r - retorna -1 si no puede crear - Si existe, lo deja de largo cero - Un archivo recién creado, queda abierto para escritura (independiente de permisos) K.R. 8.3 • lssek : - Acceso aleatorio - Se posiciona dentro del archivo. ( se prepara para leer o escribir) - Retorna en long, la posición corriente. Si falla -1. offset offset origen = 0 offset origen = 2 origen = 1 posición corriente • Ejemplos : K. R. 8.4 • Ejemplos de programas con llamados al sistema : cp K.R. 8.3 ; K. Pike : pág. 206 y 207 • Estructura FILE : _ptr _cnt buffer _base _flag corriente _fd • _cnt : indica los caracteres que quedan • get :predecrementa _cnt Si es -1, setea EOF Toma caracter corriente Posiciona _ptr en siguiente caracter. • _flag : octal reading 01 writing 02 unbuffered 04 buffer asignado 010 EOF 020 ERROR 040 • Tabla descripción de archivos de usuario : fd 0 1 2 3 stdin stdout stderr tipo FILE _NFILE-1 _oib #define _BUFSIZE 512 #define _NFILE 20 /* #files that can be handled */ typedef struct _iobuf { char *_ptr; /* next caracter position */ int _cnt; /* number of caracters left */ char *_base; /* location of buffer */ int _flag; /* mode of file access */ int _fd; /* file descriptor */ } FILE ; extern FILE _iob[_NFILE]; #define stdin (&_iob[0]) #define stdout (&_iob[1]) #define stderr (&_iob[2]) #define #define #define #define #define #define #define #define _READ 01 _WRITE _UNBUF _BIGBUF _EOF 020 _ERR 040 NULL 0 EOF (-1) /* file open for reading */ 02 /* file open for writing */ 04 /* file is unbuffered */ 010 /* big buffer allocated */ /* EOF has occurred on this file */ /* error has occurred on this file */ ________________________________________________ FILE -iob[_NFILE = { ( NULL, 0, NULL, _READ, 0), /* stdin */ ( NULL, 0, NULL, _WRITE, 1), /* stdout */ ( NULL, 0, NULL, _WRITE | _UNBUF , 2), /* stderr */ }; #include <stdio.h> #define PMODE0644 /* R/W for owner; R for others */ FILE *fopen(name,mode) /* open file, return file ptr */ register char *name, *mode; { register int fd; register FILE *fp; if (*mode !='r' && *mode !='w' && *mode != 'a' ) { fprintf (stderr, "illegal mode %s opening %s \n",mode, name); exit(1); } for (fp = _iob; fp < _oib + _NFILE; fp ++) if (( fp -> _flag & (_READ | _WRITE )) = = 0) break; if ( fp > _oib + _NFILE) /* no free slots */ return (NULL); if (*mode = = 'w') /* access file */ fd = creat(name, PMODE); else if ( *mode = = 'a' ) { if (( fd = open(name,1))= = -1) fd = creat(name,PMODE); lseek( fd, 0L ,2 ); } else fd = open (name, 0); if ( fd = = -1 ) /* couldn't access name */ return(NULL); fp -> _fd = fd ; fp -> _cnt = 0 ; fp -> _base = NULL; fp -> _flag & = * ( _READ | _WRITE ) ; fp -> _flag |= (*mode = = 'r' ) ? _READ : _WRITE; return(fp); } /*** Fin del Programa ***/ #define getc(p) (--(p)->_cnt >=0 ? *(p) -> _ptr++ \ & 0377 : _fillbuf(p)) #define getchar() getc(stdin) #define putc(x,p) (--(p)->_cnt >=0 ? *(p) -> _ptr++ \ = (x) : _flushbuf((x)p)) #define putchar(x) putc(x,stdout) #include <stdio.h> _fillbuf(fp) /* allocate and fill input buffer */ register FILE *fp; { static char smallbuf[_NFILE]; /* for unbuffered I/O */ char *calloc(); if (( fp -> _flag & _READ )= = 0 || ( fp -> _flag &(_EOF | _ERR)) !=0 ) return(EOF); } while ( fp -> _base = = NULL) /* find buffer space */ if ( fp -> _flag & _UNBUF) /* unbuffered */ fp->_base = &smallbuf[fp -> _fd]; else if (( fp -> _base=calloc(_BUFSIZE,1))= =NULL) fp->_flag |= _UNBUF; /* can't get big buf */ else fp->_flag |= _BIGBUF; /* got big one */ fp->_ptr = fp->_base; fp->_cnt = read(fp->_fd, fp->_ptr, fp->_flag &_UNBUF ? 1 : _BUFSIZE; if (--fp->_cnt <0 ) { if (fp->_cnt= = -1) fp->_flag |= _EOF; else fp->_flag |= _ERR; fp->_cnt=0; return(EOF); } return(*fp->_ptr++ & 0377); /* make char positive*/ /*** Fin del Programa ***/