El problema es que esos /proc
los archivos en Linux aparecen como archivos de texto hasta stat()/fstat()
se preocupa, pero no se comporte como tal.
Debido a que son datos dinámicos, solo puedes hacer una read()
llamada del sistema sobre ellos (al menos para algunos de ellos). Hacer más de uno podría obtener dos fragmentos de dos contenidos diferentes, por lo que parece un segundo read()
en ellos simplemente no devuelve nada (es decir, final de archivo) (a menos que lseek()
volver al principio (y sólo al principio)).
El read
La utilidad necesita leer el contenido de los archivos un byte a la vez para asegurarse de no leer más allá del carácter de nueva línea. Eso es lo que dash
hace:
$ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
read(0, "1", 1) = 1
read(0, "", 1) = 0
Algunas conchas como bash
tener una optimización para no tener que hacer tantos read()
llamadas del sistema. Primero verifican si el archivo se puede buscar y, de ser así, lo leen en fragmentos, ya que saben que pueden volver a colocar el cursor justo después de la nueva línea si han leído más allá:
$ strace -e lseek,read bash -c 'read a' < /proc/sys/fs/file-max
lseek(0, 0, SEEK_CUR) = 0
read(0, "1628689\n", 128) = 8
Con bash
, aún tendría problemas con los archivos proc que tienen más de 128 bytes y solo se pueden leer en una llamada al sistema de lectura.
bash
también parece deshabilitar esa optimización cuando -d
se utiliza la opción.
ksh93
lleva la optimización aún más lejos hasta el punto de volverse falsa. read
de ksh93 busca hacia atrás, pero recuerda los datos adicionales que ha leído para el siguiente read
, por lo que el próximo read
(o cualquiera de sus otras funciones integradas que leen datos como cat
o head
) ni siquiera intenta read
los datos (incluso si esos datos han sido modificados por otros comandos intermedios):
$ seq 10 > a; ksh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 2
$ seq 10 > a; sh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 st
Si te interesa saber ¿por qué? esto es así, puedes ver la respuesta en las fuentes del núcleo aquí:
if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
Básicamente, buscando (*ppos
not 0) no está implementado para lecturas (!write
)de valores sysctl que son números. Cada vez que se realiza una lectura desde /proc/sys/fs/file-max
,la rutina en cuestión__do_proc_doulongvec_minmax()
se llama desde la entrada para file-max
en la tabla de configuración en el mismo archivo.
Otras entradas, como /proc/sys/kernel/poweroff_cmd
se implementan a través de proc_dostring()
que permite búsquedas, por lo que puede hacer dd bs=1
en él y lea desde su shell sin problemas.
Tenga en cuenta que desde el kernel 2.6 la mayoría /proc
las lecturas se implementaron a través de una nueva API llamada seq_file y esto admite búsquedas, por ejemplo, leer /proc/stat
no debe causar problemas. El /proc/sys/
La implementación, como podemos ver, no usa thisapi.
En el primer intento, esto parece un error en los proyectiles que devuelven menos que un Bourne Shell real o sus derivados (sh, bosh, ksh, heirloom).
El Bourne Shell original intenta leer un bloque (64 bytes); las variantes más nuevas de Bourne Shell leen 128 bytes, pero comienzan a leer de nuevo si no hay un carácter de nueva línea.
Antecedentes:/procfs e implementaciones similares (por ejemplo, el /etc/mtab
montado archivo virtual) tienen contenido dinámico y un stat()
la llamada no provoca la recreación del contenido dinámico primero. Por esta razón, el tamaño de dicho archivo (desde la lectura hasta el EOF) puede diferir de lo que stat()
regresa.
Dado que el estándar POSIX requiere que las utilidades esperen lecturas cortas en cualquier momento, el software que crea que un read()
que devuelve menos de lo pedido cantidad de bytes es una indicación EOF están rotos. Una utilidad implementada correctamente llama a read()
una segunda vez en caso de que devuelva menos de lo esperado, hasta que se devuelva un 0. En el caso del read
incorporado, por supuesto sería suficiente leer hasta EOF
o hasta un NL
se ve.
Si ejecuta truss
o un clon de truss, debería poder verificar ese comportamiento incorrecto para los shells que solo devuelven 6
en su experimento.
En este caso especial, parece ser un error del kernel de Linux, consulte:
$ sdd -debug bs=1 if= /proc/sys/fs/file-max
Simple copy ...
readbuf (3, 12AC000, 1) = 1
writebuf (1, 12AC000, 1)
8readbuf (3, 12AC000, 1) = 0
sdd: Read 1 records + 0 bytes (total of 1 bytes = 0.00k).
sdd: Wrote 1 records + 0 bytes (total of 1 bytes = 0.00k).
El kernel de Linux devuelve 0 con el segundo read
y esto es, por supuesto, incorrecto.
Conclusión:los shells que primero intentan leer una porción de datos lo suficientemente grande no desencadenan este error del kernel de Linux.