Рассмотрим следующую программу на языке 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
. Важно, чтобы вы запускали его с терминала (окна терминала, сеанса SSH и т. д.), поэтому stdout
— это терминал.
В Linux это завершится с ошибкой lseek failed: Illegal seek
— что имеет смысл, поиск по tty не имеет особого смысла.
Однако в macOS это ведет себя по-другому — программа завершает работу, ничего не печатая, а затем — если вы используете bash
в качестве оболочки, оболочка также завершает работу. (Напротив, если вы используете zsh
в качестве оболочки, приглашение оболочки по-прежнему работает, но никакие команды не отображают никаких результатов.) Я могу только предположить, что это связано с тем, что tty был переведен в какое-то недопустимое состояние. Но что здесь происходит на самом деле?
Я предполагал, что macOS будет вести себя аналогично FreeBSD. Но FreeBSD 14.1-RELEASE (amd64) снова ведет себя по-другому: она печатает:
Seek successful to position 9223372036854775807
Это более понятно, чем то, что делает macOS (рассматривая бессмысленную операцию как недействующую, вместо того, чтобы выдавать ошибку, как это делает Linux).
🤔 А знаете ли вы, что...
C является компилируемым языком, что означает, что программа на C должна быть скомпилирована перед выполнением.
Пользователь jepler на Hacker News указал на объяснение.
Обратите внимание на этот код в функции 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
, если только вы не вернетесь куда-то раньше.Причина выхода bash
заключается в том, что большинство оболочек завершают работу, когда они не могут выполнить запись в stdout/stderr. Что касается того, почему zsh
этого не делает, я не уверен, но предполагаю, что код редактирования строки zsh
открывает терминальное устройство напрямую и, следовательно, обращается к нему с независимой позицией файла.
Если вы измените мою тестовую программу, чтобы, например, 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
.