【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件

news2024/11/25 7:56:56

目录

​编辑

前言

系统调用

open

参数flags

 参数mode

write

追加方式

read

close

 文件描述符

打开多个文件并观察其文件描述符

C语言文件操作

理解一切皆文件 

理解open操作


前言

各类语言的文件操作其实是对系统调用的封装

我们经常说,创建一个文件,或者打开一个文件,其实并不是我们用户本身创建,而是进程创建文件。文件是存储在磁盘上的,是硬件,像文件写入其实就是向硬件写入,而我们用户这一角色是没有权力直接写入的,操作系统(OS)是硬件的管理者,必须要通过OS写入;

但是,OS不相信任何人,只是提供了系统调用接口对外,想访问文件,就需要使用系统调用;而C/C++..语言都提供了这些系统调用的封装,为何要封装呢?跨平台性。

系统调用

open

打开一个文件,返回一个文件描述符,可以以flags形式打开文件

//2号手册 是系统调用(System Calls)  查找系统调用里面的open函数
man 2 open   

pathname:表示文件的路径名,也可以是一个文件名;如果没有路径名,默认在当前文件下;
flags:表示打开文件的方式选项,参见选项有: 

  • O_WRONLY:只写打开
  • O_CREAT:文件不存在时创建文件
  • O_RDONLY:只读打开
  • O_RDWR:读写方打开
  • O_APPEND:追加方式打开
  • O_TRUNC:清空文件,重新写入

mode:如果这个文件不存在,那么以写的方式打开的时候就会创建这个文件,在创建文件的时候需要给这个文件设定权限(使用八进制数)(会被umask影响);

        如果这个文件存在的话,那么就不用传第三个参数了,因为文件的权限已经确定了

参数flags

open函数定义中,flags是以整形定义的,有32比特位,满足参数个数不固定的情况,以二进制形式传参;

        实际上是一个32位二进制的位图,位图(Bitmap)是一种基于位操作的数据结构,用于表示一组元素的集合信息。位图中的每个二进制位都表示着某个元素是否在集合中。

        比如宏O_WRONLY假设表示的是2即10(二进制),O_CREAT表示4,即100(二进制)。那么O_WDONLY|O_CREAT=110(二进制)。在open函数内部就会有相应的机制监测flags的二进制位哪些是1,再分别对应其代表的功能

 参数mode

表示一个四位八进制的数,取后三位来表示各个角色的权限;

例:0666,取666用二进制表示的就是110 110 110分别对应文件的拥有者、所属组、其他人的(other)权限。

如果当文件不存在时,就要设置该参数,不然就会出现权限处乱码

当新建一个文件时,一定要加上mode参数,来设置权限

 

umask默认为002,也就是过滤掉了“其他人”的w权限,所以得到的最终文件权限编码是664。 

文件权限和权限掩码的关系:文件权限& (~umask权限掩码)

write

用于向文件中写入数据。通过指定文件描述符、数据来源的缓冲区地址和要写入的字节数;可以将数据写入到文件中。如果写入失败则返回-1,否则返回写入的字节数;

  • fd表示的是文件标识符
  • buf表示的是数据的地址;
    • 对于系统调用来说,它并不在意写入的数据是什么类型的,它接收到的数据都是二进制的数字,然后按照字节为单位写入。
  • count表示的是字节数
  • 返回值类型: ssize_t 类型表示有符号整型,输出格式为%ud.

 

追加方式

以追加方式写入,只需要在open(...)第二个参数中 把O_TRUNC换成O_APPEND

read

用于从文件中读取数据。通过指定文件描述符、缓冲区地址和读取的字节数;

可以将文件中的数据读取到缓冲区中。

读取成功返回读取的字节数,否则返回-1.

int fd:打开文件时返回的文件描述符。
void* buf:从文件中读取的数据放在这个数组中,同样系统不管文件中的数据类型是什么,都是按字节放入这个数组中。
size_t count:要读取的字节个数。
ssize_t:读取了多少个返回多少。 

使用只读方式打开(目标文件已经存在,open(...)中mode参数可以不用加入设置);

将读取的内容放在ch_arr数组中。 

close

用于关闭一个文件描述符,释放系统资源。(也就是令struct file* fd_array[]对应下标fd指向空。)

在文件操作完成后,应该及时关闭文件描述符,以防资源泄漏。

关闭成功返回0,否则返回-1.

 文件描述符

 操作系统要管理文件,必定要让文件先加载到内存,然后先描述,再组织。内核中要有描述对应文件的结构体——也就是struct file

        struct file中最核心的数据可以分为3大类,属性,方法集,缓冲区。而文件描述符fd就存在属性当中。

        文件是由进程发起创建的,而一个进程实际上是一个PCB(task_struct),里面有一个结构体指针struct file_struct* files,指向了一个结构体。这个结构体中又有一个指针数组struct file* fd_array[N]该指针数组存放了指向进程所打开文件的结构体下标,也就是文件描述符fd;

  • 内核会返回一个小的非负整数。这个非负整数就叫做描述符,也叫文件描述符。文件描述符是用于唯一标识文件的号码。
  • 进程实际上并不记录文件本身,而只需要记录一个为一个文件ID。所有被打开的文件的信息都被集中在一起被内核管理,内核向进程提供文件的接口。

打开多个文件并观察其文件描述符

创建的每一个进程开始的时候都有三个打开的文件:标准输入流(fd=0),标准输出流(fd=1),标准错误流(fd=2)。

 这三个文件分别对应的硬件设备是键盘、显示器、显示器。

而这三个都是默认自动打开的

 

当进程打开文件时,会在struct file*数组中找到当前没有被使用的最小的下标,作为新的文件标识符。

如果在打开文件之前,把这个三个文件流关闭,那么就会自动分配到当前的最小下标。

C语言文件操作

C语言文件操作默认打开三个输入输出流,分别是stdin,stdout,stderr。 

 之前提到过,不同语言的文件操作不过是对系统调用的封装,这里发现C语言的返回值和系统调用open..返回值不一样,我们大胆猜测FILE是一种封装

 打印输入输出流

 

 可以发现,FILE结构体中是有文件描述符的。

文件描述符fd的分别规则是:从小到大,按顺序查找,将没有被占用的数组下标作为被打开文件的文件描述符fd值。

理解一切皆文件 

这个文件可以理解成结构体

  • 每一个硬件,操作系统都会维护一个struct file类型的结构体,硬件的各种信息都在这个结构体中,并且还有对应读写函数指针(对硬件的操作主要就是读写)。
  • 每个硬件的具体读写函数的实现方式都在驱动层中,使用到相应的硬件时,操作系统会通过维护的结构体中的函数指针调用相应的读写函数。

 

  •  站在操作系统的角度来看下层,无论驱动层和硬件层中有什么,在它看来都是struct file结构体,都是通过维护这个结构体来控制各种硬件。
  • 站在操作系统的角度来看上层,无论用户层以及系统调用有什么,在它看来都是一个个进程,都是一个个的task_struct结构体,都是通过维护这个结构体来调度各个进程的。
  • 真正的文件在操作系统中的体现也是结构体,操作系统维护的同样是被打开文件的结构体而不是文件本身。

理解open操作

  • 创建struct file(包括fd)
  • 开辟文件缓存区,加载文件中的数据(延后)
  • 查进程的文件描述符表(fd_array数组)
  • 将file的内存地址填入到fd_array[fd]中
  • 返回fd. 

 

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

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

相关文章

【 C++ 】 一文搞定——引用、内联、命名空间、缺省、重载

前言:这篇文章将带您了解C基础中的知识点——命名空间、引用、内联、缺省、重载 😘我的主页:OMGmyhair-CSDN博客 一、命名空间namespace 1.可以嵌套定义,但是只能定义在全局 namespace ly {int student 1;int age 21;void Pr…

剑和沙盒 6 - 线程辱骂 – 使用线程名称进行攻击

强调: 进程注入是攻击者工具包中的重要技术之一。在下面的文章中 解释了如何滥用线程描述 API 来绕过端点保护产品。提出了一种新的注入技术:Thread Name-Calling,并给出了实施保护的相关建议。 介绍 进程注入是攻击者使用的重要技术之一 。…

Go-知识panic

Go-知识panic 1. 介绍2. 工作机制2.1 panic函数2.2 工作流程2.3 总结 3. 原理3.1 数据结构3.2 gopanic没有deferdefer函数处理嵌套defer 4. 总结 Go-知识error :https://blog.csdn.net/a18792721831/article/details/140430350 Go-知识defer : https://blog.csdn.net/a18792721…

单片机基于TXW8301的Wi-Fi Halow物联网控制

目前市面上基于2.4/5.8G wifi通讯信号干扰很频繁,基于Wi-Fi Halow的sub1g wifi既可以绕过干扰还可以达到公里级别控制,并且实现高清图传非常方便。 什么是Wi-Fi Halow?有何优势? 早在2016年3月,Wi-Fi联盟就针对物联网…

YOLO v8目标检测(二)—v8理论与模型推理

YOLO v8目标检测 数据增强 使用场景:在数据加载器加载数据的过程中会使用到数据增强的相关方法,来构造数据集。模型推理方法进行学习之前首先复习了解图像数据增强的相关方法和步骤。 其中在v8的源代码中 augment.py 的源代码文件。它包含了几个用于数据…

【python】python基于 Q-learning 算法的迷宫游戏(源码+论文)【独一无二】

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

java项目数据库 mysql 迁移到 达梦

目录 一、下载安装达梦数据库 1、下载 2、解压 3、安装 二、迁移 三、更改SpringBoot 的 yml文件 1、达梦创建用户 2、修改yml 一、下载安装达梦数据库 1、下载 下载地址 https://eco.dameng.com/download/ 点击下载 开发版 (X86平台) , 然后选择操作系统并点击立…

重装win10系统,“我们无法创建新的分区 也找不到现有的分区”“我们无法更新系统保留的分区”

重装系统,最开始用这篇教程保留数据的重装系统教程!(win10系统)_win10重装系统保留c盘数据-CSDN博客里win10官方的更新方法。想保留C盘以外的数据来重装系统 然后就会提示“我们无法更新系统保留的分区” 查到网上说这是因为MSR分…

python基础巩固

基本数据类型 可以用isinstance来判断 a111 isinstance(a,int) True数值运算: >>> 2 / 4 # 除法,得到一个浮点数 0.5 >>> 2 // 4 # 除法,得到一个整数 0 >>> 17 % 3 # 取余 2Python 字符串不能被改变。向一个…

支持向量机 及其分类案例详解(附Python 代码)

支持向量机分类器预测收入等级 我们将构建一个支持向量机(SVM)分类器,以预测一个人基于14个属性的收入等级。我们的目标是判断收入是否高于或低于每年$50,000。因此,这是一个二元分类问题。我们将使用在此处可用的人口普查收入数…

MySQL数据库(基础篇)

🌏个人博客主页:心.c 前言:今天讲解的是MySQL的详细知识点的,希望大家可以收货满满,话不多说,直接开始搞! 🔥🔥🔥文章专题:MySQL 😽感…

语言转文字

因为工作原因需要将语音转化为文字,经常搜索终于找到一个免费的好用工具,记录下使用方法 安装Whisper 搜索Colaboratory 右上方链接服务 执行 !pip install githttps://github.com/openai/whisper.git !sudo apt update && sudo apt install f…

Arduino IDE界面和设置(基础知识)

Arduino IDE界面和设置(基础知识) 1-2 Arduino IDE界面和设置如何来正确选择Arduino开发板型号如何正确选择Arduino这个端口如何来保存一个Arduino程序Arduino ide 的界面功能按钮验证编译上传新建打开保存工作状态 1-2 Arduino IDE界面和设置 大家好这…

软设之数据库概念结构设计

集成的方法: 多个局部E-R图一次集成 逐步集成,用累加的方式一次集成两个局部E-R 集成产生的冲突及解决办法: 属性冲突:包括属性冲突和属性取值冲突 命名冲突:包括同名异义和异名同义 结构冲突:包括同…

react.16+

1、函数式组件 在vite脚手架中执行&#xff1a; app.jsx: import { useState } from react import reactLogo from ./assets/react.svg import viteLogo from /vite.svg import ./App.cssfunction App() {console.log(this)return <h2>我是函数式组件</h2> }exp…

如何通过注解注入一个自定义的FactoryBean

一、引入依赖二、定义一个注解三、创建一个FactoryBean四、创建一个BeanPostProcessor4.1 其他关联类AnnotationUtilsServiceBeanNameBuilder 五、注入InstantiationAwareBeanPostProcessor到IoC中5.1 实现ImportBeanDefinitionRegistrar接口5.2 通过Import注入 六、使用6.1 打…

【proteus经典项目实战】51单片机用计数器中断实现100以内的按键计数并播放音乐

一、简介 一个基于8051微控制器的计数器系统&#xff0c;该系统能够通过按键输入递增计数&#xff0c;并且能够在达到100时归零。该系统将使用计数器中断和外部中断来实现其功能。 51单片机因其简单易用和成本效益高&#xff0c;成为电子爱好者和学生的首选平台。通过编程单片…

猫头虎分享 || 最全Python的Scapy库基础知识点汇总

&#x1f431;‍&#x1f464; 猫头虎分享 || Python的Scapy库基础知识点汇总 摘要 Scapy 是一个强大的Python库&#xff0c;用于网络数据包的生成、解析和操作。通过Scapy&#xff0c;开发者可以轻松地创建自定义数据包&#xff0c;捕获网络流量&#xff0c;并执行网络扫描。…

算法日记day 22

一、二叉搜索树中的插入操作 题目&#xff1a; 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和要插入树中的值 value &#xff0c;将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 &#xff0c;新值和原始二叉搜索树中的任意节点值都不同。…

Python学习笔记46:游戏篇之外星人入侵(七)

前言 到目前为止&#xff0c;我们已经完成了游戏窗口的创建&#xff0c;飞船的加载&#xff0c;飞船的移动&#xff0c;发射子弹等功能。很高兴的说一声&#xff0c;基础的游戏功能已经完成一半了&#xff0c;再过几天我们就可以尝试驾驶 飞船击毁外星人了。当然&#xff0c;计…