文章目录
- 基础IO要讲的知识点介绍
- fd周边问题
基础IO要讲的知识点介绍
1.复习一下C语言的接口
 2.直接使用系统接口
 3.分析系统接口的细节,引入fd(文件描述符)
 4.fd的周边问题(fd的理解、fd和file的关系、fd分配规则、fd重定向…)
fd周边问题
我们前面学了fd,那么fd到底是什么?
 进行访问文件必须先打开文件,那么一个进程可以打开多个文件吗?可以的!
 一般而言进程 : 打开的文件 = 1 : n的关系。
 文件要被访问,前提是加载到内存中,才能被直接访问!
因为进程 : 打开的文件 = 1 : n的关系,那么如果多个进程都打开自己的文件呢?
 系统中就会存在大量被打开的文件!
 所以OS要不要把如此多的打开文件管理起来?要
 如何管理?先描述,再组织!
 在内核中如何看待打开的文件?
 OS内部为了管理每一个被打开的文件,所以为每个打开的文件构建了struct file。
 
 创建struct file的对象充当被打开的文件,如果有很多呢?
 再用双链表组织起来
进程和文件的对应关系如下图:
 
 再具体点
 
所以fd本质就是一个数组的下标的。
文件分两类:
 1.被打开的文件(内存文件)
 2.没有被打开的文件(磁盘文件)
而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。**每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!**所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。
 
 fopen和fwrite的底层调用逻辑
 fopen->open->fd(用fd封装FILE文件)->FILE->*FILE**(返回给)->fopen
fwrite->FILE*->fd->write->write(fd,.....)->进程自己执行OS内部的write方法->能找到进程的task_struct->*files->file_struct->fd_array[fd]->struct_file->内存文件被找到->开始操作
理解了什么是fd接下来就要知道fd的分配规则到底是什么。
 
 
 我们看到关闭0号文件,它占用的就是0号描述符。
 所以fd的分配规则:最小的,没有被占用的文件描述符
输出重定向
 
 运行结果
 
 我们内容确实达到显示器上了,但是如果我们关闭1,显示器也就关闭了,这内容会打向哪里呢?
 
 
 我们看到内容确实没打到显示器上。
 那么打印到了哪里呢?
 
 打印到了log.txt中,这就是输出重定向。
 原理:
 
而接下来向1中写的内容不会写到stdout中只会写到log.txt中,这就是重定向的原理。
 重定向本质:在OS内更改fd对应的内容指向!
输入重定向的演示:
 #include <stdio.h>2 #include <string.h>3 #include <unistd.h>4 #include <sys/types.h>5 #include <sys/stat.h>6 #include <fcntl.h>7 8 int main()9 {10   //默认打开0 1 2 关闭0号文件描述符11   close(0);12   int fd = open("log.txt",O_RDONLY,0666);13   if(fd < 0)14   {15     perror("open");16   }17   else18   {19     char buffer[64];20     while(fgets(buffer,sizeof(buffer),stdin) != NULL)21     {22 23       fprintf(stdout,buffer,NULL);                                                                                                                                  24     }close(fd);39   }40   return 0;41 }运行代码
 
 我们看到本来应该从stdin读取的内容变成从log.txt里面读取。
追加重定向:
 我们只要把输出重定向的O_TRUNC改成O_APPEND即可。
 这里大家自己更加,我便不演示了。
 
我们之前都是自己手动关闭0,1的,这样太挫了,下面就有个函数帮助我们。
 
 
 问题:
 dup2拷贝是在拷贝什么?拷贝的是文件描述符数组里面的指针。
 谁是谁的拷贝?
 是把new拷给old?
 还是把old拷给new?
 看英文可知是把:oldfd拷贝给newfd。所以最后newfd里面的指针和oldfd一样
 理解:

 输出重定向更改
 
 运行代码
 
追加重定向更改
 
 运行代码
 
 输入重定向这里就不更改了,逻辑一样大家自行更改即可。
如何理解一切皆文件呢?->Linux的设计哲学->体现在OS的软件设计层面!
LinuxC语言写的!如何用C语言实现面向对象,甚至是运行时多态呢?->struct类但是我们知道struct类是没有成员函数的那么如何实现呢?函数指针!

 底层不同的硬件,一定对应的是不同的操作方法!但是上面的设备都是外设,所以每一个设备都核心访问函数都可以是read,write的I/O操作。
 所有设备都可以有自己的read和write,但是代码的实现一定是不一样的。
 

 所以在struct file的这层往上就没有任何硬件的区别了
 看待所有文件的方式,都统一成为了struct file->所以就有了Linux下一切皆文件的说法。
接下来就是缓冲区的理解:
 1.什么是缓冲区?就是一段内存空间
 (这个空间谁提供?用户?语言?OS?)
 2.为什么要有缓冲区?
故事:
 小明在云南大学想给远在北京邮电的小华送一些书过去。
 送书有2种方法:
 1.自己骑车送过去(写透模式(WT) 成本高,花时间多,慢)
 2.快递发送点
 
 这是写回模式(WB) 成本低,花时间少,快
那么顺丰是立马发送吗?
 不是,是快递累积到一定数量后再发送
在这个故事里的顺丰就是典型的缓冲区的意义:目的是体改整机的效率
类比:
 顺丰:缓冲区
 小明:用户
 小华:磁盘
 书: 数据
缓冲区主要是为了提高用户的响应速度。
缓冲区刷新策略:
 顺丰发送策略对应缓冲区的刷新策略:
 1.立即发送(立即刷新)
 2.架子上的快递一行一行发送的行发送(行缓冲)
 3.快递架子满了在发送(全缓冲)
特殊情况:
 1.用户强制刷新(fflush)
 2.进程退出
缓冲策略 = 一般加特殊
那么缓冲区到底谁提供的?
 C?OS?
 写一段代码:
 
 运行代码
 
 我们发现把内容打印到stdout的内容重定向到log.txt中,log.txt中
 C语言提供的接口打印了两次
 OS提供的接口打印了一次
 接来下我们把fork注释掉看一下,运行结果
 
 
 这里说明一个问题,这个现象绝对个fork有关。
 那么为什么OS提供的接口打印一次,C提供的接口打印两次下次再说。