公司网站建设内容建议,搜狗推广管家,濮阳建网站,网站建设课设报告重要概念:fopen()返回的是一个结构体的指针
_IO_FILE 结构体在什么时候被创建#xff1f;
_IO_FILE 结构体的实例是在程序使用标准 I/O 函数#xff08;如 fopen、fclose、fread、fwrite 等#xff09;时创建和管理的。这个结构体实际上是 GNU C Library (glibc) 用于处理…重要概念:fopen()返回的是一个结构体的指针
_IO_FILE 结构体在什么时候被创建
_IO_FILE 结构体的实例是在程序使用标准 I/O 函数如 fopen、fclose、fread、fwrite 等时创建和管理的。这个结构体实际上是 GNU C Library (glibc) 用于处理文件流的底层实现细节。当你在程序中打开一个文件或者创建一个流时glibc 会在后台分配和初始化一个 _IO_FILE 结构体并返回一个指向它的指针即 FILE* 类型。
下面是一些常见场景说明 _IO_FILE 结构体是如何被创建和使用的 使用 fopen 打开文件 当你调用 fopen 打开一个文件时glibc 会分配一个 _IO_FILE 结构体并进行初始化。例如 FILE *file fopen(example.txt, r);在这个例子中fopen 函数会创建一个 _IO_FILE 结构体实例来管理 example.txt 文件的读操作并返回一个指向该结构体的指针 file。 使用 fdopen 关联文件描述符和文件流 如果你有一个现有的文件描述符并希望将其与一个标准 I/O 流关联可以使用 fdopen 函数 int fd open(example.txt, O_RDONLY);
FILE *file fdopen(fd, r);fdopen 会创建一个新的 _IO_FILE 结构体实例并将文件描述符 fd 关联到这个结构体上。 标准输入输出 标准输入stdin、标准输出stdout和标准错误stderr也是通过 _IO_FILE 结构体来管理的。它们在程序启动时由运行时库自动初始化。 fprintf(stdout, Hello, World!\n);_IO_FILE 结构体的创建过程
当函数如 fopen 被调用时glibc 内部会进行以下步骤 分配内存 glibc 会调用内存分配函数如 malloc为 _IO_FILE 结构体分配内存。 初始化结构体 分配内存后glibc 会初始化 _IO_FILE 结构体的各个字段。例如它会设置缓冲区指针、文件描述符、文件模式等。 返回指针 初始化完成后glibc 会返回一个指向这个 _IO_FILE 结构体的指针即 FILE* 类型的指针。
_IO_FILE 在文件流操作中的生命周期 创建 当你使用标准 I/O 函数如 fopen、fdopen打开或创建一个文件流时glibc 会创建一个 _IO_FILE 结构体实例。 使用 在文件流的生命周期内所有对该文件流的读写操作如 fread、fwrite、fgets、fputs 等都会通过这个 _IO_FILE 结构体来管理缓冲区、文件描述符和流的状态。 销毁 当你调用 fclose 关闭文件流时glibc 会执行以下操作 刷新缓冲区中的数据如果有需要。释放与文件流关联的资源如缓冲区内存。关闭文件描述符。最后释放 _IO_FILE 结构体的内存。
示例代码
以下是一个简单的示例代码展示了 _IO_FILE 结构体实例的创建和使用过程
#include stdio.hint main() {// 打开文件创建一个 _IO_FILE 结构体实例FILE *file fopen(example.txt, w);if (file NULL) {perror(Failed to open file);return 1;}// 使用文件流进行写操作fprintf(file, Hello, World!\n);// 关闭文件销毁 _IO_FILE 结构体实例fclose(file);return 0;
}在这个示例中当调用 fopen 时glibc 会创建并初始化一个 _IO_FILE 结构体实例。当调用 fclose 时glibc 会销毁这个实例并释放相关资源。
_IO_FILE 结构体
在 Linux 系统中_IO_FILE 结构体是 GNU C Library (glibc) 中实现标准 I/O (stdio) 的核心数据结构之一。它用于描述文件流FILE*的内部状态和缓冲区信息。理解 _IO_FILE 结构体对于某些高级的漏洞利用技术如利用格式字符串漏洞或缓冲区溢出漏洞非常重要。
以下是 _IO_FILE 结构体的一般布局具体布局可能会随着 glibc 版本的不同而变化
struct _IO_FILE {int _flags; // 文件流的状态标志char* _IO_read_ptr; // 缓冲区读取指针char* _IO_read_end; // 缓冲区读取结束指针char* _IO_read_base; // 缓冲区读取基地址char* _IO_write_base; // 缓冲区写入基地址char* _IO_write_ptr; // 缓冲区写入指针char* _IO_write_end; // 缓冲区写入结束指针char* _IO_buf_base; // 缓冲区基地址char* _IO_buf_end; // 缓冲区结束地址char *_IO_save_base; // 保存的缓冲区基地址char *_IO_backup_base; // 备份的缓冲区基地址char *_IO_save_end; // 保存的缓冲区结束地址struct _IO_marker *_markers; // 标记链表struct _IO_FILE *_chain; // 文件流链表int _fileno; // 文件描述符int _flags2; // 额外的标志__off_t _old_offset; // 旧的偏移量unsigned short _cur_column;// 当前列号signed char _vtable_offset;// 虚表偏移char _shortbuf[1]; // 短缓冲区_IO_lock_t *_lock; // 锁__off64_t _offset; // 偏移量void *__pad1; // 填充void *__pad2; // 填充void *__pad3; // 填充void *__pad4; // 填充size_t __pad5; // 填充int _mode; // 模式char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];// 未使用的填充
};关键字段
_flags: 用于描述文件流的状态标志例如是否为读模式、写模式等。_IO_read_ptr, _IO_read_end, _IO_read_base: 分别指向当前读取的位置、读取的结束位置和读取缓冲区的基地址。_IO_write_base, _IO_write_ptr, _IO_write_end: 分别指向当前写入的位置、写入的结束位置和写入缓冲区的基地址。_IO_buf_base, _IO_buf_end: 分别指向缓冲区的基地址和结束地址。_IO_save_base, _IO_backup_base, _IO_save_end: 用于保存缓冲区状态的指针。_markers: 指向标记结构的链表用于支持多种流操作。_chain: 指向下一个文件流的指针形成一个文件流链表。_fileno: 文件描述符。_flags2: 额外的标志位。_old_offset: 用于记录偏移量。_cur_column: 当前列号主要用于格式化输出。_vtable_offset: 虚表偏移用于支持面向对象的操作。_shortbuf: 一个短缓冲区。_lock: 指向用于同步的锁。_offset: 文件流的位置偏移量。填充字段: 用于对齐和扩展。
stdin、stdout 和 stderr指针
这些指针是程序的标准输入、标准输出和标准错误流stdin、stdout 和 stderr在内存中的地址。它们是全局变量通常在程序启动时被初始化以指向相应的 FILE 结构体。
解释每个指针 stdout 标准输出 地址0x602020指向的地址0x00007fe6e8e03620 stdin 标准输入 地址0x602030指向的地址0x00007fe6e8e028e0 stderr 标准错误 地址0x602040指向的地址0x00007fe6e8e03540
每个地址如 0x602020 是全局变量的地址而对应的值如 0x00007fe6e8e03620是这些全局变量指向的 FILE 结构体实例的地址。
内存布局和用途 stdout: 地址0x602020指向的地址0x00007fe6e8e03620用途标准输出通常用于打印普通输出信息默认连接到终端的显示设备。 stdin: 地址0x602030指向的地址0x00007fe6e8e028e0用途标准输入用于读取输入数据默认连接到终端的键盘输入。 stderr: 地址0x602040指向的地址0x00007fe6e8e03540用途标准错误用于打印错误信息默认也连接到终端的显示设备。
背后的机制
在程序启动时C 标准库如 glibc会初始化这几个标准流。具体来说它们会分配相应的 FILE 结构体并将 stdin、stdout 和 stderr 这些全局变量指向这些结构体。
以下是一个简化的示意图展示了这些指针和 FILE 结构体的关系
---------------- ----------------
| 0x602020 | -------- | FILE for stdout|
| (stdout) | | 0x00007fe6e8e03620 |
---------------- -------------------------------- ----------------
| 0x602030 | -------- | FILE for stdin |
| (stdin) | | 0x00007fe6e8e028e0 |
---------------- -------------------------------- ----------------
| 0x602040 | -------- | FILE for stderr|
| (stderr) | | 0x00007fe6e8e03540 |
---------------- ----------------示例代码验证
下面是一些示例代码可以用来验证这些指针的地址
#include stdio.hint main() {printf(Address of stdout: %p\n, (void*)stdout);printf(Address of stdin: %p\n, (void*)stdin);printf(Address of stderr: %p\n, (void*)stderr);printf(Pointer value of stdout: %p\n, (void*)stdout);printf(Pointer value of stdin: %p\n, (void*)stdin);printf(Pointer value of stderr: %p\n, (void*)stderr);return 0;
}运行这段代码你应该会看到标准流指针的地址和它们指向的 FILE 结构体的地址这与你提供的内存地址应该是一致的。
总结
这些指针stdout、stdin 和 stderr是全局变量指向标准 I/O 流的 FILE 结构体实例。这些实例在程序启动时由 C 标准库初始化用于管理标准输入、输出和错误流。