fp->_wide_data->_wide_vtable = jmp; } else /* Cause predictable crash when a wide function is called on a byte stream. */ fp->_wide_data = (struct _IO_wide_data *) -1L; fp->_freeres_list = NULL; }
void _IO_new_file_init_internal (struct _IO_FILE_plus *fp) { /* POSIX.1 allows another file handle to be used to change the position of our file descriptor. Hence we actually don't know the actual position before we do the first fseek (and until a following fflush). */ fp->file._offset = _IO_pos_BAD; fp->file._flags |= CLOSED_FILEBUF_FLAGS;
if (__wcsmbs_named_conv (&fcts, ccs[2] == '\0' ? upstr (ccs, cs + 5) : ccs) != 0) { /* Something went wrong, we cannot load the conversion modules. This means we cannot proceed since the user explicitly asked for these. */ (void) _IO_file_close_it (fp); free (ccs); __set_errno (EINVAL); returnNULL; }
/* Clear the state. We start all over again. */ memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t)); memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t));
if (__wcsmbs_named_conv (&fcts, ccs[2] == '\0' ? upstr (ccs, cs + 5) : ccs) != 0) { /* Something went wrong, we cannot load the conversion modules. This means we cannot proceed since the user explicitly asked for these. */ (void) _IO_file_close_it (fp); free (ccs); __set_errno (EINVAL); returnNULL; }
/* Clear the state. We start all over again. */ memset (&fp->_wide_data->_IO_state, '\0', sizeof (__mbstate_t)); memset (&fp->_wide_data->_IO_last_state, '\0', sizeof (__mbstate_t));
33 FILE * 34 __fopen_maybe_mmap (FILE *fp) 35 { 36#if _G_HAVE_MMAP 37if ((fp->_flags2 & _IO_FLAGS2_MMAP) && (fp->_flags & _IO_NO_WRITES)) 38 { 39/* Since this is read-only, we might be able to mmap the contents 40 directly. We delay the decision until the first read attempt by 41 giving it a jump table containing functions that choose mmap or 42 vanilla file operations and reset the jump table accordingly. */ 43 44if (fp->_mode <= 0) 45 _IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_maybe_mmap; 46else 47 _IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_maybe_mmap; 48 fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_maybe_mmap; 49 } 50#endif 51return fp; 52 }
if (fp->_IO_buf_base == NULL) { /* Maybe we already have a push back pointer. */ if (fp->_IO_save_base != NULL) { free (fp->_IO_save_base); fp->_flags &= ~_IO_IN_BACKUP; }// 第一部分,如果fp->_IO_buf_base为空的话则调用`_IO_doallocbuf` _IO_doallocbuf (fp); }
while (want > 0)//!!!注意这个循环++++++ { have = fp->_IO_read_end - fp->_IO_read_ptr; if (want <= have)// 第二部分,输入缓冲区里已经有足够的字符,则直接把缓冲区里的字符给目标buff { memcpy (s, fp->_IO_read_ptr, want); fp->_IO_read_ptr += want;//---从这一部分也可以看出,输入多余程序指定的内容,多余部分会被留在缓冲区中供下一次使用--- want = 0; } else { if (have > 0)// 第二部分,输入缓冲区里有部分字符,但是没有达到fread的size需求,先把已有的拷贝至目标buff { s = __mempcpy (s, fp->_IO_read_ptr, have); want -= have; fp->_IO_read_ptr += have;//那么_IO_read_ptr==_IO_read_end } //可能有人会有疑惑,读入后s没有变化,那么每次读入不都是读入到同一个地方,会覆盖之前的吗 //见注1
/* Check for backup and repeat */ if (_IO_in_backup (fp)) { _IO_switch_to_main_get_area (fp); continue; }
/* If we now want less than a buffer, underflow and repeat the copy. Otherwise, _IO_SYSREAD directly to the user buffer. */ if (fp->_IO_buf_base && want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base))//want小于buf承载极限 { if (__underflow (fp) == EOF) // 第三部分,输入缓冲区里不能满足需求,调用__underflow读入数据,当然这是在want<buffer空间的情况下 break;
continue;//返回ptr则continue } //循环真正的内容一般执行到这里就结束了 //再往下的内容是buf分配失败情况下,直接调用sysread的代码 --------------------------------------------------------------------------------- /* These must be set before the sysread as we might longjmp out waiting for input. */ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
/* Try to maintain alignment: read a whole number of blocks. */ count = want; if (fp->_IO_buf_base) { size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base; if (block_size >= 128) count -= want % block_size; }
if (fp->_IO_buf_base == NULL) { /* Maybe we already have a push back pointer. */ if (fp->_IO_save_base != NULL) { free (fp->_IO_save_base); fp->_flags &= ~_IO_IN_BACKUP; } _IO_doallocbuf (fp); }
/* FIXME This can/should be moved to genops ?? */ if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED)) { /* We used to flush all line-buffered stream. This really isn't required by any standard. My recollection is that traditional Unix systems did this for stdout. stderr better not be line buffered. So we do just that here explicitly. --drepper */ _IO_acquire_lock (stdout);
/* This is very tricky. We have to adjust those pointers before we call _IO_SYSREAD () since we may longjump () out while waiting for input. Those pointers may be screwed up. H.J. */ fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base; fp->_IO_read_end = fp->_IO_buf_base; fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end = fp->_IO_buf_base;//重置诸多缓存指针 //为什么要重置write指针,因为read和write用的是同一个缓冲区,如果不重置wirte的指针的话,那么调用write显然就会冲突 count = _IO_SYSREAD (fp, fp->_IO_buf_base, fp->_IO_buf_end - fp->_IO_buf_base);//最多读buf大小 if (count <= 0) { if (count == 0) fp->_flags |= _IO_EOF_SEEN; else fp->_flags |= _IO_ERR_SEEN, count = 0; } fp->_IO_read_end += count;//read_end指针拔高,这样退出该函数再次进入上层循环时就能直接从read_ptr指针处获得数据 if (count == 0) { /* If a stream is read to EOF, the calling application may switch active handles. As a result, our offset cache would no longer be valid, so unset it. */ fp->_offset = _IO_pos_BAD; return EOF; } if (fp->_offset != _IO_pos_BAD) _IO_pos_adjust (fp->_offset, count); return *(unsignedchar *) fp->_IO_read_ptr; }
/* These must be set before the sysread as we might longjmp out waiting for input. */ _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
/* Try to maintain alignment: read a whole number of blocks. */ count = want; if (fp->_IO_buf_base) { size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base; if (block_size >= 128) count -= want % block_size; }
size_t _IO_fwrite (constvoid *buf, size_t size, size_t count, FILE *fp) { size_t request = size * count; size_t written = 0; CHECK_FILE (fp, 0); if (request == 0) return0; _IO_acquire_lock (fp); if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1) written = _IO_sputn (fp, (constchar *) buf, request); _IO_release_lock (fp); /* We have written all of the input in case the return value indicates this or EOF is returned. The latter is a special case where we simply did not manage to flush the buffer. But the data is in the buffer and therefore written as far as fwrite is concerned. */ if (written == request || written == EOF) return count; else return written / size; }
if (n <= 0) return0; /* This is an optimized implementation. If the amount to be written straddles a block boundary (or the filebuf is unbuffered), use sys_write directly. */
/* First figure out how much space is available in the buffer. */ if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) { count = f->_IO_buf_end - f->_IO_write_ptr; if (count >= n) { constchar *p; for (p = s + n; p > s; ) { if (*--p == '\n') { count = p - s + 1; must_flush = 1; break; } } } } elseif (f->_IO_write_end > f->_IO_write_ptr) count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */
/* Then fill the buffer. */ if (count > 0) { if (count > to_do) count = to_do; f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count); s += count; to_do -= count; } if (to_do + must_flush > 0) { size_t block_size, do_write; /* Next flush the (full) buffer. */ if (_IO_OVERFLOW (f, EOF) == EOF) /* If nothing else has to be written we must not signal the caller that everything has been written. */ return to_do == 0 ? EOF : n - to_do;
/* Try to maintain alignment: write a whole number of blocks. */ block_size = f->_IO_buf_end - f->_IO_buf_base; do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);
if (do_write) { count = new_do_write (f, s, do_write); to_do -= count; if (count < do_write) return n - to_do; }
/* Now write out the remainder. Normally, this will fit in the buffer, but it's somewhat messier for line-buffered files, so we let _IO_default_xsputn handle the general case. */ if (to_do) to_do -= _IO_default_xsputn (f, s+do_write, to_do); } return n - to_do; }
可以看到整体逻辑与fread几乎是一致的
0x1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) { count = f->_IO_buf_end - f->_IO_write_ptr; if (count >= n) { constchar *p; for (p = s + n; p > s; ) { if (*--p == '\n') { count = p - s + 1; must_flush = 1; break; } } } } elseif (f->_IO_write_end > f->_IO_write_ptr) count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */
如果文件流属于行缓冲模式
则倒序搜索数据串中是否存在’\n’
存在则设置count和must_flush标志
如果不处于行缓冲模式则根据缓冲区中是否有数据设置count
0x2
1 2 3 4 5 6 7 8
if (count > 0) { if (count > to_do) count = to_do; f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count); s += count; to_do -= count; }
if (to_do + must_flush > 0) { size_t block_size, do_write; /* Next flush the (full) buffer. */ if (_IO_OVERFLOW (f, EOF) == EOF) /* If nothing else has to be written we must not signal the caller that everything has been written. */ return to_do == 0 ? EOF : n - to_do;
/* Try to maintain alignment: write a whole number of blocks. */ block_size = f->_IO_buf_end - f->_IO_buf_base; do_write = to_do - (block_size >= 128 ? to_do % block_size : 0); //清空缓冲区 if (do_write) { count = new_do_write (f, s, do_write); to_do -= count; if (count < do_write) return n - to_do; }
/* Now write out the remainder. Normally, this will fit in the buffer, but it's somewhat messier for line-buffered files, so we let _IO_default_xsputn handle the general case. */ if (to_do) to_do -= _IO_default_xsputn (f, s+do_write, to_do); }
如果 todo 还有剩余(即剩余空间不够)或 must_flush 被置为 1 的情况(即上面有 flush 的情况),需要做如下的处理:
先调用_IO_OVERFLOW 将前面写满的 buffer 写入物理文件中,如果此时写入失败的话,那就需要做处理,如果 to_do == 0,即本次要写入的东西都写到缓冲 buffer 里面了,所以是写入失败的,需要返回 EOF,否则,说明 n - todo 字节的 buffer 被写入缓冲了。
int _IO_new_file_overflow (FILE *f, int ch) { if (f->_flags & _IO_NO_WRITES) /* SET ERROR */ { f->_flags |= _IO_ERR_SEEN;//文件不允许写入 __set_errno (EBADF); return EOF; } /* If currently reading or no buffer allocated. */ if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL) { /* Allocate a buffer if needed. */ if (f->_IO_write_base == NULL) { _IO_doallocbuf (f); _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base); } /* Otherwise must be currently reading. If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end, logically slide the buffer forwards one block (by setting the read pointers to all point at the beginning of the block). This makes room for subsequent output. Otherwise, set the read pointers to _IO_read_end (leaving that alone, so it can continue to correspond to the external position). */ if (__glibc_unlikely (_IO_in_backup (f))) { size_t nbackup = f->_IO_read_end - f->_IO_read_ptr; _IO_free_backup_area (f); f->_IO_read_base -= MIN (nbackup,f->_IO_read_base - f->_IO_buf_base); f->_IO_read_ptr = f->_IO_read_base; }
size_t _IO_default_xsputn (FILE *f, constvoid *data, size_t n) { constchar *s = (char *) data; size_t more = n; if (more <= 0) return0; for (;;) { /* Space available. */ if (f->_IO_write_ptr < f->_IO_write_end) { size_t count = f->_IO_write_end - f->_IO_write_ptr; if (count > more) count = more; if (count > 20) { f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count); s += count; } elseif (count) { char *p = f->_IO_write_ptr; ssize_t i; for (i = count; --i >= 0; ) *p++ = *s++; f->_IO_write_ptr = p; } more -= count; } if (more == 0 || _IO_OVERFLOW (f, (unsignedchar) *s++) == EOF) break; more--; } return n - more; } libc_hidden_def (_IO_default_xsputn)
printf
调用栈
1 2 3 4 5 6
► f 0 0x7f6117fd43b0 write f 1 0x7f6117f55c0f _IO_file_write+143 f 2 0x7f6117f5639a _IO_file_xsputn+426 f 3 0x7f6117f2cfa4 buffered_vfprintf+308 f 4 0x7f6117f2a33d vfprintf+445 f 5 0x7f6117f328a9 printf+153
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) /* We desperately try to help programs which are using streams in a strange way and mix old and new functions. Detect old streams here. */ if (_IO_vtable_offset (fp) != 0) return _IO_old_fclose (fp); #endif
/* First unlink the stream. */ if (fp->_flags & _IO_IS_FILEBUF) _IO_un_link ((struct _IO_FILE_plus *) fp);
_IO_acquire_lock (fp); if (fp->_flags & _IO_IS_FILEBUF) status = _IO_file_close_it (fp); else status = fp->_flags & _IO_ERR_SEEN ? -1 : 0; _IO_release_lock (fp); _IO_FINISH (fp); if (fp->_mode > 0) { /* This stream has a wide orientation. This means we have to free the conversion functions. */ struct _IO_codecvt *cc = fp->_codecvt;
/* The possibilities for the third argument to `setvbuf'. */ #define _IOFBF 0 /* Fully buffered. */ #define _IOLBF 1 /* Line buffered. */ #define _IONBF 2 /* No buffering. */
int _IO_setvbuf (FILE *fp, char *buf, int mode, size_t size) { int result; CHECK_FILE (fp, EOF);//同样的校验 _IO_acquire_lock (fp);//同样的上锁 switch (mode) { case _IOFBF://全缓冲 fp->_flags &= ~(_IO_LINE_BUF|_IO_UNBUFFERED);//置空行缓冲和无缓冲标志 if (buf == NULL)//未指定buffer地址 { if (fp->_IO_buf_base == NULL) { /* There is no flag to distinguish between "fully buffered mode has been explicitly set" as opposed to "line buffering has not been explicitly set". In both cases, _IO_LINE_BUF is off. If this is a tty, and _IO_filedoalloc later gets called, it cannot know if it should set the _IO_LINE_BUF flag (because that is the default), or not (because we have explicitly asked for fully buffered mode). So we make sure a buffer gets allocated now, and explicitly turn off line buffering. A possibly cleaner alternative would be to add an extra flag, but then flags are a finite resource. */ if (_IO_DOALLOCATE (fp) < 0)//为其分配一块内存 { result = EOF; goto unlock_return; } fp->_flags &= ~_IO_LINE_BUF;//因为缓存分配函数默认会将行缓冲标志设为1,再次清空,可以看一下上面一大段英文 } result = 0; goto unlock_return; } break; case _IOLBF: fp->_flags &= ~_IO_UNBUFFERED;//清空无缓冲标志 fp->_flags |= _IO_LINE_BUF;//设置行缓冲模式 if (buf == NULL) { result = 0; goto unlock_return; } break; case _IONBF: fp->_flags &= ~_IO_LINE_BUF;//设置无缓冲标志 fp->_flags |= _IO_UNBUFFERED;//清空行缓冲模式 buf = NULL; size = 0; break; default: result = EOF; goto unlock_return; } result = _IO_SETBUF (fp, buf, size) == NULL ? EOF : 0;//设置buf