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

【Linux】linux基础IO(c语言程序接口,常用文件调用详解)

本文是小编巩固自身而作,如有错误,欢迎指出!

目录

一、IO共识原理

二、C语言文件接口

fopen/fclose

fwrite

fprintf

输出重定向 >和追加重定向 >>

1. 输入重定向 >

2. 追加重定向 >>

三、文件系统调用

文件系统调用概念

比特位方式传递标志位(Bit Flags)

open

close

注意事项

write

read

四、访问文件的本质

文件是内核的数据结构

文件描述符(fd)

核心总结


一、IO共识原理

在正式学习IO之前,我们先回忆一下之前学习的关于文件的知识:

文件 = 内容 + 属性

文件分为打开的文件和没打开的文件

对于打开的文件:谁来打开?其实是由进程打开,本质是研究进程和文件的关系

对于没打开的文件:是放在哪里?其实是放在磁盘上。我们最关注的是什么问题?其实也就是是文件的存储问题,即没有被打开的文件非常多,那么文件如何被分门别类的放置好?文件被分门别类的放置好后,进而我们就可以快速的对文件进行增删查改,也就是快速的找到文件


二、C语言文件接口

fopen/fclose

函数原型

#include <stdio.h> FILE *fopen(const char *pathname, const char *mode);
  • pathname:文件路径,可以是相对路径或绝对路径。
  • mode:打开模式,常用选项包括:
    • "r":只读,文件必须存在
    • "w":只写,如果文件存在则清空,不存在则创建
    • "a":追加写,如果文件不存在则创建
    • "r+":读写,文件必须存在
    • "w+":读写,文件存在则清空,不存在则创建
    • "a+":读写,写入操作追加到文件末尾
#include <stdio.h> int fclose(FILE *stream);
  • stream:指向fopen返回的FILE*
  • 返回值
    • 0:关闭成功
    • EOF:关闭失败

这些基础用法我们在之前就已经学习过了,现在重提的原因主要在于,我们今天要重点了解文件换缓冲机制

fopen返回的FILE*会有缓冲区(默认全缓冲或行缓冲),所以写入磁盘不一定是即时的。

当使用fwriteprintf写入文件时,数据先进入用户级缓冲区(由FILE*管理),并不会立即写入磁盘。

fclose会确保缓冲区的数据被写入磁盘,从而保证文件内容完整

我们先在看一看下面的示例代码

#include <stdio.h> #include <string.h> #include <unistd.h> int main() { const char *filename = "buffer_test.txt"; const char *msg = "Hello, Linux I/O buffer!\n"; // 打开文件用于写入 FILE *fp = fopen(filename, "w"); if (!fp) { perror("fopen"); return 1; } // 写入缓冲区,但不调用 fclose fwrite(msg, strlen(msg), 1, fp); printf("Data written to buffer, but not yet flushed to disk.\n"); printf("Check the file content with: cat %s\n", filename); // 这里注释掉 fclose 查看缓冲区效果 // fclose(fp); // 如果你取消上面 fclose 注释,缓冲区会被刷新到磁盘 printf("Now closing file to flush buffer...\n"); fclose(fp); printf("File closed. Buffer flushed. Check the file content again.\n"); return 0; }
  1. 打开文件
    • 使用fopen("buffer_test.txt", "w")打开一个文件用于写入
    • 得到一个FILE*指针,内部维护一个用户级缓冲区
  2. 写入数据到缓冲区
    • 使用fwrite(msg, strlen(msg), 1, fp)将字符串写入缓冲区
    • 注意,这时候数据还未写入磁盘,仍在内存中
  3. 输出提示
    • 提示用户数据已写入缓冲区,但尚未刷新到磁盘
    • 可以用cat buffer_test.txt查看文件内容,此时可能为空
  4. 关闭文件(可选)
    • 调用fclose(fp)
    • fclose刷新缓冲区:把缓冲区内的数据写入磁盘
    • 同时释放文件描述符,关闭文件
  5. 观察效果
    • 不调用 fclose:文件内容可能为空(数据仍在缓冲区)
    • 调用 fclose:数据写入磁盘,文件显示完整内容

fwrite

#include <stdio.h> size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
  • ptr:指向要写入的数据起始地址
  • size:每个元素的字节数
  • nmemb:要写入元素的个数
  • stream:目标文件的FILE*
  • 返回值:成功写入的元素个数(不是字节数)
  • fwrite写入的是用户缓冲区,不是直接写磁盘
  • fclose刷新缓冲区并关闭文件
  • 可以用fflush(fp)代替fclose实现中途刷新缓冲区

fprintf

#include <stdio.h> int fprintf(FILE *stream, const char *format, ...);
  • stream:目标文件的FILE*
  • format:格式化字符串
  • ...:对应格式的变量
  • 返回值:成功写入的字符数,如果出错返回负值
#include <stdio.h> int main() { FILE *fp = fopen("fprintf_test.txt", "w"); if (!fp) { perror("fopen 出错"); return 1; } int a = 42; const char *name = "Linux I/O"; // 写入格式化内容 fprintf(fp, "这是一个测试:变量 a = %d, name = %s\n", a, name); // 刷新缓冲区并关闭文件 fclose(fp); printf("已写入 fprintf_test.txt,缓冲区已刷新。\n"); return 0; }

输出重定向 >和追加重定向>>

1. 输入重定向 >

作用:将程序的标准输出(stdout)写入文件,如果文件已存在,会覆盖原文件内容

2. 追加重定向>>

作用:将程序的标准输出(stdout)写入文件,如果文件已存在,内容追加到末尾

三、文件系统调用

文件系统调用概念

  • 文件系统调用是操作系统提供的接口,允许程序访问磁盘上的文件和设备
  • C 标准库函数(fopen/fwrite/fclose)不同,系统调用是直接与内核交互

比特位方式传递标志位(Bit Flags)

  • 在 Linux 系统调用(如open)中,通常会有多个选项
  • 为了节省参数,同时可以组合多个选项,Linux 使用比特位(bit)表示每个选项
  • 每个选项对应整数中的一位(或多位),通过按位或|来组合
#define O_RDONLY 0x0000 // 0000 0000 #define O_WRONLY 0x0001 // 0000 0001 #define O_RDWR 0x0002 // 0000 0010 #define O_CREAT 0x0040 // 0100 0000 #define O_APPEND 0x0400 // 0100 0000 0000
int flags = O_WRONLY | O_CREAT | O_APPEND;

内存中的二进制表示:

0000 0000 0000 0001 (O_WRONLY) 0000 0000 0100 0000 (O_CREAT) 0100 0000 0000 0000 (O_APPEND) ------------------- 0100 0000 0100 0001 (组合后的 flags)
  • 节省参数:一个整数可以表示多个选项
  • 易于扩展:新选项只需分配新的一位
  • 组合灵活:用|组合,用&检查

open

#include <fcntl.h> int open(const char *pathname, int flags, mode_t mode);
  • pathname:文件路径
  • flags:打开方式,例如:
    • O_RDONLY只读
    • O_WRONLY只写
    • O_RDWR读写
    • O_CREAT文件不存在则创建
    • O_APPEND追加写
    • O_TRUNC打开时清空文件
  • mode:权限位(仅在创建文件时使用,如 0666)
  • 返回值:文件描述符 fd(>=0 表示成功,-1 表示失败)

close

#include <unistd.h> int close(int fd);
  • fd:文件描述符,由open或其他系统调用返回
  • 返回值
    • 0→ 成功
    • -1→ 出错(并设置errno

注意事项

  1. 不要重复关闭同一个 fd
    • 重复调用close(fd)会导致未定义行为
  2. fd 会被操作系统重用
    • 下一个open可能会分配相同的 fd
  3. 和库函数 fclose 的区别
    • close(fd)只关闭 fd
    • fclose(FILE* fp)先刷新缓冲区再调用close

write

#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);
  • fd:文件描述符,由open或其他系统调用返回
  • buf:指向要写入的数据内存地址
  • count:写入字节数
  • 返回值:实际写入的字节数,如果出错返回 -1 并设置 errno
特性writefwrite
缓冲区内核缓冲区用户缓冲区 + 内核缓冲区
调用层级系统调用(内核)C 标准库封装(调用 write)
输出类型文件描述符 fdFILE*(可以是 stdout、stderr 或文件)
刷新控制立即写入内核缓冲区需要fflushfclose才写入内核

read

#include <unistd.h> ssize_t read(int fd, void *buf, size_t count);
  • fd:文件描述符,由open或其他系统调用返回
  • buf:指向内存缓冲区,用于存储读取的数据
  • count:要读取的最大字节数
  • 返回值
    • 成功 → 实际读取的字节数
    • 到文件末尾 → 返回 0
    • 出错 → 返回 -1 并设置 errno

四、访问文件的本质

在 Linux 中,“访问文件”不仅仅是打开文件或读写文件,而是操作内核中的文件系统和文件描述符

文件是内核的数据结构

  • 文件在 Linux 内核中不仅是磁盘上的字节流,也对应内核维护的文件结构
    • struct file:内核维护每个打开文件的状态
    • 包含:
      • 文件指针(读写偏移位置)
      • 访问权限(只读/只写/读写)
      • 文件状态(打开、追加、锁定等)
      • 关联的 inode(文件的元信息,如大小、权限、设备类型)

文件在磁盘上的字节 + 内核维护的元信息 = 文件的本质

文件描述符(fd)

  • 用户程序通过文件描述符 fd操作文件
  • fd 是一个索引或句柄,指向内核维护的struct file
  • Linux 默认:
    • 0→ stdin
    • 1→ stdout
    • 2→ stderr
  • 每次open返回的 fd,其实就是指向内核文件表的索引

文件操作 = 通过 fd 间接操作内核文件结构

核心总结

  • 文件不仅是磁盘上的数据,更是内核维护的数据结构 + 元信息
  • 用户程序访问文件 = 通过 fd 间接操作内核文件结构
  • 所有 I/O(read/write/fread/fwrite/printf)本质都是数据在用户缓冲区 ↔ 内核缓冲区 ↔ 磁盘之间流动
  • 文件描述符、缓冲区、系统调用三者构成文件访问的完整链路

本次分享就到这里结束了,后续会继续更新,感谢阅读!

http://www.jsqmd.com/news/649091/

相关文章:

  • 如何通过Jasminum插件提升中文文献管理效率80%:完整操作指南
  • openEuler(CentOS8)防火墙firewall与Selinux实战配置指南
  • mac上如何安装openclaw,并在微信中使用clawbot
  • 终极视频PPT提取指南:三分钟实现智能自动化处理
  • HeteroFlow v2 企业版:统一异构算力调度,让国产 GPU 物尽其用!
  • 二维核密度估计实战:用Seaborn的kdeplot函数,从数据探索到模型诊断
  • FogGate-YOLO:直击雾天检测痛点,基于通道选择的 YOLOv8 优化方案
  • 北京正规上门回收名家字画、明清古籍等藏品 6家靠谱机构汇总 - 品牌排行榜单
  • Z-Image-Turbo保姆级部署教程:开箱即用,无需下载模型,小白也能搞定
  • 多模态长尾问题正在吞噬你的模型ROI!:2023–2024全球117个落地项目统计——长尾处理滞后导致平均交付延期22.6天,成本超支31%
  • 消息队列系统的消息持久化顺序保证与消费确认
  • 从CTF实战出发:手把手教你用Python脚本破解RSA共模攻击(附完整代码)
  • Hyperliquid 的故事
  • 小白也能玩转Qwen3.5-2B:无需GPU,开箱即用的多模态对话体验
  • Blender化学品插件终极指南:从零开始创建专业3D分子模型
  • LangChain入门指南:轻松掌握大模型应用开发,收藏必备技能!
  • 用 30% 的钱,达到 94% 的能力!国产大模型 GLM-5.1 深度测评,附专属优惠购买通道
  • 5分钟上手:让你的Windows任务栏变身透明艺术品的终极指南
  • Graphormer科研助手:支持论文复现的OGB基准数据集加载与预测工具
  • SeqGPT-560M模型安全指南:防御对抗攻击策略
  • 简单免费:猫抓浏览器插件帮你轻松获取网页视频和音频资源的完整指南
  • Zotero Citation插件终极指南:让Word文献引用更简单清晰的完整教程
  • 从Demo到产线:工业级图像识别开发中的“环境陷阱”与深度定制视觉服务实务
  • 折腾了一晚上,终于把Codex跑在本地了
  • AWS MSK Kafka min.insync.replicas 配置风险排查与修复实战
  • Python的__class_getitem__实现泛型别名
  • 第7.2章:StarRocks性能调优实战——Query Profile深度解析与优化策略
  • GPT-5架构泄露?Kubernetes 1.31发布与Rust重构浪潮下的云原生之变
  • Xinference-v1.17.1零售分析应用:顾客行为理解
  • 2026靠谱的企业服务公司推荐,深聊长春会赢企服的信誉度、性价比和售后 - 工业设备