PDF de programación - Buffers C

Imágen de pdf Buffers C

Buffers Cgráfica de visualizaciones

Publicado el 1 de Marzo del 2017
1.097 visualizaciones desde el 1 de Marzo del 2017
877,2 KB
7 paginas
Creado hace 16a (03/05/2008)
El buffer de la entrada estándar en GNU/Linux

Matías Zabaljáuregui ([email protected])
http://linux.linti.unlp.edu.ar

0. Introducción
Se intentará describir en términos sencillos la forma de tratar los datos ingresados por el 
usuario a través de la entrada estándar de un proceso explicando brevemente el 
funcionamiento de los buffers de los stream estándar de entrada/salida. 
Parte de la información se obtiene del manual de la librería glibc, cuya lectura se 
recomienda y puede encontrarse en: 
http 

 ://   www.gnu.org

 

 /software/
 

 /manual/
 libc 
 
 

1. Streams Entrada/Salida
Un stream es una abstracción de alto nivel que representa un canal de comunicación 
hacia o desde un archivo, un dispositivo o un proceso. Este concepto se implementa a 
través de punteros a estructuras de tipo FILE declaradas en el archivo stdio.h. 
Cuando un proceso comienza su ejecución, el sistema operativo abre tres streams:  stdin 
(standard input), stdout (standard output) y stderr (standard error). Generalmente el 
primero se asocia al teclado y los otros dos al monitor. Ésto puede modificarse al 
momento de invocar el proceso utilizando las facilidades de redirección y pipes del shell 
de Linux. 

2. Buffers
Los caracteres que escribimos en un stream, normalmente se van acumulando en un 
buffer (espacio de memoria) para ser transmitidos asincrónicamente hacia el dispositivo o 
archivo. Por ejemplo, si estamos escribiendo en un archivo de texto, cada vez que 
enviamos un carácter al stream, este es almacenado temporalmente en un buffer en 
espacio de usuario, hasta que por algún evento (buffer lleno, carácter de fin de línea, 
función de limpiado de buffers, etc) se invoca a la llamada al sistema que envía los datos 
al disco[1]. 

[1] En realidad, los datos pasarán por toda una jerarquía de buffers 

antes de ser escritos en el dispositivo. Estos buffers se implementan en los subsistemas de Page Cache, 
Virtual File System (VFS) y el driver del dispositivo de almacenamiento, todos incluidos en el kernel Linux.

1

Del mismo modo, cuando ingresamos caracteres del teclado u otro dispositivo de entrada, 
normalmente estos se van acumulando en un buffer en espacio de usuario (previo 
buffering en espacio del kernel aunque hay algunas excepciones a esto). Cuando se 
llama a una función de lectura sobre stdin, en realidad estamos leyendo el contenido de 
este buffer, como se observa en el siguiente diagrama:

fgets (miBuffer, 15, stdin)

(espacio de usuario)

^
^

buffer de stdin

(espacio de usuario)

^
^

driver / subsistema TTY (kernel)

^
^

dispositivo de entrada

(hardware)

3. Estrategias de Buffering
Existen tres estrategias diferentes de buffering: 
• Unbeffered Stream

• Line Buffered Stream

 : Los caracteres escritos al stream son transmitidos en bloques  al 

 : Los caracteres escritos o leídos desde el stream son transmitidos 

 
individualmente hacia o desde el dispositivo/archivo tan pronto como sea posible.
 
archivo recién cuando se escribe un carácter newline ('\n').
 Fully Buffered Stream:  Los caracteres escritos o leídos desde el stream son 
transmitidos hacia o desde el dispositivo/archivo en bloques de tamaño arbitrario.



Al crear un stream, éste normalmente utiliza una estrategia Fully Buffered. Sin embargo 
existe una excepción importante: los stream de salida asociados a un dispositivo 
interactivo como una terminal, los cuales responden a una estrategia Line Buffered. Esto 
implica que los mensajes de salida que terminen con el carácter newline deberían 
aparecer inmediatamente en pantalla. 

2

Al principio este proceso suele resultar un tanto engorroso por el hecho de que en 
realidad se está realizando una operación tan simple como por ejemplo la lectura de 
caracteres del teclado. Sin embargo debería tenerse en cuenta que la interfaz ofrecida 
por los streams nos permite el acceso a todo tipo de hardware [2] y la posibilidad de 
optimizar el mecanismo de buffering subyacente puede implicar mejoras importantes de la 
performance. 

[2] Se suele decir que en la familia de sistemas operativos tipo Unix  todo es un archivo. Por ejemplo, el 
código para enviar una serie de caracteres a un archivo de texto, al puerto serie o al monitor es el mismo.

 

4. Flush y Purge
Hacer un flush de la salida  en el buffer de un stream significa transmitir al 
archivo/dispositivo todos los caracteres acumulados en el buffer. Existen varios eventos 
que disparan una operación de flush automáticamente, pero cuando necesitamos 
invocarla explícitamente, se utiliza la función fflush  declarada en stdio.h: 

 int fflush (FILE *stream)

This function causes any buffered output on stream to be delivered to the file. If stream 
is a null pointer, then fflush causes buffered output on all open output streams to be 
flushed. 

This function returns EOF if a write error occurs, or zero otherwise. 

En algunas situaciones donde la información en el buffer no es útil y simplemente se 
puede desechar  es posible utilizar una función que fue introducida en Solaris y que si 
bien NO es estándar, se incluye en la librería glibc. Notar que es necesario incluir el 
archivo stdio_ext.h, en cuyo caso ya no será necesario incluir stdio.h.

void __fpurge (FILE *stream)

The __fpurge function causes the buffer of the stream stream to be emptied. If the 
stream is currently in read mode all input in the buffer is lost. If the stream is in output 

3

mode the buffered output is not written to the device (or whatever other underlying 
storage) and the buffer the cleared. 

This function is declared in stdio_ext.h. 

5. Ejemplos de cómo limpiar el buffer de teclado
Ejecutando el siguiente código:

#include <stdio.h>

int main()

{

}

char nombre[50];

printf("Nombre: ");

scanf("%s", nombre);

printf("%s", nombre);

printf("­newline\n");

return 0;

Se puede observar que scanf deja el carácter newline en el buffer de stdin en lugar de 
copiarlo a nombre. Este es uno de los casos en que en las próximas llamadas a scanf (u 
otra función) tendremos comportamientos extraños. Pero no todas las funciones de 
lectura se comportan igual, por ejemplo: 

#include <stdio.h>

int main()

char nombre[50];

printf("Nombre: ");

fgets(nombre,sizeof(nombre),stdin);

printf("%s", nombre);

printf("­newline\n");

{

4

return 0;

}

Este código demuestra que fgets no deja el carácter newline en el buffer de stdin, en 
cambio lo copia a nombre. Sin embargo, si ingresamos por teclado un string de longitud 
mayor o igual a 50 tendremos problemas si luego leemos nuevamente de stdin ya que 
fgets lee caracteres del buffer hasta uno menos que la cantidad indicada en su segundo 
argumento (en este caso lee 49 y en la última posición coloca el carácter '\0', o fin de 
string).

Por ejemplo, si se compila el siguiente código 

#include <stdio.h>

int main()

{

char nombre[5];

printf("Nombre: ");

fgets(nombre,sizeof(nombre),stdin);

printf("%s", nombre);

printf("­newline\n");

fgets(nombre,sizeof(nombre),stdin);

printf("%s", nombre);

printf("­newline\n");

return 0;

}

y se ingresa un string de 10 caracteres en la primer oportunidad que nos da el programa, 
el segundo fgets toma el remanente dejado en el buffer por el primer fgets.

A continuación se presentan dos ejemplos de cómo se puede limpiar el buffer del stream 

5

stdin en GNU/Linux. Es importante recordar que fflush() no está definida para stdin, sino 
para buffers de salida como el de stdout.

 En el primer ejemplo se utiliza un loop para terminar de leer caracteres que pueden 
haber quedado en el buffer por ingresos previos de caracteres. Este es el código

#include <stdio.h>

int main()

{

char nombre[5];

printf("Nombre: ");

fgets(nombre,sizeof(nombre),stdin);

printf("%s", nombre);

printf("­newline\n");

printf("Apellido: ");

while(getchar() != '\n');/*LEE DEL BUFFER HASTA QUE ENCUENTRA 

fgets(nombre,sizeof(nombre),stdin);    UN CARACTER NEWLINE*/

printf("%s", nombre);

printf("­newline\n");

return 0;

}

Es posible definir una macro que implemente el loop, como se hace a continuación:

#define FLUSH  while(getchar() != '\n')

El segundo ejemplo utiliza la función __fpurge()  explicada en el punto 4.  Debe 
destacarse que __fpurge no es estandar y que si la portabilidad del código que estamos 
generando es un factor importante, se debería utilizar la solución anterior, sin embargo 
este código funciona sin problemas en GNU/Linux (asi como en otras variantes de Unix).

6

#include <stdio_ext.h>

char nombre[5];

printf("Nombre: ");

fgets(nombre,sizeof(nombre),stdin);

printf("%s", nombre);

printf("­newline\n");

printf("Apellido: ");

__fpurge(stdin);   /*BORRA EL CONTENIDO DEL BUFFER DE STDIN*/

fgets(nombre,sizeof(nombre),stdin);

printf("%s", nombre);

printf("­newline\n");

return 0;

{

}

7
  • Links de descarga
http://lwp-l.com/pdf2508

Comentarios de: Buffers C (0)


No hay comentarios
 

Comentar...

Nombre
Correo (no se visualiza en la web)
Valoración
Comentarios...
CerrarCerrar
CerrarCerrar
Cerrar

Tienes que ser un usuario registrado para poder insertar imágenes, archivos y/o videos.

Puedes registrarte o validarte desde aquí.

Codigo
Negrita
Subrayado
Tachado
Cursiva
Insertar enlace
Imagen externa
Emoticon
Tabular
Centrar
Titulo
Linea
Disminuir
Aumentar
Vista preliminar
sonreir
dientes
lengua
guiño
enfadado
confundido
llorar
avergonzado
sorprendido
triste
sol
estrella
jarra
camara
taza de cafe
email
beso
bombilla
amor
mal
bien
Es necesario revisar y aceptar las políticas de privacidad