En todos los lenguajes de programación (que utilizo al menos), debe abrir un archivo antes de poder leerlo o escribirlo.
Pero, ¿qué hace realmente esta operación abierta?
Las páginas de manual para funciones típicas en realidad no le dicen nada más que 'abre un archivo para leer / escribir':
http://www.cplusplus.com/reference/cstdio/fopen/
https://docs.python.org/3/library/functions.html#open
Obviamente, a través del uso de la función, puede decir que implica la creación de algún tipo de objeto que facilita el acceso a un archivo.
Otra forma de decir esto sería, si tuviera que implementar una open
función, ¿qué necesitaría hacer en Linux?
C
Linux; ya que lo que hacen Linux y Windows es diferente. De lo contrario, es un poco demasiado amplio. Además, cualquier lenguaje de nivel superior terminará llamando a una API de C para el sistema o compilando a C para ejecutar, por lo que salir al nivel de "C" es colocarlo en el mínimo común denominador.Respuestas:
En casi todos los lenguajes de alto nivel, la función que abre un archivo es un contenedor alrededor de la correspondiente llamada al sistema del núcleo. También puede hacer otras cosas sofisticadas, pero en los sistemas operativos contemporáneos, abrir un archivo siempre debe pasar por el núcleo.
Esta es la razón por la cual los argumentos de la
fopen
función de biblioteca, o Python,open
se parecen mucho a los argumentos de laopen(2)
llamada al sistema.Además de abrir el archivo, estas funciones generalmente configuran un búfer que se utilizará en consecuencia con las operaciones de lectura / escritura. El propósito de este búfer es garantizar que cada vez que desee leer N bytes, la llamada a la biblioteca correspondiente devuelva N bytes, independientemente de si las llamadas a las llamadas del sistema subyacente devuelven menos.
En los sistemas operativos tipo Unix, una llamada exitosa a
open
devuelve un "descriptor de archivo" que es simplemente un número entero en el contexto del proceso del usuario. En consecuencia, este descriptor se pasa a cualquier llamada que interactúa con el archivo abierto, y después de invocarloclose
, el descriptor deja de ser válido.Es importante tener en cuenta que la llamada a
open
actuar actúa como un punto de validación en el que se realizan varias verificaciones. Si no se cumplen todas las condiciones, la llamada falla al regresar en-1
lugar del descriptor, y el tipo de error se indica enerrno
. Los controles esenciales son:En el contexto del núcleo, tiene que haber algún tipo de mapeo entre los descriptores de archivo del proceso y los archivos abiertos físicamente. La estructura de datos interna que se asigna al descriptor puede contener otro búfer que se ocupa de dispositivos basados en bloques, o un puntero interno que apunta a la posición actual de lectura / escritura.
fuente
man dup2
y verifique la sutileza entre un descriptor de archivo abierto (que es un FD que está abierto) y una descripción de archivo abierto (un OFD).Le sugiero que eche un vistazo a esta guía a través de una versión simplificada de la
open()
llamada al sistema . Utiliza el siguiente fragmento de código, que es representativo de lo que sucede detrás de escena cuando abre un archivo.Brevemente, esto es lo que hace ese código, línea por línea:
La
filp_open
función tiene la implementaciónque hace dos cosas:
struct file
con la información esencial sobre el inodo y devuélvalo. Esta estructura se convierte en la entrada en esa lista de archivos abiertos que mencioné anteriormente.Almacene ("instale") la estructura devuelta en la lista de archivos abiertos del proceso.
read()
,write()
, yclose()
. Cada uno de estos entregará el control al kernel, que puede usar el descriptor de archivo para buscar el puntero de archivo correspondiente en la lista del proceso, y usar la información en ese puntero de archivo para realizar realmente la lectura, escritura o cierre.Si se siente ambicioso, puede comparar este ejemplo simplificado con la implementación de la
open()
llamada del sistema en el kernel de Linux, una función llamadado_sys_open()
. No deberías tener problemas para encontrar las similitudes.Por supuesto, esta es solo la "capa superior" de lo que sucede cuando llama
open()
, o más precisamente, es el código de kernel de más alto nivel que se invoca en el proceso de abrir un archivo. Un lenguaje de programación de alto nivel podría agregar capas adicionales además de esto. Hay muchas cosas que suceden en los niveles inferiores. (Gracias a Ruslan y pjc50 por su explicación). Aproximadamente, de arriba a abajo:open_namei()
edentry_open()
invoque el código del sistema de archivos, que también es parte del núcleo, para acceder a metadatos y contenido de archivos y directorios. El sistema de archivos lee bytes sin procesar del disco e interpreta esos patrones de bytes como un árbol de archivos y directorios./dev/sda
y similares).Esto también puede ser algo incorrecto debido al almacenamiento en caché . :-P En serio, hay muchos detalles que he omitido: una persona (no yo) podría escribir varios libros que describan cómo funciona todo este proceso. Pero eso debería darte una idea.
fuente
Cualquier sistema de archivos o sistema operativo del que quieras hablar está bien para mí. ¡Agradable!
En un ZX Spectrum, la inicialización de un
LOAD
comando pondrá el sistema en un ciclo cerrado, leyendo la línea de entrada de audio.El inicio de los datos se indica mediante un tono constante, y luego sigue una secuencia de pulsos largos / cortos, donde un pulso corto es para un binario
0
y uno más largo para un binario1
( https://en.wikipedia.org/ wiki / ZX_Spectrum_software ). El bucle de carga ajustada reúne bits hasta que llena un byte (8 bits), lo almacena en la memoria, aumenta el puntero de la memoria y luego vuelve a recorrer para buscar más bits.Por lo general, lo primero que leería un cargador es un encabezado de formato corto y fijo , que indique al menos el número de bytes que se esperan, y posiblemente información adicional como el nombre del archivo, el tipo de archivo y la dirección de carga. Después de leer este breve encabezado, el programa podría decidir si continúa cargando la mayor parte de los datos o si sale de la rutina de carga y muestra un mensaje apropiado para el usuario.
Se podría reconocer un estado de fin de archivo al recibir tantos bytes como se esperaba (ya sea un número fijo de bytes, cableado en el software o un número variable como se indica en un encabezado). Se produjo un error si el bucle de carga no recibió un pulso en el rango de frecuencia esperado durante un cierto período de tiempo.
Un poco de historia sobre esta respuesta
El procedimiento descrito carga datos de una cinta de audio normal, de ahí la necesidad de escanear la entrada de audio (se conecta con un enchufe estándar a las grabadoras de cinta). Un
LOAD
comando es técnicamente lo mismo queopen
un archivo, pero está físicamente vinculado a la carga real del archivo. Esto se debe a que la computadora no controla la grabadora y no puede (con éxito) abrir un archivo pero no cargarlo.El "lazo cerrado" se menciona porque (1) la CPU, un Z80-A (si la memoria sirve), era realmente lenta: 3.5 MHz, y (2) ¡el Spectrum no tenía reloj interno! Eso significa que tenía que mantener con precisión el recuento de los estados T (tiempos de instrucción) para cada uno. soltero. instrucción. dentro de ese bucle, solo para mantener la sincronización precisa del pitido.
Afortunadamente, esa baja velocidad de la CPU tenía la clara ventaja de que podía calcular el número de ciclos en una hoja de papel y, por lo tanto, el tiempo real que tomarían.
fuente
Depende del sistema operativo lo que sucede exactamente cuando abre un archivo. A continuación, describo lo que sucede en Linux, ya que le da una idea de lo que sucede cuando abre un archivo y puede verificar el código fuente si está interesado en obtener más detalles. No estoy cubriendo los permisos, ya que haría que esta respuesta fuera demasiado larga.
En Linux cada archivo es reconocido por una estructura llamada inodo. Cada estructura tiene un número único y cada archivo solo obtiene un número de inodo. Esta estructura almacena metadatos para un archivo, por ejemplo, tamaño de archivo, permisos de archivo, marcas de tiempo y puntero a bloques de disco, sin embargo, no el nombre del archivo en sí. Cada archivo (y directorio) contiene una entrada de nombre de archivo y el número de inodo para la búsqueda. Cuando abre un archivo, suponiendo que tiene los permisos relevantes, se crea un descriptor de archivo utilizando el número de inodo único asociado con el nombre del archivo. Como muchos procesos / aplicaciones pueden apuntar al mismo archivo, inode tiene un campo de enlace que mantiene el recuento total de enlaces al archivo. Si un archivo está presente en un directorio, su recuento de enlaces es uno, si tiene un enlace fijo, su recuento de enlaces será dos y si un archivo se abre mediante un proceso, el recuento de enlaces se incrementará en 1.
fuente
Teneduría de libros, en su mayoría. Esto incluye varias verificaciones como "¿Existe el archivo?" y "¿Tengo los permisos para abrir este archivo para escribir?".
Pero eso es todo del núcleo: a menos que esté implementando su propio sistema operativo de juguete, no hay mucho en lo que profundizar (si es así, diviértase, es una gran experiencia de aprendizaje). Por supuesto, aún debe aprender todos los códigos de error posibles que puede recibir al abrir un archivo, para que pueda manejarlos correctamente, pero generalmente son pequeñas abstracciones agradables.
La parte más importante en el nivel de código es que le da un control del archivo abierto, que utiliza para todas las demás operaciones que realiza con un archivo. ¿No podría usar el nombre de archivo en lugar de este identificador arbitrario? Bueno, claro, pero usar un mango te da algunas ventajas:
read
desde la última posición en su archivo. Al usar un identificador para identificar una "apertura" particular de un archivo, puede tener múltiples identificadores simultáneos para el mismo archivo, cada uno de los cuales se lee desde sus propios lugares. En cierto modo, el identificador actúa como una ventana móvil en el archivo (y una forma de emitir solicitudes de E / S asíncronas, que son muy útiles).También hay otros trucos que puede hacer (por ejemplo, compartir manejadores entre procesos para tener un canal de comunicación sin usar un archivo físico; en sistemas unix, los archivos también se usan para dispositivos y otros canales virtuales, por lo que esto no es estrictamente necesario) ), pero no están realmente vinculados a la
open
operación en sí, por lo que no voy a profundizar en eso.fuente
En el fondo, cuando se abre para leer, no es necesario que ocurra nada lujoso . Todo lo que necesita hacer es verificar que el archivo existe y que la aplicación tiene suficientes privilegios para leerlo y crear un controlador en el que pueda emitir comandos de lectura para el archivo.
Es en esos comandos que se enviará la lectura real.
El sistema operativo a menudo obtendrá una ventaja inicial en la lectura al iniciar una operación de lectura para llenar el búfer asociado con el identificador. Luego, cuando realmente hace la lectura, puede devolver el contenido del búfer inmediatamente en lugar de tener que esperar en el disco IO.
Para abrir un nuevo archivo para escribir, el sistema operativo deberá agregar una entrada en el directorio para el nuevo archivo (actualmente vacío). Y nuevamente se crea un identificador en el que puede emitir los comandos de escritura.
fuente
Básicamente, una llamada para abrir necesita encontrar el archivo y luego registrar lo que sea necesario para que las operaciones de E / S posteriores puedan encontrarlo nuevamente. Eso es bastante vago, pero será cierto en todos los sistemas operativos en los que puedo pensar de inmediato. Los detalles varían de una plataforma a otra. Muchas respuestas ya aquí hablan sobre los sistemas operativos de escritorio modernos. He realizado un poco de programación en CP / M, por lo que ofreceré mis conocimientos sobre cómo funciona en CP / M (MS-DOS probablemente funciona de la misma manera, pero por razones de seguridad, normalmente no se hace así hoy) )
En CP / M tiene una cosa llamada FCB (como mencionó C, podría llamarlo una estructura; realmente es un área contigua de 35 bytes en RAM que contiene varios campos). El FCB tiene campos para escribir el nombre de archivo y un entero (4 bits) que identifica la unidad de disco. Luego, cuando llama al archivo abierto del núcleo, pasa un puntero a esta estructura colocándola en uno de los registros de la CPU. Algún tiempo después, el sistema operativo regresa con la estructura ligeramente modificada. Independientemente de la E / S que haga a este archivo, pasará un puntero a esta estructura a la llamada del sistema.
¿Qué hace CP / M con este FCB? Reserva ciertos campos para su propio uso, y los utiliza para realizar un seguimiento del archivo, por lo que es mejor que nunca los toque desde el interior de su programa. La operación Abrir archivo busca en la tabla al inicio del disco un archivo con el mismo nombre que el contenido de la FCB (el carácter comodín '?' Coincide con cualquier carácter). Si encuentra un archivo, copia cierta información en el FCB, incluidas las ubicaciones físicas del archivo en el disco, de modo que las llamadas de E / S posteriores finalmente llamen al BIOS, que puede pasar estas ubicaciones al controlador de disco. En este nivel, los detalles varían.
fuente
En términos simples, cuando abre un archivo, en realidad está solicitando al sistema operativo que cargue el archivo deseado (copie el contenido del archivo) del almacenamiento secundario a la memoria RAM para su procesamiento. Y la razón detrás de esto (cargar un archivo) es porque no puede procesar el archivo directamente desde el disco duro debido a su velocidad extremadamente lenta en comparación con Ram.
El comando abrir generará una llamada al sistema que a su vez copia el contenido del archivo desde el almacenamiento secundario (disco duro) al almacenamiento primario (Ram).
Y 'Cerrar' un archivo porque el contenido modificado del archivo debe reflejarse en el archivo original que se encuentra en el disco duro. :)
Espero que ayude.
fuente