Ну, здесь происходит несколько вещей.
Во-первых, в UNIX-подобной системе (а вы, похоже, в системе на основе Linux) среда, в которой каждое пользовательское пространствоПрограмма запускается, включает в себя концепцию так называемых «стандартных потоков ввода-вывода» , то есть каждая программа, загружаемая ОС и получающая управление, автоматически имеет три файловых дескриптора, открытых и доступных: представляющих стандартный вводstream, стандартный поток вывода и стандартный поток ошибок.
Во-вторых, обычно (но не всегда) порожденная программа наследует эти потоки от своей родительской программы.В случае интерактивной оболочки, запущенной в терминале (или эмуляторе терминала), эта родительская программа является оболочкой, и поэтому стандартные потоки ввода-вывода порожденной программы наследуются от оболочки.Стандартные потоки ввода-вывода оболочки, в свою очередь, естественным образом связаны с терминалом, в котором он работает: поэтому можно вводить данные в оболочку и читать то, что она печатает: вы фактически печатаете в терминале, а не в оболочке;это терминал, который доставляет эти данные в оболочку;случай вывода оболочки только обратный.
В-третьих, /dev/stderr
- это специфический для Linux «хак», который представляет собой виртуальное устройство, означающее «к чему бы ни подключен мой stderr»».То есть, когда процесс открывает этот специальный файл, он возвращает файловый дескриптор, связанный с тем, к чему процесс 'stderr уже подключен.
В-четвертых, давайте попробуем пример кода, который вы цитировали:
NewFile(uintptr(syscall.Stderr), "/dev/stderr")
Здесь выполняется вызов os.NewFile
с получением двух аргументов.Приведу документацию:
$ go doc os.NewFile
func NewFile(fd uintptr, name string) *File
NewFile
возвращает новый File
с указанным дескриптором файла и именем.Возвращаемое значение будет nil
, если fd
не является допустимым дескриптором файла.<…>
ОК, поэтому эта функция принимает необработанный дескриптор файла уровня ядра и имя файла , для которого он должен быть открыт. Этот последний бит имеет решающее значение: само ядро ОС (почти) не замечает, какой тип потока фактически представляет дескриптор файла - по крайней мере, до тех пор, пока рассматривается его открытый API.
Итак, когда NewFile
вызывается для получения экземпляра *os.File
для стандартного потока ошибок программы пакетом log
, он не открывает файл "/ dev / stderr" (даже если он существует);он просто использует свое имя, так как os.NewFile
запрашивает его.Он мог бы использовать "" там во многом в той же степени, за исключением изменений в отчетах об ошибках: если что-то не получится при использовании результирующего *os.File
, вывод ошибки не будет включать имя "/dev/stderr".
Значение syscall.Stderr
- это просто номер дескриптора файла, подключенного к стандартному потоку ошибок.В UNIX-совместимых ядрах это всегда 2
;Вы можете запустить go doc syscall.Stderr
и убедиться сами.
Напомним,
- Вызов
NewFile(...)
, на который вы ссылались, не открывает никаких файлов;он просто оборачивает уже открытый дескриптор файла, связанный со стандартным потоком ошибок текущего процесса, в значение типа os.File
, которое используется в пакете os
для ввода / вывода для файлов. - В Linuxспециальный файл виртуального устройства
/dev/stderr
действительно существует, но он не имеет ничего общего с тем, что здесь происходит. - Когда вы запускаете программу в интерактивной оболочке без использования перенаправления I / O стандартные потоки созданного процесса связаны с теми же «приемниками и источниками», что и потоки оболочки.А они, в свою очередь, большую часть времени подключены к терминалу, на котором размещена оболочка.
Теперь я призываю вас взять вступительную книгу по проектированию UNIX-подобных операционных систем и прочитать ее..