沧州礼品行业网站设计,影视网站seo描述,网站备案照相,品牌运营包括哪些内容一、Linux关于文件操作的一些系统调用
1、open和close
我们在C语言阶段已经学过很多文件操作的函数#xff0c;今天我们要来看看操作系统中对于文件是怎么操作的。
1.1、open与close的用法
C语言的库函数中有很多关于文件操作的接口#xff0c;包括fopen、fclose、fprint…一、Linux关于文件操作的一些系统调用
1、open和close
我们在C语言阶段已经学过很多文件操作的函数今天我们要来看看操作系统中对于文件是怎么操作的。
1.1、open与close的用法
C语言的库函数中有很多关于文件操作的接口包括fopen、fclose、fprintf、fput……
但是系统中对于文件操作的系统调用接口就就没有这多多了今天我们最主要要看的是四个接口open和close、read和write。 这两个函数顾名思义一个是打开文件的接口一个是关闭文件的接口他俩的功能和 C语言的fopen和fclose的功能类似。但也有些不同的地方第一个不同的地方是返回值C语言的ffopen和flose的返回值是一个IFLE*的结构体指针但是open和close的返回值是一个整型这个返回值就是我们后面要细聊的“文件描述符”。
对于open接口它的三个参数需要解释一下第一个参数就是我们要打开的文件的路径和文件名这个其实和C语言中的fopen一样第二个参数是以什么方式打开第三个参数是打开文件时的权限。
我们先来看看他俩的基础用法 这open的第二个参数的传法有点儿奇怪这其实是一个类似于位图标记的传参方式我们后面再来简单的解释一下。上面的代码中的O_CREAT | O_RDONLY表示的是文件不存在就创建并且是以只读方式打开。
而close只需要传一个文件描述符所以我们现在虽然还没有细讲文件描述符是什么但是我们可以肯定的是文件描述符一定能定位一个文件。
上面的代码运行后我们当前目录中就创建出来了一个test.txt文件 1.2、关于open函数第二个参数flags的理解
open接口的第二个参数flags其实就是利用“位图”的思想以一个参数来达到类似C语言中可变参数列表的传参方式。
我们可以以一个小例子来理解一下 运行结果 从上面可以看出flags传参其实是通过位运算来达到的因为这里的Print1~4都只有一个比特位为1而且位置都是互不相同的。所以按位或之后会将阐述flags的对应比特位都置为1所以Print函数里面在匹配的时候只要对应位置为1的宏都会被匹配上也就达到了类似传递多个参数的目的啦。
1、read和write
有了open和close我们可以打开和关闭一个文件那接下来就是要对被打开的文件的进行操作了。
今天主要介绍两个对文件读和写的接口read和write 先来看看write接口write的第一个参数表示要将信息写入到哪一个文件描述符中(文件描述符定位一个文件)第二个参数表示信息从哪一个缓冲区中读取第三个参数表示的是缓冲区的大小。 首先如果要向文件中写入要先将文件的打开方式添加上O_WRONLY表示以只写方式打开。
运行之后我们就可以看见文件中就有了我们写入的内容了 而如果我们修改写入的内容我们会发现一个与C语言的文件操作不太一样的现象 我们会发现第二次写入的时候并没有将文件内容清空而是从文件起始位置开始写并覆盖原来的内容。不像C语言的fopen以写方式打开每次都会将文件内容清空。
而如果我们将打开文件的方式再加上O_TRUNC那么在每次写入的时候就会将文件先清空了 如果我们将O_TRUNC改成O_APPEND那么写入的方式就变成了“追加” 既然我们可以向文件中写入那肯定就能向文件中读取了这就要用到read接口了 read的参数和write的参数一样第一个参数fd表示要从哪一个文件描述符中读取第二个参数表示要将内容读取到哪一个缓冲区中第三个参数表示缓冲区的大小。 首先如果要想从文件中读取数据先要将文件的打开方式修改成O_RDONLY表示易只读方式打开。
运行后我们就可以在命令行中打印出来文件中的内容了 二、文件描述符的本质
1、操作系统怎么管理被打开的文件
在了解文件描述符的本质之前我们先打印出文件描述符看看它是个什么数字 我们看到当前的文件描述符是3是一个小整数。
不急既然进程可以打开一个文件那么进程就一定能打开多个文件我们打开多个文件试试看看多个文件的文件描述符是怎样的 从结果中我们可以看出文件描述符都是一些小整数并且是按顺序的先打开的文件的文件描述符就小后打开的就大。
文件描述符的本质
既然操作系统是管理硬件资源和软件资源的系统软件而我们打开的文件在没被使用时存住在磁盘中属于硬件资源被打开时我们知道先将文件内容加载到内存也属于硬件资源。所以操作系统肯定也要将被打开的文件进行管理。
那怎么管理呢我们知道操作系统也还是C语言写的所以操作系统管理资源其实也就是通过C语言编程管理资源那就“先描述再组织”咯。
所以操作系统会为每个被打开的文件创建一个类型为struct file的结构体对象这个file对象里存储着许多该文件的信息大小、创建时间、位置(磁盘位置)、修改时间等等。并且它们之间是以双向链表的形式连接起来的 那这是操作系统做的事我们的进程又怎么找到它打开的文件所对应的struct file结构体呢
我们知道操作系统会为每个运行的进程创建一个构造一个testk_struct的结构体对象这个对象中存着很多管理进程相关的信息而其中一个与我们今天所讲的文件相关的就是一个类型为struct flies_strcut* 类型的结构体指针该结构体指针指向的是一个类型为struct file_struct类型的结构体对象这个结构体对象就是一个“进程描述符表” 而这个文件描述符表里面最重要的一个成员就是一个类型为struct file* fd_array[]指针数组这个指针数组中存的就是一个一个指向该进程打开文件的struct file对象的指针 有数组就有下标。
所以文件描述符的本质其实就是一个数组的下标这个数组就是文件描述符表中的struct file* fd_array[]数组。
所以进程在访问对应的文件的时候其实是先拿着文件描述符找到文件描述符数组中对应下标中的地址再通过地址找到对应的struct file对象再通过这个对象访问到磁盘中的文件。 三、重定向
1、先看看重定向的现象
Linux操作系统中有一些指令可以将原本要打印到命令行中的内容打印到一个文件中这个其实就是我们今天要讲的重定向——输出重定向 上面的结果就是直接将ls的内容打印到了test.txt文件中而如果我们只输入一个 test.txt指令会发现原来的文件内容被清空了 所以虽然我们现在还没讲重定向的原理但是我们可能能推断出输出重定向也是一定是要先打开文件并且打开文件的方式是O_WRONLY | O_TRUNC。
而除了输出重定向外我们还有一个指令叫做“追加重定向” 很明显这个追加重定向在打开文件的时候一定是以O_WRONLY | O_APPEND的方式打开的。
而除了输出重定向我们还有一个指令叫做输入重定向他可以将原本要从键盘中读入并打印到显示器上的操作指令修改成从某一个文件中读取并打印到显示器上。
例如我们有一个指令cat它单独写的时候其实是从键盘中读入内容再打印到显示器上的 而如果加上它就会先从指定的文件中读取内容然后再打印到显示器上 所以我们现在就明白了我们以前使用的cat指令查看文件内容的时候其实是执行了一个输入重定向只是cat后面的操作符可以省略罢了
2、操作系统会为每个进程默认打开的三个文件
那上面这些重定向的原理是怎么样的不急我们还先了解一些一下操作系统为每一个进程默认打开的三个文件。
不知道大家是否会有一个疑问就是我们上面打印出来的文件描述符都是从3开始的而上面说过文件描述符的本质其实就是数组下标但是数组下标都是从0开始的那么0、1、2下标到哪去了呢
可能大家在C语言阶段都知道了我们C语言程序在运行时候系统都会默认为我们打开三个文件标准输入、标准输出、标准错误。也就是stdin、stdout、stderr。
那怎么证明呢
我们运行程序的时候什么也不做就打印出这三个文件描述符操作系统其实是支持我们直接打印出这三个文件的文件描述符的 我们在学习C语言的时候就知道stdin这些文件其实就是一个结构体指针而这个结构体指针执行的结构体之中就有一个_fileno的成员表示的就是改文件的文件描述符。 既然操作系统都帮我们直接打开了那我们肯定就可以直接使用了而我们的read和write是可以直接从文件描述符中读取的所以我们就可以看到下面这样奇怪的现象 当然我们也可以直接标准输入的文件描述符0配合read接口读取内容再打印到显示器stdout上 3、怎么在自己的代码中实现重定向
如果想要在我们写的代码中实现一个重定向我们先要来看看文件描述符的分配规则我们已经知道在一个C语言程序中新打开一个文件它别分配到的文件描述符是3 那我们知道stdin、stdout、stderr是程序启动时候就已经加载了的并且文件描述符是0、1、2我们也可以直接使用它们那我们当然也可以直接关闭它们了
就那标准输入来说如果我们直接关闭了标准输入那么新的开的文件被分配到的文件描述符又是多少呢 结果是0那我们现在直接关闭标准错误那是不是分配到的文件描述符是2呢 结果显然是的。
所以现在我们就可以输出一个结论文件描述符的分配规则是选择最低位置的没有被使用的文件描述符分配 那如果我们直接关闭标准输出呢文件描述符是不是1呢 但是我们看结果怎么什么东西都没有打印呢
这是因为我们已经把标准输出已经关了也就是把显示器直接关了信息当然没有打印到显示器上面了那信息打印到哪里了呢
其实信息打印到了文件中了但是如果我们就以上的代码运行后查看test.txt文件是看不到任何内容的 如果想要看到就必须先要刷新一下“用户级缓冲区”: 其实我们在将内容写到stdout中的时候是并没有直接写到stdout之中的而是在中间经过了一个C语言提供的用户及缓冲区只有缓冲区遇到刷新条件的时候才会将内容刷新到stdout中。这个刷新条件其中一个就是程序结束但是我们这里在程序结束之前其实就将stdout关闭了所以信息也就没能刷新到stdout中了。(这里的stdout其实是我们打开的test.txt文件)。
所以至此我们可以再输出一个结论文件描述符的本质其实就是修改文件描述符表中特定下标内的内容
如下图 如上图我们程序开始运行的时候就已经打开的这三个文件但是我们一开始就将stdout给关闭了所以根据文件描述符的分配规则我们新打开的test.txt文件分配到的文件描述符就是1。
而这个系统对于printfprintf是不知道的因为printf里面执封装了stdout或者说pritnf只封装了文件描述符1所以printf只认文件描述符1。你下层的操作printf都不管它只知道调用的时候就去文件描述符1里面去读取所以读到的就是文件test.tx里面的内容。
所以根据上面的结论我们可以将输入重定向给实现出来 scanf默认是从键盘也就是stdin中读取数据但现在我们文件描述符1的内容已经变成了test.txt文件的地址了所以scanf也就去test.txt中读取数据了。
但是每次实现重定向如果都要我们先关闭对应的标准输入出入中的一个未免有点太麻烦所以系统也给我们提供了一个文件描述符表级别的数字内容交换接口 它有两个参数oldfd和newfd两个参数都是文件描述符。
而dup2接口的作用就是让oldfd去覆盖newfd也就是最后只剩下oldfd。
所以如果我们想要用dup2来实现输出重定向的话就应该这样写 同样的输入重定向我们也可以一键实现了