文件描述符
在上一篇博客当中,我们知道open函数会返回一个整数,它在本进程中唯一标识了一个文件;
在一个进程中,存在着一个大数组,记录了打开的文件;这个数组的索引就是open函数返回的整数,而这个索引就是文件描述符;而这个数组的每一项,都记录了与打开的文件相关的信息;
在操作系统当中,是通过进程控制块(PCB)来描述进程信息和相关资源的;实际上在Linux中,PCB就是一个巨大的结构体,即task_struct结构体;
struct task_struct { long pid; //进程号 .... //文件描述符标志位 unsigned long close_on_exec; //数组索引号就是文件描述符;NR_OPEN的值在linux 0.11 中被定义为20 struct file * filp[NR_OPEN]; .... };
上面的PCB结构体中,有一个struct file类型的数组 filp[NR_OPEN];
//struct file记录了文件的相关信息 struct file { unsigned short f_mode; // 文件权限位 unsigned short f_flags; // 文件状态位 unsigned short f_count; // 引用计数 struct m_inode * f_inode; // 文件存在磁盘上的哪个位置等等其它信息由这个字段来解释 off_t f_pos; // 当前偏移量 };下图就是,PCB,文件描述符和文件表之间的关系
上图中的flip数组的索引号,就是文件描述符;数组中的每个元素都是一个指向了struct file类型(文件表)的指针;
struct file(文件表)中记录了当前打开的文件的重要信息,其中根据 f_inode 成员就可以找到磁盘上的文件(当然,没有这么简单,但可以这样理解);
在上图中,可以看到本进程中有三个文件描述符指向了同一个struct file(文件表),那么这个文件表中的引用计数必然为 3;
而第二个文件表只有一个指针指向了它,它的引用计数就是1;
在调用 close(fd) 函数的时候,实际上做了两步:
--flip[fd]->count; flip[fd] = NULL;
当 count == 0 的时候,才真正的关闭文件;
有没有可能人为的让两个不同的文件描述符指向同一个struct file(文件表)?答案是完全可能,这没什么好奇怪的,函数 dup 和 dup2 就是干这事的。dup2(int oldfd, int newfd) 的函数做的事情大概就向下面这个样子:
int dup2(int oldfd, int newfd) { // ... close(newfd); flip[newfd] = flip[oldfd]; flip[newfd]->count++; //... return 0; }
而 dup 函数只接受一个参数 oldfd,它返回系统分配的新描述符值;
并非所有的操作系统实现方式都如图 1 中所示,在 linux 0.11 中采用的是数组,当然,完全可以采用链表来实现,这取决于不同操作系统的实现方式,万变不离其宗;
lseek 函数
// off_t 可以理解成 int off_t lseek(int fd, off_t offset, int whence);
lseek 函数,就是改变 flip[fd] 指向的 struct file 这个结构中的 f_pos 成员的;
当用 open 函数打开一个文件的时候,该偏移量 f_pos 被默认指定为 0。
- 如果 whence 等于
SEEK_SET,则f_pos = offset(offset 只能是正数) - 如果 whence 等于
SEEK_CUR, 则f_pos = f_pos + offset(offset 可正可负) - 如果 whence 等于
SEEK_whence,则f_pos = 文件长度 + offset(offset 可正可负)
如果一个文件中的内容是 hello world,当f_pos==6的时候,执行 read 函数将从字母w开始读取,执行write 也会从 w 处开始写数据;
示例:
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main() { int fd = open("test", O_RDONLY); if (fd < 0) { perror("open"); return 1; } char buf[64] = {0}; lseek(fd, 6, SEEK_SET); read(fd, buf, 64); printf("%s\n", buf); //屏幕会打印world. return 0; }
转载请标明出处:3.文件IO-文件描述符与lseek
文章来源: 3.文件IO-文件描述符与lseek