[Linux]文件/文件描述符fd

news2024/11/16 23:41:49

一、关于文件

文件=内容+属性

  • 那么所有对文件的操作,就是对内容/属性操作。
  • 内容是数据,属性也是数据,那么存储文件,就必须既存储内容数据,又存储属性数据。默认就是在磁盘中的文件。
  • 当进程访问一个文件时,都需要先把文件打开。对于普通的磁盘文件,“打开”就是将文件加载到内存。
  • 一个进程可以通过操作系统(操作系统提供系统调用接口)打开多个文件,多个进程可以通过操作系统打开多个文件。加载到内存中被打开的文件可能会存在多个。(进程:打开的文件 = 1 : n
  • 加载磁盘上的文件,一定会涉及访问磁盘设备,是由操作系统来做的。

操作系统在运行时,可能会打开很多文件,那么,操作系统该如何对文件进行管理呢?

一个文件要被打开,一定要在内核中先形成被打开的文件对象,例如:

struct file
{
    //文件的属性
    struct file* next;
    ...
}

文件的属性与文件内容一样,在磁盘中同样有保存。

我们使用链表对这些对象进行管理。那么,对于文件的管理,就转换成了对链表的增删查改。

被打开的文件在内存中,未被打开的文件在磁盘中。 所有的文件操作,都是在内存中操作的。

因此,研究文件操作的本质就是研究进程和被打开文件的关系。

二、常见文件接口(C语言)

1、fopen

我们可以在Linux中使用man来查看fopen的手册(man fopen):

打开的模式:

以“w”方式打开为例:

当文件 以“w”方式打开时,若该文件不存在,则自动创建文件。

打开文件时,会先清空文件内容。

#include<stdio.h>


//以"w"打开,若文件不存在则自动创建该文件
int main()
{
  FILE* fp = fopen("log.txt","w");
  if(NULL == fp)
  {
    perror("fopen");
    return 1;
  }

  const char* s = "hello world\n";
  int i = 0;
  for(i = 0; i < 10; i++)
  {
    fputs(s,fp);
  }
   
  fclose(fp);

  return 0;
}

在执行该程序之前,当前目录下只有两个文件。 

make会产生一个myfile.c的可执行文件myfile。

执行该程序后,当前目录下出现了一个新文件:log.txt。

我们可以看到此时该log.txt文件中,就写入了我们程序中设定的内容。

我们对该C程序进行修改:

#include<stdio.h>


//以"w"打开,若文件不存在则自动创建该文件
int main()
{
  FILE* fp = fopen("log.txt","w");
  if(NULL == fp)
  {
    perror("fopen");
    return 1;
  }
  

  fputs("aaa\n",fp);
  fclose(fp);

  return 0;
}

重复执行上述内容(注意:我们的log.txt文件并未删除,仍旧存在),我们再次查看log.txt中的内容:

可以看出,之前写入的内容已经被清空。

注:我们使用“> log.txt”也可以实现对该文件进行清空。“>”是重定向,向文件写入。要向文件写入,就一定要打开文件。此时文件内容就会被清空。

 

以"a"方式打开为例:

append:追加。

“a”方式也是写入,只不过是从文件结尾进行写入。

我们将myfile.c文件进行修改:

#include<stdio.h>

int main()
{
  FILE* fp = fopen("log.txt","a");
  if(NULL == fp)
  {
    perror("fopen");
    return 1;
  }

  fputs("bbbbbbbbb\n",fp);
  fclose(fp);

  return 0;
}

运行该程序,我们可以看出,原先log.txt文件中的内容并未变化,我们是在此基础上追加了内容。

我们可以不断执行,不断追加。

 注:我们使用“echo “cc” >> log.txt”也可以实现追加。

2、

三、系统调用接口

由于打开文件的操作只能由操作系统来完成,所以我们C语言中打开文件的接口,底层一定是封装了系统调用接口的。

1、open

第一个参数是文件名称(不带路径默认当前路径下)。

第二个参数是flags:

第三个参数表示我们要以什么权限来创建新文件。

返回值:

 file descriptor文件描述符:fd

模拟fopen("filename","w"):

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>


int main()
{
  //umask(0);//权限掩码改为0 
  //O_TRUNC:将打开的文件的长度清零(清空)
  int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
  if(fd < 0)
  {
    perror("open");
    return 1;
    
  }
 
  const char* s = "abc\n";
  write(fd, s, strlen(s));


  close(fd);
  
  return 0;
}

模拟fopen("filename","a")  :

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>


int main()
{
  //O_APPEND:在文件末尾进行写入(追加)
  int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
  if(fd < 0)
  {
    perror("open");
    return 1;
    
  }
 
  const char* s = "abc\n";
  write(fd, s, strlen(s));


  close(fd);
  
  return 0;
}

四、fd:文件描述符

 返回值类型为int,那么fd究竟是什么?

我们可以输出fd观察:

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>




int main()
{
  int fd1 = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
  int fd2 = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
  int fd3 = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
  int fd4 = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);

  printf("fd1:%d\nfd2:%d\nfd3:%d\nfd4:%d\n",fd1,fd2,fd3,fd4);
  

  close(fd1);
  close(fd2);
  close(fd3);
  close(fd4);

  return 0;
}

可以看到,fd是连续的整数。 

当进程打开文件时,操作系统会给每一个文件创建文件对象(被打开文件的描述结构体对象),并对它们使用链表进行维护。

操作系统需要对进程(task_struct)和打开的文件(struct file)之间的对应关系进行维护,就产生了struct files_struct。而task_struct中包含:struct files_struct* files,该指针就指向struct files_struct

struct files_struct这个结构体中,包含了一个数组:struct file* fd_array[]。该数组中,存放struct file的地址。

因此,当进程打开文件时,操作系统会创建struct file对象,并在该数组:struct file* fd_array[]中查询可使用的下标,将struct file对象的地址填入到该下标的位置。然后将该数组的下标返回,我们将该数字称为文件描述符。我们将struct file* fd_array[]称为:进程文件描述符表。

因此,文件描述符fd本质就是数组的下标。

那么,为什么我们显示打印的fd是从3开始的呢?

这是因为进程在运行的时候,默认打开:

标准输入:键盘                stdin                   0

标准输出:显示器             stdout                1

标准错误:显示器             stderr                 2

 stdin/stdout/stderr都是FILE*类型。FILE是一个结构体,其中封装了文件描述符。

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>

int main()
{

  printf("stdin->fd:%d\n",stdin->_fileno);
  printf("stdout->fd:%d\n",stdout->_fileno);
  printf("stderr->fd:%d\n",stderr->_fileno);



  return 0;
}

 那么,我们为什么要把stdin/stdout/stderr打开呢?

这是为了能够让我们默认进行输入输出代码编写。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1697860.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

知识分享:隔多久查询一次网贷大数据信用报告比较好?

随着互联网金融的快速发展&#xff0c;越来越多的人开始接触和使用网络贷款。而在这个过程中&#xff0c;网贷大数据信用报告成为了评估借款人信用状况的重要依据。那么&#xff0c;隔多久查询一次网贷大数据信用报告比较好呢?接下来随小易大数据平台小编去看看吧。 首先&…

YOLOV5 改进:替换backbone为EfficientNet

1、介绍 本章将会把yolov5的主干网络替换成EfficientNet V2,这里直接粘贴代码 详细的可以参考之前的内容:YOLOV5 改进:替换backbone(MobileNet为例)_yolov5主干网络更换为mobile-CSDN博客 更多的backbone更换参考本专栏: YOLOV5 实战项目(训练、部署、改进等等)_听风吹…

2024电工杯B题平衡膳食食谱的优化设计及评价原创论文分享

大家好&#xff0c;从昨天肝到现在&#xff0c;终于完成了2024电工杯数学建模B题的完整论文啦。 实在精力有限&#xff0c;具体的讲解大家可以去讲解视频&#xff1a; 给大家看一下目录吧&#xff1a; 目录 摘 要&#xff1a; 10 一、问题重述 14 二&#xff0e;问题分析 …

2024.05.26 第 399 场周赛

Leetcode 第 399 场周赛 优质数对的总数 I Leetcode 优质数对的总数 I 给你两个整数数组 nums1 和 nums2&#xff0c;长度分别为 n 和 m。同时给你一个正整数 k。 如果 nums1[i] 可以被 nums2[j] * k 整除&#xff0c;则称数对 (i, j) 为 优质数对&#xff08;0 < i < n…

航运复兴?大摩不信!

大摩认为&#xff0c;从供需关系来看红海危机只是推迟了航运业下行周期的到来&#xff0c;一旦干扰消除&#xff0c;行业可能重回周期性低迷。 红海危机加剧运力紧张&#xff0c;航运市场价格飞涨。 大摩在24日的一份报告中指出&#xff0c;受红海危机干扰航运市场运力&#…

加密与安全_AES RSA 密钥对生成及PEM格式的代码实现

文章目录 RSA&#xff08;非对称&#xff09;和AES&#xff08;对称&#xff09;加密算法一、RSA&#xff08;Rivest-Shamir-Adleman&#xff09;二、AES&#xff08;Advanced Encryption Standard&#xff09; RSA加密三种填充模式一、RSA填充模式二、常见的RSA填充模式组合三…

Python小游戏——俄罗斯方块

文章目录 项目介绍环境配置代码设计思路1.初始化和导入库&#xff1a;2.定义颜色和屏幕尺寸&#xff1a;3.定义游戏逻辑&#xff1a;4.游戏循环&#xff1a; 源代码效果图 项目介绍 俄罗斯方块游戏是一款经典的益智游戏&#xff0c;玩家通过旋转和移动各种形状的方块&#xff…

页面<html>上多了一个滚动条,定位发现是<body>里面多了一个id为trans-tooltip的div

现象分析&#xff1a; 页面根标签html多了一个滚动条&#xff0c;发现body里面多了一个id为trans-tooltip的div&#xff0c;虽然width为0&#xff0c;height为0&#xff0c;但是其子元素还是有高度&#xff0c;占据了空间&#xff0c;最终导致了滚动条&#xff1b; 根本原因&…

怎么在pyqt中显示matplotlib的绘图?

想要在pyqt中显示matplotlib的绘图&#xff0c;在绘图时&#xff0c;其实不必使用以下语句&#xff1a; matplotlib.use("Qt5Agg") # 声明使用QT5最关键的语句是&#xff1a; from matplotlib.backends.backend_qt5agg import FigureCanvasQTAggFigureCanvasQTAgg…

Selenium 自动化测试工具<2>(Selenium 常用API的使用方法)

文章目录 浏览器操作浏览器最大化设置浏览器的大小浏览器的前进和后退操作浏览器滚动条 键盘事件单个按键用法键盘组合键用法 鼠标事件不同窗口搜索定位一组元素定位多层框架下拉框定位alert、confirm、prompt 的处理上传文件操作自动截屏 继上一篇文章对 Selenium API 的使用&…

HTML蓝色爱心

目录 写在前面 HTML入门 完整代码 代码分析 运行结果 系列推荐 写在后面 写在前面 最近好冷吖&#xff0c;小编给大家准备了一个超级炫酷的爱心&#xff0c;一起来看看吧&#xff01; HTML入门 HTML全称为HyperText Markup Language&#xff0c;是一种标记语言&#…

Linux(六)

Linux&#xff08;六&#xff09; 自定义头文件自定义头文件中写什么如何引入头文件条件编译条件编译作用 gcc工作原理Make 工作管理器什么是Make什么是Makefile/makefileMakefile假目标Makefile中的变量自定义变量预定义变量自动变量 Makefile中变量展开方式递归展开方式简单展…

【Python】 如何使用.whl文件安装Python包?

基本原理 在Python的世界中&#xff0c;.whl文件是一种分发格式&#xff0c;它代表“Wheel”。Wheel是一种Python包格式&#xff0c;旨在提供一种快速、可靠且兼容的方式&#xff0c;用于安装Python库。与源代码包相比&#xff0c;Wheel文件是预编译的&#xff0c;这意味着它们…

【2024.5.26 软件设计师】记录第一次参加软考(附资料)

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎 ❤️关注 &#x1f44d;点赞 &#x1f64c;收藏 ✍️留言 文章目录 前言考试分析选择题案例分析题话外 软考总结资料 前言 这是我第一次参加软考&#xff0c;其实我并…

家乡特色|基于SprinBoot+vue的家乡特色推荐系统(源码+数据库+文档)

家乡特色推荐系统 目录 基于SprinBootvue的家乡特色推荐系统 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&…

京东Java社招面试题真题,最新面试题

Java中接口与抽象类的区别是什么&#xff1f; 1、定义方式&#xff1a; 接口是完全抽象的&#xff0c;只能定义抽象方法和常量&#xff0c;不能有实现&#xff1b;而抽象类可以有抽象方法和具体实现的方法&#xff0c;也可以定义成员变量。 2、实现与继承&#xff1a; 一个类…

SELINUX=enforcing时无法启动httpd服务的解决方案(semanage命令以及setroubleshoot-server插件的妙用)

一、问题描述&#xff1a; 当/etc/selinux/conf被要求必须是SELINUXenforcing&#xff0c;不被允许使用setenforce 0宽松模式 我们启动httpd就会报错&#xff1a; Job for httpd.service failed because the control process exited with error code. See "systemctl s…

STM32-GPIO八种输入输出模式

图片取自 江协科技 STM32入门教程-2023版 细致讲解 中文字幕 p5 【STM32入门教程-2023版 细致讲解 中文字幕】 https://www.bilibili.com/video/BV1th411z7sn/?p5&share_sourcecopy_web&vd_source327265f5c70f26411a53a9226af0b35c 目录 ​编辑 一.STM32的四种输…