Linux文件系统——文件描述符

news2024/12/26 13:53:48

在这里插入图片描述

文章目录

    • 0. 前言
    • 1. C文件接口
      • 文件打开
      • 文件写入
    • 2. 系统文件接口
      • open && write && close
      • open的返回值

本章gitee代码仓库:文件描述符

0. 前言

基础原理知识:

  1. 文件 = 内容 + 属性

  2. 文件分为:打开的文件(本章重点讲解)和没打开文件

    打开的文件,本质上是进程将其打开

    没打开的文件,存储在磁盘

  3. 要访问这个文件,根据冯诺依曼体系结构,这个文件必须先加载到内存当中,文件又是内容+属性,那么第一步肯定是要先将文件的属性先加载进来,然后根据要不要对这个文件进行读取修改,从而决定是否将内容加载。

  4. 一个进程可以打开多个文件,操作系统内部一定是存在者大量的被打开的文件,操作系统要对这些文件进行管理,肯定也是先描述,再组织。在内核当中,一个被打开的文件,都必须有自己的打开对象,这里面包含了文件的很多属性。而Linux内核是用C语言写的,所以描述这个文件,用的是struct结构体:

    struct xxx
    {
        文件属性;
        struct xxx *next;
    }
    

    这样对文件的管理就变成了对链表的增删查改。

1. C文件接口

文件打开

打开文件的接口fopen,头文件也是stdio.h

#include<stdio.h>
#include<unistd.h>
int main()
{
  printf("pid:%d\n",getpid());
  FILE *fp = fopen("log.txt","w");
  if(fp == NULL)
  {
    perror("fopen");
    return 1;
  }
  fclose(fp);
  sleep(1000);
  return 0;
}

如果不指定文件路径,则默认在当前工作目录创建或者访问这个文件

这个工作目录就是该进程的工作目录,指令ll /proc/进程pid,可查看进程的工作目录,cwdcurrent working directory

image-20231105143414654

如果我们修改当前进程的工作目录,则这个创建的文件,则会创建到修改的工作目录里面

chdir("/home/Pyh")

image-20231105145010310

文件写入

这里采用fwrite作为演示

const char *str = "hello linux\n";
fwrite(str,strlen(str),1,fp);

这里的strlen(str),不需要再加上1('\0'),因为字符串末尾加'\0'是C语言的规定,而这个和文件并没有关系,文件只关心这个内容

image-20231105151715317

我们这里发现,这里写入的时候,都会将文件的内容进行清空然后再写入,而>这个重定向符号,也是会将文件进行清空再写入。

这本质上就因为"w"操作,会将原始文件的内容进行清空再写入,所以>打开文件的方式肯定是"w" 方式。

如果想要对文件不进行清空,那么可以采用"a"操作进行打开a就就是append追加,而追加重定向>>,就是以"a"方式打开文件。

image-20231105152500697

C语言程序在启动的时候,会默认打开三个输入输出流(文件):

  • stdin:标准输入,键盘文件
  • stdout:标准输出,显示器文件
  • stderr:标准错误,显示器文件

image-20231105153137123

那么我们可以直接向这些文件里面写入,例如:

fwrite(str,strlen(str),1,stdout);

image-20231105153706841

2. 系统文件接口

文件是存储在磁盘上的,而磁盘是外设,所以访问文件就是访问硬件。而硬件是在操作系统之下,是被操作系统管理的,而我们的普通用户,是没有资格去直接访问硬件的,所以需要通过操作系统提供的接口来访问。而这就是系统调用,C语言的库函数,类似printffprintffscanffwrite这基本上都封装了系统调用。

open && write && close

在系统调用中,我们可以采用open接口打开文件

image-20231105161959113

int main()
{
    umask(0);	//修改权限掩码
    int fd = open("log.txt",O_WRONLY|O_CREAT,0666);	//读取文件|创建,0666设置权限
    if(fd < 0)	//创建失败返回-1
    {
    perror("open file error\n");
    return 1;
    }
    return 0;
}

因为涉及到创建文件,所以权限是必须要告诉系统的,不然会出现乱码。

image-20231105170001778

这里我们还修改了权限掩码,具体知识,之前在此篇文章讲到过,有兴趣可查看——Linux权限

  • O_RONLY:只读打开
  • O_WRONLY:只写打开
  • O_RDWR:读写打开

上面这三个必须指定一个且只能指定一个

  • O_CREAT:若文件不存在则创建,但必须要使用mode选项,指定文件访问的权限
  • O_APPEND:追加写入
  • O_TRUNC:清空文件内容

有了这些组合,我们就能推断出C语言库函数中的这些"w""a"封装的是哪些

image-20231105174326925

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

int main()
{
  umask(0);
  //int fd = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  int fd = open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
  if(fd < 0)
  {
    perror("open file error\n");
    return 1;
  }
  const char *str = "abc";
  write(fd,str,strlen(str));
  close(fd);                                                                          
  return 0;                          
} 

image-20231105174724562

open的返回值

在操作系统内部描述一个被打开的文件,会直接或者间接包含这些信息:

  • 在磁盘的位置
  • 文件的基本属性
  • 文件的内核缓冲区信息
  • 文件结构体指针struct file *next

在进程的task_struct结构体中,里面文件结构体指针struct files_struct *files,它指向了一个文件描述符表struct files_struct,这个表里面包含一个指针数组struct file *fd_array[],它里面存放的就是struct file *

当一个文件打开的时候,系统会创建一个文件对象,进程指向的文件描述表里面,就会给它分配一个没有占用的下标来指向这个对象。

所以这个open的返回值,其实就是返回这个数组的下标。

image-20231105181356011

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

int main()
{
  umask(0);
  //int fd = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  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);
  if(fd1 < 0)
  {
    perror("open file error\n");
    return 1;
  }
  const char *str = "abc";
  write(fd1,str,strlen(str));
  printf("fd1:%d\n",fd1);
  printf("fd2:%d\n",fd2);
  printf("fd3:%d\n",fd3);
  printf("fd4:%d\n",fd4);
  close(fd1);
  return 0;
}

我们发现这个open返回的就是这个文件描述符对应数组的下标

image-20231107134332685

但是这里是从3开始的,这就是以为C语言程序会默认打开三个输入输出流文件,这里我们也可以验证一下

C语言FILE这个结构体就包含了这个文件描述符

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

image-20231107145217515

我们发现,这三个输入输出流的文件描述符分别是0、1、2

这里程序启动打开三个输入输出流,并不单是C语言的特性,而是操作系统的特性

因为在计算机开机的时候,我们的屏幕和键盘就被默认打开了,所以我们启动进程的时候,只需要将它们的文件对象地址填到进程的pcb里面

我们再来看一个现象:

close(1);
int n = printf("hello linux\n");
fprintf(stderr,"printf %d\n",n);

image-20231107163630810

我们这里已经关闭的了标准输出,可是这里显示器上还是输出了信息,这是因为每个文件结构体对象中都包含了一个count引用计数,标准输出和标准错误都是指向的显示器文件,这里的计数就是2,所以我们close(1)其实是将该文件计数减一,然后再将1号位置置空,如果这个这个count计数不为零,则不管它,如果为零了,则系统回收这个文件对象。

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

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

相关文章

python使用pysqlcipher3对sqlite数据库进行加密

python对很多项目都需要对sqlite数据库的数据进行加密&#xff0c;最流行的加密方式是使用pysqlcipher3&#xff0c;当前使用的python版本为3.7&#xff0c;本博文是直接使用pysqlcipher3在项目上的应用&#xff0c;使用的是已编译好的pysqlcipher3包&#xff0c;如果你需要pys…

6个常用的小程序UI组件库,大厂设计师都在用!

UI组件库是设计系统的一部分&#xff0c;在一般的页面设计过程中可以直接用来构建交互界面。因此&#xff0c;一个有效的小程序UI组件库可以帮助设计师快速掌握基本的交互框架&#xff0c;提高设计师的工作效率&#xff0c;让设计师有更多的时间打磨和提高小程序的整体效果。小…

Softing新版HART多路复用器现支持图尔克excom和西门子ET 200iSP等远程I/O

Softing工业自动化最近升级了用于访问配置和诊断数据的smartLink SW-HT软件&#xff0c;现在该软件可支持访问图尔克excom和西门子ET 200iSP等远程I/O。 &#xff08;smartLink SW-HT支持访问配置和诊断数据&#xff09; 越来越多的新型远程I/O选择使用以太网来替代PROFIBUS连接…

微信小程序将后端返回的图片文件流解析显示导页面

说明 由于请求接口后端返回的图片格式不是一个完整的url,也不是其他直接能显示的图片格式&#xff0c;是一张图片 后端根据模板与二维码生成图片,返回二进制数据 返回为文件流的格式,用wx.request请求的时候&#xff0c;就自动解码成为了下面这样的数据数据格式,这样的数据没…

【无代码】【VR开发】【Unity】【VRTK】4-导入VRTK Tilia Package

【导入VRTK V4】 VRTK的Tilia Package包含了一整套空间开发方案。导入后你可以在PackageManager中看到它们。 所有的Tilia包都可以在如下页面找到: https://www.vrtk.io/tilia.html Tilia包有一个安装器,可以让你仅仅安装需要的包。包的种类很多,按照适用方向分类。 点击H…

Redis中的渐进式遍历-Scan命令

之前我们学习过遍历命令keys,而keys *是一次性的把整个redis中所有的key都获取到.在不知道当前redis中有多少key的情况下,这个操作是非常危险的,可能会一下子得到太多的key而阻塞redis服务器.从而使其他redis客户端卡顿. 通过渐进式遍历,就可以做到,既可以获取到所有的key,同时…

python链队_队列的链式存储结构

队列是一种先进先出&#xff08;first in first out,FIFO&#xff09;的线性表&#xff0c;是一种常用的数据结构。 它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;和栈一样&…

Elasticsearch:在 ES|QL 中使用 DISSECT 和 GROK 进行数据处理

目录 DISSECT 还是 GROK&#xff1f; 或者两者兼而有之&#xff1f; 使用 DISSECT 处理数据 Dissect pattern 术语 例子 DISSECT 关键修饰符 右填充修饰符 (->) 附加修饰符 () 添加顺序修饰符&#xff08; 和 /n&#xff09; 命名的跳过键&#xff08;&#xff1f…

895. 最长上升子序列

题目&#xff1a; 895. 最长上升子序列 - AcWing题库 思路&#xff1a;dp 代码&#xff1a; #include<iostream> #include<cstdio> #include<cmath> using namespace std; typedef long long ll; const int N1010; int f[N];//表示以i结尾的最大上升子序列…

ESP32 未来能够取代 STM32吗?

今日话题&#xff0c;ESP32 未来能够取代 STM32吗&#xff1f;ESP32和STM32各自有其特点和优势&#xff0c;能否取代彼此取决于具体应用和需求。STM32的流行除了性价比外&#xff0c;还有其强大的开发环境&#xff0c;例如Cubemx能够快速生成代码&#xff0c;使得上手STM32的速…

ros1 自定义Publisher消息编程实现示例

整理步骤 cd进入工作空间下的代码空间, 创建功能包&#xff0c;并配置依赖 在功能包里面的代码空间里编写C代码文件 在cmakelist文件里面配置编译规则 cd到工作空间&#xff0c;编译工作空间&#xff0c;source设置环境变量 打开roscore, 运行海龟仿真节点&#xff0c;运行功能…

简析安科瑞无功补偿在化工企业设计与应用

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;随着各种扩产、技术改造等活动的发展&#xff0c;化工企业用电设备功率大、能耗高&#xff0c;导致用电设备增加、负荷增加、负荷性质发生变化&#xff0c;加上线路损耗和变压损耗等因素。整个企业电网的功率…

Mysql进阶-视图篇

介绍 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#xff0c;视图只保存了查询的SQL逻辑&#xff0c;不保存查询结果。…

C#解析XML并反序列化为Model的方法

虽然现在json大行其道&#xff0c;但是xml格式依旧占据着广阔的编程世界&#xff0c;不管光伏锂电激光卫星汽车等等工业领域&#xff0c;基本上都是以xml为主&#xff0c;广大的.NET开发人员有很多被xml折磨的都要转java了&#xff0c;这篇小作文就来玩一种迅速完成xml到model的…

【接口测试】最全Jmeter跨线程调用变量+签名接口测试实战(超细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 跨线程调用变量 …

Android Studio(项目收获)

取消按钮默认背景色 像按钮默认背景色为深蓝色&#xff0c;即使使用了background属性指定颜色也不能生效。 参考如下的解决方法&#xff1a; 修改/res/values/themes.xml中的指定内容如下&#xff1a; <style name"Theme.TianziBarbecue" parent"Theme.Mater…

4-Tomcat服务器及标准Web项目结构

4-Tomcat服务器及标准Web项目结构 文章目录 4-Tomcat服务器及标准Web项目结构Web服务器组成Web项目的标准结构根目录下可以放置的文件及文件夹WEB标准结构Web的url组成部分和项目中资源的对应关系 Tomcat服务器部署Tomcat部署Web项目的三种方式IDEA部署项目APP到Tomcat的方式概…

响应式编程-基本介绍

响应式编程-基本介绍 什么是响应式编程 响应式宣言&#xff08;The Reactive Manifesto&#xff09; 对响应式系统进行了定义&#xff1a;响应式系统是具备以下特质即时响应性&#xff08;Responsive&#xff09;、回弹性&#xff08;Resilient&#xff09;、弹性&#xff08…

Freeswitch API调用方式

1.API调用方式 可以复制下面内容成.bat文件直接在windows下运行&#xff0c;修改成对应的ip加端口。 echo off SETLOCAL :_starting cls set inputecho echo 1 add agent 21009 11 uuid_check 21009 21 list users 31 check modules …

JAVA中类和对象的认识

1、面向对象的初步认知 1.1 什么是面向对象 Java是一门纯面向对象的语言(Object Oriented Program&#xff0c;简称OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对象。面 向对象是解决问题的一种思想&#xff0c;主要依靠对象之间的交互完成一件事情。用面向对象的…