当前位置: 首页 > news >正文

C文件/Linux内核级文件理解

Linux内核级文件

1. 回顾C文件接口

1.1 fopen

函数原型

FILE *fopen( const char *filename, const char *mode );

参数

  • filename: 文件的路径。

  • mode: 文件的打开方式。

返回值

  • 成功返回 FILE* 结构体指针;失败返回 NULL
文件打开方式含义如果文件不存在
“r”文件不存在会报错
“w”写(清空写)建立一个新的文件
“a”写(追加写)建立一个新的文件
“r+”读写文件不存在会报错
“w+”读写(清空写)建立一个新的文件
“a+”读写(追加写)建立一个新的文件
“rb”以二进制模式读文件不存在会报错
“wb”以二进制模式写(清空写)建立一个新的文件
“ab”以二进制模式写(追加写)建立一个新的文件
“rb+”以二进制模式读写文件不存在会报错
“wb+”以二进制模式读写(清空写)建立一个新的文件
“ab+”以二进制模式读写(追加写)建立一个新的文件

1.2 fclose

函数原型

int fclose( FILE *stream );

参数

  • stream: 关闭的文件流。

返回值

  • 成功返回 0 ;失败返回 EOF

1.3 fgetc

函数原型

int fgetc( FILE *stream );

参数

  • stream: 读取的文件流。

返回值

  • 读取成功返回该字符的 ASCII 值;读取失败或文件结束返回 EOF

1.4 fputs

函数原型

int fputc( int c, FILE *stream );

参数

  • c: 写入的字符

  • stream: 写入的文件流。

返回值

  • 写入成功返回该字符的 ASCII 值;写入失败返回 EOF

1.5 fgets

函数原型

char *fgets( char *string, int n, FILE *stream );

参数

  • string: 存放从文件流中读取到的字符串。

  • n: 读取的字符个数,包含 '\0',实际读到 n - 1 个字符。

  • stream: 读取的文件流。

返回值

  • 读取成功返回字符串首元素地址;读取失败或文件结束返回 NULL

1.6 fputs

函数原型

int fputs( const char *string, FILE *stream );

参数

  • string: 写入的字符串。

  • stream: 写入的文件流。

返回值

  • 写入成功返回写入的字符数;写入失败返回 EOF

1.7 fscanf

函数原型

int fscanf( FILE *stream, const char *format [, argument ]... );

参数

  • stream: 读取的文件流。

  • format: 格式化数据和 scanf 用法类似。

返回值

  • 读取成功返回读入的参数个数;读取失败返回 EOF

1.8 fprintf

函数原型

int fprintf( FILE *stream, const char *format [, argument ]...);

参数

  • stream: 写入的文件流。

  • format: 格式化数据和 printf 用法类似。

返回值

  • 写入成功返回实际写入的字符数;写入失败返回一个负数。

1.9 fread

函数原型

size_t fread( void *buffer, size_t size, size_t count, FILE *stream );

参数

  • buffer: 存放从文件流中读取到的字符串。

  • size: 每个数据项的大小。

  • count: 要读取的数据项数量。

  • stream: 读取的文件流。

返回值

  • 读取成功返回实际读取的数据项数量(<= count);读取失败或到达文件结尾,返回比 count 小的值。

1.10 fwrite

函数原型

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

参数

  • buffer: 写入的数据。

  • size: 每个数据项的大小。

  • count: 写入的数据项数量。

  • stream: 写入的文件流。

返回值

  • 写入成功返回实际写入的数据项数量(通常等于 count);写入失败或到达文件结尾,返回比 count 小的值。

2. 基础IO

2.1 理解文件

  • ⽂件在磁盘⾥。

  • 磁盘是永久性存储介质,因此⽂件在磁盘上的存储是永久性的。

  • 磁盘是外设(即是输出设备也是输⼊设备)。

  • 磁盘上的⽂件 本质是对⽂件的所有操作,都是对外设的输⼊和输出 简称 IO。

  • Linux 下⼀切皆⽂件(抽象化的过程)。

  • 文件是文件属性和文件内容的集合,所有文件的操作本质是文件内容操作和文件属性操作。

  • 对文件的操作本质是进程对文件的操作。

2.2 操作文件的系统调用

2.2.1 open

函数原型

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

参数

  • pathname: 要打开或创建的目标文件。

  • flags: 打开⽂件时,可以传⼊多个参数选项,⽤下⾯的⼀个或者多个常量进⾏或运算,构成flags。

    • O_RDONLY: 只读打开

    • O_WRONLY: 只写打开

    • O_RDWR: 读写打开

    • O_CREAT: 若⽂件不存在,则创建它。需要使⽤mode选项,来指明新⽂件的访问权限

    • O_APPEND: 追加写

    • O_TRUNC: 若文件存在,则清空文件

  • mode: 仅当使用 O_CREAT 标志时,用于指定新创建文件的权限。通常为 0666

    • 实际权限会受到进程的 umask 影响,最终权限为 mode & ~umask

返回值

  • 打开文件成功返回新打开的文件描述符,打开失败返回-1。
2.2.2 close

函数原型

#include <unistd.h>
int close(int fd);

参数

  • fd: 要关闭的文件描述符。
2.2.3 write

函数原型

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

参数

  • fd: 往该文件描述符中写。

  • buf: 数据缓冲区指针,指向要写入的数据内存地址。

  • count: 写入的字节数,表示从 buf 中写多少字节到文件。

返回值

  • 写入成功返回实际写入的字节数(ssize_t 类型,可能小于 count);失败时返回-1。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{umask(0);int fd = open("myfile", O_WRONLY|O_CREAT, 0664);if(fd < 0){perror("open");return 1;} int count = 5;const char *msg = "hello world\n";while(count--) {ssize_t s = write(fd, msg, strlen(msg));} close(fd);return 0;
}
2.2.4 read

函数原型

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

参数**

  • fd: 从该文件描述符中读。

  • buf: 数据缓冲区指针,用于存储读取到的数据。

  • count: 要读取的最大字节数(不包含 '\0')。

返回值

  • 读取成功返回实际读取的字节数。

    • > 0:成功读取的字节数(可能小于count)

    • = 0:表示到达文件末尾

  • 失败时返回-1。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;} const char *msg = "hello world\n";char buf[1024];while(1) {ssize_t s = read(fd, buf, strlen(msg));if(s > 0){buf[s] = 0;printf("%s", buf);} else {break;}} close(fd);return 0;
}

2.3 文件描述符

  • 内核级抽象:文件描述符是 进程与内核之间的桥梁,进程通过它操作文件,而无需关心底层细节。

  • 数组索引:在 Linux 内核中,每个进程有一个 文件描述符表(File Descriptor Table),文件描述符表本质是结构体指针数组,fd 是该表的索引,指向一个 文件对象(struct file)

  • 非负整数:从0开始分配,依次递增。

  • Linux进程默认情况下会打开三个文件描述符。

    • 0:标准输入(通常是键盘)

    • 1:标准输出(通常是显示器)

    • 2:标准错误(通常是显示器)

  • 文件描述符的分配规则:在 files_struct 数组当中,找到当前没有被使⽤的最⼩的⼀个下标,作为新的⽂件描述符。

在这里插入图片描述

  • task_struct:Linux 中代表进程的核心结构体(进程控制块),包含进程所有关键信息(状态、PID、文件描述符表指针等 )。图中用 struct files_struct *files 指向该进程的文件描述表结构体。
  • files_struct:管理进程打开文件的结构体,内部通过 fd_array[](文件描述符数组)存储 struct file* 指针,数组下标就是 文件描述符(如 0、1、2…)
  • struct file:位于 内核的全局文件表(所有进程共享)。存储文件的 打开状态(如读写位置、访问模式等)。

    • 关键字段:

    • f_flags:打开文件的标志(如 O_RDONLYO_WRONLY)。

    • f_inode:指向文件的 inode(实际文件数据)。

    • ref_count:引用计数,节省空间和时间(多个进程可能共享同一个 struct file,引用计数确保资源不被提前释放)。

2.4 重定向

重定向 是Linux 系统中一种 改变输入/输出默认路径 的机制,允许程序从非常规位置读取和输入。

在 Linux 中,重定向通过 文件描述符(File Descriptor) 和系统调用实现

2.4.1 dup2

dup2 是 Linux 系统中用于 复制文件描述符(File Descriptor) 的核心系统调用,主要作用是 重定向输入/输出

dup2 系统调用的核心操作是将文件描述符数组 oldfd 下标中的内容(struct file)拷贝覆盖至fd数组 newfd 下标中。

函数原型

#include <unistd.h>
int dup2(int oldfd, int newfd);

参数

  • oldfd: 需要复制的 原文件描述符

  • newfd: 指定的新文件描述符。

返回值

  • 成功:返回新的文件描述符(值与newfd相同)。

  • 失败:返回-1。

#include <unistd.h>
#include <fcntl.h>int main() {int fd = open("test.txt", O_WRONLY | O_CREAT, 0664);dup2(fd, 1);  // 将 stdout(1) 重定向到文件close(fd);printf("This goes to output.txt!\n");  // 写入文件而非终端return 0;
}

在这里插入图片描述


2.5 标准输出和标准错误的理解

标准输出(stdout)标准错误(stderr),可以通过重定向能力,把常规消息和错误消息进行分离。

  • 将正常输出保存到文件,同时让错误显示在终端:

    command 1> output.log 2> error.log
    
  • 合并两者到同一文件(2>&1&>):

    command 1>log.txt 2>&1
    command &> log.txt       # Bash 简写方式(stdout + stderr 都写入 log.txt)
    

2.6 Linux一切皆文件

Linux 一切皆文件(Everything is a file)是 Linux 系统的一个核心设计哲学,它并不是说所有东西都真的是磁盘上的文件,而是指 系统通过统一的文件接口来访问和管理各种资源Linux 一切皆文件本质上是通过软件层(主要是内核)在硬件和用户之间抽象出的统一接口,这种抽象使得用户和程序可以用相同的操作(如 readwriteopenclose)来处理不同类型的对象,极大简化了系统交互的复杂性。

  1. 隐藏硬件差异:用文件接口统一访问异构资源。

  2. 简化交互:用户只需操作文件,无需理解硬件细节。


2.7 缓冲区

缓冲区本质上是内存中的临时存储区域,用于解决速度不匹配问题。缓冲区分为语言级缓冲区系统内核缓冲区。在计算机系统中,不同组件的速度差异巨大:

  • CPU纳秒级

  • 内存纳秒到微秒级

  • 磁盘/网络毫秒级

缓冲区的核心价值:

  • 减少系统调用次数(语言级缓冲区)

  • 减少与I/O交互次数(内核级缓冲区)

缓冲策略:

  • 全缓冲区:这种缓冲⽅式要求填满整个缓冲区后才进⾏I/O系统调⽤操作。对于磁盘⽂件的操作通常使⽤全缓冲的⽅式访问

  • ⾏缓冲区:在⾏缓冲情况下,当在输⼊和输出中遇到换⾏符时,标准I/O库函数将会执⾏系统调⽤操作当所操作的流涉及⼀个终端(显示器)时(例如标准输⼊和标准输出),使⽤⾏缓冲⽅式。因为标准I/O库每⾏的缓冲区⻓度是固定的,所以只要填满了缓冲区,即使还没有遇到换⾏符,也会执⾏I/O系统调⽤操作,默认⾏缓冲区的⼤⼩为1024。

  • ⽆缓冲区:⽆缓冲区是指标准I/O库不对字符进⾏缓存,直接调⽤系统调⽤。标准出错流stderr通常是不带缓冲区的,这使得出错信息能够尽快地显⽰出来。

除了上述列举的默认刷新⽅式,下列特殊情况也会引发缓冲区的刷新:

  1. 缓冲区满

  2. 执行flush语句

  3. 进程结束

  4. 关闭文件

当程序执行写操作时,数据通常的流动路径: 程序内存 → 语言级缓冲区 → 系统级缓冲区 → 存储设备,读操作则相反方向流动。流动本质是拷贝,将语言缓冲区的内容拷贝至系统级缓冲区,以此类推。

http://www.dtcms.com/a/340021.html

相关文章:

  • 软考网工选择题-1
  • 路由器详解
  • Windows 8.1 补丁 KB2919355 安装方法 详细步骤
  • 【Netty4核心原理⑫】【异步处理双子星 Future 与 Promise】
  • 【AI】算法环境-显卡、GPU、Cuda、NVCC和cuDNN的区别与联系
  • Stimulsoft 发布 2025.3 版本:支持在报表计算中解释运行 C# 脚本
  • Apache ShenYu网关与Nacos的关联及如何配合使用
  • 基于Envoy的AI Gateway测试环境搭建
  • 基于决策树模型的汽车价格预测分析
  • DAY 50 预训练模型+CBAM模块
  • CiA402 伺服驱动标准与控制模式详解
  • STL——string的使用(快速入门详细)
  • 12.3.2设置背景色12.3.3 创建设置类12.4 添加飞船图像 12.4.1 创建Ship 类 12.4.2 在屏幕上绘制飞船
  • 【语法糖】什么是语法糖
  • RK3568 Linux驱动学习——Linux设备树
  • bun + vite7 的结合,孕育的 Robot Admin 【靓仔出道】(十四)
  • for-else 流程控制结构介绍
  • AVL树、红黑树理解
  • 人脸识别智慧检测算法在人群聚集场景应用
  • 关于less/sass两个css预处理器的总结
  • C++常用容器详解:原理、适用场景与代码示例
  • CentOS上安装Docker的完整流程
  • CSP与XSS
  • web开发,在线%服装商城开发demo,基于html,css,jquery,asp.net,webform,sqlserver数据库
  • CNN-LSTM-Attention、CNN-LSTM、LSTM三模型多变量时序光伏功率预测
  • 深度学习图解:神经网络如何学习?
  • [Linux] 网络中的 `tun` 模式
  • 无人机场景 - 目标检测数据集 - 山林野火烟雾检测数据集下载「包含VOC、COCO、YOLO三种格式」
  • Android13车机系统自定义系统栏显示策略之状态栏下拉异常
  • 决策树算法学习总结