【Linux】文件IO

Linux文件I/O系统编程详解


在编程中,文件 I/O(Input/Output)是指程序与外部文件之间的数据交互。

C标准函数与系统函数

特性 C 标准库函数(如fopenfread 系统调用函数(如openread
所属层次 用户态(基于系统调用封装的库函数) 内核态(操作系统内核提供的接口)
实现者 编译器厂商(如 GCC 的 glibc 库) 操作系统内核开发者(如 Linux 内核团队)
调用方式 直接调用库函数,由库转发到系统调用 直接触发软中断,陷入内核态执行
缓冲机制 有缓冲区(用户态缓冲),减少系统调用次数 无缓冲,每次调用直接与硬件交互
可移植性 高(遵循 C 标准,跨操作系统通用) 低(与操作系统内核相关,如 Linux 和 Windows 不同)
功能范围 除了 I/O,还包括字符串处理、数学运算等 仅提供操作系统核心功能(文件操作、进程管理等)

File 结构体

​ 在 Linux 系统中,文件读写过程涉及两个关键的 “文件结构体”:用户态的 FILE 结构体(C 标准库定义)和内核态的 struct file 结构体(Linux 内核定义)。两者通过文件描述符关联,共同管理文件读写状态,包括文件指针(当前读写位置)。

  1. 用户态:FILE 结构体(C 标准库)

FILE 结构体定义在 <stdio.h> 中(不同 libc 实现细节略有差异),用于封装系统调用,提供带缓冲的文件操作。其核心作用是管理用户态缓冲区和当前读写位置。

主要成员

typedef struct {
    int fd;               // 关联的文件描述符(指向内核态的 struct file)
    char *buf;            // 用户态缓冲区(减少系统调用次数)
    size_t buf_size;      // 缓冲区大小
    size_t buf_pos;       // 缓冲区当前位置(用户态指针)
    off_t file_pos;       // 文件当前读写位置(逻辑位置)
    int flags;            // 文件模式(如读/写/追加)
    // 其他成员:错误码、缓冲模式等
} FILE;

FILE 是 C 标准库定义的结构体,用于封装文件操作的关键信息,提升文件 I/O 效率,主要包含三部分核心内容:

​ 文件描述符:是与内核中打开的文件的关联标识,通过它能找到磁盘等设备上对应的实际文件。

​ f_pos(文件光标):记录当前文件的读写位置,就像 “光标” 一样,指示下一次读写从哪里开始。

​ buffer(缓冲区):大小通常为 8192 字节(8k)。写文件时,数据先暂存到缓冲区,等缓冲区写满 8k 后,再一次性写入磁盘文件;如果需要提前将缓冲区数据写入文件,可手动调用刷新(如fflush)操作。通过缓冲区批量 I/O,减少了与磁盘等设备的直接交互次数,从而提高文件操作效率。

  1. 内核态:struct file 结构体(Linux 内核)

struct file 定义在 Linux 内核头文件中(如 linux/fs.h),是内核管理打开文件的核心结构,记录文件的底层信息和内核态指针。

主要成员

struct file {
   
   
    struct path f_path;       // 文件的路径信息
    struct inode *f_inode;    // 关联的 inode(存储文件元数据)
    const struct file_operations *f_op;  // 文件操作函数表(read/write等)
    loff_t f_pos;             // 内核态文件指针(实际读写位置)
    unsigned int f_flags;     // 打开文件时的标志(如 O_RDWR、O_APPEND)
    // 其他成员:引用计数、文件锁、私有数据等
};

文件操作的层级流程

应用层(C 标准函数):

​ 开发者通过 fopen(打开文件)、fwrite(写入文件)、printf(输出到标准输出)等 C 标准库函数操作文件,这些函数属于应用层接口,使用更简便。

系统层(Linux 系统调用):

​ C 标准库函数会进一步调用 Linux 系统的应用层 API(如 write),write 又会触发内核层 API(如 sys_write),进入内核态处理。

驱动与设备层:

​ 内核通过驱动程序与不同设备交互,完成实际的 I/O 操作。比如要将数据写入终端,驱动就和显示设备通信;写入普通文件,驱动就和磁盘交互;涉及网络文件,驱动则与网络设备协作。

Linux下的内存

一、Linux 进程的虚拟内存空间

Linux 进程的虚拟内存总容量为 4GB,其中:

  • 用户空间:占 3GB,是应用程序(如 C 程序)运行的区域,包含代码、数据、堆、栈等。

  • 内核空间:占 1GB,是操作系统内核运行的区域,管理进程、内存、文件等底层资源。

二、进程控制块(PCB)与 files_struct 结构体

每个进程都有一个 进程控制块(PCB),其中包含 files_struct 结构体:

  • files_struct 是内核用于管理进程打开文件的核心结构,内部包含一个文件描述符数组int 数组)。

  • 文件描述符是一个整数,每打开一个文件(或设备),内核就会为其分配当前最小的未使用整数作为描述符;关闭文件时,该描述符会被释放并可复用。

三、文件描述符的默认分配与关联

Linux 进程默认打开三个文件描述符

  • 0:标准输入(如 cinscanf 从这里读取输入)。
  • 1:标准输出(如 coutprintf 向这里输出)。
  • 2:标准错误(如 perrorWSAGETLastError 向这里输出错误信息)。

当进程打开新文件时,内核会分配下一个最小的未使用描述符,并在文件描述符数组中与实际文件(或设备)建立关联。

四、用户态 FILE* 与内核态文件的关联

  • 在用户空间(3GB 区域),C 标准库通过 fopen("abc") 创建 FILE* 指针,它是对文件操作的高层封装。
  • FILE* 最终会通过文件描述符与内核 files_struct 中的对应项关联,从而实现对底层文件(如磁盘上的 abc 文件)的读写。

在这里插入图片描述
标准输入,标准输出,标准出错

e.g.验证当进程打开新文件时,内核会分配下一个最小的未使用描述符:

由上可知,打开了一个文件1,fd=3,一个文件2,fd=4,关上文件1,打开文件3,此时文件三的fd=3。

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>

int main()
{
   
   
    //1.打开aa文件
    int fd_aa = open("aa",O_RDONLY);
    if(-1==fd_aa){
   
   
        printf("error=%d\n",errno);
        perror("open aa fail");
        return 1;
    }
    //打印文件描述符
    printf("fd_aa = %d\n",fd_aa);
    //2.打开bb文件
    int fd_bb = open("bb",O_RDONLY);
    if(-1==fd_bb){
   
   
        perror("open bb fail");
        return 1;
    }
    //打印文件描述符
    printf("fd_bb = %d\n",fd_bb);
    //3.关闭aa文件
    close(fd_aa);
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值