一、概述
二、当头棒喝
去掉代码中的注释掉的 fflush 再试下:
系统默认会打开三个文件描述符(stdin,stdout,stderr),在程序中 close(1) 关掉了标准输出,此时 open 打开返回的是最小可用的文件描述符,也就是 fd = 1,因此 printf 本应该打印到文件中,但是 close(fd) 不会触发 buffer 刷新,因此既不会输出到屏幕也没有输出到 msg.log 中。
当打开 fflush,则会刷新 buffer,因此就可以看到 msg.log 文件中"hello world"。
三、函数概述
Linux C库IO函数工作流程:
pcb和文件描述符:
- 文件描述符
- int 类型
- 一个进程最多可打开多少文件
- pcb
- 进程控制块
- 在其中有一个文件描述符表 – 数组[1024]
虚拟地址空间就是程序启动起来之后,从硬盘上会有一块虚拟内存分配出来。
CPU 为什么要使用虚拟地址空间与物理地址空间映射?解决了什么样的问题?
1)方便编译器和操作系统安排程序的地址分布
程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。通过虚拟地址空间与物理地址空间映射,解决不连续的缓冲区的问题。
2)方便进程之间隔离
不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程使用的物理内存。
3)方便OS使用你那可怜的内存。
程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
虚拟地址空间的布局如下:
C库函数与系统函数的关系
- FD:文件描述符
- FP_POS:文件指针
- BUFFER:缓冲区
- write:对0-3G的用户空间进行操作
- sys_write:对3-4G的内核空间进行操作
四、IO函数介绍
1)open
-
作用:打开文件(可能创建文件)
-
头文件:
-
参数说明:
- pathname 文件名
- flags 标志位
- 必选项:
- O_RDONLY 只读
- O_WRONLY 只写
- O_RDWR 读写
- 可选项:
- O_APPEND 追加
- O_CREAT 创建文件
- O_EXCL和O_CREATE一起使用,如果文件存在则报错
- O_NONBLOCK 非阻塞
- 必选项:
- Mode 权限位,最终(mode&~umask)
-
返回值:
- 成功:返回最小的可用文件描述符
- 失败:返回-1,并且设置errno
-
open 函数中的 errno:
- 使用 open 实现一个 touch 功能
- 一个进程打开的最大文件数(1024)
2)close
- 作用:关闭文件描述符
- 头文件:
- 参数说明:
- fd 文件描述符
- 返回值:
- 成功:返回 0
- 失败:返回 -1,并且设置 errno
3)read
- 作用:从文件描述符读
- 头文件:
- 参数说明
- fd 文件描述符
- buf 缓冲区
- count 缓冲区大小
- 返回值
- 失败:返回 -1,设置 errno
- 成功:
- 返回读到的字节数
- 0 代表读到文件末尾
- 非阻塞的情况下 read 返回 -1,但是此时需要判断 error 的值。
4)write
- 作用:写到文件描述符
- 头文件:
- 参数说明:
- fd 文件描述符
- buf 缓冲区
- count 缓冲区大小
- 返回值
- 失败:返回-1,设置 errno
- 成功:
- 返回写入的字节数
- 0 代表未写入
- 实现一个 cat 功能
- 需求:给一个文件中写入内容,写完之后打开该文件再读取写入的内容?(bug版本)
- 结果:内容写入到文件中,但是并未按预期输出到屏幕上。
- 原因:是由于 write 完成之后,fd 到了文件末尾,因此 read 时到了文件末尾,无法读取文件数据
- 解决方法:写完之后将文件指针设置到文件开头,使用请看下文要介绍的 lseek 函数。
5)lseek
- 作用:重定位读/写文件de偏移量
- 头文件:
- 参数说明
- fd 文件描述符
- offset 偏移量
- whence:
- SEEK_SET 文件开始位置
- SEEK_CUR 文件当前位置
- SEEK_END 文件结尾
- 返回值
- 失败:返回 -1,设置 errno
- 成功:返回当前位置到文件开头的长度
- lseek 作用
- 移动文件读写位置
- 计算文件大小
- 拓展文件
- 移动文件读写位置(修改上例的 bug(写入文件内容并读取文件内容打印到屏幕))
- 计算文件大小(输出文件字节数)
- 拓展文件
6)fcntl
- 作用:操作文件描述符
- 头文件:
- 参数说明:
- fd 文件描述符
- cmd 命令
- 返回值
- 不同的 cmd 返回值不同
- 使用 fcntl 函数实现读非阻塞
五、利用IO函数实现一个copy函数