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.mzph.cn/news/542492.shtml

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

相关文章

moore和mealy_Mealy机和Moore机的比较研究 目录

moore和mealyFinite automata may also have outputs corresponding to each input symbol. Such finite automata are known as finite automata with the output. 有限自动机还可以具有与每个输入符号相对应的输出。 这种有限自动机称为输出的有限自动机。 There are two fi…

oracle sys连接不上,oracle – 为什么我不能在SYS拥有的对象上创建触发器?

在尝试创建名为ghazal_current_bef_upd_row的触发器时&#xff1a;create trigger ghazal_current_bef_upd_rowbefore update on ghazal_currentfor each rowwhen (new.Rating < old.Rating)begininsert into ghazal_current_audit(GhazalName,Old_Rating,New_Rating)values…

大一python编程题_请教python编程问题(作业就剩这几道题了)

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼1. def cleanword(word):(用Python写出程序&#xff0c;使程序可以通过下面的doctest)""">>> cleanword(what?)what>>> cleanword("now!")now>>> cleanword(?"word!,$…

Linux笔记1-5 --用户

## 1 ## 用户理解用户就是系统使用者的身份在系统中用户存储为若干窜字符若干个系统配置文件用户信息涉及到的系统配置文件&#xff1a;/etc/passwd ###用户信息用户&#xff1a;密码&#xff1a;uid&#xff1a;gid&#xff1a;说明&#xff1a;家目录&#xff1a;用户使用…

python运维开发培训_运维架构师-Python 自动化运维开发-014

运维架构师-Python 自动化运维开发-014九、标准数据类型1、为什么编程语言中要有类型类型有以下几个重要角色&#xff1a;对机器而言&#xff0c;类型描述了内存中的电荷是怎么解释的。对编译器或者解释器而言&#xff0c;类型可以协助确保上面那些电荷、字节在程序的运行中始终…

JavaScript | 演示函数中按值调用的示例

Here, we are designing a function named change() that has an argument and we are trying to change the value of the passed argument inside the function, but it will not effect to the main/actual argument that is passed as the argument while calling. 在这里&…

机器视觉支架制作(带效果测试)

图像处理系统中&#xff0c;镜头、光源的选配&#xff0c;对于最后能否产生稳定的识别效果至关重要。而搭载镜头、光源的是支架。机器视觉的支架一般都是根据项目的具体需要进行配置的&#xff0c;搜索淘宝能够得到一些商品。 这些支架形状不一&#xff0c;价格在数百元到千元之…

c语言中将整数转换成字符串_在C语言中将ASCII字符串(char [])转换为十六进制字符串(char [])...

c语言中将整数转换成字符串Given an ASCII string (char[]) and we have to convert it into Hexadecimal string (char[]) in C. 给定一个ASCII字符串(char [])&#xff0c;我们必须在C中将其转换为十六进制字符串(char [])。 Logic: 逻辑&#xff1a; To convert an ASCII …

redis rdb aof区别_理解Redis的持久化机制:RDB和AOF

什么是Redis持久化?Redis作为一个键值对内存数据库(NoSQL)&#xff0c;数据都存储在内存当中&#xff0c;在处理客户端请求时&#xff0c;所有操作都在内存当中进行&#xff0c;如下所示&#xff1a;这样做有什么问题呢&#xff1f;注 意文末有&#xff1a;3625页互联网大厂面…

python--批量下载豆瓣图片

溜达豆瓣的时候&#xff0c;发现一些图片&#xff0c;懒得一个一个扒&#xff0c;之前写过c#和python版本的图片下载&#xff0c;因此拿之前的Python代码来改了改&#xff0c;折腾出一个豆瓣版本&#xff0c;方便各位使用 # -*- coding:utf8 -*- import urllib2, urllib, socke…

linux touch权限不够,Linux下的Access、Modify、Change , touch的使用以及权限问题

每个文件在linux下面都会记录许多的时间参数&#xff0c;其实是有三个主要的变动时间&#xff0c;那么&#xff0c;这三个时间的意义又是什么&#xff1f;下面我们来介绍&#xff1a;* Modify time(mtime)当该文件的“内容数据”更改时&#xff0c;就会更新这个时间。内容数据指…

scala 获取数组中元素_从Scala中的元素列表中获取随机元素

scala 获取数组中元素We can access a random element from a list in Scala using the random variable. To use the random variable, we need to import the Random class. 我们可以使用随机变量从Scala中的列表访问随机元素。 要使用随机变量&#xff0c;我们需要导入Rand…

ubuntu14.04下安装cudnn5.1.3,opencv3.0,编译caffe及配置matlab和python接口过程记录

已有条件: ubuntu14.04cuda7.5anaconda2(即python2.7)matlabR2014a 上述已经装好了,开始搭建caffe环境. 1. 装cudnn5.1.3,参照:2015.08.17 Ubuntu 14.04cuda 7.5caffe安装配置 详情:先下载好cudnn-7.5-linux-x64-v5.1-rc.tgz安装包(貌似需要官网申请) 解压: tar -zxvf cudnn-7.…

python excel导入oracle数据库_【Python代替Excel】12:Python操作oracle数据库

日常工作中&#xff0c;如果有数据库权限&#xff0c;那么在oracle中提取数据、在Python中处理是比较方便的。Python也提供了一个库专门操纵数据库。今天就专门来讲讲如何在Python中操作数据库。准备工作需要工具&#xff1a;oracle、PL/SQL、Pythonimport cx_Oracle如果用anac…

Linux 金字塔 的shell命令,linux下保留文件系统下剩余指定数目文件的shell脚本

原文出处&#xff1a;http://www.jbxue.com/article/13808.html (原创文章&#xff0c;转载请注明出处)本节内容&#xff1a;保留文件系统下剩余指定数目的文件例子&#xff1a;#!/bin/bash#-------------------------------#Description: Back up your files#site: www.jbxue.…

前端干货之JS最佳实践

持续更新地址 https://wdd.js.org/js-best-pr... 1. 风格 一千个读者有一千个哈姆雷特&#xff0c;每个人都有自己的code style。我也曾为了要不要加分号给同事闹个脸红脖子粗&#xff0c;实际上有必要吗&#xff1f; 其实JavaScript已经有了比较流行的几个风格 JavaScript Sta…

python requests和urllib_Python——深入理解urllib、urllib2及requests(requests不建议使用?)...

深入理解urllib、urllib2及requestsPython 是一种面向对象、解释型计算机程序设计语言&#xff0c;由Guido vanRossum于1989年底发明&#xff0c;第一个公开发行版发行于1991年&#xff0c;Python 源代码同样遵循 GPL(GNU General PublicLicense)协议[1] 。Python语法简洁而清晰…

ssh查找linux端口,linux – 查找当前连接的端口号SSH

我正在使用SSH连接创建一个本地模拟器(未连接到Internet).我已经开始使用特定范围的端口号进行sshd,并对一系列设备进行NAT处理.我必须找到当前连接的端口号.OS CentOS 5.5OpenSSH 6.1我做了以下事情.它适用于正常使用(手动用户).但是当尝试严格的测试(自动化)时,似乎有时找不到…

this.getstate_Java线程类Thread.State getState()方法(带示例)

this.getstate线程类Thread.State getState() (Thread Class Thread.State getState()) This method is available in package java.lang.Thread.getState(). 软件包java.lang.Thread.getState()中提供了此方法。 This method is used to return the state of this thread. 此方…

Java资源大全中文版(Awesome最新版)

来源&#xff1a;http://www.cnblogs.com/best/p/5876559.html 目录 业务流程管理套件字节码操作集群管理代码分析编译器生成工具构建工具外部配置工具约束满足问题求解程序持续集成CSV解析数据库数据结构时间日期工具库依赖注入开发流程增强工具分布式应用分布式数据库发布文档…