[Linux]基础IO(上)--理解文件系统调用、文件描述符、万物皆文件

news2024/11/25 16:30:11

一、文件的理解

        每种语言都有进行文件操作的函数接口,例如C语言的fopen、fwrite、fprintf等等,但是进行文件操作的前提是代码已经跑起来,因为文件的打开与关闭要通过CPU来运行程序代码,所以打开文件的本质是进程打开文件,文件操作也是进程进行文件操作。

        既然文件的打开与关闭是依靠进程的,而操作系统中避免不了存在许多进程,那么一定有大量打开的文件,所以OS就需要将这些文件管理起来(先描述在组织),所以在OS内部,每一个被打开的文件应该有一个专门描述文件的结构,类似于PCB

        文件没有被打开的时候是在哪里呢?

------解释:

        文件没有被打开的时候是存在磁盘中的,而磁盘属于硬件,用户要进行文件操作是不可以直接访问硬件的,因为操作系统不相信任何人,用户只能通过操作系统的函数调用来进行文件操作,而我们在平常一般使用的是语言层面的文件函数接口,不同语言对于文件的函数调用却是有差异的,但它们都是对系统调用接口的封装。

二、文件系统调用接口介绍

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: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行 “” 运算,构成flags。
  • mode:给新创建的文件设置权限
  • flag参数:
  1. O_RDONLY: 只读打开
  2. O_WRONLY: 只写打开
  3. O_RDWR : 读,写打开
  4. 这三个常量,必须指定一个且只能指定一个
  5. O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
  6. O_APPEND: 追加写
  7. O_TRUNC :如果文件存在再次以写方式打开清空文件
2. 关闭文件:close
#include <unistd.h>

int close(int fd);
3. write
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);
4. read
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
代码演示:

以写的方式打开文件,若文件不存在则创建文件,再次向文件写入向后追加

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

  int main()
  {
    int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
    const char* buf="hello Linux file!";                                                                                                                                                       
    write(fd,buf,strlen(buf));
    close(fd);
    return 0;
 }

5. 如何理解open中的标记选项

        在日常写程序时,我们想创建一个标记一般会直接定义 int flag ,如果需要两个则定义

int flag1 、int flag2 ,若需要多个标记的话这样写不仅难以维护而且有点浪费空间。

       做标记无非就是要表达有或没有、是或不是的信息,一个整形变量有32个比特位,

每个比特位都单独可以做为标记位,这是OS设计系统调用的常用方式。接下来我们自己模拟实现一个位图的函数理解一下:

  #include<stdio.h>
  #define ONE (1)      //1
  #define TWO (1<<1)   //2
  #define THREE (1<<2) //4
  #define FOUR (1<<3)  //8
  
  void print(int flag)
  {
      if(flag&ONE)
          printf("one\n"); //替换成其他功能
      if(flag&TWO)
          printf("two\n");
      if(flag&THREE)
          printf("three\n");
      if(flag&FOUR)
          printf("four\n");
  }
  int main()
  {
    print(ONE);
    printf("\n");
  
    print(TWO);
    printf("\n");
  
    print(ONE|TWO);
    printf("\n");
  
    print(ONE|TWO|THREE);
    printf("\n");
    return 0;                                                                                                                                                                                  
  }

 三.理解open返回值(文件描述符)

为什么fd是从3开始的呢?0、1、2去哪里了呢?

--- 0:标准输入流(键盘)

--- 1:标准输出流(显示器)

--- 2:标准错误流(显示器)

这三个流在程序启动时就自动开启了 ,Linux下万物皆文件,操作系统将这三个标准也视为文件,所以文件创建时是从3开始的

既然0、1就可代表键盘与显示器,那向显示器打印信息除了printf还可以这样做:

为什么一个整数就可以代表一个文件呢?文件描述符的本质是什么?

---- 上面说过,操作系统需要将打开的文件进行管理,区分哪些文件被打开了,哪些文件马上就要关闭等等信息,在Linux下,每一个打开的文件都会被struct file的结构体所描述,为了便于管理,会将这些结构体用双链表进行连接管理,这个结构体中还存在一个指针,表示文件内核级的缓冲区,想要读取磁盘的文件中内容,只需将内容放入该缓冲区,程序就可以使用这些内容了,同理若想要向磁盘中的文件写入内容,需要将内容先写入缓冲区,再讲缓冲区的内容导入文件,这样对文件管理只需要对文件的struct file进行管理了

对文件管理的实质是进程对文件进行管理,那进程是如何管理自己文件的呢?

        进程在创建时会创建自己的PCB,一个进程可以打开多个文件,为了区分这些文件具体是哪些进程管理的,每个进程的PCB中存在着一个struct files_struct* 的指针,这个指针用来管理进程打开的文件,为了进程与文件的对应关系,struct files_struct结构体中保存着struct file* [ ]的指针数组,既然是数组一定会有数组下标,则操作系统会将0号下标指针指向保存标准输入的struct file的地址,将1号下标指针指向保存标准输出的struct file的地址,将2号下标指针指向保存标准错误的struct file的地址,同理将3号下标指针指向自己打开的文件struct file的地址

综上所述:

# 文件操作符的本质就是文件映射关系数组的下标,所以一个整数就可以访问一个文件

# open过程的操作:

    1.创建file

    2.开辟文件缓冲区,传输文件数据(延后)

    3.查询文件描述符表,构建映射关系

    4.返回下标

四.理解Linux下万物皆文件

        在上文中,我们谈到操作系统将键盘与显示器等也看做文件,但是他们本身为硬件,该怎么理解万物皆文件呢?在Linux下是怎么做到的?

        对于设备其实我们关心的数据就两个,属性与操作方法,对于属性一个结构体就可以描述,而方法的话,不同设备的方法一定是不同的,但是可以把他们的参数与返回值设置的类型相同。

        操作系统在打开一个设备时,就会创建其对应的struct file,该结构体除了一些关于文件信息的内容,还会存在一些函数指针来实指向对应设备的方法,struct file结构体中一定还有一个指针指向设备的属性,可以让我们访问到,所以想要访问一个设备,只需要找到其对应的struct file通过内部函数指针或者指向描述属性结构体的指针就可以操作这个设备。在Linux下一套叫做vfs(虚拟文件系统)

         所以在上层看来,不论是磁盘级的文件还是设备级的文件不做区分,只将他们看做struct file,实现了万物皆文件

五、重新理解不同语言对文件操作的封装

        上文解释到在Linux操作系统下,进程是依靠文件描述符来操控文件的,但是C语言中对于文件操作的函数却使用的是FILE*的指针,连stdout、stdin、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()  
  {  
    FILE* fp=fopen("log.txt","w");  
    if(fp==NULL)  
    {
      perror("fopen");
      return 1;
    }
    printf("stdin:%d\n",stdin->_fileno);
    printf("stdout:%d\n",stdout->_fileno);   
    printf("stderr:%d\n",stderr->_fileno);   
    printf("fp:%d\n",fp->_fileno);                                                                                                                                                             
    return 0;                                                                                                                                                
  }                                                                                                                                                          
       

语言的文件操作函数,本质底层都是对系统调用的封装!

那C语言为什么要这样做呢?

        因为C语言(其他语言也是这样)想实现跨平台性,不同操作系统的系统调用是不同的,仅使用系统调用的代码不具有跨平台性,所以语言会对系统调用进行封装,在哪个操作系统下就使用该操作系统的系统调用,这样就可以实现语言的跨平台性。

        

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

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

相关文章

day01 51单片机

51单片机学习 1 51单片机概述 1.1 51单片机简介 目前使用的51单片机一般是宏晶STC89系列,这其中流传最广的版本,也是我们课程的主角,就是STC89C52RC。 1.2 命名规则 1.3 单片机最小应用系统 2 点亮LED灯 2.1 硬件原理图 这个原理图非常简单,VCC接保护电阻R1,串联LED1最…

企业周年庆3d云展厅促进了客企间交流与互动

在数字化浪潮席卷而来的今天&#xff0c;传统的展示方式已难以满足现代人对信息获取与体验的高标准需求。为此&#xff0c;一种革命性的展示方式——线上3D虚拟展厅应运而生&#xff0c;以其独特的魅力逐渐引领展示方式的革新。 线上3D虚拟展厅开发&#xff0c;不仅为参与者带来…

突破编程_前端_JS编程实例(分割窗体组件)

1 开发目标 分隔窗体组件旨在提供灵活的窗体分隔功能&#xff0c;支持横向分割与纵向分隔两种类型&#xff0c;并具备拖拽调整窗体比例的功能&#xff0c;同时提供最小比例设置&#xff0c;以防止窗体被过度缩小&#xff1a; 2 详细需求 2.1 分隔窗体类型 &#xff08;1&…

软件工程学习笔记14——案例解析篇

案例解析篇 一、大型开源项目对软件工程的应用1、开发迭代过程 二、大厂是怎样应用软件工程的1、软件项目开发团队组成&#xff08;1&#xff09;软件开发团队规模小&#xff08;2&#xff09;没有专职测试&#xff08;3&#xff09;DevOps 文化 2、开发工具的使用3、项目开发流…

结构体,联合体,枚举( 2 )

目录 2.联合体 2.1联合体类型的声明 2.2联合体的特点 2.3联合体的内存大小 3.枚举 3.1枚举类型的声明 3.2枚举类型的优点 3.3枚举类型的使用 2.联合体 联合体&#xff08;Union&#xff09;是另一种复合数据类型&#xff0c;它允许我们在同一内存位置存储不同的数据类型…

PyYAML,一个强大的 Python 库!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个强大的 Python 库 - pyyaml。 Github地址&#xff1a;https://github.com/yaml/pyyaml/ 在处理配置文件、序列化数据等场景中&#xff0c;YAML&#xff08;YAML Ain’t Ma…

雷勒过滤与您相约2024第13届生物发酵展

参展企业介绍 青岛雷勒过滤科技有限公司是一家致力于不锈钢过滤网&#xff0c;涂料筛&#xff0c;滤袋等研发、生产和服务一体的综合型公司。雷勒专业生产各种高品质的不锈钢过滤元件&#xff0c;用于各种关键的过滤与分离环节。 雷勒拥有先进的生产设备&#xff0c;完善的产…

BGP-BGP选路、BGP4+

BGP-BGP选路&#xff0c;BGP4 支持IPv6的BGP&#xff0c;又称为MP-BGP&#xff0c;BGP4 BGP&#xff1a;外部网关协议、使用TCP作为其传输层协议、支持CIDR、增量更新、路径矢量路由协议、无环路、路由策略丰富、可防止路由震荡、易于扩展。 BGP工作原理——报文类型&#x…

信捷 XD/XL plc 单精度/双精度浮点数比较 ECMP,EDCMP

对于单精度浮点数&#xff0c;用ECMP指令。 对于双精度浮点数&#xff0c;用EDCMP指令 注意&#xff1a;EDCMP 指令中寄存器的首地址必须为偶数。

教育信创 | 云轴科技ZStack联合飞腾发布全场景教育信创白皮书

随着数字化时代的到来&#xff0c;教育行业正面临着前所未有的挑战与机遇。为了推动教育行业的数字化转型和信创人才培养&#xff0c;云轴科技ZStack联合飞腾于3月28日正式发布了《教育行业数字化自主创新飞腾生态解决方案白皮书》&#xff08;简称《教育白皮书》&#xff09;。…

Day57:WEB攻防-SSRF服务端请求Gopher伪协议无回显利用黑白盒挖掘业务功能点

目录 SSRF-原理&挖掘&利用&修复 SSRF无回显解决办法 SSRF漏洞挖掘 SSRF协议利用 http:// &#xff08;常用&#xff09; file:/// &#xff08;常用&#xff09; dict:// &#xff08;常用&#xff09; sftp:// ldap:// tftp:// gopher:// &#xff08;…

Python中的全栈开发前端与后端的完美融合【第160篇—全栈开发】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python中的全栈开发&#xff1a;前端与后端的完美融合 全栈开发已成为当今软件开发领域中的…

YOLOv9有效改进专栏汇总|未来更新卷积、主干、检测头注意力机制、特征融合方式等创新![2024/4/1]

​ 专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;助力高效涨点&#xff01;&#xff01;&#xff01; 专栏介绍 YOLOv9作为最新的YOLO系列模型&#xff0c;对于做目标检测的同学是必不可少的。本专栏将针对2024年最新推出的YOLOv9检测模型&#xff0…

Java数据结构-栈

目录 1. 栈的概念2. 栈的实现2.1 顺序栈2.2 链式栈 3. 栈的应用3.1 栈的使用3.2 括号匹配3.3 逆波兰表达式求值3.4 出栈入栈次序匹配3.4 最小栈 1. 栈的概念 栈是一种顺序结构&#xff0c;只允许在一端进行插入和删除&#xff0c;插入删除的一端叫栈顶&#xff0c;另一端叫栈底…

蓝桥杯嵌入式学习笔记(7):ADC程序设计

目录 前言 1. ADC原理 1.1 主要特性 1.2 模拟输出电路图 2. 使用CubeMX进行源工程的配置 2.1 引脚配置 2.2 配置AD1 2.3 配置AD2 2.4 配置时钟 3. 代码编程 3.1 预备工作 3.2 bsp_adc.h文件编写 3.3 bsp_adc.c文件编写 3.4 main.c编写 3.4.1 时钟函数配置 3…

内网渗透学习-漏洞利用

1、漏洞搜索与利用 漏洞1&#xff1a;后台弱口令 漏洞2&#xff1a; 在yxcms有个留言功能&#xff0c;常常存在存储型xss漏洞 先用<h1>123xss</h1>测试一下会不会解析 登录管理员账号审核留言&#xff0c;发现xss脚本解析了 写入xss弹窗 再次返回管理员审核页面&…

Hive on Spark 配置

目录 1 Hive 引擎简介2 Hive on Spark 配置2.1 在 Hive 所在节点部署 Spark2.2 在hive中创建spark配置文件2.3 向 HDFS上传Spark纯净版 jar 包2.4 修改hive-site.xml文件2.5 Hive on Spark测试2.6 报错 1 Hive 引擎简介 Hive引擎包括&#xff1a;MR&#xff08;默认&#xff09…

leaflet知识点:leaflet.draw的使用指南

一&#xff0c;安装插件 npm i leaflet-draw --save 二&#xff0c;引入插件 import "leaflet-draw"; import "leaflet-draw/dist/leaflet.draw.css";三&#xff0c;使用插件 leaflet-draw的插件使用有两种方法。 1. 作为工具栏控件加入到地图种使用 //…

linux离线安装NodeJs

一、官方下载 地址&#xff1a;Node.js — Download Node.js 选择linux系统版本 为了防止安装过程出现一些适配问题&#xff0c;我没有选择下载最新版&#xff0c;实际应该下载你的前端所用的nodejs版本 未完待续。。

深度解析Android APP加固中的必备手段——代码混淆技术

Android APP 加固是优化 APK 安全性的一种方法&#xff0c;常见的加固方式有混淆代码、加壳、数据加密、动态加载等。下面介绍一下 Android APP 加固的具体实现方式。 混淆代码 使用 ipaguard工具可以对代码进行混淆&#xff0c;使得反编译出来的代码很难阅读和理解&#xff…