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

一、文件的理解

        每种语言都有进行文件操作的函数接口,例如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语言(其他语言也是这样)想实现跨平台性,不同操作系统的系统调用是不同的,仅使用系统调用的代码不具有跨平台性,所以语言会对系统调用进行封装,在哪个操作系统下就使用该操作系统的系统调用,这样就可以实现语言的跨平台性。

        

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C/C++】C++学籍信息管理系统(源码+报告)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

C++初阶:list类及模拟实现

list的介绍及使用 list的介绍 list 1. list 是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list 的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向…

BGP-安全特性、扩展特性、增强特性

BGP-安全特性&#xff0c;扩展特性 BGP路由反射器和联盟的比较 反射器 联盟 不需要更改现有的网络拓扑&#xff0c;兼容性号 需要修改逻辑拓扑 配置方便&#xff0c;客户机不知道自己是客户机 所有设备需要重新进行配置&#xff0c;且所有设备必须支持联盟功能 集群与集群…

爬虫逆向实战(39)-某某兔装修网登陆(RSA)

一、数据接口分析 主页地址&#xff1a;某某兔装修网 1、抓包 通过抓包可以发现登陆是表单提交 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块&#xff0c;可以发现有一个val和password的加密参数 请求头是否加密&#xff1f; 无响应是否加密…

苹果开发者账号注册步骤中的常见疑问解答与技巧分享

转载&#xff1a;注册苹果开发者账号的方法 在2020年以前&#xff0c;注册苹果开发者账号后&#xff0c;就可以生成证书。 但2020年后&#xff0c;因为注册苹果开发者账号需要使用Apple Developer app注册开发者账号&#xff0c;所以需要缴费才能创建ios证书了。 所以新政策出…

【React】React知识要点记录

描述UI 万物皆组件 为什么多个 JSX 标签需要被一个父元素包裹&#xff1f; 切勿将数字放在 && 左侧 React 中为什么需要 key&#xff1f; React 为何侧重于纯函数? 渲染树 模块依赖树 添加交互 React如何传递事件处理函数&#xff1f; React 如何知道返回哪个 sta…

【THM】SQL Injection(SQL注入)-初级渗透测试

简介 SQL(结构化查询语言)注入,通常称为 SQLi,是对 Web 应用程序数据库服务器的攻击,导致执行恶意查询。当 Web 应用程序使用未经正确验证的用户输入与数据库进行通信时,攻击者有可能窃取、删除或更改私人数据和客户数据,并攻击 Web 应用程序身份验证方法以获取私有数据…

Keil MDK 5.37 及之后版本 安装 AC5(ARMCC) 编译器详细步骤

由于 Keil 5.37 及之后版本不再默认安装 AC5(ARMCC) 编译器&#xff0c;这就会导致由 AC5 编译的工程无法正常编译&#xff0c;往往输出窗口会提示以下信息&#xff1a;*** Target ‘STM32xxxx‘ uses ARM-Compiler ‘Default Compiler Version 5‘ which is not available. —…

UDS 诊断入门-1 概述

1、简介 诊断服务是介于诊断设备和ECU之间的一种信息交互方式。通常由诊断设备发出请求&#xff0c;ECU做出回应。 doCAN&#xff1a;基于CAN协议的诊断 DoIP&#xff1a;Diagnostic communication Over IP (DoIP) is a standard developed by ISO-13400 for vehicle commun…

STC8H8K64U 学习笔记 - PWM

STC8H8K64U 学习笔记 - PWM 环境说明引脚说明 PWM呼吸灯震动马达 乐谱 环境说明 该内容仅针对我自己学习的开发板做的笔记&#xff0c;在实际开发中需要针对目标电路板的原理图进行针对性研究。 芯片&#xff1a;STC8H8K64U烧录软件&#xff1a;stc-isp-v6.92G编码工具&#xf…

CSS面试题---基础

1、css选择器及优先级 选择器优先级&#xff1a;内联样式>id选择器>类选择器、属性选择器、伪类选择器>标签选择器、微元素选择器 注意&#xff1a; !important优先级最高&#xff1b; 如果优先级相同&#xff0c;则最后出现的样式生效&#xff1b; 继承得到的样式优先…

Google DeepMind 大语言模型中的长形态事实性

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 论文标题&#xff1a;Long-form factuality in large language models 论文链接&#xff1a;https://arxiv.org/abs/2403.18802 论文的关键信息总结如下&#xff1a; 研究问题是什么&#xff1f;论文…

Python+requests+Pytest+logging+allure+pymysql框架详解

一、框架目录结构 1)tools目录用来放公共方法存储,如发送接口以及读取测试数据的方法,响应断言 数据库断言 前置sql等方法;2)datas目录用例存储接口用例的测试数据,我是用excel来存储的数据,文件数据 图片数据等;3)testcases目录用来存放测试用例,一个python文件对应…

docker容器之etcd安装

一、etcd介绍 1、etcd是什么 etcd是CoreOS团队于2013年6月发起的开源项目&#xff0c;它的目标是构建一个高可用的分布式键值(key-value)数据库。 2、etcd特点 简单的接口&#xff0c;通过标准的HTTP API进行调用&#xff0c;也可以使用官方提供的 etcdctl 操作存储的数据。…

Java | Leetcode Java题解之第3题无重复字符的最长子串

题目&#xff1a; 题解&#xff1a; class Solution {public int lengthOfLongestSubstring(String s) {// 哈希集合&#xff0c;记录每个字符是否出现过Set<Character> occ new HashSet<Character>();int n s.length();// 右指针&#xff0c;初始值为 -1&#…

【好书推荐4】图机器学习

【好书推荐4】图机器学习 写在最前面编辑推荐内容简介作者简介目录前言/序言本书读者内容介绍 &#x1f308;你好呀&#xff01;我是 是Yu欸 &#x1f30c; 2024每日百字篆刻时光&#xff0c;感谢你的陪伴与支持 ~ &#x1f680; 欢迎一起踏上探险之旅&#xff0c;挖掘无限可能…

67、yolov8目标检测和旋转目标检测算法batchsize=1/6部署Atlas 200I DK A2开发板上

基本思想:需求部署yolov8目标检测和旋转目标检测算法部署atlas 200dk 开发板上 一、转换模型 链接: https://pan.baidu.com/s/1hJPX2QvybI4AGgeJKO6QgQ?pwd=q2s5 提取码: q2s5 from ultralytics import YOLO# Load a model model = YOLO("yolov8s.yaml") # buil…

辽宁梵宁教育:设计领域的靠谱正规线上教育机构典范

辽宁梵宁教育&#xff0c;作为一家专注于学习设计的线上教育机构&#xff0c;近年来在业界崭露头角&#xff0c;赢得了广大学习者的认可和好评。接下来&#xff0c;本文将从多个维度详细阐述梵宁教育为何是一家靠谱且正规的线上教育机构。 梵宁教育在师资力量上表现出色。其拥有…

0基础学习Mybatis系列数据库操作框架——目录结构

大纲 配置的修改代码的修改Main.java文件所在包下新增org.example.model包新增org.example.mapper包 单元测试 在《0基础学习Mybatis系列数据库操作框架——最小Demo》一文中&#xff0c;我们用最简单的方法组织出一个Mybatis应用项目。为了后续构建更符合日常开发环境的项目&a…

校园跑腿(源码+文档)

校园跑腿管理系统&#xff08;小程序、ios、安卓都可部署&#xff09; 文件包含内容程序简要说明含有功能项目截图客户端店铺代购用户条款隐私协议租借服务行李代搬拨打客服电话注册界面我的界面申请骑手登录界面快递带取资料修改快递代寄主页万能帮 管理端代购管理添加用户订单…