我们以 freopen为例研究 glibc打开一个新文件的过程(忽略宽字符处理,删除了部分对老 glibc的兼容代码),先检查传入的 fp本身是否有active的fd,并将其关闭(_IO_file_close_it→_IO_new_file_close_it→_IO_SYSCLOSE),然后将 fp外面的 vtable 重新赋值为默认的 _IO_file_jumps_wide_data的vtable被初始化为_IO_wfile_jumps ,再次 fopen

FILE *
freopen (const char *filename, const char *mode, FILE *fp)
{
  FILE *result;
  CHECK_FILE (fp, NULL);
  if (!(fp->_flags & _IO_IS_FILEBUF))
    return NULL;
  _IO_acquire_lock (fp);
  int fd = _IO_fileno (fp);
  const char *gfilename = (filename == NULL && fd >= 0
                           ? fd_to_filename (fd) : filename);
  fp->_flags2 |= _IO_FLAGS2_NOCLOSE;
    {
      _IO_file_close_it (fp);
      _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
      if (_IO_vtable_offset (fp) == 0 && fp->_wide_data != NULL)
        fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
      result = _IO_file_fopen (fp, gfilename, mode, 1);
      if (result != NULL)
        result = __fopen_maybe_mmap (result);
    }
  fp->_flags2 &= ~_IO_FLAGS2_NOCLOSE;
  if (result != NULL)
    {
      /* unbound stream orientation */
      result->_mode = 0;

      if (fd != -1)
        {
              __dup3 (_IO_fileno (result), fd,
                      (result->_flags2 & _IO_FLAGS2_CLOEXEC) != 0
                      ? O_CLOEXEC : 0);
          __close (_IO_fileno (result));
          _IO_fileno (result) = fd;
        }
    }
  else if (fd != -1)
    __close (fd);
  if (filename == NULL)
    free ((char *) gfilename);

  _IO_release_lock (fp);
  return result;
}