Definición de un descriptor de archivo
Cynthia75
-
WhiteDeath313 -
WhiteDeath313 -
Hola a todos,
bueno, tengo muchas dificultades con la noción de "descriptor de archivo". He estado buscando definiciones en internet, en Wikipedia, etc., pero no está realmente claro. ¿Podrían explicarme con palabras bastante simples qué es un descriptor de archivo y cuál es su utilidad?
Gracias :)
Configuración: Linux / Firefox 43.0
bueno, tengo muchas dificultades con la noción de "descriptor de archivo". He estado buscando definiciones en internet, en Wikipedia, etc., pero no está realmente claro. ¿Podrían explicarme con palabras bastante simples qué es un descriptor de archivo y cuál es su utilidad?
Gracias :)
Configuración: Linux / Firefox 43.0
1 respuesta
Hola Cynthia,
¿Qué es un descriptor de archivo?
Un descriptor de archivo permite identificar un archivo (en sentido amplio) con el fin de leer o escribir en él.
¿Qué entiendo por archivo "en sentido amplio"? Todo lo que se llama "archivo" en Linux:
- los directorios (por ejemplo
- los archivos de datos (por ejemplo
- los sockets de red,
- los enlaces simbólicos,
- un tubo (o pipe en inglés, el famoso
- la entrada estándar (
- y muchos más ...
¿Para qué sirve?
Tu programa puede potencialmente interactuar con cualquier archivo(s) (en sentido amplio) y, por lo tanto, el día que quieras, por ejemplo, escribir en un archivo, tendrás que indicarlo a
Cuando comenzaste en C, probablemente te explicaron que el día que quisieras escribir en un terminal, tendrías que escribir, por ejemplo:
Ahora supongamos que quieres escribir en la salida de error, o en un archivo de datos, necesitarás una función que te permita indicar dónde escribir. Ese es el papel del descriptor de archivo.
La instrucción anterior es equivalente a
Ten en cuenta que en el mismo principio,
¿Por qué se crearon los descriptores de archivo?
Los flujos estándar son directamente utilizables y, por lo tanto, no es necesario preparar un descriptor de archivo para acceder a ellos. Esto no sería, por supuesto, el caso para leer o escribir en cualquier archivo: ¿cómo podría el programa adivinar de qué archivo estamos hablando? ¿Queremos tener que pasar sistemáticamente la ruta de este archivo y encontrarlo en el disco duro? Esa es la razón que motiva el uso de un descriptor de archivo.
Concretamente, un descriptor de archivo es un entero, y el sistema ha sido "preparado" para que cuando el programa escriba en él, sepa con quién debe trabajar. Esta preparación tiene un costo: hay que encontrar el archivo en el disco duro, verificar que el programa tiene derecho a acceder a él, etc. No es una operación trivial, y, en todo caso, no es un trabajo que querríamos desencadenar cada vez que nuestro programa quiera leer o escribir en un archivo.
¿Cómo manipular descriptores de archivo? (archivos regulares)
Para un archivo regular, esto se asegura mediante la función
Mientras este descriptor de archivo que accede al archivo en lectura (o escritura) exista, se puede leer (o escribir) en él.
Una vez que hemos terminado de trabajar con este archivo, debemos informar al sistema cuándo puede liberarlo. Esa es la función de
Así, en C, abrir un archivo se hace así:
Nota que
Para ir más allá (1/2)
Para entender un concepto, es interesante ver cómo se materializa en otros lenguajes, especialmente en un lenguaje orientado a objetos.
En C++, la noción de descriptor de archivo está enmascarada por la noción de flujo. Estos son objetos que "envuelven" esos famosos descriptores de archivo y que se utilizan exactamente de la misma manera.
Así, los fd
Para escribir en un archivo, se utiliza un flujo de salida (
Para ir más allá (2/2)
Para hacer un programa que trabaje con otro programa a través de la red, se utilizan sockets. En C, los sockets también se manipulan a través de fd. El principio sigue siendo fundamentalmente el mismo:
1) creación del socket (para recuperar un fd)
2) uso del fd (escritura para enviar datos, lectura para recibir)
3) cierre del socket para interrumpir la comunicación (o cerrarlo en caso de problema)
Las funciones son, por supuesto, un poco diferentes pero el principio sigue siendo idéntico. Atención a tener en cuenta que dependiendo del protocolo de transporte (UDP o TCP), el uso de los sockets difiere un poco, por lo que no hay que mezclar. Para comprender bien, te remito a un tutorial sobre sockets ;-)
Buena suerte.
¿Qué es un descriptor de archivo?
Un descriptor de archivo permite identificar un archivo (en sentido amplio) con el fin de leer o escribir en él.
¿Qué entiendo por archivo "en sentido amplio"? Todo lo que se llama "archivo" en Linux:
- los directorios (por ejemplo
/home/toto)
- los archivos de datos (por ejemplo
/home/toto/fichero.txt),
- los sockets de red,
- los enlaces simbólicos,
- un tubo (o pipe en inglés, el famoso
|que a menudo se ve en Linux)
- la entrada estándar (
/dev/stdin), la salida estándar (
/dev/stdout), y la salida de error estándar (
/dev/stderr)
- y muchos más ...
¿Para qué sirve?
Tu programa puede potencialmente interactuar con cualquier archivo(s) (en sentido amplio) y, por lo tanto, el día que quieras, por ejemplo, escribir en un archivo, tendrás que indicarlo a
fprintf.
Cuando comenzaste en C, probablemente te explicaron que el día que quisieras escribir en un terminal, tendrías que escribir, por ejemplo:
printf("coucou");. Es decir, escribir en stdoutla cadena de caracteres "coucou".
Ahora supongamos que quieres escribir en la salida de error, o en un archivo de datos, necesitarás una función que te permita indicar dónde escribir. Ese es el papel del descriptor de archivo.
La instrucción anterior es equivalente a
fprintf(stdout, "coucou");. Si queremos escribir en la salida de error escribiríamos
fprintf(stderr, "coucou");.
Ten en cuenta que en el mismo principio,
stdines el descriptor de archivo desde el que lee
scanf(entrada estándar), y esta función es en realidad equivalente a
fscanf(stdin, ...).
¿Por qué se crearon los descriptores de archivo?
Los flujos estándar son directamente utilizables y, por lo tanto, no es necesario preparar un descriptor de archivo para acceder a ellos. Esto no sería, por supuesto, el caso para leer o escribir en cualquier archivo: ¿cómo podría el programa adivinar de qué archivo estamos hablando? ¿Queremos tener que pasar sistemáticamente la ruta de este archivo y encontrarlo en el disco duro? Esa es la razón que motiva el uso de un descriptor de archivo.
Concretamente, un descriptor de archivo es un entero, y el sistema ha sido "preparado" para que cuando el programa escriba en él, sepa con quién debe trabajar. Esta preparación tiene un costo: hay que encontrar el archivo en el disco duro, verificar que el programa tiene derecho a acceder a él, etc. No es una operación trivial, y, en todo caso, no es un trabajo que querríamos desencadenar cada vez que nuestro programa quiera leer o escribir en un archivo.
¿Cómo manipular descriptores de archivo? (archivos regulares)
Para un archivo regular, esto se asegura mediante la función
fopen. Dependiendo de quién ejecute el programa y de los derechos asociados a este archivo, el sistema aceptará o no crear un descriptor de archivo, y por eso el retorno de
fopendebe ser controlado.
Mientras este descriptor de archivo que accede al archivo en lectura (o escritura) exista, se puede leer (o escribir) en él.
Una vez que hemos terminado de trabajar con este archivo, debemos informar al sistema cuándo puede liberarlo. Esa es la función de
fclose. Es indispensable cerrar el archivo de manera adecuada, ya que la escritura efectiva de datos en un archivo solo se garantiza cuando se cierra el archivo. De hecho, para optimizar la ejecución del programa, el sistema puede decidir esperar a tener más datos para escribir antes de proceder a la escritura. Esta elección técnica se debe a que una operación de entrada/salida es una operación lenta en informática, por lo tanto, se trata de limitar su número. Además, dejar un descriptor de archivo abierto puede provocar agujeros de seguridad.
Así, en C, abrir un archivo se hace así:
#include <stdio.h> int main() { const char * filename = "/home/toto.txt"; int x = 7; FILE * fd = fopen(filename, "w"); if (fd) { // éxito // uso del archivo fprintf(fd, "coucou %d\n", x); fclose(fd); } else { // fallo fprintf(stderr, "Can't read %s\n", filename); } return 0; } Nota que
fclosesolo debe ser llamado si el descriptor de archivo se abrió con éxito. También podemos imaginar casos más complicados, en los que manipulas simultáneamente varios archivos. En este caso, será necesario tener tantos archivos como descriptores de archivo.
Para ir más allá (1/2)
Para entender un concepto, es interesante ver cómo se materializa en otros lenguajes, especialmente en un lenguaje orientado a objetos.
En C++, la noción de descriptor de archivo está enmascarada por la noción de flujo. Estos son objetos que "envuelven" esos famosos descriptores de archivo y que se utilizan exactamente de la misma manera.
Así, los fd
stdin,
stderr,
stdout, corresponden respectivamente a los flujos
std::cin,
std::cerr,
std::cout. Las operaciones de escritura (
fprintf) se convierten en
<<, y las de lectura (
fscanf) se convierten en
>>.
Para escribir en un archivo, se utiliza un flujo de salida (
std::ostream), y más concretamente un
std::ofstreamcuando se trata de un archivo de datos. El programa anterior se convierte entonces en:
#include <fstream> #include <iostream> int main() { const char * filename = "/home/toto.txt"; int x = 7; std::ofstream ofs(filename); if (ofs) { // éxito // uso del archivo ofs << "coucou " << x << std::endl; ofs.close(); } else { // fallo std::cerr << "Can't read " << filename << std::endl; } return 0; } Para ir más allá (2/2)
Para hacer un programa que trabaje con otro programa a través de la red, se utilizan sockets. En C, los sockets también se manipulan a través de fd. El principio sigue siendo fundamentalmente el mismo:
1) creación del socket (para recuperar un fd)
2) uso del fd (escritura para enviar datos, lectura para recibir)
3) cierre del socket para interrumpir la comunicación (o cerrarlo en caso de problema)
Las funciones son, por supuesto, un poco diferentes pero el principio sigue siendo idéntico. Atención a tener en cuenta que dependiendo del protocolo de transporte (UDP o TCP), el uso de los sockets difiere un poco, por lo que no hay que mezclar. Para comprender bien, te remito a un tutorial sobre sockets ;-)
Buena suerte.
Sadikoi
¡Respuesta clara y completa! Gracias.
WhiteDeath313
Muchas gracias por su ayuda :) .