首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux系统】从 C 语言文件操作到系统调用的核心原理

【Linux系统】从 C 语言文件操作到系统调用的核心原理

作者头像
suye
发布于 2025-05-29 06:43:18
发布于 2025-05-29 06:43:18
18700
代码可运行
举报
文章被收录于专栏:17的博客分享17的博客分享
运行总次数:0
代码可运行

前言

本文将从文件的基本概念出发,先回顾 C 语言中文件操作的常用接口,再逐步过渡到 Linux 系统调用,解析文件描述符、文件打开对象、进程与文件的关系等关键概念。通过代码示例和原理分析,带你揭开 Linux 基础 IO 的神秘面纱,理解操作系统如何管理文件、进程如何与文件交互的底层逻辑。


lesson 15_基础IO

一、共识原理

  • 文件 = 内容 + 属性。
  • 文件分为 打开的文件没打开的文件
  • 打开的文件:谁打开的?进程!—— 本质是研究进程和文件的关系。
  • 没打开的文件:在哪里放着呢?在磁盘上。我们最关注的问题?没有被打开的文件非常多,文件如何被分门别类的放置好(如何存储) —— 我们要快速的进行增删查改 —— 快速找到文件。
  • 文件被打开,必须先被加载到内存!
  • 进程:打开的文件 = 1:n。

小结:操作系统内部,一定存在大量的别打开的文件!—— OS 要不要管理这些被打开的文件呢? —— 怎么管理???—— 先描述,在组织。—— 在内核中,一个被打开的文件都必须有自己的文件打开对象,包含文件的很多属性。struct XXX{文件属性;struct XXX *next};

二、回顾C语言接口

2.1 文件的打开操作

fopen 函数用于打开文件,格式为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
FILE *fopen(const char *path, const char *mode);

path: 文件路径或文件名。如果只有文件名,操作系统会在当前工作目录(cwd)下查找该文件。

mode: 文件打开模式。常见模式有:

  • w: 如果文件已存在,先清空文件再写入。如果文件不存在,创建新文件。
  • a: 以追加模式打开文件,在文件末尾添加内容。

当前路径 (cwd): 每个进程维护一个当前工作目录,操作系统会根据该目录来查找文件。如果路径没有指定,fopen 会使用进程的当前工作路径。

2.2 文件的读取与写入操作

fwrite 用于向文件写入数据。其函数声明为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
  • ptr: 指向要写入数据的指针。
  • size: 每个写入对象的大小。
  • nmemb: 要写入的对象个数。
  • stream:文件流指针

举例使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    FILE *fp = fopen("log.txt", "w");
    if (fp == NULL)
    {
        perror("fopen");
        return errno;
    }
    char* str = "Hello Linux!";
    fwrite(str, strlen(str), 1, fp);
    fclose(fp);
    return 0;
}
  • fwrite的第二个参数是指每个写入对象的大小,strlen函数返回的值是不包含字符串结束标识符,那么我们传参是加一还是不加一呢?加一就代表把\0写入到文件中,那么我们是应该怎么选择呢?这里不妨试一试加一的结果: 注意 log.txt 文件中,字符串的末尾有一个^@,是什么意思呢?实际上这个字符组合是表示\0的ASCII码,所以写入字符串时,使用 strlen 计算字符串长度时,不包括结束符 \0。通常不需要将 \0 写入文件,因为它是 C 语言中的结束标志,而在其它语言中读取文件时,可能不希望看到这些无关的字符。
2.3 三个标准输入输出流

C 程序启动时,会自动打开以下三个标准流:

  • stdin: 标准输入流(通常与键盘连接)。
  • stdout: 标准输出流(通常与显示器连接)。
  • stderr: 标准错误流(通常与显示器连接)。

这三个流都由操作系统和 C 标准库提供,并用于处理程序与外部交互的基本输入输出。

三、过渡到系统,认识文件系统调用

文件其实是在磁盘上的,磁盘是外部设备,访问磁盘文件其实是访问硬件!几乎所有的库只要是访问硬件设备,必定要封装系统调用。

3.1 open 系统调用

open 是一个用于打开文件或创建文件的系统调用,其原型为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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: 打开文件时的标志,例如:
      • O_RDONLY:只读打开。
      • O_WRONLY:只写打开。
      • O_RDWR:读写打开。
      • O_CREAT:文件不存在时创建文件。
      • O_TRUNC:打开文件时清空文件内容。
      • O_APPEND:以追加模式打开文件。
    • mode: 在使用 O_CREAT 时,需要指定新文件的访问权限。
  • 返回值:成功返回文件描述符,失败返回 -1。
1. 比特位标志位示例

通过按位或(|)传递多个标志位,可以在同一次调用中同时指定多个选项。

代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define ONE (1<<0) // 1
#define TWO (1<<1) // 2
#define FOUR (1<<2) // 4
#define EIGHT (1<<3) // 8

void show(int flags)
{
    if(flags & ONE) printf("function1\n");
    if(flags & TWO) printf("function2\n");
    if(flags & FOUR) printf("function3\n");
    if(flags & EIGHT) printf("function4\n");

    return;
}

int main()
{
    printf("--------------------------------------\n");
    show(ONE);
    printf("--------------------------------------\n");
    show(ONE | TWO);
    printf("--------------------------------------\n");
    show(ONE | TWO | FOUR );
    printf("--------------------------------------\n");
    show(ONE | TWO | FOUR | EIGHT);
    printf("--------------------------------------\n");
    return 0;
}

输出示例:

3.2 write 系统调用

write 用于将数据写入文件,其原型为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ssize_t write(int fd, const void *buf, size_t count);
  • 参数说明:
    • fd: 文件描述符。
    • buf: 指向数据缓冲区的指针。
    • count: 要写入的数据字节数。
  • 返回值:实际写入的字节数。
1. 模拟实现 w 选项

模拟 fopenw 模式(清空文件后写入):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    umask(0); // 将权限掩码设置成0000
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); 
    if(fd < 0)
    {
        printf("open file error\n");
        return 1;
    }

    const char* str = "bbb";
    ssize_t ret = write(fd, str, strlen(str));

    close(fd);

    return 0;
}
2. 模拟实现 a 选项

模拟 fopena 模式(追加写):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    umask(0); 
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666); 
    if(fd < 0)
    {
        printf("open file error\n");
        return errno;
    }

    const char* str = "bbb";
    ssize_t ret = write(fd, str, strlen(str));

    close(fd);

    return 0;
}
3.3 read 系统调用

read 用于从文件中读取数据,其原型为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ssize_t read(int fd, void *buf, size_t count);
  • 参数说明
    • fd: 文件描述符。
    • buf: 存储读取数据的缓冲区。
    • count: 缓冲区的大小。
  • 返回值:实际读取的字节数。

四、访问文件的本质

  1. struct file 结构体的作用
  • 当文件被打开时,操作系统为该文件创建一个 struct file 结构体对象,负责管理该文件的元数据和访问信息。
  • 操作系统对文件的管理本质上就是对这些 struct file 结构体对象的管理,它们被组织成一个双链表,保存所有当前打开的文件。
  1. 文件描述符表(files_struct
  • 每个进程都有一个 struct files_struct 类型的对象,它记录了该进程所打开的所有文件的信息。
  • struct files_struct 中有一个文件描述符表,维护了一个 struct file* 类型的数组。数组的下标就是文件描述符,指向进程打开的文件的 struct file 结构体对象。
  1. 文件描述符的分配规则
  • 操作系统会为进程打开的新文件分配一个文件描述符,分配从 3 开始(因为标准输入、输出、错误流占用文件描述符 0、1、2)。
  • 新打开的文件将从进程的文件描述符表中找到最小的未使用下标,作为文件描述符。
  1. FILE 类型在 C 语言中的作用

FILE 是 C 语言库中的封装类型,用于描述文件,它提供了更高层次的文件操作接口。

FILE 类型内部封装了文件描述符,_fileno 字段就是对应的文件描述符,可以通过它来访问底层的文件描述符。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    umask(0); 
    int fd1 = open("log1.txt", O_WRONLY | O_CREAT | O_APPEND, 0666); 
    int fd2 = open("log2.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
    int fd3 = open("log3.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
    int fd4 = open("log4.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
    printf("fd1: %d\n", fd1);
    printf("fd2: %d\n", fd2);
    printf("fd3: %d\n", fd3);
    printf("fd4: %d\n", fd4);

    return 0;
}
  1. 文件的引用计数与关闭
  • 文件可以被多个进程同时打开,struct file 中有一个 f_count 字段来记录文件的引用计数。
  • 当进程关闭文件时,close 系统调用会将文件描述符表中对应位置的内容置为 NULL,减少文件的引用计数。如果引用计数为 0,操作系统会回收该文件对应的资源。
  1. 标准输入、输出和错误流(文件描述符 0, 1, 2)
  • 操作系统会在程序启动时自动打开标准输入(文件描述符 0)、标准输出(文件描述符 1)和标准错误(文件描述符 2)。
  • 这三个文件描述符是预留的,程序中打开的新文件会从文件描述符 3 开始。
  1. 文件描述符的关闭与输出

通过 close 系统调用关闭文件描述符后,进程无法再通过该文件描述符进行文件操作。例如,关闭标准输出(close(1))会导致后续的 printf 输出无法显示,但其他流如标准错误仍然有效。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    close(1); // 将 stdout 关闭
    int ret = printf("stdin->fd: %d\n", stdin->_fileno);
    printf("stdout->fd: %d\n", stdout->_fileno);
    printf("stderr->fd: %d\n", stderr->_fileno);

    fprintf(stderr, "printf ret: %d\n", ret);
    return 0;
}

结语

IO 操作是操作系统的 “血脉”,理解其底层原理不仅能帮助我们写出更健壮的代码,还能为深入学习进程通信、网络编程等高级主题奠定基础。希望本文能成为你探索 Linux 系统编程的一块基石,在后续的学习中,你可以尝试结合实际项目,对比不同 IO 接口的性能差异,或深入分析内核源码中的文件管理逻辑,进一步提升技术深度。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
人工智能免费公开课一网打尽!14个类别、230门课程,GitHub标星6000+
这原本是吴恩达在斯坦福大学开授的课程,课程视频在网络上受到了学习者的广泛好评,后来还推出了专门的网课。
磐创AI
2019/10/25
6250
【AI大咖】认真认识一代AI教父Hinton
Geoffrey Everest Hinton,加拿大认知心理学家和计算机科学家,1947年生人,现年72岁,有两任妻子,两个孩子,被誉为”人工智能教父“。
用户1508658
2019/07/25
9680
【AI大咖】认真认识一代AI教父Hinton
重磅 | 128篇论文,21大领域,深度学习最值得看的资源全在这了(附一键下载)
从全局到枝干、从经典到前沿、从理论到应用、还有最新的研究...,所有你不需要的需要的,现在不需要的未来需要的,你不需要的周边小伙伴需要的...反正全都在这了。拿走不谢,就在AI科技大本营。 整理 | AI科技大本营(rgznai100) 参考 - https://zhuanlan.zhihu.com/p/23080129 对于大多数想上手深度学习的小伙伴来说,“我应当从那篇论文开始读起?” 这是一个亘古不变的话题。 而对那些已经入门的同学来说,了解一下不同方向的论文,也是不时之需。 有没有一份完整的深度
AI科技大本营
2018/04/27
1.9K0
重磅 | 128篇论文,21大领域,深度学习最值得看的资源全在这了(附一键下载)
【每周论文推荐】 初入深度学习CV领域必读的几篇文章
很多朋友都希望我们开通论文推荐和阅读板块,那就开吧,此专栏名为《每周论文推荐》。在这个专栏里,还是本着有三AI一贯的原则,专注于让大家能够系统性完成学习,所以我们推荐的文章也必定是同一主题的。
用户1508658
2019/07/30
1.3K0
【每周论文推荐】 初入深度学习CV领域必读的几篇文章
TensorFlow从1到2 - 0 - 前言
我是黑猿大叔,转战AI的大叔程序猿,你好。 本篇是《TensorFlow从1到2》的前言,本主题将会涵盖现代卷积网络基础,及其TensorFlow实现。 我将延续先前的承诺: 它不会止于翻译和笔记、语
袁承兴
2018/04/11
7970
TensorFlow从1到2 - 0 - 前言
126篇殿堂级深度学习论文分类整理 从入门到应用(上)
█ 如果你有非常大的决心从事深度学习,又不想在这一行打酱油,那么研读大牛论文将是不可避免的一步。而作为新人,你的第一个问题或许是:“论文那么多,从哪一篇读起?” 本文将试图解决这个问题——文章标题本来是:“从入门到绝望,无止境的深度学习论文”。请诸位备好道具,开启头悬梁锥刺股的学霸姿势。 开个玩笑。 但对非科班出身的开发者而言,读论文的确可以成为一件很痛苦的事。但好消息来了——为避免初学者陷入迷途苦海,昵称为 songrotek 的学霸在 GitHub 发布了他整理的深度学习路线图,分门别类梳理了新入门者最
AI研习社
2018/03/29
7860
从图灵奖看人工智能的历史沉浮
五期飞跃计划开始报名,联系小编,获取你的专属算法工程师学习计划(联系小编SIGAI_NO2)
SIGAI学习与实践平台
2019/04/26
8410
从图灵奖看人工智能的历史沉浮
【资源】深度学习 Top100:近 5 年被引用次数最高论文(下载)
【新智元导读】这里是近5年100篇被引用次数最多的深度学习论文,覆盖了优化/训练方法、无监督/生成模型、卷积网络模型和图像分割/目标检测等十大子领域。重要的论文能够超越其应用领域让人获益。新智元在每个领域都选择了一篇论文重点介绍,这将是你纵览深度学习研究绝好的开始。 这里是100篇被引用次数最多的深度学习论文,从海量的相关论文中脱颖而出。无论其应用领域是什么,都值得一读,而在其各自的领域,它们是必读之作。 此前已经有一些很棒的深度学习论文的榜单了,比如说Deep Vision和Awesome Recurre
新智元
2018/03/27
1K0
干货分享 | 深度学习零基础进阶大法!
编者按:新手上路都会有一个疑问,如果自己没有相关基础,如何学习晦涩的专业知识?此前雷锋网编译了《从0到1:我是如何在一年内无师自通机器学习的?》,这篇文章讲述了 Per Harald Borgen 的自学历程。而关于深度学习,GitHub的 songrotek 同样有话要说。原文名为《Deep Learning Papers Reading Roadmap》,雷锋网奕欣及老吕IO整理编译,未经许可不得转载。 0. 深度学习的“圣经” 提到入门级的书,就不得不提这一本 Bengio Yoshua,Ian J.
AI科技评论
2018/03/08
8970
引用次数在15000次以上的都是什么神仙论文?
本文结合总结梳理了知乎上“引用次数在15000次以上的都是什么论文?”这一问题的经典回答,希望能帮助到各位进一步了解领域内的相关进展。并且通过阅读这些经典论文或许也会给您带来不少启发。
1480
2021/07/12
1.2K0
2018图灵奖公布!Hinton、Bengio、LeCun深度学习三巨头共享
据官方公告介绍,因三位巨头在深度神经网络概念和工程上的突破,使得 DNN 成为计算的一个重要构成,从而成为 2018 年图灵奖得主。
机器之心
2019/04/29
9540
2018图灵奖公布!Hinton、Bengio、LeCun深度学习三巨头共享
TensorFlow从1到2 | 前言
本篇是《TensorFlow从1到2》的前言,本主题将会涵盖现代卷积网络基础,及其TensorFlow实现。 我将延续先前的承诺: 它不会止于翻译和笔记、语言和工具,而是坚持通过启发性的方式,循序渐进构建系统化的理解,搭建一个坚实可靠的、连接“零基础”与“AI/机器学习/深度学习”领域之间的缓坡道。 废话少说,本文的剩余部分仍然提供干货。 学习资源 TensorFlow(模型库)(https://www.tensorflow.org/),Google Deep Learning(开源中译版 中文纸质版)(
用户1332428
2018/03/08
6850
赋予人工智能记忆的人,带你梳理深度学习核心算法
作者介绍:Jürgen Schmidhuber 被称为是赋予人工智能记忆的人,递归神经网络之父,2004 年到 2009 年,担任慕尼黑大学认知与机器人领域的教授,从 1995 年起就在瑞士人工智能实验室 IDSIA 担任负责人。2009至2012年年间,他的研究小组赢得了模式识别和机器学习的八个国际比赛。如今 Jürgen Schmidhuber 创办了 Nnaisense 公司。 注:这篇文章经过了很多同僚的评阅。 1960年-2013年深度学习时间线亮点 [A] 1962年:来自简单细胞和复杂细胞的
新智元
2018/03/13
9750
赋予人工智能记忆的人,带你梳理深度学习核心算法
【深度学习Deep Learning】资料大全
  最近在学深度学习相关的东西,在网上搜集到了一些不错的资料,现在汇总一下: Free Online Books Deep Learning66 by Yoshua Bengio, Ian Goodfellow and Aaron Courville Neural Networks and Deep Learning42 by Michael Nielsen Deep Learning27 by Microsoft Research Deep Learning Tutorial23 by LISA lab,
Charlotte77
2018/01/09
6K0
【Richard S. Sutton】谈 The Bitter Lesson(AI 研究中痛苦的教训)
从 70 年的 AI 研究中可以读出的最大教训是,利用计算的一般方法最终是最有效的,而且幅度很大。造成这种情况的最终原因是摩尔定律,或者更确切地说是它对每单位计算成本持续呈指数下降的概括。大多数 AI 研究已经进行,就好像智能体可用的计算是恒定的(在这种情况下,利用人类知识将是提高性能的唯一方法之一),但是,在比典型研究项目稍长的时间里,大量的计算量不可避免地变得可用。为了寻求在短期内产生影响的改进,研究人员试图利用他们对该领域的人类知识,但从长远来看,唯一重要的是利用计算。这两者不需要相互对立,但在实践中它们往往会发生冲突。花在一个上的时间是没有花在另一个上的时间。对一种方法或另一种方法的投资存在心理承诺。人类知识方法往往会使方法复杂化,使其不太适合利用利用计算的一般方法。有很多人工智能研究人员迟来的惨痛教训的例子,回顾一些最突出的例子是有启发性的。
深度强化学习实验室
2022/09/23
2.6K0
【干货荟萃】机器学习&深度学习知识资料大全集(二)(论文/教程/代码/书籍/数据/课程等)
【导读】转载来自ty4z2008(GItHub)整理的机器学习&深度学习知识资料大全荟萃,包含各种论文、代码、视频、书籍、文章、数据等等。是学习机器学习和深度学习的必备品! ty4z2008前言:希望转载的朋友,你可以不用联系我.但是一定要保留原文链接,因为这个项目还在继续也在不定期更新.希望看到文章的朋友能够学到更多.此外:某些资料在中国访问需要梯子. 昨天介绍了第一篇: 【干货荟萃】机器学习&深度学习知识资料大全集(一)(论文/教程/代码/书籍/数据/课程等) 今天第二篇: 《Image Scalin
WZEARW
2018/04/10
1.8K0
刚刚,深度学习“三巨头”共同斩获2018图灵奖!
当地时间3月27日,美国计算机协会(ACM)宣布,把2018年的图灵奖(Turing Award)颁给人工智能科学家Yoshua Bengio,Geoffrey Hinton和Yann LeCun,以表彰他们为当前人工智能的繁荣发展所奠定的基础。
大数据文摘
2019/04/26
1K0
刚刚,深度学习“三巨头”共同斩获2018图灵奖!
人工智能领头人邓力当选加拿大国家工程院院士!
邓力本科毕业于中科大,先后在美国威斯康星大学获硕士和博士学位,之后在加拿大滑铁卢大学任教获得终身正教授,其间还担任麻省理工学院和日本位于京东的 ATR 研究所职位。
新智元
2019/05/08
7600
人工智能领头人邓力当选加拿大国家工程院院士!
【专知荟萃01】深度学习知识资料大全集(入门/进阶/论文/代码/数据/综述/领域专家等)(附pdf下载)
【导读】主题荟萃知识是专知的核心功能之一,为用户提供AI领域系统性的知识学习服务。主题荟萃为用户提供全网关于该主题的精华(Awesome)知识资料收录整理,使得AI从业者便捷学习和解决工作问题!在专知人工智能主题知识树基础上,主题荟萃由专业人工编辑和算法工具辅助协作完成,并保持动态更新!另外欢迎对此创作主题荟萃感兴趣的同学,请加入我们专知AI创作者计划,共创共赢! 今天专知为大家呈送第一篇专知主题荟萃-深度学习知识资料全集荟萃 (入门/进阶/论文/代码/数据/专家等),请大家查看!专知访问www.zhuan
WZEARW
2018/04/09
1.3K0
【专知荟萃01】深度学习知识资料大全集(入门/进阶/论文/代码/数据/综述/领域专家等)(附pdf下载)
资源 | 如何开启深度学习之旅?这三大类125篇论文为你导航(附资源下载)
选自Github 作者:songrotek 机器之心编译 参与:晏奇、黄小天 如果你现在还是个深度学习的新手,那么你问的第一个问题可能是「我应该从哪篇文章开始读呢?」在 Github 上,songrotek 准备了一套深度学习阅读清单,而且这份清单在随时更新。至于文中提到的 PDF,读者们可点击阅读原文下载机器之心打包的论文,或点开下面的项目地址下载自己喜欢的学习材料。 项目地址:https://github.com/songrotek/Deep-Learning-Papers-Reading-Road
机器之心
2018/05/07
7040
推荐阅读
相关推荐
人工智能免费公开课一网打尽!14个类别、230门课程,GitHub标星6000+
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 前言
  • lesson 15_基础IO
    • 一、共识原理
    • 二、回顾C语言接口
      • 2.1 文件的打开操作
      • 2.2 文件的读取与写入操作
      • 2.3 三个标准输入输出流
    • 三、过渡到系统,认识文件系统调用
      • 3.1 open 系统调用
      • 3.2 write 系统调用
      • 3.3 read 系统调用
    • 四、访问文件的本质
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档