本节内容:文件管理
1.文件的管理
1.1基本操作的函数(重中之重)
(1)lseek函数
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
第一个参数:文件描述符, open函数的返回值
第二个参数:用于指定偏移量
当offset > 0时,表示向后偏移,也就是向文件末尾方向
当offset = 0时,表示偏移量为0
当offset < 0时,表示向前偏移,也就是向文件开头方向
第三个参数:用于指定起始位置
SEEK_SET - 文件开头位置
- 向后偏移合理,向前偏移不合理
SEEK_CUR - 文件当前位置
- 向前向后偏移都合理,不考虑特殊情况
SEEK_END - 文件末尾位置
- 向前向后偏移都合理,不考虑特殊情况
返回值:成功返回偏移后的位置就离开头位置的偏移量,失败返回(off_t)-1;
函数功能:
主要用于调整文件的读写位置;
扩展(很有用!):
- 如何获取一个文件的大小:
a.使用fseek函数调整读写位置到末尾,使用fteel函数读取大小
b.使用lseek函数调整读写位置到末尾,返回值就是大小
注意:
- 当把文件的读写位置移动到SEEK_END后面的位置写入数据时,数据还是可以写入的,只是中间有一块区域空间,该区域叫做文件的空洞现象,该区域会被计算到文件的大小中,但是没有有效的数据,读取内容时得到这‘\0’;
1.2标c文件操作函数和uc文件操作函数的比较
- 由程序结果可知,标c的文件操作函数比uc的文件操作函数效率高一些,因为标c文件操作函数内部提供里输入输出缓冲区,当数据积累到一定数量之后才会访问内核,才会将数据写如到文件中,因此效率比较高;
注意: 通过自定义缓冲区的方式可以提高uc文件操作函数的效率,但并不是缓冲区越大则效率一定越高。
1.3文件描述符的工作原理(尽量理解)
- 文件描述符本质上就是一个整数,可以代表一个可以打开的文件,但是文件的信息并不是存放在文件描述符中,而是存放在文件表等数据结构中当使用open函数打开文件时,会将文件的相关信息加载到文件表等数据结构中,但是出于安全和效率等因素的考虑,文件表等结构并不适合直接操作,而是给文件结构指定一个编号,拿编号进行操作,该编号就叫做文件描述符。
- 在每个进程的内部,都有一张文件描述符总表,当有新的文件描述符需求时,会区文件描述符总表中查找最小的未被使用的文件描述符返回,文件描述符虽然是int类型,但本质上是非负数,也就是从0开始,其中0、1、2被系统占用,分别代表标准输入、标准输出以及标准错误,因此一般从3开始用,最大到OPEN_MAX(当前教学环境是1024);
- 当使用close函数关闭文件时,本质上就是将文件描述符和文件表结构之间对应的关系从文件描述符总表中移除,不一定会立刻删除文件表,只有当文件
表结构没有和其他任何文件描述符对应时(也就是文件表结构可以同时对应多个文件描述符),才会删除文件表,close函数也不会改变文件描述符的整数值,而是
让该文件描述符无法代表一个文件而已。
1.4dup/dup2函数
#include <unistd.h>
int dup(int oldfd);
返回值:成功返回新的文件描述符,失败返回-1;
函数功能:
主要用于创建参数oldfd的副本,该函数会选择最小的未被使用的描述符作为最新的文件描述符。
#include <unistd.h>
int dup2(int oldfd, int newfd);
返回值:成功返回新的文件描述符,失败返回-1
函数功能:
主要用于实现oldfd到newfd的复制,如果newfd已经被占用,则先关闭再复制
注意:
- 复制文件描述符的本质上就是复制文件描述符所对应的文件表地址信息,也就是让多个文件描述符对应同一个文件表结构,也就是对应同一个文件;
1.5 fcntl函数(目前函数中功能最复杂的)
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
第一个参数:文件描述符,open函数返回值;
第二个参数:具体操作命令
F_SETLK/F_SETLKW/F_GETLK - 主要用于实现建议锁功能
第三个参数:可变长参数,是否需要取决于cmd
当实现建议锁功能时,该参数是一个指向以下结构体的指针,具体的结构体类型如下:
struct flock {
...
short l_type; /* Type of lock锁类型: F_RDLCK,读锁
F_WRLCK写锁, F_UNLCK解锁 */
short l_whence; /* How to interpret l_start:
SEEK_SET锁定的起始位置, SEEK_CUR当前位置, SEEK_END末尾位置 */
off_t l_start; /* Starting offset for lock */相对于起始位置锁定的偏移量
off_t l_len; /* Number of bytes to lock */锁定的字节数,也就是长度
pid_t l_pid; /* PID of process blocking our lock加锁的进程号,默认给-1;( F_GETLK only) */
...
};
返回值:成功返回0,失败返回-1
函数功能:
主要用于对指定的文件描述符执行指定的操作;
练习:
- a. vi 07write_emp.c文件,首先定义一个员工类型的结构体变量并初始化,再使用write函数写入文件emp.dat中,
其中员工主要信息有:编号,姓名、薪水- b. vi 08read_emp.c 文件,读取文件emp.dat中现有数据,并答应出来;