考虑下面的 C 程序,名为
weird.c
(因为它所做的事情没有意义):
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
off_t result = lseek(STDOUT_FILENO, (off_t)INT64_MAX, SEEK_SET);
if (result != (off_t)-1)
printf("Seek successful to position %" PRId64 "\n", (int64_t)INT64_MAX);
else
perror("lseek failed");
return 0;
}
从终端窗口编译并运行它,例如
gcc -o weird weird.c && ./weird
。从 tty(终端窗口、SSH 会话等)运行它非常重要,因此 stdout
是一个 tty。
在 Linux 上,它将失败并出现错误
lseek failed: Illegal seek
– 这是有道理的,在 tty 上查找没有多大意义。
但是,在 macOS 上,它的行为有所不同 - 程序退出而不打印任何内容,然后 shell 也退出。我只能假设这是因为 tty 已进入某种非法状态。但这里到底发生了什么?
顺便说一句,我猜测 macOS 的行为与 FreeBSD 类似。但是 FreeBSD 14.1-RELEASE (amd64) 再次有不同的行为 – 它打印:
Seek successful to position 9223372036854775807
这比 macOS 的做法更容易理解(例如,将无意义的操作视为无操作,而不是像 Linux 那样出错。)
jepler
指出了解释。
注意
vn_write
函数中的这段代码bsd/vfs/vfs_vnops.c
:
if (write_offset == INT64_MAX) {
/* writes are not possible */
error = EFBIG;
goto error_out;
}
基本上发生的事情是这样的:
lseek
设置终端的偏移量 - 即使偏移量实际上没有执行任何操作INT64_MAX
,进一步的写入尝试就会失败并出现 EFBIG
,除非您返回到更早的某个位置。shell 退出的原因是大多数 shell 在无法写入 stdout/stderr 时退出。
如果您修改我的测试程序以寻求例如
INT64_MAX-100
,在所有输出开始失败之前,您将能够打印 100 个字节的输出。
此代码似乎已在
xnu-7195.50.7.100.1
(macOS 11 Big Sur) 中引入 - 它在 xnu-6153.11.26
(macOS 10.15 Catalina) 中不存在。
FreeBSD 似乎还允许您在终端中
lseek
到任意偏移量 - 但与 macOS 11+ 不同,如果您查找到 INT64_MAX
,则不会拒绝进一步写入。