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

Linux编程:1、文件编程

一、Linux 文件编程与 C 语言文件编程的区别

特性C 语言 I/O 库函数Linux 文件编程(系统调用)
实现层面用户空间(glibc 库)内核空间(系统调用)
跨平台性跨平台(Windows/Linux)仅限 Linux 系统
性能带用户空间缓冲区,默认高效需手动管理缓冲区,使用得当更快
底层依赖依赖系统调用(如 Linux 的 open)直接操作内核 API

核心差异

  • C 语言函数是系统调用的封装,例如fopen底层调用open
  • 系统调用适合底层控制(如文件锁、内存映射),库函数适合通用文件操作。

二、文件描述符(File Descriptor, FD)

1. 基本概念
  • 作用:Linux 中对文件(含设备)的唯一标识,通过整数(FD)操作文件。
  • 进程关联:每个进程维护独立的 FD 表,记录打开的文件。
2. 预定义描述符
FD宏定义用途
0STDIN_FILENO标准输入
1STDOUT_FILENO标准输出
2STDERR_FILENO标准错误输出
3. 范围与限制
  • 默认范围:0 ~ OPEN_MAX-1
  • 查看系统限制cat /proc/sys/fs/file-max
  • 修改限制(需 root):echo 2048 > /proc/sys/fs/file-max
4. 用法示例
  • Shell 脚本中使用 FD
    FILE *fp = fopen("data.txt", "r");
    int fd = fileno(fp);  // 转换为文件描述符

三、文件操作基础

1. 打开文件(open
  • 函数原型:
    int open(const char *pathname, int flags);          // 无权限模式
    int open(const char *pathname, int flags, mode_t mode); // 带权限模式
  • 头文件#include <fcntl.h>#include <sys/stat.h>
  • 关键参数
    • flags:必选其一(O_RDONLY/O_WRONLY/O_RDWR),可组合其他标志:
      • O_CREAT:文件不存在时创建。
      • O_EXCL:与O_CREAT联用,若文件存在则打开失败。
      • O_APPEND:写入时追加到文件末尾。
    • mode:新文件权限(如0644表示 rw-r--r--),需与O_CREAT配合使用。
  • 返回值:成功返回 FD(非负整数),失败返回 - 1。

示例:创建并读写文件

int fd = open("data.txt", O_RDWR | O_CREAT, 0600); // 0600表示所有者可读写
if (fd < 0) { perror("open failed"); }
2. 创建文件(creat
  • 等价于open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode)
  • 函数原型int creat(const char *pathname, mode_t mode);
3. 关闭文件(close
  • 函数原型int close(int fd);
  • 注意:不关闭可能导致 FD 耗尽,影响后续打开文件。

 四、读写文件

1. 读文件(read
  • 函数原型
    ssize_t read(int fd, void *buf, size_t count);
  • 返回值
    • 成功:实际读取字节数(可能小于count,如文件末尾)。
    • 失败:-1,设置errno
  • 性能优化:避免频繁小尺寸读取,利用页缓存(4KB 为单位)。
2. 写文件(write
  • 函数原型
    ssize_t write(int fd, const void *buf, size_t count);
  • 返回值:成功返回实际写入字节数,失败返回 - 1。
  • 示例:追加写入
    int fd = open("data.txt", O_WRONLY | O_APPEND);
    write(fd, "Hello World!", 12);
3. C 语言与系统调用性能对比
  • 场景:写入 100 万次数据。
    • C 语言(fwrite:用户空间缓冲区优化,耗时约 0.02 秒。
      #include <stdio.h>
      #include <stdlib.h>#define MAX 1000000int main(void)
      {FILE* fp = fopen("data1", "wb");if (fp == NULL) {perror("fopen failed.");exit(1);}for (int i = 0; i < MAX; i++) {fwrite(&i, sizeof(int), 1, fp);}fclose(fp);return 0;
      }

    • 系统调用(write:无用户缓冲区,耗时约 0.18 秒,需手动添加缓冲区优化。
      #include <fcntl.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>#define MAX 1000000int main(void)
      {int fd = open("data2", O_RDWR | O_CREAT | O_TRUNC, 0666);if (fd == -1) {perror("open file failed.");exit(1);}for (int i = 0; i < MAX; i++) {write(fd, &i, sizeof(int));}close(fd);return 0;
      }
    • 原因:
      系统函数在用户层没有缓冲区,在内核层有缓冲区,但是缓冲区很小。所以大量数据在写入文件时,频繁刷新缓冲区降低了写入速率。
      缓冲区满了才真正写入) 所以系统函数(如 write)需要加自定义缓冲区以提高速率, 而标准 C 函数(如 fwrite)在用户层有默认的缓冲区,所以案例一比案例二更快
  • 改进系统调用的代码:
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>#define MAX 1000000
    #define BUFF_SIZE 512int main(void)
    {int fd = open("data2", O_RDWR | O_CREAT | O_TRUNC, 0666);if (fd == -1) {perror("open file failed.");exit(1);}int buffer[BUFF_SIZE];for (int i = 0; i < MAX; i++) {buffer[i % BUFF_SIZE] = i;if ((i + 1) % BUFF_SIZE == 0) {write(fd, buffer, sizeof(buffer));}}close(fd);return 0;
    }

结论:在使用系统函数接口时,如果自定义了一个合适的缓冲区,会使速度显著提升


五、文件偏移量与定位(lseek

  • 作用:调整文件读写位置(字节偏移量)。
  • 函数原型
    off_t lseek(int fd, off_t offset, int whence);
  • whence参数
    • SEEK_SET:从文件开头偏移offset
    • SEEK_CUR:从当前位置偏移offset(可正可负)。
    • SEEK_END:从文件末尾偏移offset(通常为负数)。
  • 示例:修改文件指定位置内容
    int fd = open("test.txt", O_RDWR);
    lseek(fd, 5, SEEK_SET);  // 定位到第6字节(索引从0开始)
    write(fd, "ABC", 3);     // 覆盖写入

 六、文件状态与元数据(stat系列函数)

  • 作用:获取文件类型、权限、大小、时间戳等信息。
  • 相关函数
    函数参数说明
    stat路径名获取文件或符号链接指向的文件信息
    lstat路径名获取符号链接本身的信息
    fstat文件描述符通过 FD 获取文件信息
  • 关键结构体
    struct stat {mode_t st_mode;   // 文件类型(如`S_ISDIR`判断目录)off_t st_size;    // 文件大小(字节)ino_t st_ino;     // inode节点号(唯一标识)time_t st_mtime;  // 最后修改时间
    };

示例:判断文件类型 

struct stat status;
stat("test.txt", &status);
if (S_ISREG(status.st_mode)) { printf("普通文件\n"); }

七、硬链接与软链接

1. 硬链接(Hard Link)
  • 本质:多个文件名指向同一 inode,共享物理数据。
  • 特点
    • 不能跨文件系统,不能链接目录。
    • 删除任意硬链接不影响其他链接,仅当所有硬链接删除后文件才被删除。
  • 创建命令ln <源文件> <链接名>
2. 软链接(符号链接,Symbolic Link)
  • 本质:独立文件,存储目标文件路径。
  • 特点
    • 可跨文件系统,可链接目录。
    • 源文件删除后成为 “死链接”,访问时报错。
  • 创建命令ln -s <源文件或目录> <链接名>
3. 对比表格
特性硬链接软链接
文件类型与源文件相同(普通文件)特殊文件(类型为l
跨文件系统不支持支持
源文件删除影响无(仅硬链接数减 1)失效(死链接)
存储空间共享 inode,不占用额外空间存储路径,占用少量空间

八、文件锁机制

1. 作用

避免多进程并发访问文件导致的数据竞争,分为建议性锁强制性锁

2. 核心函数(fcntl
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>int fcntl(int fd, int cmd, struct flock *lock);
  • cmd参数
    • F_SETLK:设置锁(非阻塞,失败直接返回)。
    • F_GETLK:查询锁状态。
  • struct flock结构体
    struct flock {short l_type;   // 锁类型(F_RDLCK读锁/F_WRLCK写锁/F_UNLCK解锁)off_t l_start;  // 偏移量(配合l_whence)off_t l_len;    // 加锁长度(0表示到文件末尾)
    };
3. 建议性锁(默认)
  • 特点:不强制阻止访问,需进程主动检查锁状态。
  • 示例:检测写锁
    struct flock lock = {.l_type = F_WRLCK, .l_whence = SEEK_SET};
    fcntl(fd, F_GETLK, &lock);
    if (lock.l_pid != -1) { printf("文件被进程%d锁定\n", lock.l_pid); }
4. 强制性锁
  • 启用条件
    1. 挂载文件系统时添加mand选项:sudo mount -o remount,mand /
    2. 设置文件权限为g+s且取消组执行权限:chmod g+s,g-x test.txt
  • 特点:不兼容的操作会被阻塞(如读锁存在时写操作阻塞)。

九、内存映射(mmap

1. 作用

将文件数据映射到进程地址空间,直接通过指针操作文件,减少内核与用户空间的数据拷贝。

2. 核心函数
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length); // 解除映射
  • 关键参数
    • prot:权限(如PROT_READ | PROT_WRITE)。
    • flags
      • MAP_SHARED:修改会写回文件,可被其他进程共享。
      • MAP_PRIVATE:修改仅在当前进程可见,不影响原文件。
    • offset:必须为 4KB(4096 字节)的整数倍。
3. 优势与场景
  • 优势
    • 减少read/write系统调用的拷贝开销(仅 1 次拷贝)。
    • 简化随机访问(直接操作指针)。
  • 适用场景
    • 大文件随机读写。
    • 多进程共享文件数据(通过MAP_SHARED)。
  • 缺点
    • 不适合频繁写操作或变长文件。

 4. 示例:读取文件内容

int fd = open("test.txt", O_RDONLY);
off_t len = lseek(fd, 0, SEEK_END);
char *buf = (char*)mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
printf("%s", buf); // 直接打印映射内容
munmap(buf, len);

十、关键快捷键与命令

  • man命令:查看帮助文档
    • man 2 open:查看系统调用open的手册。
    • man -f open:查看open所属章节。
  • man快捷键
    • Space:下翻一页;b:上翻一页;/字符串:搜索。
http://www.xdnf.cn/news/12424.html

相关文章:

  • AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年6月6日第100弹
  • pdf2zh 简明本地部署和api调用,以及离线部署总结
  • 行业案例 | ASOS 借助 Azure AI Foundry(国际版)为年轻时尚爱好者打造惊喜体验
  • 在Windows下利用LoongArch-toolchain交叉编译Qt
  • QuaggaJS用法详解
  • 分布式协同自动化办公系统-工作流引擎-流程设计
  • aardio 简单网页自动化
  • 命令行以TLS/SSL显式加密方式访问FTP服务器
  • 应用分享 | 精准生成和时序控制!AWG在确定性三量子比特纠缠光子源中的应用
  • http头部注入攻击
  • MySQL基础(二)SQL语言、客户端工具
  • 中国首套1公里高分辨率大气湿度指数数据集(2003~2020)
  • 服务器健康摩尔斯电码:深度解读S0-S5状态指示灯
  • ADI的BF609双核DSP怎么做开发,我来说一说(五)LAN口测试
  • 在.NET Core控制器中获取AJAX传递的Body参数
  • 【行驶证识别成表格】批量OCR行驶证识别与Excel自动化处理系统,行驶证扫描件和照片图片识别后保存为Excel表格,基于QT和华为ocr识别的实现教程
  • AI大模型学习三十三、HeyGem.ai 服务端(ubuntu)docker 安装 /客户端(win)分离部署
  • 【Linux】虚拟机代理,自动化脚本修改~/.bashrc
  • GAN生成模型评价体系:从主观感知到客观度量的技术演进
  • LeetCode 2434.使用机器人打印字典序最小的字符串:贪心(栈)——清晰题解
  • 推荐算法八股总结
  • 区块链可投会议CCF A--SP 2026 截止11.13 附录用率
  • mitmproxy 爬虫,下载自己的博客图片
  • 【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
  • 5.2 HarmonyOS NEXT应用性能诊断与优化:工具链、启动速度与功耗管理实战
  • transformer和 RNN以及他的几个变体区别 改进
  • 【Redis】分布式锁的介绍与演进之路
  • Windows系统中如何使用符号链接将.vscode等配置文件夹迁移到D盘(附 CMD PowerShell 双版本命令)
  • 人机融合智能 | “人智交互”跨学科新领域
  • MAX3490