关闭
Hit
enter
to search or
ESC
to close
May I Suggest ?
#leanote #leanote blog #code #hello world
Mutepig's Blog
Home
Archives
Tags
Search
About Me
Play with FILE Structure [1]
无
934
0
1
mut3p1g
学习一下台湾大佬对文件结构的一些利用姿势:[Play with FILE Structure](http://4ngelboy.blogspot.tw/2017/11/play-with-file-structure-yet-another.html) 以下图片皆从ppt上截取而得。 <!--more--> ## 0x00 调试glibc源码 首先安装glibc符号表: ``` sudo apt-get install libc6-dbg ``` 然后找个可写目录下载源码: ``` sudo apt-get source libc6-dev ``` 下载好了直接用在`~.gdbinit`中设置`directory`,注意该命令是不递归的,所以得把源码中的目录都写进去,这里列一条: ``` directory /home/mutepig/git/glibc/glibc-2.23/intl ``` 接下来就能在`gdb`调试时看到`glibc`中的函数的源码了。 ## 0x01 基础知识 ### 1. _IO_FILE 首先复习一下`_IO_FILE`结构` ``` 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;//这个就是linux内核中文件描述符fd #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE }; struct _IO_FILE_plus { _IO_FILE file; const struct _IO_jump_t *vtable;//IO函数跳转表 }; ``` 文件结构主要分为下面几个部分: * flags 标记了文件流的一些属性,且是只读的。 ``` int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags ``` * stream buffer 流缓冲区,包括了读、写以及一些保留的缓冲区。 ``` 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. */ ``` * 备份 备份的区域,当需要撤销时可以还原。 ``` 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; ``` * 文件链表 用来将多个文件链接起来 ``` struct _IO_FILE *_chain; // 指向下一个fp ``` ![](https://leanote.com/api/file/getImage?fileId=5a0c5eaaab6441596e003236) * 文件描述符 ``` int _fileno;//这个就是linux内核中文件描述符fd ``` * file plus 在`_IO_FILE`结构下面,还加入了一个`vtable`,用来指向各个文件操作函数的地址。 ``` struct _IO_FILE_plus { _IO_FILE file; const struct _IO_jump_t *vtable;//IO函数跳转表 }; const struct _IO_jump_t _IO_file_jumps = { JUMP_INIT_DUMMY, JUMP_INIT(finish, INTUSE(_IO_file_finish)), JUMP_INIT(overflow, INTUSE(_IO_file_overflow)), JUMP_INIT(underflow, INTUSE(_IO_file_underflow)), JUMP_INIT(uflow, INTUSE(_IO_default_uflow)), JUMP_INIT(pbackfail, INTUSE(_IO_default_pbackfail)), JUMP_INIT(xsputn, INTUSE(_IO_file_xsputn)), JUMP_INIT(xsgetn, INTUSE(_IO_file_xsgetn)), JUMP_INIT(seekoff, _IO_new_file_seekoff), JUMP_INIT(seekpos, _IO_default_seekpos), JUMP_INIT(setbuf, _IO_new_file_setbuf), JUMP_INIT(sync, _IO_new_file_sync), JUMP_INIT(doallocate, INTUSE(_IO_file_doallocate)), JUMP_INIT(read, INTUSE(_IO_file_read)), JUMP_INIT(write, _IO_new_file_write), JUMP_INIT(seek, INTUSE(_IO_file_seek)), JUMP_INIT(close, INTUSE(_IO_file_close)), JUMP_INIT(stat, INTUSE(_IO_file_stat)), JUMP_INIT(showmanyc, _IO_default_showmanyc), JUMP_INIT(imbue, _IO_default_imbue) }; ``` ### 2. 文件操作函数 #### a. fopen `fopen`的操作如图所示 ![](https://leanote.com/api/file/getImage?fileId=5a0ff1c2ab64411c3a000ad7) 不过我还是偏向于看[源码](https://code.woboq.org/userspace/glibc/libio/iofopen.c.html#__fopen_internal)一些: ##### i) __fopen_internal 首先声明了一个结构体`locked_FILE`,就是`file plus`后面加上了一个`lock`,然后`malloc`一块区域用来存放这个结构体。之后会初始化其流缓冲区以及`vtable`,接着就调用`_IO_new_file_init_internal`来进行初始化,尝试用`_IO_file_fopen`打开文件。成功则返回,失败则从链表中取出并`free`对应的空间。 ``` 55 _IO_FILE * 56 __fopen_internal (const char *filename, const char *mode, int is32) 57 { 58 struct locked_FILE 59 { 60 struct _IO_FILE_plus fp; 61 #ifdef _IO_MTSAFE_IO 62 _IO_lock_t lock; 63 #endif 64 struct _IO_wide_data wd; 65 } *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE)); // malloc新file存放的空间,大小为0x230 66 67 if (new_f == NULL) // 空间申请失败 68 return NULL; 69 #ifdef _IO_MTSAFE_IO 70 new_f->fp.file._lock = &new_f->lock; 71 #endif 72 _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps); // 将file的流缓冲区全部置空 73 _IO_JUMPS (&new_f->fp) = &_IO_file_jumps; // 初始化vtable,指向_IO_file_jump 74 _IO_new_file_init_internal (&new_f->fp); // 75 #if !_IO_UNIFIED_JUMPTABLES 76 new_f->fp.vtable = NULL; 77 #endif 78 if (_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32) != NULL) // 打开文件成功 79 return __fopen_maybe_mmap (&new_f->fp.file); 80 81 _IO_un_link (&new_f->fp); // 失败则将file从链表中取出 82 free (new_f); // 释放空间 83 return NULL; 84 } ``` ##### ii) _IO_new_file_init_internal 这里就初始化一下`offset`为-1,并且设置一下相关`flag`,之后就是调用`_IO_link_in`将`fp`链接到文件链表中。 ``` 105 void 106 _IO_new_file_init_internal (struct _IO_FILE_plus *fp) 107 { 108 /* POSIX.1 allows another file handle to be used to change the position 109 of our file descriptor. Hence we actually don't know the actual 110 position before we do the first fseek (and until a following fflush). */ 111 fp->file._offset = _IO_pos_BAD; 112 fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS; 113 114 _IO_link_in (fp); 115 fp->file._fileno = -1; 116 } ``` ##### iii) _IO_link_in 这个函数就是用来将`fp`插入到文件链表的头部,文件链表头部就是`_IO_list_all`。 链接结束之后文件链表就变成下面这样了: ![](https://leanote.com/api/file/getImage?fileId=5a0da1fcab64416553002f48) ``` 93 void 94 _IO_link_in (struct _IO_FILE_plus *fp) 95 { 96 if ((fp->file._flags & _IO_LINKED) == 0) // 如果fp还没链接 97 { 98 fp->file._flags |= _IO_LINKED; // 设置已链接标志 99 #ifdef _IO_MTSAFE_IO 100 _IO_cleanup_region_start_noarg (flush_cleanup); 101 _IO_lock_lock (list_all_lock); 102 run_fp = (_IO_FILE *) fp; 103 _IO_flockfile ((_IO_FILE *) fp); 104 #endif 105 fp->file._chain = (_IO_FILE *) _IO_list_all; // 指向之前的文件头 106 _IO_list_all = fp; // 现在链表头为当前fp 107 ++_IO_list_all_stamp; 108 #ifdef _IO_MTSAFE_IO 109 _IO_funlockfile ((_IO_FILE *) fp); 110 run_fp = NULL; 111 _IO_lock_unlock (list_all_lock); 112 _IO_cleanup_region_end (0); 113 #endif 114 } 115 } ``` ##### iv) _IO_new_file_fopen 上面将`fp`链接到文件链表中之后,就回到`_IO_new_file_init_internal`并调用`_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32)`来打开文件了,而`_IO_file_fopen`就是`_IO_new_file_fopen`。这个函数总的来说就是对`mode`进行了一下判断,然后通过`_IO_file_open`来打开文件。 #### b. fread fread的操作如图所示 ![](https://leanote.com/api/file/getImage?fileId=5a0ff1adab64411c3a000acc) 下面继续从源码开始进行分析: ##### i) _IO_fread 首先是用`CHECK_FILE`判断一下`fp`是否合法,然后就调用`_IO_sgetn`来读取数据了。 ``` 29 _IO_size_t 30 _IO_fread (void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp) 31 { 32 _IO_size_t bytes_requested = size * count; 33 _IO_size_t bytes_read; 34 CHECK_FILE (fp, 0); 35 if (bytes_requested == 0) 36 return 0; 37 _IO_acquire_lock (fp); 38 bytes_read = _IO_sgetn (fp, (char *) buf, bytes_requested); 39 _IO_release_lock (fp); 40 return bytes_requested == bytes_read ? count : bytes_read / size; 41 } ``` ##### ii) CHECK_FILE 这里`_OLD_MAGIC_MASK`并没有找到,而这两个判断我也都没懂是干啥的。。 ``` 8 # define CHECK_FILE(FILE, RET) COERCE_FILE (FILE) 770 # define COERCE_FILE(FILE) \ 771 (((FILE)->_IO_file_flags & _IO_MAGIC_MASK) == _OLD_MAGIC_MASK \ 772 && (FILE) = *(FILE**)&((int*)fp)[1]) 773 #endif 94 #define _IO_MAGIC_MASK 0xFFFF0000 ``` ##### iii) _IO_file_xsgetn `_IO_size_t`函数就是调用了一下`_IO_XSGETN`,实际上也就是调用`vtable->xsgetn`,也就是`_IO_file_xsgetn`。 1. 首先,如果`fp->_IO_buf_base`为空的话,就会调用`_IO_doallocbuf (fp)`申请一篇空间给它; 2. 接着就是向`data`中写入数据了,先判断已读缓冲区`_IO_read`中是否有足够的数据,如果足够就将数据直接写入并返回; 3. 如果已读缓冲区中的数据不够,则将剩余的数据写入,同时`read_ptr`向后指;如果当前`IO`正在备份中,则调用`_IO_switch_to_main_get_area`调整到`main_get`区域;如果存在预留区域,且我们需求大小比预留区域小,那么调用`underflow`调用`sysread`来将数据读入到预留缓冲区同时也是已读缓冲区; 4. 如果预留缓冲区不够了,则调用`sysread`将数据直接读入`data`。 ``` 1290 _IO_size_t 1291 _IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n) // data为写入的位置,n为要读入的长度 1292 { 1293 _IO_size_t want, have; 1294 _IO_ssize_t count; 1295 char *s = data; 1296 1297 want = n; 1298 1299 if (fp->_IO_buf_base == NULL) // 如果没有预留空间 1300 { 1301 /* Maybe we already have a push back pointer. */ 1302 if (fp->_IO_save_base != NULL) // 不做备份 1303 { 1304 free (fp->_IO_save_base); 1305 fp->_flags &= ~_IO_IN_BACKUP; 1306 } 1307 _IO_doallocbuf (fp); // 申请空间给_IO_buf_base 1308 } 1309 1310 while (want > 0) // 如果需求还未满足 1311 { 1312 have = fp->_IO_read_end - fp->_IO_read_ptr; // 已经读了的数据 1313 if (want <= have) // 已读数据能满足需求的长度 1314 { 1315 memcpy (s, fp->_IO_read_ptr, want); // 直接将对应长度的内容复制进去 1316 fp->_IO_read_ptr += want; // read_ptr向后指 1317 want = 0; 1318 } 1319 else 1320 { 1321 if (have > 0) // 如果已读数据不能满足需求,但存在已读数据 1322 { 1323 s = __mempcpy (s, fp->_IO_read_ptr, have); // 将文件剩余数据读完 1324 want -= have; // 需求减去对应大小 1325 fp->_IO_read_ptr += have; // read_ptr向后指 1326 } 1327 1328 /* Check for backup and repeat */ 1329 if (_IO_in_backup (fp)) // 如果fp存在备份 1330 { 1331 _IO_switch_to_main_get_area (fp); // 从备份缓冲区转换到main_get区 1332 continue; 1333 } 1334 1335 /* If we now want less than a buffer, underflow and repeat 1336 the copy. Otherwise, _IO_SYSREAD directly to 1337 the user buffer. */ 1338 if (fp->_IO_buf_base 1339 && want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base)) // 如果存在预留区域,且我们需求大小比预留区域小 1340 { 1341 if (__underflow (fp) == EOF) // 那么调用 __underflow 1342 break; 1343 1344 continue; 1345 } 1346 1347 /* These must be set before the sysread as we might longjmp out 1348 waiting for input. */ 1349 _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); 1350 _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base); 1351 1352 /* Try to maintain alignment: read a whole number of blocks. */ 1353 count = want; 1354 if (fp->_IO_buf_base) 1355 { 1356 _IO_size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base; 1357 if (block_size >= 128) 1358 count -= want % block_size; 1359 } 1360 1361 count = _IO_SYSREAD (fp, s, count); // 直接将数据读入到缓冲区中 1362 if (count <= 0) 1363 { 1364 if (count == 0) 1365 fp->_flags |= _IO_EOF_SEEN; 1366 else 1367 fp->_flags |= _IO_ERR_SEEN; 1368 1369 break; 1370 } 1371 1372 s += count; 1373 want -= count; 1374 if (fp->_offset != _IO_pos_BAD) 1375 _IO_pos_adjust (fp->_offset, count); 1376 } 1377 } 1378 1379 return n - want; 1380 } ``` ##### iv) _IO_switch_to_* * _IO_switch_to_main_get_area 这个函数主要是将`IO`从备份区域转换到主读取区,先设置标志位不在备份中,接着将`read_base/read_end`和`save_base/save_end`交换,最后设置`read_ptr`为`read_base`。 ``` /* Switch current get area from backup buffer to (start of) main get area. */ ``` * _IO_switch_to_backup_area 这个函数主要是将`IO`从当前区域转换到备份区域,先设置标志位在备份中,剩下的就和上面那个函数一样了。 ``` /* Switch current get area from main get area to (end of) backup area. */ ``` * _IO_switch_to_get_mode 这个函数主要是将模式从写模式转换为读模式。 * _IO_switch_to_put_mode 这个函数主要是将模式从读模式转换为写模式。 ##### v) _IO_file_doallocate 总之,就是申请`st.st_blksize`(<0x2000)的空间给`fp`,并设置`f->_IO_buf_base`和`f->_IO_buf_end`为对应空间的起末地址,同时相关标志位也会被设置。 ``` 76 int 77 _IO_file_doallocate (_IO_FILE *fp) 78 { 79 _IO_size_t size; 80 char *p; 81 struct stat64 st; 82 83 size = _IO_BUFSIZ; // 8192 84 if (fp->_fileno >= 0 && __builtin_expect (_IO_SYSSTAT (fp, &st), 0) >= 0) 85 { 86 if (S_ISCHR (st.st_mode)) 87 { 88 /* Possibly a tty. */ 89 if ( 90 #ifdef DEV_TTY_P 91 DEV_TTY_P (&st) || 92 #endif 93 local_isatty (fp->_fileno)) 94 fp->_flags |= _IO_LINE_BUF; 95 } 96 #if _IO_HAVE_ST_BLKSIZE 97 if (st.st_blksize > 0 && st.st_blksize < _IO_BUFSIZ) 98 size = st.st_blksize; 99 #endif 100 } 101 p = malloc (size); // 申请空间p给fp作为预留空间 102 if (__glibc_unlikely (p == NULL)) 103 return EOF; 104 _IO_setb (fp, p, p + size, 1); 105 return 1; 106 } 354 void 355 _IO_setb (_IO_FILE *f, char *b, char *eb, int a) 356 { // 设置f->_IO_buf_base=b,f->_IO_buf_end = eb 357 if (f->_IO_buf_base && !(f->_flags & _IO_USER_BUF)) 358 free (f->_IO_buf_base); 359 f->_IO_buf_base = b; 360 f->_IO_buf_end = eb; 361 if (a) 362 f->_flags &= ~_IO_USER_BUF; 363 else 364 f->_flags |= _IO_USER_BUF; 365 } ``` ##### vi) __underflow 这里先来几个判断有没有`EOF`,或者已读缓冲区还没被读完,如果没有就调用`_IO_UNDERFLOW`。 ``` 294 int 295 __underflow (_IO_FILE *fp) 296 { 297 if (_IO_vtable_offset (fp) == 0 && _IO_fwide (fp, -1) != -1) // 如果fp的vtable没初始化并且其流也没初始化,则返回EOF 298 return EOF; 299 300 if (fp->_mode == 0) 301 _IO_fwide (fp, -1); 302 if (_IO_in_put_mode (fp)) 303 if (_IO_switch_to_get_mode (fp) == EOF) 304 return EOF; 305 if (fp->_IO_read_ptr < fp->_IO_read_end) 306 return *(unsigned char *) fp->_IO_read_ptr; 307 if (_IO_in_backup (fp)) 308 { 309 _IO_switch_to_main_get_area (fp); 310 if (fp->_IO_read_ptr < fp->_IO_read_end) 311 return *(unsigned char *) fp->_IO_read_ptr; 312 } 313 if (_IO_have_markers (fp)) 314 { 315 if (save_for_backup (fp, fp->_IO_read_end)) 316 return EOF; 317 } 318 else if (_IO_have_backup (fp)) 319 _IO_free_backup_area (fp); 320 return _IO_UNDERFLOW (fp); 321 } ``` ##### vii) _IO_new_file_underflow 这个函数同样先来几个判读是否返回`EOF`或已读缓冲区是否没读完,然后将`_IO_[read|write]_[ptr|base|end]`都赋值为`_IO_buf_base`,然后调用`sysread`将数据读入到`_IO_buf_base`,读取成功的话就置`fp->_IO_read_end += count`,表示已读区域的终结位置。 ``` 468 int 469 _IO_new_file_underflow (_IO_FILE *fp) 470 { 471 _IO_ssize_t count; 472 #if 0 473 /* SysV does not make this test; take it out for compatibility */ 474 if (fp->_flags & _IO_EOF_SEEN) // 如果已经EOF了则返回EOF 475 return (EOF); 476 #endif 477 478 if (fp->_flags & _IO_NO_READS) // 如果文件不可读,也返回EOF 479 { 480 fp->_flags |= _IO_ERR_SEEN; 481 __set_errno (EBADF); 482 return EOF; 483 } 484 if (fp->_IO_read_ptr < fp->_IO_read_end) // 如果读缓冲区还没被读完,则继续读 485 return *(unsigned char *) fp->_IO_read_ptr; 486 487 if (fp->_IO_buf_base == NULL) // 如果预留缓冲区为空 488 { 489 /* Maybe we already have a push back pointer. */ 490 if (fp->_IO_save_base != NULL) // 且备份缓冲区不为空 491 { 492 free (fp->_IO_save_base); 493 fp->_flags &= ~_IO_IN_BACKUP; 494 } 495 _IO_doallocbuf (fp); // 申请一个空间给与流缓冲区 496 } 497 498 /* Flush all line buffered files before reading. */ 499 /* FIXME This can/should be moved to genops ?? */ 500 if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED)) // 调用sysread前冲洗line缓冲区数据 501 { 502 #if 0 503 _IO_flush_all_linebuffered (); 504 #else 505 /* We used to flush all line-buffered stream. This really isn't 506 required by any standard. My recollection is that 507 traditional Unix systems did this for stdout. stderr better 508 not be line buffered. So we do just that here 509 explicitly. --drepper */ 510 _IO_acquire_lock (_IO_stdout); 511 512 if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF)) 513 == (_IO_LINKED | _IO_LINE_BUF)) 514 _IO_OVERFLOW (_IO_stdout, EOF); 515 516 _IO_release_lock (_IO_stdout); 517 #endif 518 } 519 520 _IO_switch_to_get_mode (fp); 521 522 /* This is very tricky. We have to adjust those 523 pointers before we call _IO_SYSREAD () since 524 we may longjump () out while waiting for 525 input. Those pointers may be screwed up. H.J. */ 526 fp->_IO_read_base = fp->_IO_read_ptr = fp->_IO_buf_base; 527 fp->_IO_read_end = fp->_IO_buf_base; 528 fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_write_end 529 = fp->_IO_buf_base; 530 531 count = _IO_SYSREAD (fp, fp->_IO_buf_base, 532 fp->_IO_buf_end - fp->_IO_buf_base); // 调用sysread读取数据到_IO_buf_base中 533 if (count <= 0) // EOF或读取失败 534 { 535 if (count == 0) 536 fp->_flags |= _IO_EOF_SEEN; 537 else 538 fp->_flags |= _IO_ERR_SEEN, count = 0; 539 } 540 fp->_IO_read_end += count; // 已读区域为read_base -- read_end 541 if (count == 0) 542 { 543 /* If a stream is read to EOF, the calling application may switch active 544 handles. As a result, our offset cache would no longer be valid, so 545 unset it. */ 546 fp->_offset = _IO_pos_BAD; 547 return EOF; 548 } 549 if (fp->_offset != _IO_pos_BAD) 550 _IO_pos_adjust (fp->_offset, count); 551 return *(unsigned char *) fp->_IO_read_ptr; 552 } ``` #### c. fwrite fwrite的流程如图所示: ![](https://leanote.com/api/file/getImage?fileId=5a0ff19cab64411c3a000acb) ##### i) _IO_fwrite 首先是用`CHECK_FILE`判断一下`fp`是否合法,然后就调用`_IO_sputn`来写入数据了。 ``` 27 #include "libioP.h" 28 29 _IO_size_t 30 _IO_fwrite (const void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp) 31 { 32 _IO_size_t request = size * count; 33 _IO_size_t written = 0; 34 CHECK_FILE (fp, 0); 35 if (request == 0) 36 return 0; 37 _IO_acquire_lock (fp); 38 if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1) 39 written = _IO_sputn (fp, (const char *) buf, request); 40 _IO_release_lock (fp); 41 /* We have written all of the input in case the return value indicates 42 this or EOF is returned. The latter is a special case where we 43 simply did not manage to flush the buffer. But the data is in the 44 buffer and therefore written as far as fwrite is concerned. */ 45 if (written == request || written == EOF) 46 return count; 47 else 48 return written / size; 49 } ``` ##### ii) _IO_new_file_xsputn 1. 获取缓冲区可用空间大小,存入`count` 2. 如果可用空间不为空,那么直接写入缓冲区,剩余的记作`to_do` 3. 如果需求未满足,那么调用`_IO_OVERFLOW`将写缓冲区中的数据写入文件 4. 如果没有,那么调用`_IO_default_xsputn`将数据写入缓冲区 ``` 1215 _IO_size_t 1216 _IO_new_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n) 1217 { 1218 const char *s = (const char *) data; 1219 _IO_size_t to_do = n; 1220 int must_flush = 0; 1221 _IO_size_t count = 0; 1222 1223 if (n <= 0) 1224 return 0; 1225 /* This is an optimized implementation. 1226 If the amount to be written straddles a block boundary 1227 (or the filebuf is unbuffered), use sys_write directly. */ 1228 1229 /* First figure out how much space is available in the buffer. */ 1230 if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) 1231 { 1232 count = f->_IO_buf_end - f->_IO_write_ptr; 1233 if (count >= n) 1234 { 1235 const char *p; 1236 for (p = s + n; p > s; ) 1237 { 1238 if (*--p == '\n') 1239 { 1240 count = p - s + 1; 1241 must_flush = 1; 1242 break; 1243 } 1244 } 1245 } 1246 } 1247 else if (f->_IO_write_end > f->_IO_write_ptr) 1248 count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */ 1249 1250 /* Then fill the buffer. */ 1251 if (count > 0) 1252 { 1253 if (count > to_do) 1254 count = to_do; 1255 f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count); 1256 s += count; 1257 to_do -= count; 1258 } 1259 if (to_do + must_flush > 0) 1260 { 1261 _IO_size_t block_size, do_write; 1262 /* Next flush the (full) buffer. */ 1263 if (_IO_OVERFLOW (f, EOF) == EOF) 1264 /* If nothing else has to be written we must not signal the 1265 caller that everything has been written. */ 1266 return to_do == 0 ? EOF : n - to_do; 1267 1268 /* Try to maintain alignment: write a whole number of blocks. */ 1269 block_size = f->_IO_buf_end - f->_IO_buf_base; 1270 do_write = to_do - (block_size >= 128 ? to_do % block_size : 0); 1271 1272 if (do_write) 1273 { 1274 count = new_do_write (f, s, do_write); 1275 to_do -= count; 1276 if (count < do_write) 1277 return n - to_do; 1278 } 1279 1280 /* Now write out the remainder. Normally, this will fit in the 1281 buffer, but it's somewhat messier for line-buffered files, 1282 so we let _IO_default_xsputn handle the general case. */ 1283 if (to_do) 1284 to_do -= _IO_default_xsputn (f, s+do_write, to_do); 1285 } 1286 return n - to_do; 1287 } ``` ##### iii) _IO_new_file_overflow 1. 首先判断一下文件是否可写 2. 接着判断一下文件是否在写状态或者`write_base`是否初始化(那么说明现在是在读状态),如果`write_base`没有初始化则调用`_IO_doallocbuf`申请预留缓冲区,并赋值给`read`缓冲区 3. 如果`read_ptr==buf_end`,那么置`read_end=read_ptr=buf_base`;接着置`write_ptr=write_base=read_ptr`,`write_end=buf_end`,最后置`read_base=read_ptr=read_end` 4. 调用`_IO_do_write`将已写缓冲区中的数据写入文件 ``` 744 int 745 _IO_new_file_overflow (_IO_FILE *f, int ch) 746 { 747 if (f->_flags & _IO_NO_WRITES) /* SET ERROR */ // 文件不可写 748 { 749 f->_flags |= _IO_ERR_SEEN; 750 __set_errno (EBADF); 751 return EOF; 752 } 753 /* If currently reading or no buffer allocated. */ 754 if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL) // 如果当前不在写状态,那么必然在读状态 755 { 756 /* Allocate a buffer if needed. */ 757 if (f->_IO_write_base == NULL) // 如果写缓冲区为空 758 { 759 _IO_doallocbuf (f); // 申请空间给预留缓冲区 760 _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base); // buf_base赋值给read的三个值 761 } 762 /* Otherwise must be currently reading. 763 If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end, 764 logically slide the buffer forwards one block (by setting the 765 read pointers to all point at the beginning of the block). This 766 makes room for subsequent output. 767 Otherwise, set the read pointers to _IO_read_end (leaving that 768 alone, so it can continue to correspond to the external position). */ 769 if (__glibc_unlikely (_IO_in_backup (f))) 770 { 771 size_t nbackup = f->_IO_read_end - f->_IO_read_ptr; 772 _IO_free_backup_area (f); 773 f->_IO_read_base -= MIN (nbackup, 774 f->_IO_read_base - f->_IO_buf_base); 775 f->_IO_read_ptr = f->_IO_read_base; 776 } 777 778 if (f->_IO_read_ptr == f->_IO_buf_end) 779 f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base; 780 f->_IO_write_ptr = f->_IO_read_ptr; 781 f->_IO_write_base = f->_IO_write_ptr; 782 f->_IO_write_end = f->_IO_buf_end; 783 f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end; 784 785 f->_flags |= _IO_CURRENTLY_PUTTING; 786 if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED)) 787 f->_IO_write_end = f->_IO_write_ptr; 788 } 789 if (ch == EOF) 790 return _IO_do_write (f, f->_IO_write_base, 791 f->_IO_write_ptr - f->_IO_write_base); // 调用do_write 792 if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */ 793 if (_IO_do_flush (f) == EOF) 794 return EOF; 795 *f->_IO_write_ptr++ = ch; 796 if ((f->_flags & _IO_UNBUFFERED) 797 || ((f->_flags & _IO_LINE_BUF) && ch == '\n')) 798 if (_IO_do_write (f, f->_IO_write_base, 799 f->_IO_write_ptr - f->_IO_write_base) == EOF) // 调用do_write将write缓冲区中的数据写入文件 800 return EOF; 801 return (unsigned char) ch; 802 } ``` ##### iv) _IO_new_do_write 这个函数就是将已读缓冲区中的数据写入文件,并调整相关缓冲区地址。 ``` 429 int 430 _IO_new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do) 431 { 432 return (to_do == 0 433 || (_IO_size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF; 439 new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do) 440 { 441 _IO_size_t count; 442 if (fp->_flags & _IO_IS_APPENDING) // 是否是追加模式 443 /* On a system without a proper O_APPEND implementation, 444 you would need to sys_seek(0, SEEK_END) here, but is 445 not needed nor desirable for Unix- or Posix-like systems. 446 Instead, just indicate that offset (before and after) is 447 unpredictable. */ 448 fp->_offset = _IO_pos_BAD; 449 else if (fp->_IO_read_end != fp->_IO_write_base) 450 { 451 _IO_off64_t new_pos 452 = _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1); // 调整seek 453 if (new_pos == _IO_pos_BAD) 454 return 0; 455 fp->_offset = new_pos; 456 } 457 count = _IO_SYSWRITE (fp, data, to_do); // 写入文件 458 if (fp->_cur_column && count) 459 fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1; 460 _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); 461 fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base; // 已读缓冲区向后指 462 fp->_IO_write_end = (fp->_mode <= 0 463 && (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED)) 464 ? fp->_IO_buf_base : fp->_IO_buf_end); 465 return count; 466 } ``` ##### v) _IO_default_xsputn 这个函数就是将数据写入写缓冲区中。 ``` 396 _IO_size_t 397 _IO_default_xsputn (_IO_FILE *f, const void *data, _IO_size_t n) 398 { 399 const char *s = (char *) data; 400 _IO_size_t more = n; 401 if (more <= 0) 402 return 0; 403 for (;;) 404 { 405 /* Space available. */ 406 if (f->_IO_write_ptr < f->_IO_write_end) // 如果写缓冲区空间还足够 407 { 408 _IO_size_t count = f->_IO_write_end - f->_IO_write_ptr; // 写缓冲区剩余空间 409 if (count > more) // 满足需求 410 count = more; 411 if (count > 20) // 能写入的值超过20,就用__mempcpy写入 412 { 413 f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count); // 直接写入写缓冲区 414 s += count; 415 } 416 else if (count) // 如果能写入的太少,就一个个写入 417 { 418 char *p = f->_IO_write_ptr; 419 _IO_ssize_t i; 420 for (i = count; --i >= 0; ) 421 *p++ = *s++; 422 f->_IO_write_ptr = p; 423 } 424 more -= count; 425 } 426 if (more == 0 || _IO_OVERFLOW (f, (unsigned char) *s++) == EOF) 427 break; 428 more--; 429 } 430 return n - more; 431 } ``` #### d. fclose fclose的流程图如图所示: ![](https://leanote.com/api/file/getImage?fileId=5a0ff18eab64411e2b000aee) ##### i) _IO_new_fclose 1. 首先仍然是`CHECK_FILE` 2. 将`fp`从文件链表中剔除 3. 调用`_IO_file_close_it`关闭文件 4. `free`掉`fp` ``` 33 _IO_new_fclose (_IO_FILE *fp) 34 { 35 int status; 36 37 CHECK_FILE(fp, EOF); 38 39 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) 40 /* We desperately try to help programs which are using streams in a 41 strange way and mix old and new functions. Detect old streams 42 here. */ 43 if (_IO_vtable_offset (fp) != 0) 44 return _IO_old_fclose (fp); 45 #endif 46 47 /* First unlink the stream. */ 48 if (fp->_IO_file_flags & _IO_IS_FILEBUF) 49 _IO_un_link ((struct _IO_FILE_plus *) fp); // 将fp从文件链表中剔除 50 51 _IO_acquire_lock (fp); 52 if (fp->_IO_file_flags & _IO_IS_FILEBUF) 53 status = _IO_file_close_it (fp); 54 else 55 status = fp->_flags & _IO_ERR_SEEN ? -1 : 0; 56 _IO_release_lock (fp); 57 _IO_FINISH (fp); 58 if (fp->_mode > 0) 59 { 60 /* This stream has a wide orientation. This means we have to free 61 the conversion functions. */ 62 struct _IO_codecvt *cc = fp->_codecvt; 63 64 __libc_lock_lock (__gconv_lock); 65 __gconv_release_step (cc->__cd_in.__cd.__steps); 66 __gconv_release_step (cc->__cd_out.__cd.__steps); 67 __libc_lock_unlock (__gconv_lock); 68 } 69 else 70 { 71 if (_IO_have_backup (fp)) 72 _IO_free_backup_area (fp); 73 } 74 if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr) 75 { 76 fp->_IO_file_flags = 0; 77 free(fp); 78 } 79 80 return status; 81 } ``` ##### ii) _IO_un_link 这个函数就是将`fp`从文件链表中踢出去。 ``` 55 void 56 _IO_un_link (struct _IO_FILE_plus *fp) 57 { 58 if (fp->file._flags & _IO_LINKED) 59 { 60 struct _IO_FILE **f; 61 #ifdef _IO_MTSAFE_IO 62 _IO_cleanup_region_start_noarg (flush_cleanup); 63 _IO_lock_lock (list_all_lock); 64 run_fp = (_IO_FILE *) fp; 65 _IO_flockfile ((_IO_FILE *) fp); 66 #endif 67 if (_IO_list_all == NULL) // 链表还未初始化 68 ; 69 else if (fp == _IO_list_all) // fp正好是链表头,那么直接让_IO_list_all为下一个 70 { 71 _IO_list_all = (struct _IO_FILE_plus *) _IO_list_all->file._chain; 72 ++_IO_list_all_stamp; 73 } 74 else 75 for (f = &_IO_list_all->file._chain; *f; f = &(*f)->_chain) // 从链表头开始遍历,寻找fp,设置其上一个指向fp->_chain 76 if (*f == (_IO_FILE *) fp) 77 { 78 *f = fp->file._chain; 79 ++_IO_list_all_stamp; 80 break; 81 } 82 fp->file._flags &= ~_IO_LINKED; // 设置fp标志为不在链表中 83 #ifdef _IO_MTSAFE_IO 84 _IO_funlockfile ((_IO_FILE *) fp); 85 run_fp = NULL; 86 _IO_lock_unlock (list_all_lock); 87 _IO_cleanup_region_end (0); 88 #endif 89 } 90 } ``` ##### iii) _IO_new_file_close_it 1. 先判断文件是否打开,若没有则返回EOF 2. 判断文件是否已写模式打开,若是则调用`_IO_do_flush`将写缓冲区的剩余内容写入文件 3. 调用`sysclose`关闭文件 4. 设置缓冲区为空,设置标志为关闭,设置`fileno`为-1 ``` 127 int 128 _IO_new_file_close_it (_IO_FILE *fp) 129 { 130 int write_status; 131 if (!_IO_file_is_open (fp)) 132 return EOF; 133 134 if ((fp->_flags & _IO_NO_WRITES) == 0 135 && (fp->_flags & _IO_CURRENTLY_PUTTING) != 0) 136 write_status = _IO_do_flush (fp); 137 else 138 write_status = 0; 139 140 _IO_unsave_markers (fp); 141 142 int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0 143 ? _IO_SYSCLOSE (fp) : 0); 144 145 /* Free buffer. */ 146 if (fp->_mode > 0) 147 { 148 if (_IO_have_wbackup (fp)) 149 _IO_free_wbackup_area (fp); 150 _IO_wsetb (fp, NULL, NULL, 0); 151 _IO_wsetg (fp, NULL, NULL, NULL); 152 _IO_wsetp (fp, NULL, NULL); 153 } 154 _IO_setb (fp, NULL, NULL, 0); 155 _IO_setg (fp, NULL, NULL, NULL); 156 _IO_setp (fp, NULL, NULL); 157 158 _IO_un_link ((struct _IO_FILE_plus *) fp); 159 fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS; 160 fp->_fileno = -1; 161 fp->_offset = _IO_pos_BAD; 162 163 return close_status ? close_status : write_status; 164 } ```
觉得不错,点个赞?
提交评论
Sign in
to leave a comment.
No Leanote account ?
Sign up now
.
1
条评论
More...
文章目录
No Leanote account ? Sign up now.