目录
1.文件描述符fd
2.系统调用的0/1/2
3.C语言的stdin/stdout/stderr
4.系统调用的0/1/2和C语言的stdin/stout/stderr二者的关系❓
5.文件描述表
5.1 文件描述符概念
5.3 文件对象strcut file
5.4 进程和文件对应关系
5.5 文件描述符理解
5.6 源码查看
1.文件描述符fd
- open系统调用的返回值是一个整数fd。
- 成功创建一个新的文件描述符(整数),失败就-1和错误码被设置。
- man open
- /return val
2.系统调用的0/1/2
- open返回值这个整数是什么❓
- 查看下文件描述符☞形成了文件,打印的文件描述符是3/4/5/6
- open的返回值是3/4/5/6,为什么呢❓正常情况下,打开失败,返回值是<= -1的,怎么不见0/1/2呢❓
- 我们自己的打开普通文件描述符是从3/4/5/6开始,可见0/1/2是已经被打开且占用了。
- 0/1/2是系统启动时自动默认给我们打开得三个设备文件。
- 0:标准输入,键盘。
- 1:标准输出,显示器。
- 2:标准错误,显示器。
3.C语言的stdin/stdout/stderr
- 在C语言当中,也默认自动会打开三个输入输出文件流。(FILE*类型的文件流)它们三个所对应的类型都是FILE*类型。
- C语言中的fopen文件打开操作中的返回值类型是一样的☞FILE*类型。
- man 3 fopen
- man 3 stdin
- man 3 fprintf
- extern FILE *stdin;
- extern FILE *stdout;
- extern FILE *stderr;
- C语言中也可以使用fpirnt来打印(传参是文件流FILE*类型),不仅仅可以使用printf。
- 数据刷新,也可以使用stdout文件流。
4.系统调用的0/1/2和C语言的stdin/stout/stderr二者的关系❓
- 无论是在linux当中还是C语言当中,都是把键盘/显示器等当成文件来看待的。
- 如果想要对键盘/显示器进行操作。可以使用C语言中的stdin,stdout等(fprintf),也可以使用系统调用中中的0/1/2(write)。
- 系统会给我们打开三个设备文件,C语言也会默认给我们打开三个文件流。
- 系统和语言二者存在怎样的关系呢❓
- ❓我们并不知道C语言如何使三个文件流打开,一定是类似fopen的接口。
5.文件描述表
5.1 文件描述符概念
- 系统调用:1:标准输出,显示器。(为例)
- 系统调用接口write向指定的文件描述符(数字fd)写入,就是往一个指定的文件写入。
- 类似使用C语言的printf 和 fprintf 往stdout 打印数据。直接使用系统调用接口,往显示器文件所代表的文件描述符1打印。
- 显示器文件设备不用打开,是程序启动时默认打开的。
- man 2 write
像正常合法的0123456的连续的小整数被称为 文件描述符
❓怎么理解write向一个整数里面写入,就相当于向文件里面写呢。
❓fd的本质是什么
5.3 文件对象strcut file
- 系统调用是OS给用户提供的接口。程序运行起来,才能把文件打开,才能文件操作。
- 研究打开文件(文件操作)本质是:研究进程和文件的关系。
- 程序必须启动起来,成为在OS中的进程,执行到open打开文件代码,文件才被打开。
- 一个进程可以打开多个文件,OS当中有很多进程,对应打开很多文件。
- OS当中一定会存在非常多的被打开的文件。(没有打开的文件在磁盘中),OS对内核中被打开的文件,必须要管理❗
- 管理:先描述再组织
- 描述:OS必须为被打开的文件,创建对应的内核数据结构struct file ☞源码
- struct file一般都包含的是文件的属性(权限/打开方式/标记位等等)
- 组织:OS会创建非常多的struct file内核数据结构,同时把每个被打开文件的struct file以双链表的形式联系起来。形成双链表。OS对文件的管理,转化成为对双链表的增删查改。
strcut file
- struct file 里面会包含一个指针,指针指向一段 系统与该文件对应的内核级的缓存(OS给每个被打开的文件申请的一段内存)
- 文件=内容+属性
- 使用磁盘文件的属性 初始化 在OS中被打开的文件的struct file
- 使用磁盘文件的内容 加载 在OS中被打开的文件的内核级缓存中(想读直接读,想写就写入,最后再刷新到磁盘即可)
5.4 进程和文件对应关系
- OS存在很多进程task_struct,也存在很多被打开的文件struct file❓哪个进程对应哪个被打开的文件。
- OS是把所有打开文件管理在一起的
- 进程 : 文件 = 1:n
- 一定要能表征进程和它打开文件之间的对应关系。OS必须建立进程和文件的关系。
- OS在进程PCB(task_struct)中存在一个属性指针struct files_struct *files
- 结构体对象 struct files_struct 是一种数据结构
- 在struct files_struct结构体内部存在 一个指针数组 struct file* fd_arry[N]
- 指针数组就存在数组的下表:0/1/2.....
- 指针数组 struct file* fd_arry[N] 的每个数组下表对应的数组元素(指针)指向文件结构体对象 struct file
- 进程和对应打开的文件建立联系:只需要把描述该文件的 结构体变量 的地址填入指针数组即可。
- 文件描述符fd本质:内核的进程和文件映射关系的数据下标❗
- 以上看☞源码证明
综上所述:一个进程想要找到对应被打开的文件,只需要把该文件 对应的 指针数组元素的下标拿到手即可。返回这个指针数组的下标给上层用户,拿到下标就可以对这个文件进行文件操作。
5.5 文件描述符理解
- 文件描述符fd本质:内核的进程和文件映射关系的数据下标❗
- man 2 open
- man 2 write
- man 2 read
- man 2 close
写的流程
- 首先通过open拿到文件描述符(下标),使用write/close/read等系统调用接口文件操作。
- 在使用系统调用接口的时候,都必须传入fd参数。
- OS拿到fd,就知道用户要访问当前进程的哪个下标了,就根据下标找到文件。
- 把用户级缓冲区的数据,拷贝到对应文件的内核级缓存当中。
- 最后由OS定时刷新到外设磁盘当中,完成一次文件的写入。