En los sistemas Unix, ¿por qué tenemos que explícitamente `abrir ()` y `cerrar ()` archivos para poder `leer ()` o `escribir ()`?

50

¿Por qué existen open()y close()existen en el diseño del sistema de archivos Unix?

¿No podría el sistema operativo detectar la primera vez read()o write()fue llamado y hacer lo open()que normalmente haría?

usuario5977637
fuente
22
Vale la pena señalar que este modelo no es parte del sistema de archivos, sino más bien de la API de Unix . El sistema de archivos solo tiene que ver con dónde van los bytes en el disco y dónde colocar el nombre de archivo, etc. Sería perfectamente posible tener el modelo alternativo que usted describe encima de un sistema de archivos Unix como UFS o ext4, sería hasta kernel para traducir esas llamadas en las actualizaciones adecuadas para el sistema de archivos (tal como es ahora).
marcelm
18
Como lo expresé, creo que se trata más de por qué open()existe. "¿No podría el sistema operativo detectar la primera vez read () o write () y hacer lo que normalmente haría open ()?" ¿Hay alguna sugerencia correspondiente para cuándo ocurriría el cierre ?
Joshua Taylor
77
¿Cómo le dirías read()o a write()qué archivo acceder? Presumiblemente pasando el camino. ¿Qué sucede si la ruta del archivo cambia mientras está accediendo (entre dos read()o write()llamadas)?
user253751
2
Además, por lo general, no se hace control de acceso read()y write()solo se activa open().
Pavel Šimerda
66
@Johnny: Quizás estés olvidando cuán limitado era el hardware en esos días. El PDP-7 en el que se implementó Unix por primera vez tenía (según Google) un máximo de 64K de RAM y un reloj de 0.333 MHz, en lugar de un microcontrolador simple en estos días. Hacer tal recolección de basura, o usar el código del sistema para monitorear el acceso a los archivos, habría puesto al sistema de rodillas.
jamesqf

Respuestas:

60

Dennis Ritchie menciona en «La evolución del sistema de tiempo compartido Unix» que open, y closejunto con read, writey createstuvieron presentes en el sistema desde el principio.

Supongo que un sistema sin openy closeno sería inconcebible, sin embargo, creo que complicaría el diseño. En general, desea realizar múltiples llamadas de lectura y escritura, no solo una, y eso probablemente fue especialmente cierto en aquellas computadoras viejas con RAM muy limitada en la que se originó UNIX. Tener un controlador que mantenga su posición actual de archivo simplifica esto. Si readowritepara devolver el identificador, tendrían que devolver un par, un identificador y su propio estado de devolución. La parte de manejo del par sería inútil para todas las demás llamadas, lo que haría que ese arreglo fuera incómodo. Dejar el estado del cursor en el núcleo le permite mejorar la eficiencia no solo mediante el almacenamiento en búfer. También hay algunos costos asociados con la búsqueda de rutas: tener un identificador le permite pagarlo solo una vez. Además, algunos archivos en la visión del mundo UNIX ni siquiera tienen una ruta de acceso al sistema de archivos (o no la tenían, ahora lo hacen con cosas como /proc/self/fd).

PSkocik
fuente
77
El costo de la búsqueda de rutas y la verificación de permisos, etc., etc. es muy significativo. Si quisieras hacer un sistema sin open/ close, estarías seguro de implementar cosas como /dev/stdoutpermitir tuberías.
Peter Cordes
55
Creo que otro aspecto de esto es que puedes mantener ese identificador en el mismo archivo cuando usas múltiples lecturas cuando mantienes el archivo abierto. De lo contrario, podría tener casos en los que otro proceso desvincula y vuelva a crear un archivo con el mismo nombre, y leer un archivo en fragmentos podría ser completamente incoherente. (Algo de esto también puede depender del sistema de archivos).
Bruno
2
Diseñé uno sin cerrar (); pasa el número de inodo y el desplazamiento a read () y write (). No puedo prescindir de open () muy fácilmente porque ahí es donde vive la resolución de nombres.
Joshua
3
@Joshua: Tal sistema tiene una semántica fundamentalmente diferente porque los descriptores de archivos unix no se refieren a archivos (inodes) sino a descripciones de archivos abiertos , de los cuales puede haber muchos para un archivo dado (inode).
R ..
@Joshua, sólo cambió el nombre open()a get_inode()e hizo que todo el sistema sea más rígida (imposible de leer / escribir el mismo archivo en varias posiciones al mismo tiempo).
vonbrand
53

Entonces todas las llamadas ready writetendrían que pasar esta información en cada operación:

  • el nombre del archivo
  • los permisos del archivo
  • si la persona que llama está agregando o creando
  • si la persona que llama ha terminado de trabajar con el archivo (para descartar los búferes de lectura no utilizados y garantizar que los búferes de escritura realmente terminen de escribir)

Si se tiene en cuenta los independientes llamadas open , read, writey closepara ser más simple que una E / S de propósito único mensaje se basa en su filosofía de diseño. Los desarrolladores de Unix optaron por utilizar operaciones y programas simples que se pueden combinar de muchas maneras, en lugar de una sola operación (o programa) que hace todo.

Thomas Dickey
fuente
Las personas que llaman también en la mayoría de los casos tienen que especificar el desplazamiento deseado dentro de un archivo. Hay algunas situaciones (por ejemplo, un protocolo UDP que permite el acceso a los datos) en las que cada solicitud puede identificar independientemente un archivo y un desplazamiento, ya que elimina la necesidad de que un servidor mantenga el estado, pero en general es más conveniente tener el servidor realizar un seguimiento de la posición del archivo. Además, como se señaló en otra parte, el código que va a escribir archivos a menudo necesita bloquearlos de antemano y luego bloquearlos; Peinar esas operaciones con abrir / cerrar es muy conveniente.
supercat
55
El "archivo" puede no tener un nombre o permisos en primer lugar; ready writeno están restringidos a archivos que viven en un sistema de archivos, y esa es una decisión fundamental de diseño en Unix, como explica pjc50.
reinierpost
1
También cuando en el archivo para leer / escribir que - al principio, al final o una posición arbitraria (que suelen ser inmediatamente después del final de la última lectura / escritura) - el núcleo realiza un seguimiento de esto para usted (con un modo de dirija todas las escrituras al final del archivo, o de lo contrario, los archivos se abren con la posición al principio y avanzan con cada lectura / escritura y se pueden mover lseek)
Random832
51

El concepto del identificador de archivos es importante debido a la elección de diseño de UNIX de que "todo es un archivo", incluidas las cosas que no forman parte del sistema de archivos. Como unidades de cinta, el teclado y la pantalla (¡o teletipo!), Lectores de tarjetas / cintas perforadas, conexiones en serie, conexiones de red y (la invención clave de UNIX) conexiones directas a otros programas llamados "tuberías".

Si observa muchas de las simples utilidades estándar de UNIX como grep, especialmente en sus versiones originales, notará que no incluyen llamadas a open()y close()sino solo ready write. El shell configura los identificadores de archivos fuera del programa y los pasa cuando se inicia. Por lo tanto, el programa no tiene que preocuparse si está escribiendo en un archivo o en otro programa.

Así como open, las otras formas de obtener los descriptores de fichero son socket, listen, pipe, dup, y un mecanismo muy Heath Robinson para el envío de descriptores de archivos a través de canalizaciones: https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux -enchufe

Editar: algunas notas de clase que describen las capas de indirección y cómo esto le permite a O_APPEND trabajar con sensatez. Tenga en cuenta que mantener los datos del inodo en la memoria garantiza que el sistema no tendrá que ir a buscarlos nuevamente para la próxima operación de escritura.

pjc50
fuente
1
Además creat, y listenno crea un fd, pero cuando (y si) entra una solicitud mientras se escucha, acceptcrea y devuelve un fd para el nuevo socket (conectado).
dave_thompson_085
18
Esta es la respuesta correcta. El famoso (pequeño) conjunto de operaciones en los descriptores de archivos es una API unificadora para todo tipo de recursos que producen o consumen datos. Este concepto es MUY exitoso. Una cadena podría tener una sintaxis que defina el tipo de recurso junto con la ubicación real (¿URL cualquiera?), Pero copiar cadenas alrededor de las cuales ocupan varios por ciento de la RAM disponible (¿qué era en el PDP 7? 16 kB?) Parece excesivo .
Peter - Restablece a Monica
Quizás lo sería, si las llamadas de bajo nivel y el shell se desarrollaran al mismo tiempo. Pero pipese introdujo unos años después del inicio del desarrollo en Unix.
Thomas Dickey
1
@Thomas Dickey: lo que simplemente muestra cuán bueno era el diseño original, ya que permitía la extensión simple a tuberías y c :-)
jamesqf
Pero siguiendo esa línea de argumento, esta respuesta no proporciona nada nuevo.
Thomas Dickey
10

La respuesta es no, porque open () y close () crean y destruyen un controlador, respectivamente. Hay momentos (bueno, todo el tiempo, realmente) en los que es posible que desee garantizar que es la única persona que llama con un nivel de acceso particular, ya que otra persona (por ejemplo) que escribe en un archivo que está analizando inesperadamente podría abandonar una aplicación en un estado desconocido o que conduzca a un punto muerto o un punto muerto, por ejemplo, el lema de Dining Philosophers.

Incluso sin esa consideración, hay implicaciones de rendimiento a considerar; close () permite que el sistema de archivos (si es apropiado o si lo solicitó) vacíe el búfer que estaba ocupando, una operación costosa. Varias ediciones consecutivas en un flujo en memoria son mucho más eficientes que varios ciclos de lectura-escritura-modificación esencialmente no relacionados a un sistema de archivos que, por lo que usted sabe, existe a medio mundo de distancia disperso en un centro de datos de almacenamiento masivo de alta latencia. Incluso con el almacenamiento local, la memoria suele ser muchos órdenes de magnitud más rápida que el almacenamiento masivo.

msaunier
fuente
7

Open () ofrece una forma de bloquear archivos mientras están en uso. Si el sistema operativo abriera, leyera / escribiera y luego volviera a cerrar el sistema operativo, no habría nada que impidiera que otras aplicaciones cambien esos archivos entre operaciones.

Si bien esto puede ser manejable (muchos sistemas admiten acceso a archivos no exclusivo) por simplicidad, la mayoría de las aplicaciones asumen que los archivos que tienen abiertos no cambian.

あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ
fuente
5

Debido a que la ruta del archivo podría moverse mientras se supone que permanecerá igual.

Mehrdad
fuente
4

Leer y escribir en un sistema de archivos puede implicar una gran variedad de esquemas de almacenamiento en búfer, mantenimiento del sistema operativo, administración de discos de bajo nivel y una gran cantidad de otras posibles acciones. Entonces, las acciones de open()y close()sirven como la configuración para este tipo de actividades bajo el capó. Las diferentes implementaciones de un sistema de archivos podrían personalizarse según sea necesario y seguir siendo transparentes para el programa de llamada.

Si el sistema operativo no tenía abrir / cerrar, entonces con reado write, esas acciones de archivo aún tendrían que realizar cualquier inicialización, limpieza / gestión de búfer, etc. cada vez. Eso es una gran carga para imponer para lecturas y escrituras repetitivas.

PeterT
fuente
Sin olvidar que open () y close () mantienen también la posición en el archivo (para la próxima lectura o la próxima escritura). Entonces, al final o read () y write () necesitarían una estructura para manejar todos los parámetros, o necesitarían argumentos para cada parámetro. Crear una estructura es equivalente (sitio del programador) a un abierto, por lo que si el sistema operativo también sabe acerca de abrir, solo tenemos más ventajas.
Giacomo Catenazzi
1

El mantra de Unix es "ofrecer una forma de hacer las cosas", lo que significa "factorizar" en piezas (reutilizables) que se combinarán a voluntad. Es decir, en este caso, separe la creación y destrucción de identificadores de archivos de su uso. Beneficios importantes llegaron más tarde, con tuberías y conexiones de red (también se manipulan a través de identificadores de archivos, pero se crean de otras maneras). Ser capaz de enviar identificadores de archivos (por ejemplo, pasarlos a procesos secundarios como "archivos abiertos" que sobreviven exec(2)e incluso a procesos no relacionados a través de una tubería) solo es posible de esta manera. Particularmente si desea ofrecer acceso controlado a un archivo protegido. Entonces puedes, por ejemplo, abrir/etc/passwd para escribir y pasar eso a un proceso secundario que no tiene permitido abrir ese archivo para escribir (sí, sé que este es un ejemplo ridículo, siéntase libre de editar con algo más realista).

vonbrand
fuente