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下,一切皆文件!
相关文章:

Linux之基础I/O
目录 一、C语言中的文件操作 二、系统文件操作I/O 三、文件描述符fd 1、文件描述符的引入 2、对fd的理解 3、文件描述符的分配规则 四、重定向 1、重定向的原理 2、重定向的系统调用dup2 五、Linux下一切皆文件 一、C语言中的文件操作 1、打开和关闭 在C语言的文…...

Linux开发工具——gcc篇
gcc的使用 文章目录 gcc的使用 历史遗留问题(普通用户sudo) gcc编译过程 预处理(进行宏替换) 编译(生成汇编) 汇编(生成机器可识别代码) 链接(生成可执行文件或库文件&a…...
C#通讯——关于Winform中的简单的Http服务器与客户端
C#通讯——关于Winform中的简单的Http服务器与客户端 前言一、Http是什么?二、简单的Http服务器三、简单的Http客户端四、实际调用五、Winform中Http服务器和WebApi的区别? 前言 在实际项目中通讯的交互的过程中,遇见数据传输时同事和我说用…...
Mendelson AS2 介绍下载和配置
最近与一家国外公司做EDI对接,并且EDI通讯工具是基于AS2协议的。目前开源的as2的开源项目有openas2,Mendelson AS2,和国人写的freeas2但是,现在freeas2已经被从开源中国不能下载了,变为收费的版本了。 如果你需要使用基于AS2协议…...

旅游海报图怎么做二维码展示?扫码即可查看图片
现在旅游攻略的海报可以做成二维码印刷在宣传单单页或者分享给用户来了解目的地的实际情况,出行路线、宣传海报等。用户只需要扫描二维码就可以查看内容,更加的方便省劲,那么旅游海报的图片二维码制作的技巧有哪些呢?使用图片二维…...
常用git指令
初始化Git仓库:git init 添加文件到暂存区:git add <file> 提交更改到本地仓库:git commit -m "commit message" 查看本地仓库的提交历史:git log 创建分支:git branch <branch_name> 切换分支:git checkout <branch_name> 查看所有分支:git…...

【FPGA】分享一些FPGA协同MATLAB开发的书籍
在做FPGA工程师的这些年,买过好多书,也看过好多书,分享一下。 后续会慢慢的补充书评。 【FPGA】分享一些FPGA入门学习的书籍【FPGA】分享一些FPGA协同MATLAB开发的书籍 【FPGA】分享一些FPGA视频图像处理相关的书籍 【FPGA】分享一些FPGA高速…...

幺模矩阵-线性规划的整数解特性
百度百科:幺模矩阵 在线性规划问题中,如果A为幺模矩阵,那么该问题具有最优整数解特性。也就是说使用单纯形法进行求解,得到的解即为整数解。无需再特定使用整数规划方法。 m i n c T x s . t . { A x ≥ b x ≥ 0 \begin{align*} min \quad…...
数据分析思维
Why&What 数据分析是为了驱动决策赋能业务。在数据分析过程中需要对目标进行拆解量化,如何拆解量化目标便是数据分析思维。 在任务拆解过程中使用的软件、统计模型、分析方法等为分析工具和手段,如何在恰当的场景合理的使用这些工具、模型、方法、手…...

C++ boost planner_cond_.wait(lock) 报错1225
1.如下程序段 boost unique_lock doesn’t own the mutex: Operation not permitted 问题: 其中makePlan是一个线程。这里的unlock导致错误这个报错 boost unique_lock doesn’t own the mutex: Operation not permitted bool navigation::makePlan(){ //cv::named…...

LeetCode刷题--- 字母大小写全排列
个人主页:元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 http://t.csdnimg.cn/6AbpV 数据结构与算法 http://t.csdnimg.cn/hKh2l 前言:这个专栏主要讲述递归递归、搜索与回…...
165. 小猫爬山(DFS之剪枝与优化)
165. 小猫爬山 - AcWing题库 翰翰和达达饲养了 N 只小猫,这天,小猫们要去爬山。 经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。 翰翰和达达只好花钱让它们…...

【Linux系统基础】(6)在Linux上大数据NoSQL数据库HBase集群部署、分布式内存计算Spark环境及Flink环境部署详细教程
大数据NoSQL数据库HBase集群部署 简介 HBase 是一种分布式、可扩展、支持海量数据存储的 NoSQL 数据库。 和Redis一样,HBase是一款KeyValue型存储的数据库。 不过和Redis设计方向不同 Redis设计为少量数据,超快检索HBase设计为海量数据,…...

多维时序 | MATLAB实CNN-BiGRU-Mutilhead-Attention卷积网络结合双向门控循环单元网络融合多头注意力机制多变量时间序列预测
多维时序 | MATLAB实现CNN-BiGRU-Mutilhead-Attention卷积网络结合双向门控循环单元网络融合多头注意力机制多变量时间序列预测 目录 多维时序 | MATLAB实现CNN-BiGRU-Mutilhead-Attention卷积网络结合双向门控循环单元网络融合多头注意力机制多变量时间序列预测预测效果基本介…...
vs快捷键
ctrlMo 折叠代码块 ctrlML 打开代码块...
linux 内核时间计量方法
定时器中断由系统定时硬件以规律地间隔产生; 这个间隔在启动时由内核根据 HZ 值来编 程, HZ 是一个体系依赖的值, 在 <linux/param.h>中定义或者它所包含的一个子平台文 件中. 在发布的内核源码中的缺省值在真实硬件上从 50 到 1200 嘀哒每秒, 在软件模拟 器中往下到 24.…...
循环神经网络中的梯度消失或梯度爆炸问题产生原因分析(二)
上一篇中讨论了一般性的原则,这里我们具体讨论通过时间反向传播(backpropagation through time,BPTT)的细节。我们将展示目标函数对于所有模型参数的梯度计算方法。 出于简单的目的,我们以一个没有偏置参数的循环神经…...

JWT signature does not match locally computed signature
1. 问题背景 最近在协助团队小盆友调试一个验签问题,结果还“节外生枝”了,原来不是签名过程的问题,是token的问题。 当你看到“JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not…...

vitepress项目使用github的action自动部署到github-pages中,理论上可以通用所有
使用github的action自动部署到github-pages中 创建部署的deploy.yml文件,在项目的根目录下面 .github\workflows\deploy.yml 完整的代码:使用的是pnpm进行依赖安装。 name: 部署VitePresson:push:branches:- docs # 这段是在推送到 docs 分支时触发该…...
Python爬虫---解析---JSONPath
Xpath可以解析本地文件和服务器响应的文件,JSONPath只能解析本地文件 1. 安装jsonpath:pip install jsonpath 注意:需要安装在python解释器相同的位置,例如:D:\Program Files\Python3.11.4\Scripts 2. 使用步骤 2.1 导入&…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...