Estoy leyendo la fuente del MIT.sistema operativo xv6. Este fragmento aparece al principio de sh.c
:
// Ensure that three file descriptors are open.
while((fd = open("console", O_RDWR)) >= 0){
if(fd >= 3){
close(fd);
break;
}
}
Entiendo que esto comprueba sial menosSe abren 3 descriptores de archivos (presumiblemente para stdin, stdout y stderr) verificando si el descriptor de archivo recién asignado está por encima (o es igual a) 3.
1) ¿Cómo es posible acceder open
al mismo dispositivo varias veces desde el mismo proceso y esperar diferentes descriptores de archivos?
2) Para entender esto, ejecuté un fragmento similar en mi máquina host (x86_64 Linux 4.6.0.1). El programa de prueba open
editó repetidamente un archivo de texto en un bucle para ver si podemos esperar un fd diferente, pero siempre produjo el mismo descriptor de archivo. A partir de esto, llegué a la conclusión de que open
-ing un archivo real y un dispositivo (como /dev/console
) de alguna manera difiere porque el fragmento de xv6 obviamente funciona (probado en Qemu). ¿Cuál es exactamente la diferencia?
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
int main(void)
{
int fd;
int cnt = 0;
while ((fd = open("sample.txt", O_RDWR) > 0)) {
if (cnt != 10) {
cnt++;
printf("File descriptor opened: %d\n", fd);
} else {
break;
}
}
return 0;
}
Aquí está el resultado al ejecutarlo:
$ ./a.out
File descriptor opened: 1
File descriptor opened: 1
[snip]
File descriptor opened: 1
File descriptor opened: 1
EDITARSegún una de las respuestas, ejecuté strace
el ejecutable y descubrí queopen
en efectodevuelve varios descriptores de archivos, pero ninguno de ellos se imprime por algún motivo. ¿Por qué sería eso?
3) Algo no relacionado, pero ¿la convención de usar transmisiones stdio en fds 0-2 no es solo eso: una convención? Por ejemplo, si la secuencia de inicialización asignara los descriptores del archivo de entrada/salida a otra cosa, ¿afectaría de alguna manera la forma en que sus hijos realizan su E/S?
Respuesta1
En realidad son 3 preguntas. Deseche el número 2 inmediatamente porque el programa es incorrecto:
while ((fd = open("sample.txt", O_RDWR) > 0)) {
probablemente quisiste decir
while ((fd = open("sample.txt", O_RDWR)) > 0) {
con los paréntesis colocados incorrectamente, solo está probando si fd
es mayor que cero (lo cual, dado que los descriptores de archivo 0, 1 y 2 están abiertos, probablemente sea una buena suposición).
Para el #1: elopen
La llamada (cuando tiene éxito) se define para devolver descriptores de archivos distintos. Si el dispositivo no se puede volver a abrir, open
regresará -1
.
° 3: claro, eso es unconvención, sino también en elPOSIXestándar. Otros sistemas han utilizado otras convenciones, incluido tener una cuarta secuencia abierta para cada programa.
Otras lecturas:Uso de su entorno Aegis (julio de 1988)
Consulte la página 6-9, que dice que Apollo Domain/OS tuvo un erroraportey **salida*
Respuesta2
No, el código nocontrolardescriptores, en realidad los abre. Sin dar descriptores todavía. Cada apertura dará un nuevo descriptor de archivo, es decir, 0,1,2,3. El código se rompe al llegar a fd 3 dejando abierto de 0 a 2.
Cada descriptor de archivo es simplemente un puntero a alguna ubicación en algún archivo. Por tanto, no supone ningún problema tener más de un descriptor para el mismo archivo.
Si su programa de prueba proporciona el mismo fd para diferentes llamadas abiertas, hay un error. Por favor muestra el código.
Sí, existe una fuerte convención sobre fd 0 a 2. Si algún código quiere imprimirse en stdout, en realidad se imprime en fd 1. No hay forma de "asignar" stdout a otra cosa.
Respuesta3
1) Si el sistema admite la apertura del mismo archivo desde múltiples procesos al mismo tiempo, ¿por qué no permitir que un proceso se abra también varias veces? Dado que los descriptores de archivos se heredan, de todos modos podría terminar con el mismo archivo dos veces en el mismo proceso, es decir, si se hereda una vez y el proceso mismo lo abre una vez. Verificar qué archivos tiene abiertos el proceso y devolver una referencia al anterior sería un trabajo adicional.
Además, está la cuestión de si diferentes partes del proceso utilizan el mismo archivo simultáneamente. Digamos que una biblioteca usa algún archivo de configuración al mismo tiempo que lo usa el programa principal. El descriptor de archivo está vinculado al modo de acceso y la posición del puntero del archivo. Si solo hubiera una copia de ellos, sucederían cosas extrañas. Además, si el archivo se abrió dos veces (en el mismo fd), ¿qué debería pasar cuando se cierre? Podría haber otra capa de recuento de referencias para decidir cuándoen realidadcierre el archivo, pero eso no ayudaría con los otros problemas.
2) Depende de tu código. Un lenguaje inteligente (es decir, no C) podría volver a contar la variable que contiene el archivo abierto y cerrarla justo antes de volver a abrir el archivo. Es difícil decirlo sin ver el código.
Pero una pequeña prueba, abrir el mismo archivo dos veces con la misma variable en Perl da como resultado el mismo número de FD:
perl -e 'open F, "test.txt"; printf "%d ", fileno(F); open F, "test.txt"; printf "%d\n", fileno(F)'
3 3
Ejecutarlo strace
muestra que el archivo se cierra inmediatamente antes de volver a abrirlo.
Con dos variables diferentes obtenemos dos números FD:
perl -e 'open F, "test.txt"; printf "%d ", fileno(F); open G, "test.txt"; printf "%d\n", fileno(G)'
3 4
3) Técnicamente, se podría decir que los números de archivo estándar son una convención. Una convención codificada enPOSIX y el estándar ISO C:
Al iniciar el programa, se predefinirán tres flujos y no será necesario abrirlos explícitamente: entrada estándar (para leer la entrada convencional), salida estándar (para escribir la salida convencional) y error estándar (para escribir la salida de diagnóstico).
Pero de todos modos es una convención en el sentido de que es posible ejecutar un programa sin ellos, sin que le importe al núcleo. O puede que no: al leer las especificaciones de las exec
llamadas, parece serpermitió que una implementación abriera algo para usted:
Si el descriptor de archivo 0, 1 o 2 se cerraría después de una llamada exitosa a una de la familia de funciones exec, las implementaciones pueden abrir un archivo no especificado para el descriptor de archivo en la nueva imagen del proceso.
(Por supuesto, puedes cerrarlos en el propio programa).
Si decide no tenerlos al inicio, la compatibilidad se descarta:
Si se ejecuta una utilidad estándar o una aplicación conforme con el descriptor de archivo 0 no abierto para lectura o con el descriptor de archivo 1 o 2 no abierto para escritura, el entorno en el que se ejecuta la utilidad o aplicación se considerará no conforme y, en consecuencia, el Es posible que la utilidad o aplicación no se comporte como se describe en este estándar.
ElLas páginas de manual de Linux lo expresan de forma algo práctica.:
Como principio general, ningún programa portátil, ya sea privilegiado o no, puede asumir que estos tres descriptores de archivos permanecerán cerrados en un execve().