关于这三个标准输入输出,由于是从父进程继承下来的,在libc内并没有打开的过程,而是直接以 0/1/2 三个fd包装上 _IO_file_jumps 作为 vtable ,定义产生_IO_2_1_stdin_/_IO_2_1_stdout_/_IO_2_1_stderr__IO_stdin/_IO_stdout/_IO_stderr 则是对应的 _IO_FILE 指针,最后再导出为 FILE * 类型的 stdin/stdout/stderr

// libio/stdfiles.c, line #61
#  define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \\
  struct _IO_FILE_plus NAME \\
    = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), \\
       &_IO_file_jumps};
# endif
#endif

DEF_STDFILE(_IO_2_1_stdin_, 0, 0, _IO_NO_WRITES);
DEF_STDFILE(_IO_2_1_stdout_, 1, &_IO_2_1_stdin_, _IO_NO_READS);
DEF_STDFILE(_IO_2_1_stderr_, 2, &_IO_2_1_stdout_, _IO_NO_READS+_IO_UNBUFFERED);

// libio/libio.h, line #315
extern struct _IO_FILE_plus _IO_2_1_stdin_;
extern struct _IO_FILE_plus _IO_2_1_stdout_;
extern struct _IO_FILE_plus _IO_2_1_stderr_;
#define _IO_stdin ((_IO_FILE*)(&_IO_2_1_stdin_))
#define _IO_stdout ((_IO_FILE*)(&_IO_2_1_stdout_))
#define _IO_stderr ((_IO_FILE*)(&_IO_2_1_stderr_))

// libio/stdio.c, line #30
#undef stdin
#undef stdout
#undef stderr
_IO_FILE *stdin = (FILE *) &_IO_2_1_stdin_;
_IO_FILE *stdout = (FILE *) &_IO_2_1_stdout_;
_IO_FILE *stderr = (FILE *) &_IO_2_1_stderr_;

#undef _IO_stdin
#undef _IO_stdout
#undef _IO_stderr
#ifdef _LIBC