3.文件IO-文件描述符与lseek

匿名 (未验证) 提交于 2019-12-03 00:26:01

文件描述符

在上一篇博客当中,我们知道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; }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!