2.23 IO相关源码阅读笔记

开始IO啦!hgame week4的两道IO都摆烂放弃了,打算之后再系统学IO,how2heap暂时告一段落了就IO走起
ps:先马马虎虎翻一遍,以后如果有需要再细看

基本结构体与全局变量

_IO_FILE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//glibc-2.23\libio\libio.h

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;
#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_complete
{
struct _IO_FILE _file;
#endif
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
_IO_off64_t _offset;
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
/* Wide character stream stuff. */
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
# else
void *__pad1;
void *__pad2;
void *__pad3;
void *__pad4;
# endif
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)];
#endif
};

重要成员:

  • _flags:高位字为IO_MAGIC,剩余的部分是flag
  • _IO_read_ptr:input指针现在的位置
  • _IO_read_end:input缓冲区的结束地址
  • _IO_read_base:input缓冲区的基址
  • _IO_write_base:output缓冲区的基址
  • _IO_write_ptr:output指针现在的位置
  • _IO_write_end:output指针的结束地址
  • _IO_buf_base:缓冲区基址
  • _IO_buf_end:缓冲区结束地址
  • _chain:相当于fd指针,用于形成单链表串联所有的file stream
  • _fileno:与文件相关的文件描述符
  • _vtable_offset:存放虚表(virtual table)的偏移
  • _offset:存放当前文件的偏移
    ps:在看fopen源码时注意到有的成员不属于_IO_FILE而属于_IO_FILE_complete,根据宏定义#ifdef _IO_USE_OLD_IO_FILE,如果没有定义_IO_USE_OLD_IO_FILE,那么_IO_FILE_complete之后的成员是属于_IO_FILE的(应该是为了兼容性考虑)

_IO_FILE_plus

1
2
3
4
5
6
7
//glibc-2.23\libio\libioP.h

struct _IO_FILE_plus
{
_IO_FILE file;
const struct _IO_jump_t *vtable;
};

成员偏移:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
amd64:

0x0:'_flags',
0x8:'_IO_read_ptr',
0x10:'_IO_read_end',
0x18:'_IO_read_base',
0x20:'_IO_write_base',
0x28:'_IO_write_ptr',
0x30:'_IO_write_end',
0x38:'_IO_buf_base',
0x40:'_IO_buf_end',
0x48:'_IO_save_base',
0x50:'_IO_backup_base',
0x58:'_IO_save_end',
0x60:'_markers',
0x68:'_chain',
0x70:'_fileno',
0x74:'_flags2',
0x78:'_old_offset',
0x80:'_cur_column',
0x82:'_vtable_offset',
0x83:'_shortbuf',
0x88:'_lock',
0x90:'_offset',
0x98:'_codecvt',
0xa0:'_wide_data',
0xa8:'_freeres_list',
0xb0:'_freeres_buf',
0xb8:'__pad5',
0xc0:'_mode',
0xc4:'_unused2',
0xd8:'vtable'

_IO_jump_t

_IO_FILE_plus前一部分是_IO_FILE结构体,后一部分就是指向_IO_jump_t结构体的vtable指针,其实就是虚表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//glibc-2.23\libio\libioP.h

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);
#if 0
get_column;
set_column;
#endif
};

_IO_wide_data

为数据较多的流设置,根据宏

1
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T

判断是否启用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct _IO_wide_data
{
wchar_t *_IO_read_ptr; /* Current read pointer */
wchar_t *_IO_read_end; /* End of get area. */
wchar_t *_IO_read_base; /* Start of putback+get area. */
wchar_t *_IO_write_base; /* Start of put area. */
wchar_t *_IO_write_ptr; /* Current put pointer. */
wchar_t *_IO_write_end; /* End of put area. */
wchar_t *_IO_buf_base; /* Start of reserve area. */
wchar_t *_IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
wchar_t *_IO_save_base; /* Pointer to start of non-current get area. */
wchar_t *_IO_backup_base; /* Pointer to first valid character of
backup area */
wchar_t *_IO_save_end; /* Pointer to end of non-current get area. */

__mbstate_t _IO_state;
__mbstate_t _IO_last_state;
struct _IO_codecvt _codecvt;

wchar_t _shortbuf[1];

const struct _IO_jump_t *_wide_vtable;
};

_IO_file_jumps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//glibc-2.23\libio\fileops.c

const struct _IO_jump_t _IO_file_jumps =
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, _IO_file_finish),
JUMP_INIT(overflow, _IO_file_overflow),
JUMP_INIT(underflow, _IO_file_underflow),
JUMP_INIT(uflow, _IO_default_uflow),
JUMP_INIT(pbackfail, _IO_default_pbackfail),
JUMP_INIT(xsputn, _IO_file_xsputn),
JUMP_INIT(xsgetn, _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, _IO_file_doallocate),
JUMP_INIT(read, _IO_file_read),
JUMP_INIT(write, _IO_new_file_write),
JUMP_INIT(seek, _IO_file_seek),
JUMP_INIT(close, _IO_file_close),
JUMP_INIT(stat, _IO_file_stat),
JUMP_INIT(showmanyc, _IO_default_showmanyc),
JUMP_INIT(imbue, _IO_default_imbue)
};

其中的宏定义如下,实际上就是各个函数的跳转表:

1
2
JUMP_INIT(dummy, 0), JUMP_INIT (dummy2, 0)
#define JUMP_INIT(NAME,VALUE) VALUE

_IO_list_all

_IO_list_all->_IO_2_1_stderr_->_IO_2_1_stdout_->_IO_2_1_stdin_

1
2
3
//glibc-2.23\libio\stdfiles.c

struct _IO_FILE_plus *_IO_list_all = &_IO_2_1_stderr_;

_IO_2_1_stderr_、_IO_2_1_stdout_和_IO_2_1_stdin_的定义其实在这(搜索的时候没找到)

珍爱生命,远离宏定义

1
2
3
4
5
//glibc-2.23\libio\stdfiles.c

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);

第一层宏定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
# define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \
static struct _IO_wide_data _IO_wide_data_##FD \
= { ._wide_vtable = &_IO_wfile_jumps }; \
struct _IO_FILE_plus NAME \
= {FILEBUF_LITERAL(CHAIN, FLAGS, FD, &_IO_wide_data_##FD), \
&_IO_file_jumps};
# else
# define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \
struct _IO_FILE_plus NAME \
= {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), \
&_IO_file_jumps};
# endif
#endif

第一层解宏定义

1
2
3
struct _IO_FILE_plus _IO_2_1_stdin_ = {FILEBUF_LITERAL(0, 8, 0, NULL), &_IO_file_jumps};
struct _IO_FILE_plus _IO_2_1_stdout_ = {FILEBUF_LITERAL(&_IO_2_1_stdin_, 4, 1, NULL), &_IO_file_jumps};
struct _IO_FILE_plus _IO_2_1_stderr_ = {FILEBUF_LITERAL(&_IO_2_1_stdout_, 4+2, 2, NULL), &_IO_file_jumps};

第二层宏定义

1
2
3
4
5
6
7
//glibc-2.23\libio\libioP.h

# define FILEBUF_LITERAL(CHAIN, FLAGS, FD, WDP) \
{ _IO_MAGIC+_IO_LINKED+_IO_IS_FILEBUF+FLAGS, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (_IO_FILE *) CHAIN, FD, \
0, _IO_pos_BAD, 0, 0, { 0 }, 0, _IO_pos_BAD, \
0 }

_IO_list_all_stamp

记录_IO_list_all单链表更改的次数

1
2
3
4
//glibc-2.23\libio\genops.c

/* Used to signal modifications to the list of FILE decriptors. */
static int _IO_list_all_stamp;

fopen

函数调用流程图:

stdio.h中的fopen实际上是_IO_new_fopen

1
2
3
4
//glibc-2.23\include\stdio.h

extern _IO_FILE *_IO_new_fopen (const char*, const char*);
# define fopen(fname, mode) _IO_new_fopen (fname, mode)

或者iofopen.c中也有

1
2
3
//glibc-2.23\libio\iofopen.c

# define _IO_new_fopen fopen

_IO_new_fopen又调用了__fopen_internal

1
2
3
4
5
6
7
//glibc-2.23\libio\iofopen.c

_IO_FILE *
_IO_new_fopen (const char *filename, const char *mode)
{
return __fopen_internal (filename, mode, 1);
}

__fopen_internal的流程如下:

  • malloc分配内存
  • 调用_IO_no_init null初始化结构体数据
  • 设置vtable为_IO_file_jumps
  • 将_IO_FILE结构体链入_IO_list_all
  • 打开文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    //glibc-2.23\libio\iofopen.c

    _IO_FILE *
    __fopen_internal (const char *filename, const char *mode, int is32)
    {
    //步骤一:为locked_FILE结构体new_f分配内容
    struct locked_FILE
    {
    struct _IO_FILE_plus fp;
    #ifdef _IO_MTSAFE_IO
    _IO_lock_t lock;
    #endif
    struct _IO_wide_data wd;
    } *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE));

    //如果分配内存失败直接返回
    if (new_f == NULL)
    return NULL;
    #ifdef _IO_MTSAFE_IO
    new_f->fp.file._lock = &new_f->lock;
    #endif

    //步骤二:调用_IO_no_init初始化_IO_FILE
    #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
    _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps);
    #else
    _IO_no_init (&new_f->fp.file, 1, 0, NULL, NULL);
    #endif

    //初始化(&new_f->fp)->vtable为_IO_file_jumps
    //#define _IO_JUMPS(THIS) (THIS)->vtable
    _IO_JUMPS (&new_f->fp) = &_IO_file_jumps;

    //步骤三:调用_IO_file_init将_IO_FILE_plus结构体链入_IO_list_all
    _IO_file_init (&new_f->fp);
    #if !_IO_UNIFIED_JUMPTABLES
    new_f->fp.vtable = NULL;
    #endif

    //步骤四:调用_IO_file_fopen打开文件
    //成功则调用__fopen_maybe_mmap,最终返回一个_IO_FILE结构体
    if (_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32) != NULL)
    return __fopen_maybe_mmap (&new_f->fp.file);

    //如果失败则将_IO_FILE_plus从单链表中摘除,释放结构体并返回null
    _IO_un_link (&new_f->fp);
    free (new_f);
    return NULL;
    }

_IO_no_init

调用了_IO_old_init,然后进行了一系列null初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//glibc-2.23\libio\genops.c

void
_IO_no_init (_IO_FILE *fp, int flags, int orientation,
struct _IO_wide_data *wd, const struct _IO_jump_t *jmp)
{
_IO_old_init (fp, flags);
fp->_mode = orientation;
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
if (orientation >= 0)
{
fp->_wide_data = wd;
fp->_wide_data->_IO_buf_base = NULL;
fp->_wide_data->_IO_buf_end = NULL;
fp->_wide_data->_IO_read_base = NULL;
fp->_wide_data->_IO_read_ptr = NULL;
fp->_wide_data->_IO_read_end = NULL;
fp->_wide_data->_IO_write_base = NULL;
fp->_wide_data->_IO_write_ptr = NULL;
fp->_wide_data->_IO_write_end = NULL;
fp->_wide_data->_IO_save_base = NULL;
fp->_wide_data->_IO_backup_base = NULL;
fp->_wide_data->_IO_save_end = NULL;

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;
#endif
fp->_freeres_list = NULL;
}

_IO_old_init

也是一系列null初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//glibc-2.23\libio\genops.c

void
_IO_old_init (_IO_FILE *fp, int flags)
{
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;
#if _IO_JUMPS_OFFSET
fp->_vtable_offset = 0;
#endif
#ifdef _IO_MTSAFE_IO
if (fp->_lock != NULL)
_IO_lock_init (*fp->_lock);
#endif
}

_IO_file_init

_IO_file_init也是一个宏,实际调用的函数是_IO_new_file_init

1
2
3
//glibc-2.23\libio\fileops.c

# define _IO_new_file_init _IO_file_init

_IO_new_file_init进行了一些初始化并调用了_IO_link_in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//glibc-2.23\libio\fileops.c

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;//其实就是-1
fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS;

_IO_link_in (fp);
fp->file._fileno = -1;
}
1
2
3
4
5
//glibc-2.23\libio\fileops.c

#define CLOSED_FILEBUF_FLAGS \
(_IO_IS_FILEBUF+_IO_NO_READS+_IO_NO_WRITES+_IO_TIED_PUT_GET)
//0x2000+0x4+0x8+0x400

_IO_link_in将_IO_FILE_plus结构体链入_IO_list_all单链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//glibc-2.23\libio\genops.c

void
_IO_link_in (struct _IO_FILE_plus *fp)
{
//检查flag的标志位是否是_IO_LINKED
if ((fp->file._flags & _IO_LINKED) == 0)
{
//设置_IO_LINKED
fp->file._flags |= _IO_LINKED;
#ifdef _IO_MTSAFE_IO
_IO_cleanup_region_start_noarg (flush_cleanup);
_IO_lock_lock (list_all_lock);
run_fp = (_IO_FILE *) fp;
_IO_flockfile ((_IO_FILE *) fp);
#endif
//将结构体链入_IO_list_all,递增_IO_list_all_stamp
fp->file._chain = (_IO_FILE *) _IO_list_all;
_IO_list_all = fp;
++_IO_list_all_stamp;
#ifdef _IO_MTSAFE_IO
_IO_funlockfile ((_IO_FILE *) fp);
run_fp = NULL;
_IO_lock_unlock (list_all_lock);
_IO_cleanup_region_end (0);
#endif
}
}

_IO_file_fopen

这其实也是个宏定义,实际上调用的函数是_IO_new_file_fopen

1
2
3
//glibc-2.23\libio\fileops.c

# define _IO_new_file_fopen _IO_file_fopen

_IO_new_file_fopen的过程如下:

  • _IO_file_is_open((fp)->_fileno != -1)

    判断文件是否已经打开是则返回0

  • 处理文件模式并写入结构体

  • 调用_IO_file_open打开文件

  • 如果打开成功则进行一(mei)系(kan)列(dong)设定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//glibc-2.23\libio\fileops.c

_IO_FILE *
_IO_new_file_fopen (_IO_FILE *fp, const char *filename, const char *mode,
int is32not64)
{
int oflags = 0, omode;
int read_write;
int oprot = 0666;
int i;
_IO_FILE *result;
#ifdef _LIBC
const char *cs;
const char *last_recognized;
#endif
//步骤一:判断文件是否已经打开
if (_IO_file_is_open (fp))
return 0;

//步骤二:处理文件模式并写入_IO_FILE结构体
switch (*mode)
{
case 'r':
omode = O_RDONLY;
read_write = _IO_NO_WRITES;
break;
case 'w':
omode = O_WRONLY;
oflags = O_CREAT|O_TRUNC;
read_write = _IO_NO_READS;
break;
case 'a':
omode = O_WRONLY;
oflags = O_CREAT|O_APPEND;
read_write = _IO_NO_READS|_IO_IS_APPENDING;
break;
default:
__set_errno (EINVAL);
return NULL;
}
#ifdef _LIBC
last_recognized = mode;
#endif
for (i = 1; i < 7; ++i)
{
switch (*++mode)
{
case '\0':
break;
case '+':
omode = O_RDWR;
read_write &= _IO_IS_APPENDING;
#ifdef _LIBC
last_recognized = mode;
#endif
continue;
case 'x':
oflags |= O_EXCL;
#ifdef _LIBC
last_recognized = mode;
#endif
continue;
case 'b':
#ifdef _LIBC
last_recognized = mode;
#endif
continue;
case 'm':
fp->_flags2 |= _IO_FLAGS2_MMAP;
continue;
case 'c':
fp->_flags2 |= _IO_FLAGS2_NOTCANCEL;
continue;
case 'e':
#ifdef O_CLOEXEC
oflags |= O_CLOEXEC;
#endif
fp->_flags2 |= _IO_FLAGS2_CLOEXEC;
continue;
default:
/* Ignore. */
continue;
}
break;
}


//步骤三:调用_IO_file_opwn打开文件
result = _IO_file_open (fp, filename, omode|oflags, oprot, read_write,
is32not64);

//步骤四:如果打开成功则进行一(mei)系(kan)列(dong)设定
if (result != NULL)
{
#ifndef __ASSUME_O_CLOEXEC
if ((fp->_flags2 & _IO_FLAGS2_CLOEXEC) != 0 && __have_o_cloexec <= 0)
{
int fd = _IO_fileno (fp);
if (__have_o_cloexec == 0)
{
int flags = __fcntl (fd, F_GETFD);
__have_o_cloexec = (flags & FD_CLOEXEC) == 0 ? -1 : 1;
}
if (__have_o_cloexec < 0)
__fcntl (fd, F_SETFD, FD_CLOEXEC);
}
#endif

/* Test whether the mode string specifies the conversion. */
cs = strstr (last_recognized + 1, ",ccs=");
if (cs != NULL)
{
/* Yep. Load the appropriate conversions and set the orientation
to wide. */
struct gconv_fcts fcts;
struct _IO_codecvt *cc;
char *endp = __strchrnul (cs + 5, ',');
char *ccs = malloc (endp - (cs + 5) + 3);

if (ccs == NULL)
{
int malloc_err = errno; /* Whatever malloc failed with. */
(void) _IO_file_close_it (fp);
__set_errno (malloc_err);
return NULL;
}

*((char *) __mempcpy (ccs, cs + 5, endp - (cs + 5))) = '\0';
strip (ccs, ccs);

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);
return NULL;
}

free (ccs);

assert (fcts.towc_nsteps == 1);
assert (fcts.tomb_nsteps == 1);

fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_read_end;
fp->_wide_data->_IO_write_ptr = fp->_wide_data->_IO_write_base;

/* 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));

cc = fp->_codecvt = &fp->_wide_data->_codecvt;

/* The functions are always the same. */
*cc = __libio_codecvt;

cc->__cd_in.__cd.__nsteps = fcts.towc_nsteps;
cc->__cd_in.__cd.__steps = fcts.towc;

cc->__cd_in.__cd.__data[0].__invocation_counter = 0;
cc->__cd_in.__cd.__data[0].__internal_use = 1;
cc->__cd_in.__cd.__data[0].__flags = __GCONV_IS_LAST;
cc->__cd_in.__cd.__data[0].__statep = &result->_wide_data->_IO_state;

cc->__cd_out.__cd.__nsteps = fcts.tomb_nsteps;
cc->__cd_out.__cd.__steps = fcts.tomb;

cc->__cd_out.__cd.__data[0].__invocation_counter = 0;
cc->__cd_out.__cd.__data[0].__internal_use = 1;
cc->__cd_out.__cd.__data[0].__flags
= __GCONV_IS_LAST | __GCONV_TRANSLIT;
cc->__cd_out.__cd.__data[0].__statep =
&result->_wide_data->_IO_state;

/* From now on use the wide character callback functions. */
_IO_JUMPS_FILE_plus (fp) = fp->_wide_data->_wide_vtable;

/* Set the mode now. */
result->_mode = 1;
}
}

return result;
}

_IO_file_open

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//glibc-2.23\libio\fileops.c

_IO_FILE *
_IO_file_open (_IO_FILE *fp, const char *filename, int posix_mode, int prot,
int read_write, int is32not64)
{
int fdesc;
#ifdef _LIBC
if (__glibc_unlikely (fp->_flags2 & _IO_FLAGS2_NOTCANCEL))
fdesc = open_not_cancel (filename,
posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot);
else
fdesc = open (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot);
#else
//调用系统调用打开文件
fdesc = open (filename, posix_mode, prot);
#endif
if (fdesc < 0)
return NULL;

//将文件描述符赋值给_fileno字段
fp->_fileno = fdesc;
//flag字段设置
_IO_mask_flags (fp, read_write,_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
/* For append mode, send the file offset to the end of the file. Don't
update the offset cache though, since the file handle is not active. */
//对于追加模式,将文件偏移量发送到文件末尾
//但不更新偏移缓存,因为文件句柄未激活(百度翻译)
if ((read_write & (_IO_IS_APPENDING | _IO_NO_READS))
== (_IO_IS_APPENDING | _IO_NO_READS))
{
_IO_off64_t new_pos = _IO_SYSSEEK (fp, 0, _IO_seek_end);
if (new_pos == _IO_pos_BAD && errno != ESPIPE)
{
close_not_cancel (fdesc);
return NULL;
}
}
//再次调用_IO_link_in确保文件结构体链入
_IO_link_in ((struct _IO_FILE_plus *) fp);
return fp;
}

__fopen_maybe_mmap

当有_G_HAVE_MMAP定义时使用,根据读写模式决定操作方式?没看懂。最终返回_IO_FILE结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//glibc-2.23\libio\iofopen.c

_IO_FILE *
__fopen_maybe_mmap (_IO_FILE *fp)
{
#ifdef _G_HAVE_MMAP
if ((fp->_flags2 & _IO_FLAGS2_MMAP) && (fp->_flags & _IO_NO_WRITES))
{
/* Since this is read-only, we might be able to mmap the contents
directly. We delay the decision until the first read attempt by
giving it a jump table containing functions that choose mmap or
vanilla file operations and reset the jump table accordingly. */

if (fp->_mode <= 0)
_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps_maybe_mmap;
else
_IO_JUMPS_FILE_plus (fp) = &_IO_wfile_jumps_maybe_mmap;
fp->_wide_data->_wide_vtable = &_IO_wfile_jumps_maybe_mmap;
}
#endif
return fp;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//glibc-2.23\libio\genops.c

void
_IO_un_link (struct _IO_FILE_plus *fp)
{
//如果文件结构体已经链入
if (fp->file._flags & _IO_LINKED)
{
struct _IO_FILE **f;
#ifdef _IO_MTSAFE_IO
_IO_cleanup_region_start_noarg (flush_cleanup);
_IO_lock_lock (list_all_lock);
run_fp = (_IO_FILE *) fp;
_IO_flockfile ((_IO_FILE *) fp);
#endif
if (_IO_list_all == NULL)
//如果_IO_list_all为null,则不做任何操作
;
//如果fp就是头节点,直接摘下
else if (fp == _IO_list_all)
{
_IO_list_all = (struct _IO_FILE_plus *) _IO_list_all->file._chain;
//递增_IO_list_all单链表更改次数
++_IO_list_all_stamp;
}
else
//如果不是头节点则遍历单链表搜索并摘下fd
for (f = &_IO_list_all->file._chain; *f; f = &(*f)->_chain)
if (*f == (_IO_FILE *) fp)
{
*f = fp->file._chain;
++_IO_list_all_stamp;
break;
}
//改变_IO_FILE的flag中表示连接的标志位
fp->file._flags &= ~_IO_LINKED;
#ifdef _IO_MTSAFE_IO
_IO_funlockfile ((_IO_FILE *) fp);
run_fp = NULL;
_IO_lock_unlock (list_all_lock);
_IO_cleanup_region_end (0);
#endif
}
}

fread

流程图如下:

执行fread函数时实际上执行的是_IO_fread函数,_IO_fread函数又调用了_IO_sgetn函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//glibc-2.23\libio\iofread.c

_IO_size_t
_IO_fread (void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{
_IO_size_t bytes_requested = size * count;
_IO_size_t bytes_read;
CHECK_FILE (fp, 0);
if (bytes_requested == 0)
return 0;
_IO_acquire_lock (fp);
bytes_read = _IO_sgetn (fp, (char *) buf, bytes_requested);
_IO_release_lock (fp);
return bytes_requested == bytes_read ? count : bytes_read / size;
}

_IO_sgetn函数又调用了_IO_XSGETN函数,实际上就是跳转表中的__xsgetn函数

1
2
3
4
5
6
7
8
9
10
//glibc-2.23\libio\genops.c

_IO_size_t
_IO_sgetn (_IO_FILE *fp, void *data, _IO_size_t n)
{
/* FIXME handle putback buffer here! */
return _IO_XSGETN (fp, data, n);
}

#define _IO_XSGETN(FP,DATA,N) JUMP2 (__xsgetn, FP, DATA, N)

_IO_file_xsgetn执行步骤如下:

  • 如果缓冲区为空调用_IO_doallocbuf分配缓冲区
  • 如果want<=have则读入want个字节并将want清零,否则执行以下步骤
  • 如果还有字节剩余,则读入剩余have字节
  • 如果存在backup则调用_IO_switch_to_main_get_area调用backup
  • 如果缓冲区容量大于需求则调用__underflow向缓冲区中读入
  • 将所有读写相关指针指向缓冲区基址
  • 计算count,直接从文件中读取count字节
  • 错误处理
  • 调整文件偏移
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    _IO_size_t
    _IO_file_xsgetn (_IO_FILE *fp, void *data, _IO_size_t n)
    {
    _IO_size_t want, have;
    _IO_ssize_t count;
    char *s = data;

    want = n;

    //步骤一:如果缓冲区为空则分配缓冲区
    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);
    }

    //步骤二:循环读入字节直到满足要求
    while (want > 0)
    {
    have = fp->_IO_read_end - fp->_IO_read_ptr;

    //分支1:已有字节数满足要求则直接读入
    if (want <= have)
    {
    memcpy (s, fp->_IO_read_ptr, want);
    fp->_IO_read_ptr += want;
    want = 0;
    }
    else
    {
    //分支2-1:读入已有字节,此时缓冲区已清空
    if (have > 0)
    {
    #ifdef _LIBC
    s = __mempcpy (s, fp->_IO_read_ptr, have);
    #else
    memcpy (s, fp->_IO_read_ptr, have);
    s += have;
    #endif
    want -= have;
    fp->_IO_read_ptr += have;
    }

    //分支2-2:有backup,查看backup并读入
    /* Check for backup and repeat */
    if (_IO_in_backup (fp))
    {
    _IO_switch_to_main_get_area (fp);
    continue;
    }

    //分支2-3:缓冲区的容量大于需求则调用__underflow
    if (fp->_IO_buf_base
    && want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base))
    {
    if (__underflow (fp) == EOF)
    break;

    continue;
    }

    //设置指针
    _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);

    //分支2-4:计算直接从文件中读的字节个数
    //want剩下的待读入缓冲区后从缓冲区中读
    /* Try to maintain alignment: read a whole number of blocks. */
    count = want;
    if (fp->_IO_buf_base)
    {
    _IO_size_t block_size = fp->_IO_buf_end - fp->_IO_buf_base;
    if (block_size >= 128)
    count -= want % block_size;
    }
    //调用_IO_SYSREAD直接从文件中读取count字节
    //_IO_SYSREAD实际上就是跳转表中的_IO_file_read
    //最终调用的是系统调用read
    count = _IO_SYSREAD (fp, s, count);
    //判断读入是否成功
    if (count <= 0)
    {
    if (count == 0)
    fp->_flags |= _IO_EOF_SEEN;
    else
    fp->_flags |= _IO_ERR_SEEN;

    break;
    }

    s += count;
    want -= count;
    //步骤三:调整文件偏移
    if (fp->_offset != _IO_pos_BAD)
    _IO_pos_adjust (fp->_offset, count);
    }
    }

    return n - want;
    }

_IO_doallocbuf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//glibc-2.23\libio\genops.c

void
_IO_doallocbuf (_IO_FILE *fp)
{
//已有缓冲区则直接返回
if (fp->_IO_buf_base)
return;
//模式或者flag符合要求则调用_IO_DOALLOCATE
//_IO_DOALLOCATE实际上就是跳转表中的_IO_file_doallocate
if (!(fp->_flags & _IO_UNBUFFERED) || fp->_mode > 0)
if (_IO_DOALLOCATE (fp) != EOF)
return;
//如果缓冲区初始化失败则调用_IO_setb
_IO_setb (fp, fp->_shortbuf, fp->_shortbuf+1, 0);
}

_IO_file_doallocate

主要过程就是malloc一块缓冲区然后调用_IO_setb设定缓冲区相关指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//glibc-2.23\libio\filedoalloc.c

int
_IO_file_doallocate (_IO_FILE *fp)
{
_IO_size_t size;
char *p;
struct stat64 st;

#ifndef _LIBC
if (__glibc_unlikely (_IO_cleanup_registration_needed != NULL))
(*_IO_cleanup_registration_needed) ();
#endif

size = _IO_BUFSIZ;
//调用_IO_SYSSTAT(跳转表中的_IO_file_stat)获取FILE信息
if (fp->_fileno >= 0 && __builtin_expect (_IO_SYSSTAT (fp, &st), 0) >= 0)
{
if (S_ISCHR (st.st_mode))
{
/* Possibly a tty. */
if (
#ifdef DEV_TTY_P
DEV_TTY_P (&st) ||
#endif
local_isatty (fp->_fileno))
fp->_flags |= _IO_LINE_BUF;
}
#if _IO_HAVE_ST_BLKSIZE
if (st.st_blksize > 0)
size = st.st_blksize;
#endif
}
p = malloc (size);
if (__glibc_unlikely (p == NULL))
return EOF;
_IO_setb (fp, p, p + size, 1);
return 1;
}

_IO_setb

设置缓冲区指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//glibc-2.23\libio\genops.c

void
_IO_setb (_IO_FILE *f, char *b, char *eb, int a)
{
if (f->_IO_buf_base && !(f->_flags & _IO_USER_BUF))
free (f->_IO_buf_base);
f->_IO_buf_base = b;
f->_IO_buf_end = eb;
if (a)
f->_flags &= ~_IO_USER_BUF;
else
f->_flags |= _IO_USER_BUF;
}

_IO_switch_to_main_get_area

将缓冲区移至backup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//glibc-2.23\libio\genops.c

void
_IO_switch_to_main_get_area (_IO_FILE *fp)
{
char *tmp;
fp->_flags &= ~_IO_IN_BACKUP;
/* Swap _IO_read_end and _IO_save_end. */
tmp = fp->_IO_read_end;
fp->_IO_read_end = fp->_IO_save_end;
fp->_IO_save_end= tmp;
/* Swap _IO_read_base and _IO_save_base. */
tmp = fp->_IO_read_base;
fp->_IO_read_base = fp->_IO_save_base;
fp->_IO_save_base = tmp;
/* Set _IO_read_ptr. */
fp->_IO_read_ptr = fp->_IO_read_base;
}

__underflow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//glibc-2.23\libio\genops.c

int
__underflow (_IO_FILE *fp)
{
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
if (_IO_vtable_offset (fp) == 0 && _IO_fwide (fp, -1) != -1)
return EOF;
#endif

//一些检查
if (fp->_mode == 0)
_IO_fwide (fp, -1);
if (_IO_in_put_mode (fp))
if (_IO_switch_to_get_mode (fp) == EOF)
return EOF;
if (fp->_IO_read_ptr < fp->_IO_read_end)
return *(unsigned char *) fp->_IO_read_ptr;
if (_IO_in_backup (fp))
{
_IO_switch_to_main_get_area (fp);
if (fp->_IO_read_ptr < fp->_IO_read_end)
return *(unsigned char *) fp->_IO_read_ptr;
}
if (_IO_have_markers (fp))
{
if (save_for_backup (fp, fp->_IO_read_end))
return EOF;
}
else if (_IO_have_backup (fp))
_IO_free_backup_area (fp);
//调用_IO_UNDERFLOW(跳转表中的_IO_new_file_underflow)
//JUMP0 (__underflow, fp)
//# define _IO_new_file_underflow _IO_file_underflow
return _IO_UNDERFLOW (fp);
}

_IO_new_file_underflow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
int
_IO_new_file_underflow (_IO_FILE *fp)
{
_IO_ssize_t count;
#if 0
if (fp->_flags & _IO_EOF_SEEN)
return (EOF);
#endif

if (fp->_flags & _IO_NO_READS)
{
fp->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return EOF;
}
//缓冲区未清空则直接返回
if (fp->_IO_read_ptr < fp->_IO_read_end)
return *(unsigned char *) fp->_IO_read_ptr;

//没有缓冲区则调用_IO_doallocbuf分配缓冲区
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);
}

if (fp->_flags & (_IO_LINE_BUF|_IO_UNBUFFERED))
{
#if 0
_IO_flush_all_linebuffered ();
#else
_IO_acquire_lock (_IO_stdout);

if ((_IO_stdout->_flags & (_IO_LINKED | _IO_NO_WRITES | _IO_LINE_BUF))
== (_IO_LINKED | _IO_LINE_BUF))
_IO_OVERFLOW (_IO_stdout, EOF);

_IO_release_lock (_IO_stdout);
#endif
}

_IO_switch_to_get_mode (fp);

//设置结构体指针
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;

//调用_IO_SYSREAD函数读入数据
count = _IO_SYSREAD (fp, fp->_IO_buf_base,
fp->_IO_buf_end - fp->_IO_buf_base);
if (count <= 0)
{
if (count == 0)
fp->_flags |= _IO_EOF_SEEN;
else
fp->_flags |= _IO_ERR_SEEN, count = 0;
}
fp->_IO_read_end += count;
if (count == 0)
{
fp->_offset = _IO_pos_BAD;
return EOF;
}
if (fp->_offset != _IO_pos_BAD)
_IO_pos_adjust (fp->_offset, count);
return *(unsigned char *) fp->_IO_read_ptr;
}

其他输入函数

scanf

scanf的函数调用栈如下,最终是使用跳转表中的_IO_new_file_underflow实现输入的:

1
2
3
4
5
6
read
_IO_new_file_underflow->_IO_doallocbuf->_IO_file_doallocate
__IO_default_uflow
__uflow
__vfscanf_internal
__isoc99_scanf

gets

和scanf差不多

1
2
3
4
5
read
_IO_new_file_underflow->_IO_doallocbuf->_IO_file_doallocate
__IO_default_uflow
__uflow
gets

fwrite

函数调用流程:

执行fwrite函数实际上执行的是_IO_write函数,_IO_write又调用了_IO_sputn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//glibc-2.23\libio\iofwrite.c

_IO_size_t
_IO_fwrite (const void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{
_IO_size_t request = size * count;
_IO_size_t written = 0;
CHECK_FILE (fp, 0);
if (request == 0)
return 0;
_IO_acquire_lock (fp);
if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
written = _IO_sputn (fp, (const char *) buf, request);
_IO_release_lock (fp);
if (written == request || written == EOF)
return count;
else
return written / size;
}

_IO_sputn其实是调用跳转表中的_IO_file_xsputn(实际上是_IO_new_file_xsputn),步骤如下:

  • 将目标数据拷贝至输出缓冲区
  • 如果缓冲区还有空间则先吧数据拷贝进输出缓冲区
  • 如果还有数据剩余则说明缓冲区未建立或者缓冲区已满,调用_IO_OVERFLOW建立或清空缓冲区
  • 判断数据是否大块,是则先调用new_do_write输出小块数据,然后调用_IO_default_xsputn将剩余数据拷贝进缓冲区
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    //glibc-2.23\libio\fileops.c

    _IO_size_t
    _IO_new_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
    {
    const char *s = (const char *) data;
    _IO_size_t to_do = n;
    int must_flush = 0;
    _IO_size_t count = 0;

    if (n <= 0)
    return 0;

    //计算缓冲区空间count
    if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
    {
    count = f->_IO_buf_end - f->_IO_write_ptr;
    if (count >= n)
    {
    const char *p;
    for (p = s + n; p > s; )
    {
    if (*--p == '\n')
    {
    count = p - s + 1;
    must_flush = 1;
    break;
    }
    }
    }
    }
    else if (f->_IO_write_end > f->_IO_write_ptr)
    count = f->_IO_write_end - f->_IO_write_ptr;

    //如果缓冲区还有空间则先把数据拷贝进缓冲区
    if (count > 0)
    {
    if (count > to_do)
    count = to_do;
    #ifdef _LIBC
    f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
    #else
    memcpy (f->_IO_write_ptr, s, count);
    f->_IO_write_ptr += count;
    #endif
    s += count;
    to_do -= count;
    }
    //如果还有数据剩余则说明缓冲区未建立或已满
    if (to_do + must_flush > 0)
    {
    _IO_size_t block_size, do_write;
    //调用_IO_OVERFLOW清空缓冲区或建立缓冲区
    if (_IO_OVERFLOW (f, EOF) == EOF)

    return to_do == 0 ? EOF : n - to_do;

    //检查数据是否大块,是则更新count为小块数据数量
    block_size = f->_IO_buf_end - f->_IO_buf_base;
    do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);
    //调用new_do_write输出s中的大块数据,不经缓冲区
    if (do_write)
    {
    count = new_do_write (f, s, do_write);
    to_do -= count;
    if (count < do_write)
    return n - to_do;
    }

    if (to_do)
    to_do -= _IO_default_xsputn (f, s+do_write, to_do);
    }
    return n - to_do;
    }

_IO_new_file_overflow

_IO_OVERFLOW实际上就是跳转表中的_IO_new_file_overflow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//glibc-2.23\libio\fileops.c

int
_IO_new_file_overflow (_IO_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建立缓冲区
_IO_doallocbuf (f);
_IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
}
//如果有backup则调用backup
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;
}
//初始化指针
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;
}
//调用_IO_do_write清空缓冲区,注意第二个参数是f->_IO_write_base
if (ch == EOF)
return _IO_do_write (f, f->_IO_write_base,
f->_IO_write_ptr - f->_IO_write_base);
if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */
if (_IO_do_flush (f) == EOF)
return EOF;
*f->_IO_write_ptr++ = ch;
if ((f->_flags & _IO_UNBUFFERED)
|| ((f->_flags & _IO_LINE_BUF) && ch == '\n'))
if (_IO_do_write (f, f->_IO_write_base,
f->_IO_write_ptr - f->_IO_write_base) == EOF)
return EOF;
return (unsigned char) ch;
}

_IO_new_do_write

_IO_do_write实际上是_IO_new_do_write(宏定义),调用了new_do_write,_IO_file_xsputn后续步骤也调用了这个函数

1
2
3
4
5
6
7
8
//glibc-2.23\libio\fileops.c

int
_IO_new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{
return (to_do == 0
|| (_IO_size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF;
}

new_do_write

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//glibc-2.23\libio\fileops.c

static
_IO_size_t
new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{
//一些判断
_IO_size_t count;
if (fp->_flags & _IO_IS_APPENDING)
fp->_offset = _IO_pos_BAD;
else if (fp->_IO_read_end != fp->_IO_write_base)
{
_IO_off64_t new_pos
= _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);
if (new_pos == _IO_pos_BAD)
return 0;
fp->_offset = new_pos;
}
//调用_IO_SYSWRITE清空缓冲区
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);
return count;
}

_IO_new_file_write

_IO_SYSWRITE实际上就是跳转表中的_IO_file_write(_IO_new_file_write),调用系统调用清空缓冲区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//glibc-2.23\libio\fileops.c

_IO_ssize_t
_IO_new_file_write (_IO_FILE *f, const void *data, _IO_ssize_t n)
{
_IO_ssize_t to_do = n;
while (to_do > 0)
{
_IO_ssize_t count = (__builtin_expect (f->_flags2
& _IO_FLAGS2_NOTCANCEL, 0)
? write_not_cancel (f->_fileno, data, to_do)
: write (f->_fileno, data, to_do));
if (count < 0)
{
f->_flags |= _IO_ERR_SEEN;
break;
}
to_do -= count;
data = (void *) ((char *) data + count);
}
n -= to_do;
if (f->_offset >= 0)
f->_offset += n;
return n;
}

_IO_default_xsputn

调用_IO_OVERFLOW清空缓冲区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//glibc-2.23\libio\genops.c

_IO_size_t
_IO_default_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
{
const char *s = (char *) data;
_IO_size_t more = n;
if (more <= 0)
return 0;
for (;;)
{
/* Space available. */
if (f->_IO_write_ptr < f->_IO_write_end)
{
_IO_size_t count = f->_IO_write_end - f->_IO_write_ptr;
if (count > more)
count = more;
if (count > 20)
{
#ifdef _LIBC
f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
#else
//输出长度大于20,调用memcpy拷贝
memcpy (f->_IO_write_ptr, s, count);
f->_IO_write_ptr += count;
#endif
s += count;
}
else if (count)
{
//小于20直接赋值
char *p = f->_IO_write_ptr;
_IO_ssize_t i;
for (i = count; --i >= 0; )
*p++ = *s++;
f->_IO_write_ptr = p;
}
more -= count;
}
//调用_IO_OVERFLOW清空缓冲区
if (more == 0 || _IO_OVERFLOW (f, (unsigned char) *s++) == EOF)
break;
more--;
}
return n - more;
}

注意一点

main函数返回的时候_IO_cleanup会调用_IO_flush_all_lockp函数最终调用系统函数write清空缓冲区之后讨论

fclose

函数调用流程图:

fclose实际上是_IO_new_fclose

1
2
3
//glibc-2.23\libio\iofclose.c

# define _IO_new_fclose fclose

_IO_new_fclose函数的执行流程如下:

  • 调用_IO_un_link将fp从_IO_list_all单链表中摘除
  • 调用_IO_file_close_it关闭文件并释放缓冲区
  • 调用_IO_FINISH(_IO_new_file_finish)确定_IO_FILE已从链表中取出且缓冲区已释放
  • 如果fp不是标准输入、输出或错误,则释放fp结构体
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    //glibc-2.23\libio\iofclose.c

    int
    _IO_new_fclose (_IO_FILE *fp)
    {
    int status;

    CHECK_FILE(fp, EOF);

    #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
    if (_IO_vtable_offset (fp) != 0)
    return _IO_old_fclose (fp);
    #endif

    //步骤一:调用_IO_un_link将fp从_IO_list_all单链表中摘除
    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);
    else
    status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;
    _IO_release_lock (fp);
    //步骤三:确定_IO_FILE已从链表中取出且缓冲区已释放
    _IO_FINISH (fp);
    if (fp->_mode > 0)
    {
    #if _LIBC
    /* This stream has a wide orientation. This means we have to free
    the conversion functions. */
    struct _IO_codecvt *cc = fp->_codecvt;

    __libc_lock_lock (__gconv_lock);
    __gconv_release_step (cc->__cd_in.__cd.__steps);
    __gconv_release_step (cc->__cd_out.__cd.__steps);
    __libc_lock_unlock (__gconv_lock);
    #endif
    }
    else
    {
    if (_IO_have_backup (fp))
    _IO_free_backup_area (fp);
    }
    //步骤四:如果fp不是标准输入、输出或错误,则释放fp结构体
    if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr)
    {
    fp->_IO_file_flags = 0;
    free(fp);
    }

    return status;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//glibc-2.23\libio\genops.c

void
_IO_un_link (struct _IO_FILE_plus *fp)
{
if (fp->file._flags & _IO_LINKED)
{
struct _IO_FILE **f;
#ifdef _IO_MTSAFE_IO
_IO_cleanup_region_start_noarg (flush_cleanup);
_IO_lock_lock (list_all_lock);
run_fp = (_IO_FILE *) fp;
_IO_flockfile ((_IO_FILE *) fp);
#endif
//如果_IO_list_all非空且头节点为fp则取下头节点
if (_IO_list_all == NULL)
;
else if (fp == _IO_list_all)
{
_IO_list_all = (struct _IO_FILE_plus *) _IO_list_all->file._chain;
++_IO_list_all_stamp;
}
//否则遍历单链表搜索fp并取下,修改标志位
else
for (f = &_IO_list_all->file._chain; *f; f = &(*f)->_chain)
if (*f == (_IO_FILE *) fp)
{
*f = fp->file._chain;
++_IO_list_all_stamp;
break;
}
fp->file._flags &= ~_IO_LINKED;
#ifdef _IO_MTSAFE_IO
_IO_funlockfile ((_IO_FILE *) fp);
run_fp = NULL;
_IO_lock_unlock (list_all_lock);
_IO_cleanup_region_end (0);
#endif
}
}

_IO_file_close_it

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//glibc-2.23\libio\genops.c 

int
_IO_new_file_close_it (_IO_FILE *fp)
{
int write_status;
if (!_IO_file_is_open (fp))
return EOF;

//调用_IO_do_flush(_IO_do_write)刷新缓冲区
if ((fp->_flags & _IO_NO_WRITES) == 0
&& (fp->_flags & _IO_CURRENTLY_PUTTING) != 0)
write_status = _IO_do_flush (fp);
else
write_status = 0;

_IO_unsave_markers (fp);

//调用_IO_SYSCLOSE,跳转表中的_IO_file_close
int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0
? _IO_SYSCLOSE (fp) : 0);

/* Free buffer. */
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
if (fp->_mode > 0)
{
if (_IO_have_wbackup (fp))
_IO_free_wbackup_area (fp);
_IO_wsetb (fp, NULL, NULL, 0);
_IO_wsetg (fp, NULL, NULL, NULL);
_IO_wsetp (fp, NULL, NULL);
}
#endif
//释放输入输出缓冲区并设置指针
_IO_setb (fp, NULL, NULL, 0);//设置base指针,并释放缓冲区
_IO_setg (fp, NULL, NULL, NULL);//置空输入缓冲区
_IO_setp (fp, NULL, NULL);//置空输出缓冲区

//再次调用_IO_un_link,确保结构体已从单链表中取下
_IO_un_link ((struct _IO_FILE_plus *) fp);
//更新相关结构体成员
fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
fp->_fileno = -1;
fp->_offset = _IO_pos_BAD;

return close_status ? close_status : write_status;
}

_IO_file_close

直接进行系统调用

1
2
3
4
5
6
7
//glibc-2.23\libio\fileops.c

int
_IO_file_close (_IO_FILE *fp)
{
return close_not_cancel (fp->_fileno);
}

_IO_setb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//glibc-2.23\libio\genops.c 

void
_IO_setb (_IO_FILE *f, char *b, char *eb, int a)
{
//释放缓冲区
if (f->_IO_buf_base && !(f->_flags & _IO_USER_BUF))
free (f->_IO_buf_base);
//置空缓冲区指针
f->_IO_buf_base = b;
f->_IO_buf_end = eb;
//设置相关标志位
if (a)
f->_flags &= ~_IO_USER_BUF;
else
f->_flags |= _IO_USER_BUF;
}

_IO_new_file_finish

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//glibc-2.23\libio\fileops.c

void
_IO_new_file_finish (_IO_FILE *fp, int dummy)
{
if (_IO_file_is_open (fp))
{
//刷新缓冲区
_IO_do_flush (fp);
//如果文件为关闭,则调用_IO_SYSCLOSE关闭文件
if (!(fp->_flags & _IO_DELETE_DONT_CLOSE))
_IO_SYSCLOSE (fp);
}
//调用_IO_default_finish确认缓冲区已释放且结构体不在单链表中
_IO_default_finish (fp, 0);
}

_IO_default_finish

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//glibc-2.23\libio\genops.c

void
_IO_default_finish (_IO_FILE *fp, int dummy)
{
struct _IO_marker *mark;
if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
{
free (fp->_IO_buf_base);
fp->_IO_buf_base = fp->_IO_buf_end = NULL;
}

for (mark = fp->_markers; mark != NULL; mark = mark->_next)
mark->_sbuf = NULL;

if (fp->_IO_save_base)
{
free (fp->_IO_save_base);
fp->_IO_save_base = NULL;
}

_IO_un_link ((struct _IO_FILE_plus *) fp);

#ifdef _IO_MTSAFE_IO
if (fp->_lock != NULL)
_IO_lock_fini (*fp->_lock);
#endif
}

2.23 IO相关源码阅读笔记
http://akaieurus.github.io/2023/02/10/IO相关源码阅读笔记/
作者
Eurus
发布于
2023年2月10日
许可协议