struct_IO_FILE{int_flags;/* High-order word is _IO_MAGIC; rest is flags. */#define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambuf protocol. *//* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char*_IO_read_ptr;/* Current read pointer */char*_IO_read_end;/* End of get area. */char*_IO_read_base;/* Start of putback+get area. */char*_IO_write_base;/* Start of put area. */char*_IO_write_ptr;/* Current put pointer. */char*_IO_write_end;/* End of put area. */char*_IO_buf_base;/* Start of reserve area. */char*_IO_buf_end;/* End of reserve area. *//* The following fields are used to support backing up and undo. */char*_IO_save_base;/* Pointer to start of non-current get area. */char*_IO_backup_base;/* Pointer to first valid character of backup area */char*_IO_save_end;/* Pointer to end of non-current get area. */struct_IO_marker*_markers;struct_IO_FILE*_chain;int_fileno;int_flags2;_IO_off_t_old_offset;/* This used to be _offset but it's too small. *//* 1+column number of pbase(); 0 is unknown. */unsignedshort_cur_column;signedchar_vtable_offset;char_shortbuf[1];/* char* _save_gptr; char* _save_egptr; */_IO_lock_t*_lock;_IO_off64_t_offset;/* Wide character stream stuff. */struct_IO_codecvt*_codecvt;struct_IO_wide_data*_wide_data;struct_IO_FILE*_freeres_list;void*_freeres_buf;size_t__pad5;int_mode;/* Make sure we don't get into trouble again. */char_unused2[15*sizeof(int)-4*sizeof(void*)-sizeof(size_t)];};
/* Standard streams. */externstruct_IO_FILE*stdin;/* Standard input stream. */externstruct_IO_FILE*stdout;/* Standard output stream. */externstruct_IO_FILE*stderr;/* Standard error output stream. */
/* We always allocate an extra word following an _IO_FILE.
This contains a pointer to the function jump table used.
This is for compatibility with C++ streambuf; the word can
be used to smash to a pointer to a virtual function table. */struct_IO_FILE_plus{_IO_FILEfile;conststruct_IO_jump_t*vtable;};
就是加了一个类似于 c++ 里的虚表. 通过注释也可以看到, 这个虚表就是用来同时 兼容 c++ 的 streambuf 而写的.
#define JUMP_FIELD(TYPE, NAME) TYPE NAME
struct_IO_jump_t{JUMP_FIELD(size_t,__dummy);JUMP_FIELD(size_t,__dummy2);JUMP_FIELD(_IO_finish_t,__finish);JUMP_FIELD(_IO_overflow_t,__overflow);JUMP_FIELD(_IO_underflow_t,__underflow);JUMP_FIELD(_IO_underflow_t,__uflow);JUMP_FIELD(_IO_pbackfail_t,__pbackfail);/* showmany */JUMP_FIELD(_IO_xsputn_t,__xsputn);JUMP_FIELD(_IO_xsgetn_t,__xsgetn);JUMP_FIELD(_IO_seekoff_t,__seekoff);JUMP_FIELD(_IO_seekpos_t,__seekpos);JUMP_FIELD(_IO_setbuf_t,__setbuf);JUMP_FIELD(_IO_sync_t,__sync);JUMP_FIELD(_IO_doallocate_t,__doallocate);JUMP_FIELD(_IO_read_t,__read);JUMP_FIELD(_IO_write_t,__write);JUMP_FIELD(_IO_seek_t,__seek);JUMP_FIELD(_IO_close_t,__close);JUMP_FIELD(_IO_stat_t,__stat);JUMP_FIELD(_IO_showmanyc_t,__showmanyc);JUMP_FIELD(_IO_imbue_t,__imbue);};
/* Open a file and create a new stream for it.
This function is a possible cancellation point and therefore not
marked with __THROW. */externFILE*fopen(constchar*__restrict__filename,constchar*__restrict__modes)__wur;
void_IO_old_init(_IO_FILE*fp,intflags){fp->_flags=_IO_MAGIC|flags;fp->_flags2=0;fp->_IO_buf_base=NULL;fp->_IO_buf_end=NULL;fp->_IO_read_base=NULL;fp->_IO_read_ptr=NULL;fp->_IO_read_end=NULL;fp->_IO_write_base=NULL;fp->_IO_write_ptr=NULL;fp->_IO_write_end=NULL;fp->_chain=NULL;/* Not necessary. */fp->_IO_save_base=NULL;fp->_IO_backup_base=NULL;fp->_IO_save_end=NULL;fp->_markers=NULL;fp->_cur_column=0;fp->_vtable_offset=0;if(fp->_lock!=NULL)_IO_lock_init(*fp->_lock);}void_IO_no_init(_IO_FILE*fp,intflags,intorientation,struct_IO_wide_data*wd,conststruct_IO_jump_t*jmp){_IO_old_init(fp,flags);fp->_mode=orientation;fp->_freeres_list=NULL;}
可以看到, 首先进入 _IO_old_init, 设置一下 flag 为传入的 1 (加上魔数), 即权限 r. FILE 里其他东西一律清空 (锁清空对应地址里的值, 跟进 _IO_lock_init 可以看到是清空为 0).
#define _IO_pos_BAD ((_IO_off64_t)(-1))
#define CLOSED_FILEBUF_FLAGS \
(_IO_IS_FILEBUF+_IO_NO_READS+_IO_NO_WRITES+_IO_TIED_PUT_GET)
versioned_symbol(libc,_IO_new_file_init,_IO_file_init,GLIBC_2_1);void_IO_new_file_init(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._IO_file_flags|=CLOSED_FILEBUF_FLAGS;_IO_link_in(fp);fp->file._fileno=-1;}libc_hidden_ver(_IO_new_file_init,_IO_file_init)
/* Read chunks of generic data from STREAM.
This function is a possible cancellation point and therefore not
marked with __THROW. */externsize_tfread(void*__restrict__ptr,size_t__size,size_t__n,FILE*__restrict__stream)__wur;
_IO_size_t_IO_file_xsgetn(_IO_FILE*fp,void*data,_IO_size_tn){_IO_size_twant,have;_IO_ssize_tcount;char*s=data;want=n;if(fp->_IO_buf_base==NULL){...// 省略 backup
_IO_doallocbuf(fp);}while(want>0){have=fp->_IO_read_end-fp->_IO_read_ptr;if(want<=have){memcpy(s,fp->_IO_read_ptr,want);fp->_IO_read_ptr+=want;want=0;}else{if(have>0){s=__mempcpy(s,fp->_IO_read_ptr,have);want-=have;fp->_IO_read_ptr+=have;}...// 省略 bakcup
/* 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)){if(__underflow(fp)==EOF)break;continue;}/* 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){_IO_size_tblock_size=fp->_IO_buf_end-fp->_IO_buf_base;if(block_size>=128)count-=want%block_size;}count=_IO_SYSREAD(fp,s,count);if(count<=0){if(count==0)fp->_flags|=_IO_EOF_SEEN;elsefp->_flags|=_IO_ERR_SEEN;break;}s+=count;want-=count;if(fp->_offset!=_IO_pos_BAD)_IO_pos_adjust(fp->_offset,count);}}returnn-want;}libc_hidden_def(_IO_file_xsgetn)
#ifndef _LIBC
/* If _IO_cleanup_registration_needed is non-zero, we should call the
function it points to. This is to make sure _IO_cleanup gets called
on exit. We call it from _IO_file_doallocate, since that is likely
to get called by any program that does buffered I/O. */if(__glibc_unlikely(_IO_cleanup_registration_needed!=NULL))(*_IO_cleanup_registration_needed)();#endif
_IO_size_t_IO_file_xsgetn(_IO_FILE*fp,void*data,_IO_size_tn){while(want>0){...else{.../* 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)){if(__underflow(fp)==EOF)break;continue;}...}...}returnn-want;}
这个 if 就是在判断是刷新缓冲区还是直接系统调用 read. 如果刷新缓冲区的话, 刷新完了就 continue 去读取.
刷新缓冲区就一个函数 __underflow. 叫 underflow 的原因是, 读取的缓冲区是 ptr 到 end 这部分有效, 在 base 到 end 的上部分, 而读取之后, 缓冲区的下部分 ptr 溢出了. 对应 overflow 是因为向缓冲区写数据的时候, 有数据的部分是 base 到 ptr, 是整个 base 到 end 的下部分. 写满了, 就向上溢出了.
int_IO_new_file_underflow(_IO_FILE*fp){_IO_ssize_tcount;// 检查可读权限
if(fp->_flags&_IO_NO_READS){fp->_flags|=_IO_ERR_SEEN;__set_errno(EBADF);returnEOF;}// 检查是否真的需要刷新缓冲区
if(fp->_IO_read_ptr<fp->_IO_read_end)return*(unsignedchar*)fp->_IO_read_ptr;// 检查是否还没有缓冲区, 或者使用 backup, 省略 backup
if(fp->_IO_buf_base==NULL){..._IO_doallocbuf(fp);}...// 省略如果是行设备 (stdout?), 则 IO_OVERFLOW 将数据写到 stdout, 不重要, 不管他
// 又来一次...
_IO_switch_to_get_mode(fp);/* 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. */// 设置指针先指向 base
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;// 调用 vtable 中的 _IO_SYSREAD 读取数据
count=_IO_SYSREAD(fp,fp->_IO_buf_base,fp->_IO_buf_end-fp->_IO_buf_base);// 读取到 EOF 或者读取失败, 设置 flag
if(count<=0){if(count==0)fp->_flags|=_IO_EOF_SEEN;elsefp->_flags|=_IO_ERR_SEEN,count=0;}fp->_IO_read_end+=count;// 处理读取到 EOF
// 设置 offset, 加上读取了的数据
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;returnEOF;}if(fp->_offset!=_IO_pos_BAD)fp->_offset+=count;return*(unsignedchar*)fp->_IO_read_ptr;}libc_hidden_ver(_IO_new_file_underflow,_IO_file_underflow)
_IO_size_t_IO_file_xsgetn(_IO_FILE*fp,void*data,_IO_size_tn){while(want>0){...else{.../* These must be set before the sysread as we might longjmp out
waiting for input. */// 这两个是宏, 就是设置 read 和 write 的三个指针都为 _IO_buf_base
_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){_IO_size_tblock_size=fp->_IO_buf_end-fp->_IO_buf_base;if(block_size>=128)count-=want%block_size;}// 系统调用 read 读取
count=_IO_SYSREAD(fp,s,count);if(count<=0){// 读完了
if(count==0)fp->_flags|=_IO_EOF_SEEN;// 读取失败
elsefp->_flags|=_IO_ERR_SEEN;break;}s+=count;want-=count;if(fp->_offset!=_IO_pos_BAD)fp->_offset+=count;}}returnn-want;}
_IO_size_t_IO_new_file_xsputn(_IO_FILE*f,constvoid*data,_IO_size_tn){constchar*s=(constchar*)data;_IO_size_tto_do=n;intmust_flush=0;_IO_size_tcount=0;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){// 由于行设备 (stdout) 需要逐行显示, 那么找到最后一个回车
constchar*p;for(p=s+n;p>s;){if(*--p=='\n'){count=p-s+1;// 将要能够写入的字节 count 设为到这个回车这里
must_flush=1;// 同时设置 must flush 为 1
break;}}}}// 如果不是行设备, 那么可用空间就是 end - ptr
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){_IO_size_tblock_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. */returnto_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 是整数块所有的大小
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)returnn-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);}returnn-to_do;}libc_hidden_ver(_IO_new_file_xsputn,_IO_file_xsputn)
int_IO_new_file_overflow(_IO_FILE*f,intch){// 检查权限
if(f->_flags&_IO_NO_WRITES)/* SET ERROR */{f->_flags|=_IO_ERR_SEEN;__set_errno(EBADF);returnEOF;}/* 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). */...// 省略 backup
// 看不懂的设置......
if(f->_IO_read_ptr==f->_IO_buf_end)f->_IO_read_end=f->_IO_read_ptr=f->_IO_buf_base;f->_IO_write_ptr=f->_IO_read_ptr;f->_IO_write_base=f->_IO_write_ptr;f->_IO_write_end=f->_IO_buf_end;f->_IO_read_base=f->_IO_read_ptr=f->_IO_read_end;f->_flags|=_IO_CURRENTLY_PUTTING;if(f->_mode<=0&&f->_flags&(_IO_LINE_BUF|_IO_UNBUFFERED))f->_IO_write_end=f->_IO_write_ptr;}if(ch==EOF)return_IO_do_write(f,f->_IO_write_base,f->_IO_write_ptr-f->_IO_write_base);...// 由于上层函数传入的 ch 是 EOF, 所以后面都不会执行到了, 就不看了吧
}libc_hidden_ver(_IO_new_file_overflow,_IO_file_overflow)
static_IO_size_tnew_do_write(_IO_FILE*fp,constchar*data,_IO_size_tto_do){_IO_size_tcount;if(fp->_flags&_IO_IS_APPENDING)/* On a system without a proper O_APPEND implementation,
you would need to sys_seek(0, SEEK_END) here, but is
not needed nor desirable for Unix- or Posix-like systems.
Instead, just indicate that offset (before and after) is
unpredictable. */fp->_offset=_IO_pos_BAD;elseif(fp->_IO_read_end!=fp->_IO_write_base){_IO_off64_tnew_pos=_IO_SYSSEEK(fp,fp->_IO_write_base-fp->_IO_read_end,1);if(new_pos==_IO_pos_BAD)return0;fp->_offset=new_pos;}count=_IO_SYSWRITE(fp,data,to_do);if(fp->_cur_column&&count)fp->_cur_column=_IO_adjust_column(fp->_cur_column-1,data,count)+1;_IO_setg(fp,fp->_IO_buf_base,fp->_IO_buf_base,fp->_IO_buf_base);fp->_IO_write_base=fp->_IO_write_ptr=fp->_IO_buf_base;fp->_IO_write_end=(fp->_mode<=0&&(fp->_flags&(_IO_LINE_BUF|_IO_UNBUFFERED))?fp->_IO_buf_base:fp->_IO_buf_end);returncount;}
int_IO_new_fclose(_IO_FILE*fp){intstatus;.../* First unlink the stream. */if(fp->_IO_file_flags&_IO_IS_FILEBUF)_IO_un_link((struct_IO_FILE_plus*)fp);_IO_acquire_lock(fp);if(fp->_IO_file_flags&_IO_IS_FILEBUF)status=_IO_file_close_it(fp);elsestatus=fp->_flags&_IO_ERR_SEEN?-1:0;_IO_release_lock(fp);_IO_FINISH(fp);if(fp->_mode>0)...else{...if(fp!=_IO_stdin&&fp!=_IO_stdout&&fp!=_IO_stderr){fp->_IO_file_flags=0;free(fp);}}returnstatus;}versioned_symbol(libc,_IO_new_fclose,_IO_fclose,GLIBC_2_1);strong_alias(_IO_new_fclose,__new_fclose)versioned_symbol(libc,__new_fclose,fclose,GLIBC_2_1);
int_IO_file_close(_IO_FILE*fp){/* Cancelling close should be avoided if possible since it leaves an
unrecoverable state behind. */returnclose_not_cancel(fp->_fileno);// 一个宏, 调用了 close 系统调用
}libc_hidden_def(_IO_file_close)