cuo*_*glm 21 linux shell proc read
在一些类似Bourne shell中,read内建无法读取从文件中的整条生产线/proc(该命令下面应当运行zsh,替换$=shell与$shell其他shell):
$ for shell in bash dash ksh mksh yash zsh schily-sh heirloom-sh "busybox sh"; do
printf '[%s]\n' "$shell"
$=shell -c 'IFS= read x </proc/sys/fs/file-max; echo "$x"'
done
[bash]
602160
[dash]
6
[ksh]
602160
[mksh]
6
[yash]
6
[zsh]
6
[schily-sh]
602160
[heirloom-sh]
602160
[busybox sh]
6
Run Code Online (Sandbox Code Playgroud)
read标准要求标准输入必须是一个文本文件,这个要求会导致不同的行为吗?
阅读文本文件的POSIX定义,我做了一些验证:
$ od -t a </proc/sys/fs/file-max
0000000 6 0 2 1 6 0 nl
0000007
$ find /proc/sys/fs -type f -name 'file-max'
/proc/sys/fs/file-max
Run Code Online (Sandbox Code Playgroud)
的NUL内容中没有字符/proc/sys/fs/file-max,并将其find报告为常规文件(这是错误find吗?)。
我猜外壳在引擎盖下做了一些事情,比如file:
$ file /proc/sys/fs/file-max
/proc/sys/fs/file-max: empty
Run Code Online (Sandbox Code Playgroud)
Sté*_*las 32
问题是这些/proc文件在 Linux 上显示为文本文件stat()/fstat(),但行为却并非如此。
因为它是动态数据,所以你只能read()对它们进行一次系统调用(至少对其中的一些)。做不止一个可以让你得到两个不同内容的两个块,所以似乎一秒钟read()对它们来说只是什么都不返回(意味着文件结束)(除非你lseek()回到开头(并且只回到开头))。
该read实用程序需要一次读取一个字节的文件内容,以确保不会读取超过换行符的内容。这就是dash它的作用:
$ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
read(0, "1", 1) = 1
read(0, "", 1) = 0
Run Code Online (Sandbox Code Playgroud)
一些 shell 像bash有一个优化,以避免必须做这么多的read()系统调用。他们首先检查文件是否可搜索,如果是,则分块读取,因为他们知道如果他们已经阅读了换行符,他们可以将光标放回换行符之后:
$ 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
Run Code Online (Sandbox Code Playgroud)
使用bash,对于超过 128 字节大且只能在一个 read 系统调用中读取的 proc 文件,您仍然会遇到问题。
bash-d使用该选项时,似乎也禁用了该优化。
ksh93使优化进一步变得虚假。ksh93read确实会回溯,但会记住它为下一个读取的额外数据read,因此下一个read(或其任何其他读取数据的内置函数,如cat或head)甚至不会尝试读取read数据(即使该数据已被修改)之间的其他命令):
$ 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
Run Code Online (Sandbox Code Playgroud)
meu*_*euh 11
如果你有兴趣知道为什么?就是这样,您可以在此处的内核源代码中看到答案:
if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
基本上,*ppos对于!write作为数字的 sysctl 值的读取(),不会实现搜索(不是 0)。每当从 完成读取时,都会从同一文件中配置表中的条目中调用有/proc/sys/fs/file-max问题的例程
。__do_proc_doulongvec_minmax()file-max
其他条目,例如/proc/sys/kernel/poweroff_cmd通过
proc_dostring()它实现的允许查找,因此您可以dd bs=1对其进行操作并从您的 shell 中读取,没有任何问题。
请注意,自内核 2.6 以来,大多数/proc读取都是通过名为seq_file的新 API
实现的
,这支持搜索,因此例如读取/proc/stat不应导致问题。在/proc/sys/实施中,我们可以看到,不使用此API。