做影视网站如何通过备案,为企业开发网站,长葛网站制作,网站建设做的快目录 一、共识原理
二、回顾C语言文件函数
1.fopen
2.fwrite 3.fclose
三、文件系统调用
1.open
2.write
3.访问文件的本质
4.stdinstdoutstderror
5.文件的引用计数 四、重定向
1.文件描述符的分配规则
2. 输出重定向
3.重定向系统调用
4.…目录 一、共识原理
二、回顾C语言文件函数
1.fopen
2.fwrite 3.fclose
三、文件系统调用
1.open
2.write
3.访问文件的本质
4.stdinstdoutstderror
5.文件的引用计数 四、重定向
1.文件描述符的分配规则
2. 输出重定向
3.重定向系统调用
4.追加重定向
5.输入重定向
6.1号VS2号 一、共识原理
1.文件内容 属性
我们关注文件不仅要关注它的内容也要关心它的属性一个文件即使没有内容它的大小也不是空的因为该文件的属性也是会占用空间的。
2.文件分为打开的文件和没打开的文件
3.打开的文件
谁在打开文件我们在代码中写一个fopenfwrite最终都会变成进程因此是进程在打开文件。
研究打开的文件---本质是研究进程和文件的关系文件被打开必须被加载到内存内容和属性都被加载到内存。
进程打开的文件 1 : n。一个进程是可以打开多个文件的因此进程和打开的文件关系是1:n的
操作系统内部一定存在大量的被打开的文件OS要不要管理这些打开的文件呢-怎么管理-先描述在组织在内核中一个被打开的文件都必须有自己的文件打开对象包含文件的很多属性。
4.没打开的文件在哪里放着呢在磁盘上。我们最关注什么问题没有被打开的文件非常多我怎么找到我要打开的文件因此文件必须被分门别类的归置好---方便我们快速的进行增删查改---快速找到文件。
二、回顾C语言文件函数
1.fopen fopen的第一个参数是文件名如果不带路径默认就在当前路径下打开如果带了绝对路径就在绝对路径下打开第二个参数是以什么方式打开是读呢还是写呢还是追加写下面我们打开了log.txt这个文件在当前路径下是没有log.txt这个文件的fopen如果打开不存在的文件会新建在打开是以读方式打开。果然在我们的当前路径下创建了一个log.txt文件。 下面的问题是当前路径什么是当前路径呢当前路径就是进程的当前路径如果我们把进程的当前路径修改了是不是就可以把文件新建到其它目录下呢那怎么修改进程的当前路径呢chdir。 可以看到当把 进程的当前工作目录修改了之后创建的文件log.txt的路径也随之发生了变化。
2.fwrite fwrite的第一个参数是写入内容的起始地址第二个参数是写多少个第三个是当做一个几部分写入第四个是要写入那个文件里。 这里我们直接把message当做一个整体写入到fp里也就是log.txt文件里这里有一个问题就是strlen(message)需要1吗strlen求字符串长度求到\0就结束了也就是说如果1就是把\0也写入到文件里这里需要吗答案是不需要因为\0是C语言的要求C语言不知道字符串从哪里结束才要\0但是和我文件有啥关系
运行一下在查看log.txt里的内容发现果然hello world被写入到文件中了。 下面我们给log.txt里面多加几个2字符然后在运行一下。 我们发现原来的内容全部没有了这说明w方式在写入之前都会对文件进行清空处理然后再从头开始写。 这个重定向是不是就是把log.txt打开然后以w的方式把hello world 写进去呀因此我们前面不加任何东西就是以w方式打开log.txt文件但是不写任何内容此时就把log.txt清空了。 那如果我们就想要追加写呢可以以a的方式打开a在文件结尾追加写。 3.fclose
如果我们把一个文件使用完毕了就需要使用fclose关闭一下这个文件。它的使用非常简单把打开的文件指针传进去即可。 下面就有一个问题了文件是在磁盘上的磁盘是外部设备我们上述的fwritefopen包括fclose其实实在访问硬件那我们用户能直接访问硬件吗不能操作系统不相信任何人我们要访问硬件必须通过操作系统提供的系统调用因此我们上述写的库函数一定要封装系统调用什么printf/fscanf/fwrite/fread......这些库函数都是封装了系统调用的。下面我们就学习一下这些文件相关的系统调用。
三、文件系统调用
1.open 给我们提供了两个打开文件的系统调用我们只要学会下面那个参数多的即可参数少的是参数多的一子集。
第一个参数是要打开的文件路径如果没有带路径默认就是进程的当前路径。第二个参数是以什么方式打开第三个参数是文件的权限可以设置创建文件的权限。
返回值如果失败返回0的数成功返回0的数。
我们先来以只读方式打开只读方式打开传递O_WRONLY这个宏即可。 我们发现我们直接打开文件失败了这是为啥呢这是因为如果打开的文件不存在并不会给新建因此此时要在传递一个宏O_CREAT 表示如果文件不存在就创建。此时我们可以看到就创建了log.txt文件。 但是这里细心的同学会发现有问题就是我明明文件权限设置的是666呀文件权限应该是rw-rw-rw但这里却是rw-rw-r--不是666而是664这是因为权限掩码。如果你说我就要创建权限是666的文件呢可以有设置掩码的函数umask直接把掩码设成0即可。 题外话open的第二给参数是一个整数呀可是我们给他传递了O_WRONLY和O_CREAT两个选项是咋做到的呢 其实就是简单的位运算。
#include stdio.h
#include string.h
#include sys/types.h
#include sys/stat.h
#include unistd.h
#include fcntl.h#define ONE (1 0) //1
#define TWO (1 1) //2
#define THREE (1 2)//4
#define FOUR (1 3) // 8void show(int flags)
{if(flags ONE) printf(hello funcion1\n);if(flags TWO) printf(hello funcion2\n);if(flags THREE) printf(hello funcion3\n);if(flags FOUR) printf(hello funcion4\n);
}int main()
{show(ONE);printf(----------------------------\n);show(TWO);printf(----------------------------\n);show(ONE | TWO);printf(----------------------------\n);show(TWO | THREE);printf(----------------------------\n);show(ONE | THREE | FOUR);printf(----------------------------\n);return 0;
}
通过位运算我们就通过给1给标记位传递不同的值让它表示多种信息。 2.write 第一个参数就是对应文件的id就是open的返回值第二个参数是要写入的起始位置第三个参数是写入的长度。 下面运行一下看下结果。我们运行了两次打印出来的都是一样的之后我手动往log.txt里面加了一串b在运行发现那一串b并没有被清空因此我们可以发现写入的时候是从头覆盖写的但是并不会对文件做清空处理。 那如果我也想做到清空呢 我们需要在打开文件的时候在加一个宏O_TRUNC截断的意思就是每次打开文件都做清空。 如果我想做到如同C语言中的a一样追加写呢还有一个宏O_APPEND。如果带了O_APPEND就要把O_TRUNC去掉追加和清空是冲突的。 我们现在用的都是系统调用fopen就是用open封装的w方式就会被转化成O_WRONLY | O_CAEAT | O_TRUNC.
这都没问题但是有一个东西我们一直没谈就是open的返回值可是一个int啊但是fopen的返回值是一个FILE类型的指针这两个玩意八竿子都打不着有啥关系呢下面我们就要谈谈文件的管理了。
3.访问文件的本质
操作系统里会有很多个进程每个进程可能要打开很多个文件这些被打开的文件要不要被被管理起来呢要先描述在组织。操作系统用struct file描述一个被打开的文件信息struct file里应该包含什么呢1.文件在磁盘的什么位置 2.文件的基本属性(权限大小读写位置谁打开的...)3.文件的内核缓冲区总之这个结构体里包含了文件的大部分信息类似的struct file里还有一个strcut* next指针每打开一个文件内核创建一个struct file然后用strcut* next指针链接到一起此时操作系统要对文件进行增删查改就是对文件链表的增删查改如果要添加一个文件就在文件链表里插入如果要关闭文件就是把文件的所有属性释放掉从链表删除再把数据刷新到磁盘上。
一个进程可能打开多个文件那些文件是被那个进程打开的呢我怎么知道所以必然要建立进程PCB和打开文件struct file的对应关系。
那怎么建立的呢在进程PCB里会存在一个指针struct file_struct *f这个指针指向struct file_struct结构体这个结构体里面会包含一个数组数组的名字叫做 struct file* fd_arrdy[]这个数组显然是个指针数组这个数组的下标从0开始数组每个元素的类型是 struct file*所以当我们打开一个文件的时候操作系统会创建好struct file然后在这个数组里分配一个下标把创建好的struct file的地址填到这个下标上以后每个进程就可以根据这个文件描述符表就能把打开的文件找到。 所以为啥open的返回值是个整数呢open会创建一个struct file然后在当前进程的文件描述符表里找一个没有用过的下标把创建的struct file的地址填进去然后把这个数组下标返回给用户因此这个int本质就是一个数组的下标。
所以在我们写的时候必须得把这个数组的下标传进去进程通过指针找到文件描述符表然后在通过这个数组下标索引到文件的地址从而往该文件写入。 文件和进程产生关联是通过数组下标关联的这样就可以做到文件和进程的解耦。
这个文件描述符可还没见过呢下面我们看看文件描述符是几呢 我们可以看到是3 下面我们多打开几个文件看看。 可以看到是连续的整数。但是问题来了0、1、2哪里去了呢
4.stdinstdoutstderror
C程序在默认启动的时候会打开三个标准输入输出流(文件)stdin(键盘文件)stdout(显示器文件)stderr(显示器文件)。所以我们在打印的时候为啥要包含stdio.h呢std就是标准的意思io就是输入输出是C语言会打开吗任何语言都会打开这三个文件这不是C语言的特性是操作系统的特性电脑在打开的时候键盘和显示器文件默认就会打开进程只需要把打开键盘显示器文件的地址填入即可。因此0、1、2是被这3个家伙占着呢怎么验证呢
我们直接用write往1和2里面写入。1和2是显示器文件哦直接打印出来了。 下面用read接口验证一下0号键盘文件。 为啥卡住了呢因为0是键盘文件在等待键盘就绪 现在问题又来了哦可C语言的返回值是FILE指针啊这和int有啥关系呢FILE是C语言的内置类型吗不是FILE是C库里封装的一个结构体这个结构体里面一定包含了该文件的数组下标下面验证一下。 5.文件的引用计数
关闭文件的系统调用是close现在我们把下标为1的文件stdout关闭。然后打印了stdout和stderr的filenofprintf的用法和printf基本一致只不过前面加了一个文件描述符而已。我们可以看到,printf没有打印在显示器上打印出来这肯定和我们关闭close有关因此printf底层肯定访问了stdout显示器文件然后我们把stdout关闭因此在屏幕上就打印不出来了。但是为啥stderr文件能打印出来呢?stdout和stderr都指向显示器文件因此显示器文件的引用计数就是2如果再来一个指向显示器引用计数就会继续增加。当把stdout关闭引用计数--变成了1因此stderr还是能打印出来关闭文件是把该下标的地址填成NULL。 printf也是有返回值的其实stdout已经关闭了但printf以为自己打印成功了因此就把打印的字符个数13返回了过来。 四、重定向
1.文件描述符的分配规则
我们把2号文件描述符关闭之后新创建的文件描述符就是2因此我们可以得知文件描述符的分配规则就是从0下标开始寻找最小的没有被使用的数组位置它的下标就是新的文件描述符。 2. 输出重定向
下面我们把2号文件描述符关闭然后创建一个文件fd之后调用write往1号文件里写入5次。 1号文件描述符对应的显示器文件因此我们把内容写入到了显示器上。
下面我们把1号文件描述符关闭然后创建一个文件fd之后调用write往1号文件里写入5次。 我们发现本来应该向1号文件描述符也就是显示器写入的信息居然写到了 log.txt里这是因为我们把1号文件描述符关闭了然后又创建了一个文件这个文件根据分配规则就分配到了1号描述符然后我们往1号描述符里面写就写到了文件里。本来应该往显示器写却写入到了文件里这就叫输出重定向。这里我们可以画张图理解一下。 这样写不是不可以但是要先关一次然后在打开一个文件当别人问你为啥这么做的时候你就要和别人解释半天有没有一写系统调用能帮我们做这件事呢打开文件就行了然后重定向调用函数就行有这样的接口吗是有的。
3.重定向系统调用 是有dup,dup2,dup3系统调用的常用的就dup2因此我们详细谈谈dup2dup就是duplicate复制的意思参数是2个文件描述符一个旧的文件描述符一个新的文件描述符。
那么问题来了是把旧的文件描述符内容拷贝给新的文件描述符内容还是新的文件描述符内容拷贝给旧的文件描述符内容呢这样说吧dup2之后2个文件描述符内容全都变成newfd的还是oldfd的常理来看应该是全都变成newfd吧但实际结果是全都变成oldfd这里挺奇怪的是吧也不懂老外为啥这样起名字。因此如果我们要让1号文件描述符内容是新建的文件描述符fd的内容要怎么传参呢就要dup2(fd, 1)这样传参。下面我们来使用一下。 我们用dup2就实现了同样的效果。 上面的代码忘记close了要记得close文件。
4.追加重定向
上面的代码中文件是O_APPEND方式打开我们多运行几次这个log.txt就会越来越大这就叫追加重定向。
5
5.输入重定向
read的第一个参数是文件描述符要读哪个文件第二个参数是读到哪第三个是读多少个字节返回值是实际读了多少个字节。
下面我们读取文件方式改为只读方式然后读取0号文件也就是键盘文件阻塞住了我们往键盘输入内容然后回显出来了。 下面我们把0号重定向一下。 我们可以看到本来应该从键盘文件标准输入变读取变为了从指定的文件读取这就叫输入重定向。
重定向的本质就是在内核里对文件的地址做拷贝。 6.1号VS2号
我们直接往1号和2号文件描述符里打印运行可以看到没问题都打印出来了。但是当 ./myfile normal.txt也就是把1、2号文件的打印输出重定向到normal.txt文件的时候为啥2号文件的内容没有重定向到nomral.txt里呢为啥cat只能看到1号文件描述符重定向的内容呢这是因为是把1号显示器的内容重定向到了文件里和我2号文件描述符有啥关系 如果我想把正常消息打印到一个文件错误消息打印到一个文件该咋办呢可以进行下图中的操作这里其实非常直观就是把1号文件的内容重定向到normal.log2号文件的内容重定向到err.log里。默认不写的话是把1号重定向到文件。下面就多出来了err.log文件。 那如果我要把1、2的内容重定向到一个文件里咋办呢
默认不写就是1号文件重定向到all.log文件指令是从左往右执行的1(取地址1)的意思就是把1号文件的内容写入到2号文件里因为左边的指令已经执行完了1号文件的地址已经是all.log的地址了然后把1号文件内容拷贝给2此时1和2都指向了all.log文件。最后就能都写入到all.log里了。