<Linux系统复习>文件描述符

一、本章重点

1、进程和打开文件的关系

2、简单复习c语言文件操作

3、介绍系统调用:open、clos、write、read

4、理解文件描述符

5、文件描述符分配规则

6、理解stdin、stdout、stderr与fd的关系

7、理解linux下一切皆文件

8、理解重定向的本质

9、理解stdin和stdout的区别

10、理解缓冲区

01 进程和打开文件的关系

1、需要明确一点的是打开文件是由进程打开的,那么进程可以打开多个文件吗?当然可以,打开了多个文件,那么就需要将它们管理起来,操作系统是怎么管理这些打开文件的呢?先创建struct file用来描述打开的文件,然后将多个struct file用指针链接起来。

<Linux系统复习>文件描述符_第1张图片

 那么struct file里面包含了什么呢?

1、部分磁盘文件的属性(权限、路径、最近一次修改时间等)

2、read方法、write方法。

........

关于struct file的疑惑后面会解答。

02 简单复习c语言文件操作

1、fwrite

<Linux系统复习>文件描述符_第2张图片

<Linux系统复习>文件描述符_第3张图片

<Linux系统复习>文件描述符_第4张图片

2、fread

<Linux系统复习>文件描述符_第5张图片

<Linux系统复习>文件描述符_第6张图片

3、fprintf

<Linux系统复习>文件描述符_第7张图片

<Linux系统复习>文件描述符_第8张图片

03 介绍系统调用接口:open 、close、write、read

1、open、close、write

<Linux系统复习>文件描述符_第9张图片

<Linux系统复习>文件描述符_第10张图片

 <Linux系统复习>文件描述符_第11张图片

可以看到有两个open系统调用,它们是重载关系。

ssize_t 是 typedef int ssize_t

<Linux系统复习>文件描述符_第12张图片

<Linux系统复习>文件描述符_第13张图片

 其中O_WRONLY:代表只写,O_CREATE:代表没有该文件就创建,O_TRUNC:如果写之前文件有内容就清空文件所有内容。

0666代表创建文件的默认权限,但为什么创建出来的demo.txt权限是664?

答案:因为umask为0002,最终权限等于664 & (~002),一般我们创建文件的时候,都会让umask等于0,不用系统的默认权限,因为有的操作系统的默认权限可能不是0002,因此使用umask(0)这个函数调用,可以让我们的程序更完美。

<Linux系统复习>文件描述符_第14张图片

<Linux系统复习>文件描述符_第15张图片

 <Linux系统复习>文件描述符_第16张图片

我们可以将O_WRONLY、O_CREATE、O_TRUNC看做c语言 “w” 的打开方,

O_WRONLY、O_CREATE、O_APPEND。看做c语言fopen中 “a” 的打开方式。

<Linux系统复习>文件描述符_第17张图片

<Linux系统复习>文件描述符_第18张图片

2、open、close、read

<Linux系统复习>文件描述符_第19张图片

<Linux系统复习>文件描述符_第20张图片

 <Linux系统复习>文件描述符_第21张图片

04 文件描述符

1、什么是文件描述符。

open系统调用返回的就是一个文件描述符,我们调用write和read也需要文件描述符。

<Linux系统复习>文件描述符_第22张图片

 进程的task_struct中有一个指向struct files_struct结构体的指针,struct files_struct中有一个数组array_fd[ ],该数组存储了该进程打开文件的struct file结构体,所谓的文件描述符,其实是数组下标,进程可以通过数组下标,找到打开文件的结构体,struct file里有文件的读写方法,就能够将进程的数据写到打开文件对于的磁盘文件,或者从磁盘文件读入到进程的数据中。

1、简单介绍:一个磁盘文件被打开,操作系统做了什么?

创建一个struct file内存文件结构,然后将它的地址放入进程的文件描述符表中,最后返回数组下标给用户。

05 文件描述符的分配规则

1、文件描述符的分配规则:优先分配最小的、未分配的数组下标。

<Linux系统复习>文件描述符_第23张图片

为什么这个进程的文件描述符是从3开始的,如果是数组下标的话,不是应该从0开始吗?

答案:0 1 2被分配给键盘、显示器、显示器了。一个进程默认会打开3个流,stdin、stdout、stderr,分别对于0 1 2.

如果关闭0,那么按照规则来说,fd应该是0。

<Linux系统复习>文件描述符_第24张图片

06 理解stdin、stdout、stderr与fd的关系

<Linux系统复习>文件描述符_第25张图片

 stdin是FILE* 类型,那么FILE是什么呢?它是一个结构体。

我们都知道可以这样向屏幕打印字符

<Linux系统复习>文件描述符_第26张图片

 也可以这样向屏幕打印。

<Linux系统复习>文件描述符_第27张图片

 我们也知道c语言接口和系统接口的关系,c语言库函数是对系统接口的一层封装。

<Linux系统复习>文件描述符_第28张图片

 为什么c语言接口要对系统接口进行一层封装呢?

①、系统接口不好用,对使用者要求高一点。

②、对系统接口进行封装,可以使得c语言函数在大多数操作系统都能够运行,移植性好。 

现在我们大概可以猜测出FILE里面包含了fd,如何证明?

<Linux系统复习>文件描述符_第29张图片

<Linux系统复习>文件描述符_第30张图片

07 理解linux下一切皆文件

<Linux系统复习>文件描述符_第31张图片

 驱动层:由于不同厂商制作的硬件可能有差异,操作硬件的方法可能不同,所以在操作系统和硬件之间加了一个驱动层,驱动层提供方法给操作系统,这样不管什么硬件,只要安装了相关驱动,一般而言操作系统都能够使用该硬件。

struct file是打开文件的数据结构,进程可以通过fd找到struct file,然后通过struct file里面的方法可以对硬件或者普通文件进行读写,但对每个硬件或者普通文件的读写方法肯定是不同的,因此就需要别人提供方法的地址给它,struct file就不用管它要调用哪种方法了,而是直接使用read和write。这种调用同一个函数,通过传的函数地址不同,展示的功能不同,不就是多态吗?那么struct file就能够以一种统一的视角看待硬件和普通文件了,可以把键盘、显示器、网卡、磁盘都看做文件。

08 理解重定向的本质

1、先复习一下从定向

①、输出从定向

<Linux系统复习>文件描述符_第32张图片

②、输入从定向

<Linux系统复习>文件描述符_第33张图片

③、追加从定向

<Linux系统复习>文件描述符_第34张图片

 

2、看看下面的这种现象

<Linux系统复习>文件描述符_第35张图片

<Linux系统复习>文件描述符_第36张图片

如何理解这种现象?

<Linux系统复习>文件描述符_第37张图片

 可以把printf(“%s”,"谢谢你\n")与fprintf(stdout,“%s”,"谢谢你\n")等价,又因为stdout包含了1,本来1是指向显示器的,关闭1后打开log.txt,根据文件描述符的分配规则,1指向的是log.txt,可是stdout可不管你是不是显示器,我只负责向文件描述符为1的打开文件写数据。

自然printf输入的数据到了log.txt磁盘文件中,这就是重定向的底层原理。

2、那么只能通过关闭0、1文件描述符,打开新的文件来实现重定向吗?我们还可以使用dup2()来实现重定向。

<Linux系统复习>文件描述符_第38张图片

 注意的是:它是将oldfd拷贝到newfd,不要记反了。

<Linux系统复习>文件描述符_第39张图片

 <Linux系统复习>文件描述符_第40张图片

09 stdin和stdout的区别

1、现象一

<Linux系统复习>文件描述符_第41张图片

<Linux系统复习>文件描述符_第42张图片

 似乎并没有什么区别,只要一个stdout不就好了吗?

<Linux系统复习>文件描述符_第43张图片

 一个打印到屏幕上,一个输出到log.txt文件里,如何解释这种现象?

解释:

因为重定向的缘故,1文件描述符不再指向显示器,而是指向log.txt,因此标准输入会打印到log.txt中,但2文件描述符并未重定向,依然指向的是屏幕,因此标准错误打印到屏幕上。从这里我们也知道了,>重定向的只是1文件描述符。

2、现象二

<Linux系统复习>文件描述符_第44张图片

<Linux系统复习>文件描述符_第45张图片

 

10 缓冲区

1、一个令人疑惑的现象

<Linux系统复习>文件描述符_第46张图片

<Linux系统复习>文件描述符_第47张图片 为啥无法打印出hello world?

解释:printf是c语言接口,默认向stdout流中输出数据,也就是向屏幕输出数据,但由于有c语言缓冲区的因素,该缓冲区刷新策略依靠内存文件指向的对象,如果指向屏幕,那么采取行刷新,如果是普通文件,则采取全刷新。close(1),然后再打开log.txt,导致刷新对象由显示器变为普通文件,因此printf(“hello world”)的内容会保存在用户缓冲区(c语言缓冲区),最后因为你又关闭了1文件描述符,导致用户缓冲区无法通过1描述符将数据刷新到log.txt中。

如果最后没有关闭close(1),在进程结束的时候,操作系统会自动将进程用户缓冲区的数据刷新到文件对象中。

<Linux系统复习>文件描述符_第48张图片

<Linux系统复习>文件描述符_第49张图片

2、关于close()和dup2的理解

dup2(fd,1)是将fd的内容拷贝到1中,假设fd是3,那么1和3都是指向新打开的文件的,也就是说你既可以通过1传入数据,也可以3来传入数据。

close(fd),相当于将arr_fds[ fd ]置为0,并对指向的内存文件中的引用计数--,如果该计数为0,则需要释放该内存文件。

对于dup2(fd,1),如果不需要fd和1同时指向log.txt文件,则可以关闭fd文件描述符。这个时候不能关闭1文件描述符,如果关闭,相当于dup2()调用和没调用是一样的。

<Linux系统复习>文件描述符_第50张图片

3、综合题

<Linux系统复习>文件描述符_第51张图片  

<Linux系统复习>文件描述符_第52张图片

解释:write直接输出到屏幕文件,而其他则输出到c语言提供的缓冲区中没有打印出来,由于进程的独立性,每个进程都有自己独立的缓冲区,同时子进程会继承父进程的缓冲区数据,最后在两个进程终止时,操作系统会将进程缓冲区的数据刷新到对应的文件中。

4、模拟实现一个perror

<Linux系统复习>文件描述符_第53张图片 

<Linux系统复习>文件描述符_第54张图片

你可能感兴趣的:(Linux,linux,运维,服务器)