Está en la página 1de 15

Laboratoriode

SistemasOperativos

Prctica7:

ProgramacindeSistemas
(SystemProgramming:I/O)

Objetivo
En esta prctica nos enfocaremos en los elementos bsicos para el desarrollodeaplicaciones
para ambiente GNU/Linux utilizando el lenguaje nativo del mismo: C. Nos enfocaremos
principalmenteendiferenciarelusodecdigodeusuarioycdigodesistema.

ndice
DesarrollodeprogramasusandoProgramacindesistemas
Algunasllamadasasistema
Lecturamedianteread()
Escribiendoconwrite()
Cerrararchivos
Unejemplodellamadasalsistema
FileI/O:writeback,SynchronizedI/O
SynchronizedI/O
Lecturaspredictivas
I/Ousandobuffers(lenguajeCestndar)
Apuntadoraarchivo
LeyendoStreams
Escribiendoaunstream
LabibliotecaestndardeCimplementavariasfuncionesparaescribirenunstream
abierto.
EjemplodeunprogramaqueusaI/Oenbuffers.
MapeoaMemoria(Memorymap)
Referencias
Fuentesadicionales

DesarrollodeprogramasusandoProgramacindesistemas

Programacin a nivelSistemas Linux (Linux SystemLevel Programming or Linux System


Programming)eselartedeescribir
softwaredelSistema
.

Software del sistema:


esaquelqueestencontactodirectoconelmanejodelkernely
las bibliotecas del sistema. El desarrollo de software de sistema exige que el usuario
conozcasobreelhardwaredelsistemaenelquevaatrabajarelsoftware.

Laboratoriode

SistemasOperativos

Ejemplosdesoftwaredesistemaincluyen:
Elsistemaoperativo
Losinterpretadoresdecomandos
Lossistemasdeinterfacegrfica
Los compiladores, ensambladores, controladores de versin, los depuradores,
etc.

Programacin de Aplicaciones (
Application Programmin
g
)
: es el arte de escribir
software de Aplicaciones. Un
software de aplicacin es un programa autocontenido
querealizaunafuncinespecficadirectamenteconelusuario.
Ejemplosincluyen:
Hojasdeclculo(i.e.Excel)
Softwareparaprocesamientodetexto(i.e.Office)
SoftwareparaPresentacin(i.e.PowerPoint)
Aplicacionesderedcomocorreoelectrnico,telnetyWWW.

Desde el punto de vista del programador las diferencias entre Programacin de Sistemas y
Programacin de Aplicaciones es que la Programacin de Sistemas requiere
conocimiento sobre el Hardware y el Sistemas Operativo en el que se desarrollar el
programa
. Otra diferencia son las bibliotecas a utilizar. Porotrolado,sedebendeemplearlas
mismasbuenasprcticasdeprogramacin.

La mayora del cdigo para Linux y Unix se escribe a nivel sistema. Ya que las llamadas al
sistemasondependientesdelhardware,muchodeestecdigoseescribeenlenguajeC.

La programacin a nivel sistema se hace a travs de


llamadas al sistema (
system calls or
supervisorcalls
)
.

Las
llamadas al sistema
son funciones que se invocan desde nivel usuario hacia el kernel
pararequerirunservicioorecursodelsistemaoperativo.

Ejemplosdellamadasalsistemason:
SistemadeArchivo:
open()
,
read()
,
write()
,
close()
,etc.
Procesos
fork()
,
getpid()
,
set_thread_area()
,etc.
Red:
socket()
,
bind()
,etc.

Cada Sistema Operativo define diversas llamadas al sistema. Por ejemplo en el caso de
sistemas operativos Windows, se definen miles de llamadas al sistema. En el casodeLinuxel
nmerodellamadasalsistemasonaproximadamenteunas300.

NOTA
:LaltimaafirmacincorrespondealaeradelafamiliaWindows9x.Actualmente,sepuedeveruna
listarecopiladapormetasploitdellamadasdesistemasdewindows
enestaliga
.

Algunasllamadasasistema
La documentacin completa de estas llamadas a est en losmanualesdelprogramador(man
pages). En ubuntu estaspginasn pueden no haber sido instaladas automticamente, se
debeninstalarlospaquetesmandbymanpagesdev

Laboratoriode

SistemasOperativos

Abrirarchivos:
Antes que los datos de un archivo se puedan leer,
ste debe de ser abierto utilizando las
llamadasasistema
open()
o
create()
.

intopen(constchar*name,intflags)
intopen(constchar*name,intflags,mode_tmode)

Sisepuedeabrirexitosamente,regresaun
descriptordearchivo.

Comienzaenlaposicin0delarchivo,y
Elarchivoseabredeacuerdoalcontenidodelparmetroflags.

La variable
flags puede contener diferentes valores dependiendo de la operacin que
deseamosrealizarsobreelarchivo:

O_RDONLY
,
O_WRONLY,
or
O_RDWR
.

La variable
modo puede tomar los siguientes valores:
O_APPEND,

O_CREAT,

O_EXCL

O_TRUNC
.Lainformacindetalladaseencuentraen
manopen
.

La definicin de estas funciones, as como las constantes utilizadas en los flags, se pueden
encontrarenlossiguientesencabezados.
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

Lecturamedianteread()
Elmecanismomsbsicoycomnmenteusadoparaleer,eslallamadaasistema
read()
,se
define:
#include<unistd.h>
ssize_tread(intfd,void*buf,size_tlen)

Cada llamadaleehastaunacantidadde
len bytes apartirdelaposicinactualreferenciadapor
el descriptor de archivo
fd,
y los guarda en
buf. Regresa el nmero de bytes escritosen
buf
,o
1siocurreunerror.

Escribiendoconwrite()
Lallamadaasistemamsbsicaycomnes
write()
.Sedefine:
#include<unistd.h>
ssize_twrite(intfd,constvoid*buf,size_tcount)

La llamada a
write() escribe una cantidad de
count bytes a partir de la posicin actual
referenciadapor
fd
.Regresaelnmerodebytesescritos,o1encasodeerror.

Cerrararchivos

Cuando un programa ha terminado de trabajar con un file descriptor, debe desasociar el file
descriptordelarchivomediantelallamadaasistema
close()
:

#include<unistd.h>
intclose(intfd)

Regresaun0encasodexito,oun1encasodeerror.

Laboratoriode

SistemasOperativos

Unejemplodellamadasalsistema
Esteejemploesunacodificacinsimpledelcomando
cp
:

/* PROGRAMA: copiar.c
FORMA DE USO:
./copiar origen destino
VALOR DE RETORNO:
0: si se ejecuta satisfactoriamente.
-1: si se da alguna condicion de error
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

/* open() */
/* open() */
/* open() */
/* read() y write() */

char buffer[BUFSIZ]; /*Buffer para manipular datos. */


int main(int argc, char *argv[])
{
int fd_origen;
int fd_destino;
int nbytes;
/*Analisis de los argumentos de la linea de comandos*/
if (argc != 3) {
fprintf(stderr,"Forma de uso: %s origen destino\n", argv[0]);
exit(-1);
}
/*Apertura del archivo en modo solo lectura (O_RDONLY). */
if ((fd_origen=open(argv[1],O_RDONLY))== -1) {
perror(argv[1]);
exit(-1);
}
/* Apertura o creacion de archivos en modo solo escritura. */
/* Abrir en modo solo Lectura (O_WRONLY). */
/* Si el archivo existe entonces truncar a cero bytes (O_TRUNC).*/
/* El archivo es creado en caso de que no exista (O_CREAT). */
/* El modo que se crea es 0666. */
if ((fd_destino=open(argv[2],O_WRONLY|O_TRUNC|O_CREAT,0666))== -1) {
perror(argv[2]);
exit(-1);
}
/* copiamos el archivo origen en el archivo destino. */
while ((nbytes=read(fd_origen, buffer, sizeof buffer))> 0)
write(fd_destino, buffer, nbytes);

Laboratoriode

SistemasOperativos

close(fd_origen);
close(fd_destino);
}

A la hora de ejecutar,observequecuandonohayerror,elprogramaenvaalsistema operativo


el valor 0, y si hay error el programa enva al sistema operativo el valor de 1. El sistema
operativo toma el valor y lo almacena en la variable
$?
. Entonces,
el SO puede utilizar esta
informacin en un programa shell para tomardecisiones.Acontinuacinsemuestraelejemplo
deuncasoconerror:

user@localhost~$./copiacopiar.ccopiar2.c
user@localhost~$echo$?
0
user@localhost~$./copiacopiar3.ccopiar2.c
copiar3.c:Nosuchfileordirectory
user@localhost~$echo$?
255

FileI/O:writeback,SynchronizedI/O
Comportamientodelwrite(writeback)

La disparidad en el rendimiento entre procesadores y discos duros hara que para efectos de
rendimiento se retrase laescritura.Cuandounaaplicacinenelespacio delusuariorealizauna
llamada al sistema
write(), el kernel de Linux realiza un par de revisiones, y entonces
simplemente copia los datos en un buffer interno al sistema operativo. Despus, en segundo
plano, elkerneltomatodoslos bufferssuciosdetodoslos usuarios(esdecir,que nohansido
an escritos a disco), los ordena ptimamente (de tal forma que la cabeza escritora del disco
duro minimice su trayectoria), y los escribe en el disco.
Este proceso se conoce como
writeback. Esto permite al kernel aplazar las escrituras a periodos ms desocupados y
procesarenlotemuchasescriturasjuntas.

Este comportamiento realmente mejora el rendimiento, de la misma forma en que un


read
puede leer de la memoria cach sin tener que ir al disco. Las peticiones de read y write se
intercalan segn lo previsto, y los resultados son los esperados eso s el sistema no tiene
unafallaantesdequelosdatoslleguenaldisco...

An cuando una aplicacin pueda creerqueserealizlaescrituraconxito,encasode


fallalosdatospdrannohaberllegadoaldisco.

Otro problema con las escriturasretrasadaseslaincapacidaddehacercumplirelorden


de escritura. El kernel podr reordenar las solicitudes de escritura como lo considere
oportuno. Normalmente esto es un problema solo si el sistema tiene una falla, ya que
eventualmentetodoslosbufferssonescritos.

Un problema final con las escrituras retrasadas tiene que ver con el reporte de ciertos
errores de I/O. Cualquier error de I/O que ocurra durante la escritura a disco por
ejemplo, una falla fsica de disco no puede ser reportada al proceso que emiti la

Laboratoriode

SistemasOperativos

peticindeescritura.

El kernel trata de minimizar los riesgos de estas escrituras diferidas. Para asegurar que los
datos son escritos oportunamente, el kernel establece una edadmximadelbuffer,y escribea
disco todos los buffers sucios antes de que maduren ms all del tiempo dado.Losusuarios
pueden configurar este valor en el archivo /proc/sys/vm/dirty_expire_centisecs. El valor est
especificadoencentsimasdesegundo.

SynchronizedI/O
Aunque la sincronizacin de I/O es un tema importante, los problemas asociados con las
escrituras retrasadas no deben ser temidos. Las escrituras mediante buffers proporcionan una
mejora enorme en el rendimiento, y consecuentemente, cualquier sistema operativo que
merezca la etiqueta de moderno, implementa escrituras diferidas mediante buffers. Sin
embargo, hay ocasiones en las que las aplicaciones desean controlar de manera precisa el
momento en que los datos llegan al disco. Para esos usos, el kernel de Linux proporciona un
puado de opciones que permiten negociar el rendimiento por medio de las operaciones
sincronizadas.

fsync() asegura que todos los datos sucios asociados con el archivo mapeado por el file
descriptor
fd sean escritos al disco en el momento en que se ejecuta esa llamada. Menos
ptimo, pero de ms amplio alcance, la llamada a sistema
sync() se proporciona para
sincronizartodoslosbuffersaldisco.

Otra posibilidad es asignar la bandera


O_SYNCenlallamadaasistema
open()
,indicandoque
todas las operaciones I/O deben ser sincronizadas. Por ejemplo, en el programa
copiar.c
,
cuandoelarchivodeescrituraesabierto:

if((fd_destino=open(argv[2],O_WRONLY|O_TRUNC|O_CREAT,0666))==1)

podramosincluirlabandera
O_SYNC
delasiguienteforma:
if
(

(fd_destino=open(argv[2],O_WRONLY|O_TRUNC|O_CREAT|
O_SYNC
,0666))==

1)

Lecturaspredictivas

"Readahead"serefierealaaccindeleermsdatosdeldiscoydelapginadecachenla
quesehizolapeticindelectura,enefecto,leyendounpocohaciaadelante.

I/Ousandobuffers(lenguajeCestndar)

El rendimiento de I/O es ptimo cuando las peticiones se realizan entre lmites alineados por
bloques y de tamao mltiplo entero del tamao de bloque. En la prctica, los tamaos de
bloque normalmente son 512, 1,024, 2,048,4,096.Enconsecuencia,laeleccinmssencilla
es haceroperacionesdeI/Ousandobuffersgrandesqueseanmltiplosdelostamaostpicos,
porejemplo4,096o8,192bytes.

El problema, por supuesto, es que los programas rara vez efectan estas operaciones en
trminos de bloques. Los programas trabajan con campos, lneas o caracteres, no con
abstracciones como los bloques. Para remediar esta situacin, los programas pueden utilizar

Laboratoriode

SistemasOperativos

las funciones de usuario para I/O con buffers de usuario, declarados por el programador. La
biblioteca estndar de C provee la librera estndar deI/O(comnmentellamada
stdio
),lacual
a su vez provee una solucin de utilizacin debuffersparausuarios,deformaindependientea
la arquitectura sobre la que corra el sistema. Estas rutinas de lectura y escritura utilizando
buffers de usuarios pueden utilizarse incluso no solo para escribir a disco, sino para escribir a
cualquier dispositivo futuro o incluso para escribir a sockets de red y as enviar writes y
readsprovenientesdeotrosprogramas,posiblementeresidentesenotrasmquinasdelared.

Que una aplicacin use las funciones estndar de I/O olosaccesosdirectosalasllamadasde


sistema es una decisin que el desarrollador debe tomar muy cuidadosamente despus de
sopesarlasnecesidadesdelaaplicacinysucomportamiento.

Las funciones estndar deI/Onotrabajan directamenteen losdescriptoresdearchivos.Envez


de eso, usan su propio identificador nico, conocido como "apuntador a archivo". Dentro de la
bibliotecadeC,elapuntadoraarchivoestraducidoaundescriptordearchivo.

Apuntadoraarchivo
Es un apuntador al tipo de dato FILE, definido en <stdio.h>, que representa el apuntador a
archivo. Hablando del estndar I/O , un archivo abierto es llamado "stream". Estos Streams
puedenabrirseparaescritura,lecturaoambos.
Paraabrirunarchivoseutilizaellafuncin
fopen()
yparacerrarlo
fclose().

LeyendoStreams

LabibliotecaestndardeCimplementadiferentesfuncionesparaleerdeunstreamabierto.

Leyendouncaracteralavez.
Lafuncin
fgetc()
seusaparaleerunslocaracterdelstream.

Leerunalneacompleta.
Lafuncin
fgets()
leeunacadenadecaracteres(string)delstream.

Leerdatosbinarios.
Para algunas aplicaciones, leer caracteres individuales o lneas no es suficiente. Algunas
veces, los desarrolladores quieren leer y escribir datos binarios complejos, por ejemplo
estructuras.Paraesto,labibliotecaestndarI/Oprovee
fread().

Paramayorreferenciasobreestasfunciones,consultarlosmanualesdecadafuncin.

Escribiendoaunstream
LabibliotecaestndardeCimplementavariasfuncionesparaescribirenun

streamabierto.

Escribiendouncarcteralavez
.
Lacontrapartedefgetc()esfputc():

Escribiendounacadenadecaracteres.
Lafuncin
fputs()
esusadaparaescribirunacadenaaunstreamdado
.

Laboratoriode

SistemasOperativos

E
scribiendounacadenadecaracteres.
L
afuncin
fputs()
esusadaparaescribirunacadenadecaracteresaunstreamdado.

EjemplodeunprogramaqueusaI/Oenbuffers.
Comoejemplo,sevaareescribirlafuncincopiar.cusandobuffersdeI/O.

/* PROGRAMA: fcopiar.c
FORMA DE USO:
fcopiar origen destino
VALOR DE RETORNO:
0: si se ejecuta satisfactoriamente.
-1: si se da alguna condicion de error
*/
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])


{
FILE *forigen;
FILE *fdestino;
char c;
/*Analisis de los argumentos de la linea de comandos*/
if (argc != 3) {
fprintf(stderr,"Forma de uso: %s origen destino\n", argv[0]);
exit(-1);
}
/*Apertura del archivo en modo solo lectura*/
if ((forigen=fopen(argv[1],"r"))== NULL) {
perror(argv[1]);
exit(-1);
}
/* Apertura o creacion de archivos en modo solo escritura*/
if ((fdestino=fopen(argv[2],"w"))== NULL) {
perror(argv[2]);
exit(-1);
}
/* copiamos el archivo origen en el archivo destino. */
while ((fread(&c, sizeof c, 1, forigen))> 0)
fwrite(&c, sizeof c, 1, fdestino);
fclose(forigen);
fclose(fdestino);
return 0;
}

Laboratoriode

SistemasOperativos

Despus de compilar el archivo, comparamos el tiempo que tarda en ejecutarse el programa


fcopiar contra el programa copiar. Para reproducir estos ejemplos tambin se puede usar el
archivo(>1GB)queutilizaronenlaprcticadeSystemProgramming.

MapeoaMemoria(Memorymap)
Los archivos mapeados a memoria son una copia idntica enmemoriadeunarchivoendisco.
La imagen corresponde byte por byte con el archivo en disco. Los archivos mapeados a
memoriatienendosprincipalesventajas:
LasoperacionesdeI/Osobrearchivosmapeadosamemoriaevitanlosbuffersdelkernel,
por lotantolastransferenciasI/O sonmuchomsrpidas.Nota:yaqueelkerneltieneun
overhead,estatasadetransferenciaesmsnotoriaconarchivosgrandes.
Aparte de los posibles fallos de pgina, leer de y escribir en un archivo mapeado a
memoria no incurre en ninguna llamada a sistema osobrecargaporcambiodecontexto.
Estansimplecomoaccesarmemoria.
Los datos del archivo mapeado pueden ser accedidos internamente mediante
apuntadores en lugar de las funciones comunes para la manipulacin de archivos.
Adems, buscar en el mapeo involucra manipulaciones triviales de apuntadores. Nohay
necesidaddelallamadaasistemalseek().

Porestasrazones,mmap()esunaopcininteligenteparamuchasaplicaciones.

Hayunpardepuntosamantenerenmentecuandoseusammap():
Los mapeos a memoria tienen siempre un tamao que es mltiplo entero de pginas.
As, la diferencia entre el tamao del archivo y el nmero entero de pginas es
desperdiciado como espacio sobrado. Para archivos pequeos, un porcentaje
significativo del mapeo podra ser desperdiciado. Por ejemplo: con pginas de 4KB, un
archivode7bytesmapeadodesperdicia4,089bytes.

Los mapeos a memoria debe ajustarse dentro del espacio de direcciones del proceso.
Con un espacio de direcciones de 32 bits, un nmero muy grande de mapeos de
diferentes tamaos podra resultar en la fragmentacin del espacio de direcciones,

Laboratoriode

SistemasOperativos

haciendo difcil encontrar grandes regiones libres contiguas. Este problema, por
supuesto,esmuchomenosaparenteconespaciosdedireccionesde64bits.

Hay un overhead al crear y mantener los mapeos a memoria y las estructuras dedatos
asociadas dentro del kernel. Este overhead es obviado generalmente por la eliminacin
de la doble copia mencionada en la siguiente seccin, particularmente para archivos
grandesyfrecuentementeaccedidos.

Por estas razones, los beneficios de


mmap() son extremadamente notorios cuando el archivo
mapeado es grande (y as cualquier espacio desperdiciado es un pequeo porcentaje del
mapeo total), o cuando el tamao total del archivo mapeado es divisible por el tamao de
pgina(yasnohaydesperdiciodeespacio).

POSIX.1 estandarizay Linuximplementalallamadaasistema


mmap() paramapearobjetos
en memoria. Una llamada a
mmap() pide al kernel mapear
len bytes del objeto representado
por el file descriptor
fd
, iniciando a partir de
Offset bytes en el archivo, en memoria. Si se
incluye
addr
,seindicalapreferenciadeusarunadireccindeinicioenmemoria.Lospermisos
de acceso son dictados por
prot , y el comportamiento adicional puede ser dado por
flags
(Consultarelmanualdemmap).

Acontinuacinsereescribeelprogramacopiarutilizandolasfunciones
mmap()
y
unmap()
.
/* PROGRAMA:
copiar_mm.c(utilizando mapeo de memoria)
FORMA DE USO:
./copiar_mm origen destino
VALOR DE RETORNO:
0: si se ejecuta satisfactoriamente.
-1: si se da alguna condicion de error
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <
sys/mman.h
>
int main(int argc, char *argv[])
{
int fd_origen;
int fd_destino;
int nbytes;
void *src;
struct stat statbuf;
/*Analisis de los argumentos de la linea de comandos*/
if (argc != 3) {
fprintf(stderr,"Forma de uso: %s origen destino\n", argv[0]);
exit(-1);
}
/*Apertura del archivo en modo solo lectura*/
if ((fd_origen=open(argv[1],O_RDONLY))== -1) {
perror(argv[1]);
exit(-1);
}
/* Apertura o creacion de archivos en modo solo escritura*/

Laboratoriode

SistemasOperativos

if ((fd_destino=open(argv[2],O_WRONLY|O_TRUNC|O_CREAT, 0666))== -1) {


perror(argv[2]);
exit(-1);
}
/* Obtiene la longitud del archivo de lectura. */
fstat(fd_origen, &statbuf);
/*Mapea el archivo de entrada. */
if((src =
mmap
(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd_origen, 0))<0)
err_quit("mmap");
nbytes = statbuf.st_size;
/* Escribe el archivo de memoria a disco. */
write(fd_destino, src, nbytes);
close(fd_origen);
munmap
(src, statbuf.st_size);
close(fd_destino);
return 0;
}

Note que este programa utiliza las mismas funciones freadyfwriteparaleeryescribirarchivos


mapeadosamemoria.

Con el propsito de comparar usaremos el archivo >1GB que se utiliz en la prctica


"System Programming", en este caso llamado 100wrds
.
A continuacin se presentan, en la
Tabla 1, los tiempos de ejecucin de 100 corridas de
fcopiar y
copiar_mm
, as como el
tiempo promedio. Tambin se incluyen los resultados cuando se ejecuta el comando
cp
, el
cdigo que utiliza llamadas al sistema,
copiar
, y el cdigo cuando se fuerza escribir a disco
duro,
copiar_fw
. Se puede observar que el doble buffering puede tener efectos peores que
forzarlaescrituraendisco.

user@localhost~$time./copiar_mm./100wrds./wrds

corrida
1
2
3
4
5
6
7
8
9
10
Promedio

fcopiar
27.27
26.93
27.25
27.06
27.15
26.82
26.88
27.29
27.83
27.29
27.177

copiar_mm copiar_fw
1.46
27.09
1.18
25.21
1.64
25.13
0.79
26.34
1.1 25.09
0.81
25.34
1.25
25.09
1.21
25.67
1.02
25.18
1.26
25.64
1.172
25.578

copiar
1.03
1.39
1.37
1.86
1.54
0.95
0.65

cp
1.23
1.34
1.27
1.25
1.45
1.29
1.26
1 1.21
1.31
0.99
1.32
1.51
1.242
1.28

Tabla1.Comparacinentrelostiemposdeejecucindelasvariantesdelprogramacopiar.c

CrticasalaBibliotecaI/OEstndar.

Tan ampliamente usado como es el I/O Estndar, algunos expertos apuntan a fallas en l.

Laboratoriode

SistemasOperativos

Algunas de las funciones, como


fgets()
, son ocasionalmente inadecuadas. Otras funciones,
como
gets()
, son tan terribles que han sido prcticamente expulsadas de los estndares. La
queja ms grande con I/O estndar es el impacto en el rendimiento originado por la doble
copia. Cuandoseleendatos,elI/O estndar emiteunallamadaasistema
read() paraelkernel,
copiando los datos del kernel al buffer I/O estndar. Cuando una aplicacin emiteunapeticin
de lectura mediante I/O estndarpor decir, usando
fget()
los datos soncopiadosdenuevo,
en esta ocasin del buffer I/O estndar al buffer proporcionado. Las peticiones de escritura
trabajan en direccin contraria: los datos son copiados una vez del buffer proporcionado al
bufferI/Oestndar,yentoncesdespusdelbufferI/Oestndaralkernelmediante
write()
.

Una implementacin alternativa podra evitar la doble copia al hacer que cada peticin de
lectura regrese un apuntador al buffer I/O estndar. Los datos podran entonces ser ledos
directamente, dentro del buffer I/O estndar, sin la necesidad de una copia extraa. En el
evento de que la aplicacin quisiera que los datos se almacenarn en su propio buffer
localquizs escribira lpodrasiemprerealizarlacopiamanualmente.Estaimplementacin
proveera una interfaz libre, permitiendo a las aplicaciones indicar cuando se apropian de un
pedazo del buffer de lectura. Las escrituras seran un poco ms complicadas, pero an se
evitara la doble copia. Cuando se emite una peticin de escritura, la implementacin
almacenara el puntero. Al final, cuando se est listo para vaciar los datos al kernel, la
implementacin podra recorrer su lista de apuntadores almacenados, escribiendo a disco los
datos. Esto podra ser hecho usando I/O de dispersinreunin,va
writev()
,yporlotantosolo
una llamada a sistema. Existen bibliotecas userbuffering altamente ptimas, que resuelven el
problema de la doble copia con implementaciones similares a las que se han discutido.
Alternativamente, algunos desarrolladores eligen implementar sus propias soluciones. Pero a
pesardeestasalternativas,I/Oestndarpermanecepopular.

Implicacionesdel
buffering
delEstndarI/O:
Mejoresquelasllamadasasistemaparapeticionespequeasodesalineadasdebido
albufferingdeflujo.
Cuandoseleencaracteresusebibliotecasestndar.
Cuandoseleanbloquesusellamadasasistema(porladoblecopia).
Elanchodebandaeslimitadoalcopiardatosalbufferdeflujo.
Parapeticionesgrandes,
fwrite
evitaelbuffer.
fread
noevitaelbuffer.

====Laboratorio====
Actividadesarealizarenestaprcticaseencuentrandescritaseneste
documento,susrespuestasdebenregistrarseen:

E
nlacealformulario:

Prctica7:ProgramacindeSistemas(SystemProgramming)
.

Retroalimentacin:
RetroalimentacinProgramacindeSistemasylaboratorio

En una compaa de desarrollo de software libre se tiene un programa escrito C denominado

Laboratoriode

SistemasOperativos

fusionar.c, que sirve para fusionar archivos, es decir queagregueelcontenidodeunarchivoal


contenido de otro, y guarde el resultado en unarchivodesalida. Lasintaxis parasuuso debe
serlasiguiente:

./fusionar<archivo1><archivo2><salida>

Sin embargo, por tragedias que involucran un CPU abierto durante el mantenimiento y malas
prcticas de dnde colocar un refrescodecola,eldiscoduroque contenael cdigosedaoy
solo se consigui recuperar fragmentos del cdigo fuente orginal. Es su deber reescribir el
cdigo partiendo de lo que se recuper y que se despliega en la tabla de mas abajo. Como
buen desarrollador
, entregar un archivo de parche diferencial con loscambiosenlaslineas,
es decir, utilizar la herramienta
diff
. Paraindicarcualessonlaslneasrestauradasysunuevo
valor.

#include <stdio.h>
#include <stdlib.h>
#include <sys/____.h>
#include <sys/____.h>
#include <____.h>
#include <____.h>

/
* open() */

/
* open() */

/
* open() */

/
* read() y write() , close() */

char buffer[BUFSIZ]; /*Buffer para manipular datos. */


int main(int argc, char *argv[])
{
int fd_origen;
int fd_destino;
int nbytes;
int i;
/*Analisis de los argumentos de la linea de comandos*/
if (argc <= ____) {
fprintf(stderr,"Forma de uso: %s
______ \n", argv[____]);
exit(____);
}
/* Apertura o creacion de archivos en modo solo escritura. */
/* Abrir en modo solo Lectura (O_WRONLY). */
/* Si el archivo existe entonces truncar a cero bytes (O_TRUNC)*/
/* El archivo es creado en caso de que no exista (O_CREAT). */
/* El modo que se crea es 0666. */
if ((fd_destino=
____(argv[____-1],
O_WRONLY|O_TRUNC|O_CREAT,
0666))==-1) {
fprintf(stderr,"Error al crear el archivo de salida \n");
perror(argv[3]);
exit(____);
}
for(____;i<3;i++) {
/*Apertura del archivo 1 en modo solo lectura (O_RDONLY). */
if ((fd_origen=open(____,O_RDONLY))== -1) {
fprintf(stderr,"Error al abrir el archivo de entrada: %s n", ____);
perror(argv[1]);
exit(____);
}
/* copiamos el archivo 1 en el archivo destino. */
while ((nbytes=read(____, buffer, sizeof buffer))> 0)
write(____, buffer, nbytes);
close(____);
}

Laboratoriode

SistemasOperativos

close(____);
}

Puedesverunejemplodelaejecucindeesteprograma:

user@localhost~$
ls
body.c

fusionar.c

fusionar

headers.c

user@localhost~$
catheaders.c
#include<stdio.h>
user@localhost~$
catbody.c
constchar*msg="HelloEarthlings!"

intmain()
{
printf("MessagefromMars:%s\n",msg)

return0
}
user@localhost~$
./fusionarheaders.cbody.csecretProgram.c
user@localhost~$
ls
body.c

fusionar.c

fusionar

headers.c

secretProgram.c

user@localhost~$
gccWallosecretProgramsecretProgram.c
user@localhost~$
./secretProgram
MessagefromMars:HelloEarthlings!

Adems de este cdigo original se pretende recuperar archivos que se perdieron de forma
permanenteloscualestienenlassiguientescaractersticas:

1. Una versin modificada de fusionar.c quefusionecualquiernmerodearchivosquele


des como argumento, por ejemplo: que fusione todos los archivos con extensin.cen
unsoloarchivo:
./fusionar_todo*.ctodo.c
2. Una modificacin de fusionar.C que se llame
fusionar_sync.c cuyo objetivo sea
forzar la escritura al disco (Synchronized I/O). Si esto les suena a lengua klingon le
sugerimos LEA LA PRCTICA (Como todo buen programador que primero rene
herramientasantesdetocarcdigo).

*Reportaloscambiosrealizadosencadacdigoutilizandolaherramientadiff:
diffNaurfusionar_orig.cfusionar.c

Referencias
[1]Love,Robert.
LinuxSystemProgramming
,O'REILLY,2007.Biblioteca:QA76.76.O63L692007
[2]Marquez,Fernando.
ProgramacinAvanzadaenUNIX
,3a.edicion,2004.BibliotecaQA,76.76,
.O63,M37,2006(Reserva)

Laboratoriode

SistemasOperativos

Fuentesadicionales

Secciones del capitulo 1 del libro de Marquez[2]: Introduccion (Temas:


Estructura del
Sistema, llamadas al sistema, etapas de compilacin, y
Arquitectura del Sistema
Operativo UNIX, Dispositivos tipo bloque y tipo caracter, Gestion deMemoriaeInterfaz
dellamadasalsistema
).
Puedesleermssobresystemcallsenhttp://en.wikipedia.org/wiki/System_call.
Encontraras
la
implementacin
GNU
de
libc
en:
http://www.gnu.org/software/libc/manual/html_mono/libc.html.
Hay una lista de las llamadas a sistema ms comunesconelcomando
man syscalls
y
en:http://linux.die.net/man/2/syscalls.Usaremosestaltimareferencia.

También podría gustarte