Почему программы выводят вывод на экран терминала, а не / dev / stderr? - PullRequest
0 голосов
/ 02 февраля 2019

Как я вижу в исходном коде golang, go напечатает вывод в os.Stderr, что составляет

Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")

Так почему я запускаю эту программу в своем терминале с командой go run main.go, вывод выводится вэкран терминала, а не /dev/stderr

// main.go
func main() {
    log.Println("this is my first log")
}

Ответы [ 2 ]

0 голосов
/ 02 февраля 2019

Ну, здесь происходит несколько вещей.

Во-первых, в 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) *FileNewFile возвращает новый 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-подобных операционных систем и прочитать ее..

0 голосов
/ 02 февраля 2019

В стандартных терминалах Unix / Linux и stdout, и stderr подключены к терминалу, поэтому вывод идет туда.

Вот фрагмент кода оболочки для пояснения этого:

$ echo "joe" >> /dev/stderr
joe

Несмотря на то, что мы повторили «joe» чему-то, что выглядит как файл, оно выводится на экран.Замените /dev/stderr на /tmp/foo, и вы не увидите вывод на экране (хотя он будет добавлен в файл /tmp/foo)


В Go вы можете специально выбрать, какой потоквыводить путем передачи его функциям, подобным fmt.Fprintf в первом аргументе.

...