Linux之基础I/O

目录

一、C语言中的文件操作

二、系统文件操作I/O

三、文件描述符fd

1、文件描述符的引入

2、对fd的理解

3、文件描述符的分配规则

四、重定向  

1、重定向的原理

2、重定向的系统调用dup2

五、Linux下一切皆文件


一、C语言中的文件操作

1、打开和关闭 

在C语言的文件操作中,我们要对一个文件进行写入和读写的前提是打开文件。我们使用fopen来打开文件,打开失败将会返回NULL ,而打开成功则返回文件的指针 FILE*。最后要进行的操作就是关闭(fclose)文件

函数原型:FILE *fopen(const char *path, char *mode)。path为文件名(也可以是文件路径),mode为打开方式,它们都是字符串。

int fclose(FILE *fp)。

下面我们来看一看下面的代码:

上面的代码中,我打开了一个文件log.txt。但是我的当前目录下并没有这个文件。 

这个文件并不存在,但是我们要使用,那么fopen会在当前路径下给我们创建出这个文件。那么这个当前路径是什么呢? 

简单来说,当前路径:一个进程运行起来的时候,每个进程都会去记录自己当前所处的工作路径。所以当前路径也就是当前进程的工作路径。

有了这个概念,我们就能理解了:test.c形成的可执行程序在运行后,会成为一个进程,该进程会通过调用系统接口帮助我们创建文件,因此新文件所在的路径就是当前进程的工作路径。

第一个红色方框就是当前进程的工作路径,exe就是当前的可执行文件。第二个红色方框就是表示执行的是进程工作路径下的那个可执行程序。

注:单纯以w方式打开文件,会自动清空文件原有的数据。r+(读写)代表文件不存在则出错,w+(读写)代表文件不存在则创建。(带有+的表示读写)。a代表向文件中追加内容。

2、读写文件

我们知道在C语言中,我们可以通过fgets和fputs以字符串形式进行读写,也可以通过fprint和fscanf进行格式化读写。(下面的函数在C语言中我们已经学过了,这里就不一一演示了)。

int fputs (const char * str, FILE * stream )
char * fgets (char * str, int num, FILE * stream )
int fprintf (FILE * stream, const char * format, ... )
int fscanf (FILE * stream, const char * format, ... )

二、系统文件操作I/O

操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问。其实真正能够直接访问文件的只有操作系统,而各种编程语言能够访问文件的函数的本质都是去调用了操作系统提供的各种系统接口。

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) 

O_RDONLY:只读打开        O_WRONLY : 只写打开         O_RDWR : 读写打开

O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限

O_APPEND : 追加写

O_TRUNC:打开时,清空文件内容。

返回值:成功:新打开的文件的文件描述符         失败:-1

~ 使用比特位传递选项

但是,flags是一个整型,他只表示一个参数,那么我们是怎么通过flags传入多个参数呢?这里我们使用了一种数据结构叫做比特位:一个整数有32个比特位,所以我们可以通过比特位来传递选项。

下面我们通过一个例子,来看看是怎么实现的。

因此,我们可以使用 | (或)来帮助我们传递多个参数,以此实现不同的功能。 

mode参数

如果你使用O_CREAT参数创建一个新的文件,那么你还可以通过第三个参数mode来设置该文件的权限。

2、close

//所在头文件
#include <unistd.h>//原型
int close(int fd);

3、read和write

文件打开后,我们就业对文件进行读取或者写入了。

write:写入

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

fd:要写入的文件        buf:要写入的内容       count:所写内容的大小。 

read:读取

//头文件
#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);

fd:要读取的文件   buf:存放读取内容的数组    count:读取的内容大小 

三、文件描述符fd

我们通过上面的学习,发现,open的返回值是一个整型,其实这个整型其实就是代表文件描述符fd。

1、文件描述符的引入

我们来看看下面的代码

运行结果如下

我们知道fd是一个整型了,那么为什么是从3开始的呢?那0,1,2跑哪里去了呢?

在C语言阶段,我们知道在程序运行时,操作系统会默认打开三个标准输入输出流:标准输入,标准输出,标准错误。对应到C语言当中就是stdin、stdout以及stderr。在C++中则是cin、cout、cerr,而在其他的语言中也有相应的输入输出流。

我们知道C语言中的stdin、stdout以及stderr这三个家伙实际上都是FILE*类型的,并不是int类型。因为FILE*是一个结构体指针,是C语言进行了封装的,是为了方便用户使用。而在操作系统层面,比如在Linux下,只认fd,而且只有操作系统才能直接访问文件,那么各种语言为了既方便用户使用,又要遵循操作系统的规则,必定在FILE结构体里面封装了fd,这样才能在系统层面去使用文件。

所有各种语言都有封装自己的输入输出流,实际上这种特性并不是某种语言所特有的,而是由操作系统所支持的。

那么,说到这里,我们已经有一点感觉了,0,1,2哪去了呢?会不会是分别代表着标准输入,标准输出、标准错误呢?答案是肯定的。在Linux下0,1,2就是表示这个意思。

那么我们也就能够理解了,0,1,2所表示的文件操作系统已经帮我们打开了!

下面通过代码来验证一下:

所以说,在系统层面,我们只能用0,1,2,3等整数来确定一个文件。 

2、对fd的理解

进程想要访问文件,那么要先打开文件。而文件是由进程运行时打开的,一个进程可以打开多个文件,而系统当中又存在大量进程,也就是说,在系统中任何时刻都可能存在大量已经打开的文件。所以操作系统务必要对这些已经打开的文件进行管理。那么怎么进行管理呢?先描述,再组织!

操作系统会为每个已经打开的文件创建各自的struct file结构体(其中包含了该文件几乎全部的内容),然后将这些结构体以双链表的形式连接起来,之后操作系统对文件的管理也就变成了对这张双链表的增删查改等操作。

所以,为了区分被打开的文件各自属于那个进程,操作系统必定会将进程与文件之间建立某种联系。

那么进程和文件是怎么建立联系的呢?

首先,我们来想一想fd为什么是连续的整数呢?我们学过的知识中有什么是和连续的整数有关且从0开始的呢?我们很容易就可以想到一个——数组的下标!没错,fd就是数组的下标!那么是什么数组的下标呢?

我们知道,当一个程序运行起来时,操作系统会将该程序的代码和数据加载到内存,然后为其创建对应的task_struct,task_struct中的一个指针变量指向该进程的mm_struct(进程地址空间),通过页表建立虚拟内存和物理内存之间的映射关系。

而task_struct当中有一个指针,该指针指向一个名为files_struct的结构体,在该结构体当中就有一个名为fd_array的指针数组,该数组的下标就是我们所谓的文件描述符。

当进程打开一个文件时,该文件从磁盘当中加载到内存,形成对应的struct file,OS将该struct file连入文件双链表,并将该结构体的首地址填入到fd_array数组当中下标为3的位置,使得fd_array数组中下标为3的指针指向该struct file,最后返回该文件的 fd 给进程。

所以,我们只要有某一文件的文件描述符,就可以找到该文件相关的内容,进而对文件进行一系列输入输出操作。 

3、文件描述符的分配规则

一般情况下,操作系统默认为进程打开标准输入,输出,错误,分别对应fd为0,1,2。所以0,1,2位置已经被占用了,所以只能从3开始进行分配。之后打开的文件按顺序fd为3,4,5 ......

若我们在打开新的文件前,先关闭文件描述符为0的文件,此后文件描述符的分配又会是怎样的呢?

结果如下:

可以看到,新打开的文件获取到的文件描述符变成了0。

我们再多打开几个文件:

结果如下:第一个打开的文件获取到的文件描述符变成了0,而之后打开文件获取到的文件描述符还是从3开始依次递增的。

所以:文件描述符是从最小但是没有被使用的fd_array数组下标开始进行分配的。

四、重定向  

1、重定向的原理

~ 输出重定向

运行结果如下:

根据运行结果,我们发现printf函数本应该将结果输出到显示器(标准输出)让我们看见,但是结果并没有在显示器上显示出来,但是结果却被打印到了 log.txt 里面。 这就是我们所说的输出重定向。

输出重定向:将我们本应该输出到一个文件的数据重定向输出到另一个文件中。

具体原理如下图:

close的本质其实是将进程和文件的关联关系解除。close(1)就是将1位置的指针设成NULL,但是语言层的 stdout(或者cout等)指向的是一个struct FILE类型的结构体,结构体中存储文件描述符的变量的值仍然是1。

接着创建了一个新的文件log.txt,从0开始遍历数组,发现1位置为空,所以将1位置的指针指向log.txt,这就建立了新的关联关系。

所以当你使用C语言的printf向stdout写入的时候,stdout的fd仍然是1,但是底层的1位置已经指向log.txt了,所以就写到了log.txt里面。

~ 追加重定向

追加重定向就是在输出重定向的基础上,将“清空”的参数改成“追加”的参数。

2、重定向的系统调用dup2

上面是我们根据文件描述符的分配规则,来进行重定向的。下面我们使用系统调用接口dup2来帮助我们实现重定向。

功能: dup2会将fd_array[oldfd]的内容拷贝到fd_array[newfd]当中,如果有必要的话我们需要先使用关闭文件描述符为newfd的文件。 

返回值: dup2如果调用成功,返回newfd,否则返回-1。

我们使用下面的代码举例:

五、Linux下一切皆文件

广义上的文件:站在操作系统Linux的角度,能够被input读取,或者能够被output写出的设备就叫做文件。

所以,显示器、键盘、网卡、显卡、磁盘等,几乎所有的外设都可以称为文件。

在Linux下,我们将文件分为:1、内存文件(文件已经打开,已经加载到了内存中)2、磁盘文件(文件还没有被打开,没有被加载到内存中)。

那么Linux下一切皆文件具体是怎么体现的呢?

首先,Linux内核是用C语言写的。每个外设的硬件结构是不一样的,那么我们通过操作系统访问外设的方式肯定是不一样的。但是,操作系统仅仅通过提供四个系统调用(open,close,write,read),就可以帮助用户访问显示器、磁盘等文件。那么看似相同的方法是怎么访问不同的硬件设备的呢?

我们在学习了C++或者Java等能够面向对象的编程语言后,我们知道可以使用类来描述一个文件,然后使用多态达到使用相同接口而产生不同效果,所以这些语言可以做到上面的事。可是,Linux内核是使用C语言写的,C语言可是没有面向对象的特点的,也没有多态的概念,那么它是怎么做到的呢?

任何一个被打开的文件的有自己的结构体对象struct file{ //各种文件的属性 },不同的文件对应的读写方法不一样,struct file对象里面可以有很多的(*readp)()、(*writep)()函数指针,通过函数指针指向具体的读写方法。

这样,用户就可以不用关心底层差别,统一使用文件的接口方式进行文件操作。

所以,在Linux下,一切皆文件! 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LangChain 31 模块复用Prompt templates 提示词模板

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

Tofu5m目标识别跟踪模块 跟踪模块

Tofu5m 是高性价比目标识别跟踪模块&#xff0c;支持可见光视频或红外网络视频的输入&#xff0c;支持视频下的多类型物体检测、识别、跟踪等功能。 产品支持视频编码、设备管理、目标检测、深度学习识别、跟踪等功能&#xff0c;提供多机版与触控版管理软件&#xff0c;为二次…

Redux与React环境准备、实现counter(及传参)、异步获取数据

环境说明&#xff1a; 一&#xff1a;说明 在React中使用redux&#xff0c;官方要求安装两个其他插件&#xff1a;Redux Toolkit和react-redux 1. Redux ToolKit(RTK) - 官方推荐编写Redux逻辑的方式&#xff0c;是一套工具的集合集&#xff0c;简化书写方式 &#xff08;简化…

通过字符设备驱动点亮板子上的led灯

通过字符设备驱动点亮板子上的led灯 app: test.c char buf[3] 1 0 0 0 1 0 0 0 1 ------------------|------------------------ kernel: led_driver.c -------------------|------------------------ hardware: RGB_led 应用程序如何将数据传递给驱动&#xff08;读写…

Kafka日志

位置 server.properties配置文件中通过log.dir指定日志存储目录 log.dir/{topic}-{partition} 核心文件 .log 存储消息的日志文件&#xff0c;固定大小为1G&#xff0c;写满后会新增一个文件&#xff0c;文件名表示当前日志文件记录的第一条消息的偏移量。 .index 以偏移…

计算机视觉基础(13)——深度估计

前言 本节是计算机视觉的最后一节&#xff0c;我们将学习深度估计。从深度的概念和度量入手&#xff0c;依次学习单目深度估计和双目/多目深度估计&#xff0c;需要知道深度估计的经典方法&#xff0c;掌握深度估计的评价标准&#xff0c;注意结合对极几何进行分析和思考。 一、…

医疗影像中DR的骨抑制

1 背景 在DR的拍摄中&#xff0c;根据肺部和脊肋骨两种组织&#xff0c;在不同能量X射线的照射下&#xff0c;衰减的系数不同的特点&#xff0c;可以通过两次不同剂量的曝光后&#xff0c;通过算法&#xff0c;得到一张骨骼的图像和一张肺部图像。 通过一些机构的统计&#x…

MATLAB - 读取双摆杆上的 IMU 数据

系列文章目录 前言 本示例展示了如何从安装在双摆杆上的两个 IMU 传感器生成惯性测量单元 (IMU) 读数。双摆使用 Simscape Multibody™ 进行建模。有关使用 Simscape Multibody™ 构建简易摆的分步示例&#xff0c;请参阅简易摆建模&#xff08;Simscape Multibody&#xff09…

互联网+建筑工地源码,基于微服务+Java+Spring Cloud +Vue+UniApp开发

一、智慧工地概念 智慧工地就是互联网建筑工地&#xff0c;是将互联网的理念和技术引入建筑工地&#xff0c;然后以物联网、移动互联网技术为基础&#xff0c;充分应用BIM、大数据、人工智能、移动通讯、云计算、物联网等信息技术&#xff0c;通过人机交互、感知、决策、执行和…

Nginx优化(重点)与防盗链(新版)

Nginx优化(重点)与防盗链 Nginx优化(重点)与防盗链一、隐藏Nginx版本号1、修改配置文件2、修改源代码 二、修改Nginx用户与组1、编译安装时指定用户与组2、修改配置文件指定用户与组 三、配置Nginx网页的缓存时间四、实现Nginx的日志切割1、data的用法2、编写脚本进行日志切割的…

IntelliJ IDEA Community(社区版)下载及安装自用版

IntelliJ IDEA Community&#xff08;社区版&#xff09;下载及安装自用版 估计是个开发都逃脱不了用IDEA的命运吧&#xff0c;这么好的软件&#xff0c;白嫖了好多年。感恩。 现在很多公司已经不让用商业版的破解版了&#xff0c;所以这里讲的是社区版。 区别&#xff1a; 商…

【单调栈】LeetCode:2818操作使得分最大

作者推荐 map|动态规划|单调栈|LeetCode975:奇偶跳 本文涉及的基础知识点 单调栈分类、封装和总结 题目 给你一个长度为 n 的正整数数组 nums 和一个整数 k 。 一开始&#xff0c;你的分数为 1 。你可以进行以下操作至多 k 次&#xff0c;目标是使你的分数最大&#xff1a…

零基础入门网络安全必看的5本书籍(附PDF)

书中自有黄金屋&#xff0c;书中自有颜如玉。很多人学习一门技术都会看大量的书籍&#xff0c;经常也有朋友询问&#xff1a;零基础刚入门&#xff0c;应该看哪些书&#xff1f;应该怎么学&#xff1f;等等问题。今天就整理了5本零基础入门网络安全必看书籍&#xff0c;希望能帮…

hyper-v ubuntu 3节点 k8s集群搭建

前奏 搭建一主二从的k8s集群&#xff0c;如图所示&#xff0c;准备3台虚拟机。 不会创建的同学&#xff0c;可以看我上上篇博客&#xff1a;https://blog.csdn.net/dawnto/article/details/135086252 和上篇博客&#xff1a;https://blog.csdn.net/dawnto/article/details/135…

Python 数据分析 Matplotlib篇 plot设置线条样式(第2讲)

Python 数据分析 Matplotlib篇 plot设置线条样式(第2讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

Django(二)

1.django框架 1.1 安装 pip install django3.21.2 命令行 创建项目 cd 指定目录 django-admin startproject 项目名mysite ├── manage.py [项目的管理工具] └── mysite├── __init__.py├── settings.py 【配置文件&#xff0c;只有一部分…

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

【毕业快刊】IF12分,中科院2区,仅50天录用,17天见刊!国人占比第一!

计算机类 • 好刊解读 今天小编带来Springer旗下计算机领域高分快刊&#xff0c;如您有投稿需求&#xff0c;可作为重点关注&#xff01;后文有相关领域真实发表案例&#xff0c;供您投稿参考~ 01 期刊简介 Artificial Intelligence Review ✅出版社&#xff1a;Springer ✅…

Wireshark网络工具来了

Wireshark是网络包分析工具。网络包分析工具的主要作用是尝试捕获网络包&#xff0c;并尝试显示包的尽可能详细的情况。 Wireshark是一个免费开源软件&#xff0c;不需要付费&#xff0c;免费使用&#xff0c;可以直接登陆到Wireshark的官网下载安装。 在windows环境中&#x…

一个利用摸鱼时间背单词的软件

大家好&#xff0c;我是 Java陈序员。 最近进入了考试季&#xff0c;各种考试&#xff0c;英语四六级、考研、期末考等。不知道大家的英语四六级成绩怎么样呢&#xff1f; 记得大学时&#xff0c;英语四级都是靠高中学习积累的老本才勉强过关。 而六级则是考了多次&#xff…