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