Linux入门DAY27(文件IO(系统调用)详解|open/read/write/lseek)

#Linux #嵌入式 #文件IO #系统调用 #C语言

📖前言

在Linux嵌入式开发中,文件IO是非常核心的基础知识点。Linux秉承一切皆文件的设计思想,普通文件、终端、设备、管道、套接字都可以通过文件接口操作 。

文件IO属于系统调用,由内核直接提供接口;而我们平时用的 fopen、fread 属于标准IO库函数,标准IO底层本质还是封装文件IO系统调用,并且增加用户态缓冲区提升效率 。

面试高频考点:文件描述符、open标志位、缓冲区机制、文件IO与标准IO区别、常见坑点(文件描述符泄漏、读写返回值处理)。本文结合手写笔记,梳理全套接口与易错点。

一、文件IO基本概念

文件IO是Linux内核为用户空间提供的文件操作接口,属于系统调用。

四大核心系统调用: open() 、 read() 、 write() 、 close() ,辅助接口: lseek() 。

文件描述符 fd

  1. 进程打开一个文件,内核分配一个非负小整数作为标识,就是文件描述符fd。

  2. 进程默认占用3个文件描述符:

  • 0 :标准输入 stdin

  • 1 :标准输出 stdout

  • 2 :标准错误 stderr

  1. 分配规则:最小未被使用的文件描述符。

  2. 每个进程有最大可打开文件数,打开不关闭会造成文件描述符泄漏。

二、系统调用API详解

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 :打开方式标志位,多个标志位用 | 或运算拼接

  • mode :创建文件时才生效,设置文件权限;实际权限 = mode & ~umask

flags常用标志

标志 含义 等价fopen模式

O_RDONLY 只读 "r"

O_WRONLY 只写

O_RDWR 读写 "r+"

O_CREAT 文件不存在则创建

O_TRUNC 打开清空文件原有内容 "w"

O_APPEND 追加写,光标指向文件末尾 "a"

模式等价关系

  • "w" : O_WRONLY | O_CREAT | O_TRUNC

  • "w+" : O_RDWR | O_CREAT | O_TRUNC

  • "a" : O_WRONLY | O_CREAT | O_APPEND

  • "a+" : O_RDWR | O_CREAT | O_APPEND

返回值:成功返回fd;失败返回 ‑1 。

2.close 关闭文件

#include <unistd.h>

int close(int fd);

  • 功能:关闭文件,释放文件描述符资源

  • 参数:open返回的文件描述符fd

  • 返回:成功返回0;失败返回‑1

⚠面试坑:文件打开完成必须close;不关闭会发生文件描述符泄漏,耗尽进程可用fd资源。

3.write 向文件写入数据

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

  • fd:目标文件描述符

  • buf:要写入数据的首地址

  • count:希望写入的字节数

  • 返回:成功返回实际写入字节数;失败返回‑1。

注意:实际写入字节数 ≤ count,不一定全部写完,代码需要处理返回值。

4.read 从文件读取数据

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

  • fd:文件描述符

  • buf:存放读取数据的缓冲区地址

  • count:希望读取的字节数

  • 返回:成功返回实际读到字节;读到文件末尾返回 0 ;失败返回‑1。

⚠高频易错:判断文件结束条件是返回值等于0,不能靠buf内容判断。

5.lseek 文件读写位置偏移

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

功能:修改文件读写指针位置,可实现文件空洞。

  • offset:偏移量,正数向后偏移,负数向前偏移

  • whence:偏移基准位置

  • SEEK_SET :从文件开头偏移

  • SEEK_CUR :从当前读写位置偏移

  • SEEK_END :从文件末尾偏移

  • 返回:成功返回当前读写位置距离文件开头的偏移字节;失败返回‑1。

三、文件IO vs 标准IO(C库)

对比维度 文件IO(系统调用 open/read) 标准IO库(fopen/fprintf)

操作对象 int 文件描述符 fd FILE* 文件流指针

实现来源 内核系统调用 C库函数,底层封装系统调用

缓冲区 无用户态缓冲区,内核存在页缓存 自带用户态缓冲区,减少系统调用次数

跨平台 Linux/Unix专用,可移植差 ANSI C标准,Windows/Linux通用

适用场景 嵌入式底层、设备文件、管道、套接字 普通文本文件、上层业务开发(CSDN博...)

四、标准IO缓冲区分类

标准IO有三类缓冲区,缓冲区刷新是面试高频考点:

  1. 行缓冲(默认1KB):终端交互使用;遇到 \n 换行、程序结束、fflush刷新缓冲区。

  2. 全缓冲(默认4KB):普通文件;缓冲区写满、程序结束、文件关闭、fflush才刷新。

  3. 无缓冲:stderr标准错误,数据直接输出,不做缓冲。

fflush(FILE* stream) :强制刷新用户态缓冲区,把缓冲区数据下发到底层系统调用。

五、格式化字符串函数

//输出到文件流

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

//输出到文件描述符(标准IO扩展)

int dprintf(int fd, const char *format, ...);

//输出到字符数组缓冲区

int sprintf(char *str, const char *format, ...);

  • sprintf :把数字、结构体数据格式化为字符串存入字符数组,用于字符串拼接。

安全问题:sprintf不检查数组越界,工程优先使用 snprintf 。

strtok字符串分割

char *strtok(char *str, const char *delim);

功能:字符串分割函数,按照分隔符截取子串。

坑点:strtok会修改原字符串,不可传入字符串常量。

🎯小结

  1. 文件IO是Linux内核提供系统调用,核心 open/read/write/close/lseek ,操作对象是文件描述符fd。

  2. 文件描述符遵循最小未使用分配原则,0/1/2默认对应标准输入输出错误;打开文件必须close,防止fd泄漏。

  3. open的flags标志位使用或运算 | 组合;mode权限仅O_CREAT创建文件时生效。

  4. read返回0代表读到文件末尾,write返回实际写入字节,不要忽略返回值。

  5. 文件IO无用户缓冲区;标准IO库自带缓冲区,分行缓冲、全缓冲、无缓冲三类,理解缓冲区刷新时机。

  6. 标准IO底层调用文件IO;嵌入式底层开发经常直接使用文件IO系统调用。

相关推荐
延凡科技1 小时前
从 “人管机器“ 到 “数据管人“:智慧矿山综合管控落地实践
java·后端·struts
海兰1 小时前
【插件】Logbook 插件完全指南(适配 Ubuntu 24.04)
linux·运维·人工智能·ubuntu·agent·openclaw
好评1241 小时前
【Linux】Socket编程TCP
linux·网络·tcp/ip
卓怡学长1 小时前
w180springboot基于Java的悠扬乐器管理
java·spring boot·mysql·spring·maven·intellij-idea
古法安卓2 小时前
Android-高版本 LMKD 源码解析:基于 PSI 的内存压力监控与查杀全流程
android·java·android studio
古法安卓2 小时前
Android-PSI 详解:libpsi 源码解析——116 行代码架起 lmkd 与内核之间的桥
android·java·android studio
用户36533043470942 小时前
深入理解 Java Unsafe:从底层魔法到被淘汰的命运
java
前进吧-程序员2 小时前
从零入门:eBPF 是什么、解决什么问题以及它的历史
linux
一个处女座的程序猿2 小时前
Computer之Server:caddy(可扩展的服务器平台)的简介、安装和使用方法、案例应用之详细攻略
运维·服务器·server·caddy