零、前言

程序可以理解为硬盘上的普通二进制文件;进程是加载到内存中的二进制文件,除了加载到内存中的二进制文件外,还附有所有对于该二进制文件描述信息的结构体,描述该进程的结构体叫PCB(进程控制块),在这就不在讨论。对于程序与进程,也就可以简单地理解为是否有PCB(进程控制块)。下面我们再来讨论 PCB 与 file_struct 的关系。

在每一个PCB中,都有一个文件描述符表,通过文件描述符索引指向 file 结构体 (系统打开文件表)

文件描述符在形式上是一个非负整数,实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表,当程序打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。也就是说,一个程序能够访问文件是因为给这个程序分配了文件描述符。

下面我们来讨论 file 结构体里面具体有哪些内容,file 结构体定义在 linux 系统中的(/kernels/include/linus/fs.h)文件中。

一、file 结构体

struct file
{union{struct list_head fu_list;   //文件对象链表指针 linux/include/linux/list.h 。struct rcu_head fu_rcuhead; //RCU(Read-Copy Update)是 Linux 2.6 内核中新的锁机制。} f_u;struct path f_path;           //包含 dentry 和 mnt 两个成员,用于确定文件路径。
#define f_dentry f_path.dentry    //f_path 的成员之一,当统的挂载根目录。const struct file_operations; //*f_op; 与该文件相关联的操作函数。atomic_t f_count;             //文件的引用计数(有多少进程打开该文件)。unsigned int f_flags;         //对应于 open 时指定的 flag 。mode_t f_mode;                //读写模式:open 的 mod_t mode 参数。off_t f_pos;                  //该文件在当前进程中的文件偏移量。struct fown_struct f_owner;   //该结构的作用是通过信号进行 I/O 时间通知的数据。unsigned int f_uid, f_gid;    //文件所有者 id,所有者组 id。struct file_ra_state f_ra;    //在 linux/include/linux/fs.h 中定义,文件预读相关。unsigned long f_version;
#ifdef CONFIG_SECURITYvoid *f_security;
#endif   void *private_data;
#ifdef CONFIG_EPOLLstruct list_head f_ep_links;spinlock_t f_ep_lock;
#endif  struct address_space *f_mapping;
};

其中重要参数参数介绍如下:

f_flags:表示打开文件的权限。

f_pos:表示当前读写文件的位置。

f_count:这个是一个相对来说比较重要的参数,表示打开文件的引用计数,如果有多个文件指针指向它,就会增加 f_count 的值。

f_mode:设置对文件的访问模式,例如:只读,只写等。

当然其中还定义了许多结构体等内容,这里就不在深究,下面我们来讨论一个 fd 与 files_struct 的关系。files_struct 不同于 file 结构体。在这里要区分清楚。

二、file_operations

当我们打开一个文件时,操作系统为了管理所打开的文件,都会为这个文件创建一个 file 结构体,而 file 结构体中的 f_op 指针又指向 file_operations 结构体,这个结构体中的成员除了 struct module* owner 其余都是函数指针,file_operation 就是把系统调用和驱动程序关联起来的关键数据结构。这个结构的每一个成员都对应着一个系统调用。读取 file_operation 中相应的函数指针,接着把控制权转交给函数,从而完成了 Linux 设备驱动程序的工作。

我们先来看看 file_operations 结构体的实现和相关成员的介绍。

struct file_operations
{struct module *owner;//指向拥有该模块的指针;loff_t (*llseek)(struct file *, loff_t, int);//llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值.ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);//用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型).ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);//发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数.ssize_t (*aio_read)(struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步读 -- 可能在函数返回前不结束的读操作.ssize_t (*aio_write)(struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化设备上的一个异步写.int (*readdir)(struct file *, void *, filldir_t);//对于设备文件这个成员应当为 NULL; 它用来读取目录, 并且仅对**文件系统**有用.unsigned int (*poll)(struct file *, struct poll_table_struct *);int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long);long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long);long (*compat_ioctl)(struct file *, unsigned int, unsigned long);int (*mmap)(struct file *, struct vm_area_struct *);//mmap 用来请求将设备内存映射到进程的地址空间. 如果这个方法是 NULL, mmap 系统调用返回 -ENODEV.int (*open)(struct inode *, struct file *);//打开一个文件int (*flush)(struct file *, fl_owner_t id);//flush 操作在进程关闭它的设备文件描述符的拷贝时调用;int (*release)(struct inode *, struct file *);//在文件结构被释放时引用这个操作. 如同 open, release 可以为 NULL.int (*fsync)(struct file *, struct dentry *, int datasync);//用户调用来刷新任何挂着的数据.int (*aio_fsync)(struct kiocb *, int datasync);int (*fasync)(int, struct file *, int);int (*lock)(struct file *, int, struct file_lock *);//lock 方法用来实现文件加锁; 加锁对常规文件是必不可少的特性, 但是设备驱动几乎从不实现它.ssize_t (*sendpage)(struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock)(struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);
};

文件描述符(file)是操作系统用来管理文件的数据结构,fd 就是 fd_array 的索引,FILE * 指针值就是该 fd_array 数组中元素的值,也就是 file 结构体的地址。

三、files_struct

每个进程用一个 files_struct 结构来记录文件描述符的使用情况,这个 files_struct 结构称为用户打开文件表,它是进程的私有数据。

files_struct 结构在 include/linux/fdtable.h 中定义如下:

struct files_struct
{atomic_t count;     /* 共享该表的进程数 */rwlock_t file_lock; /* 保护以下的所有域,以免在tsk->alloc_lock中的嵌套*/int max_fds;        /*当前文件对象的最大数*/int max_fdset;      /*当前文件描述符的最大数*/int next_fd;/*已分配的文件描述符加1 */struct file **fd;          /* 指向文件对象指针数组的指针 */fd_set *close_on_exec;     /*指向执行exec( )时需要关闭的文件描述符*/fd_set *open_fds;          /*指向打开文件描述符的指针*/fd_set close_on_exec_init; /* 执行exec( )时需要关闭的文件描述符的初 值集合*/fd_set open_fds_init;      /*文件描述符的初值集合*/struct file *fd_array[32]; /* 文件对象指针的初始化数组*/
};

四、概括

参考:https://www.cnblogs.com/xiangtingshen/p/11961434.html

(SAW:Game Over!)

OS / Linux / 文件描述符以及 file 结构体相关推荐

  1. linux存储--文件描述符以及file结构体(一)

    一.什么是文件描述符 在Linux下一切皆文件,对于内核而言,所有打开的文件都通过文件描述符引用,文件描述符是一个非负整数,当打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符.当读. ...

  2. Linux进程描述符task struct结构体详解--Linux进程的管理与调度(一)

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 日期 内 ...

  3. linux文件 i节点结构,Linux 文件描述符 文件表项 i节点结构

    Linux的VFS(虚拟文件系统)学习起来很痛苦,看源码不太明智,看完分析完就忘且太浪费时间,懂了后也无法应用在实际场合中.所以这里只是讨论下文件描述符,文件表项(file结构体)和inode,理清实 ...

  4. [转帖]linux文件描述符文件/etc/security/limits.conf

    linux文件描述符文件/etc/security/limits.conf https://blog.csdn.net/fanren224/article/details/79971359 需要多学习 ...

  5. 文件句柄(file handles) 文件描述符(file descriptors)

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  6. linux文件描述符、软硬连接、输入输出重定向

    引用链接:https://blog.csdn.net/qq769651718/article/details/79459346 文件描述符的作用: 文件描述符是linux操作系统中特有的概念.其相当于 ...

  7. 玩转Linux文件描述符和重定向

    本文介绍linux中文件描述符与重定向的相关知识,文件描述符是与文件输入.输出相关联的整数,它们用来跟踪已打开的文件.有需要的朋友参考下. 原文出处:http://www.jbxue.com/arti ...

  8. Linux 文件描述符的概念及与文件流指针的关系

    文件描述符 我们都知道,使用open打开一个文件后都会得到一个文件描述符,而且是一个非负正数,那这个数字是怎么来的呢? 当我们打使用open打开文件时,系统会为我们指定的文件创建一个文件描述信息结构体 ...

  9. linux 文件描述符的一些底层实现

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/38965239 1. 概述     在Linux系统中一切皆可以看成是文件,文件又可分为:普通 ...

最新文章

  1. TypeError: cannot concatenate ‘str‘ and ‘list‘ objects
  2. java string与integer_Java中Integer和String浅谈
  3. 反弹和补遗:再论Bjarne Stroustrup的基于对象的含义
  4. 【控制】《多智能体系统一致性协同演化控制理论与技术》纪良浩老师-第10章-二阶离散时间时延多智能体系统加权一致性
  5. Java中的String、StringBuffer、StringBuilder的区别和使用范围
  6. Linux Shell常用技巧(六)
  7. NDK 开发实战 - 封装 java 层 sdk 模型
  8. 销毁AWS资源:Cloud-Nuke还是AWS-Nuke?
  9. 第八章方差分析以及线性回归(2)
  10. html如何设置轮动,手把手教你构建轮动策略
  11. 2021-09-02spark streaming
  12. java 画正弦函数曲线_JAVA画正弦曲线
  13. 基于Python的网络爬虫与数据可视化分析
  14. 什么是大数据以及大数据的相关技术?
  15. vue+elementui+echarts饼状图内部显示百分比
  16. XUPT第三届新生算法赛
  17. 关于“语义通信”的名词解释
  18. linux 心脏滴血漏洞,心脏出血漏洞(heartbleeder 自动检测 OpenSSL 心脏出血漏洞 (附修复指南))...
  19. 从2021年度业绩报告看奇安信的网安“野望”
  20. 大前端养成之路:学一点MongoDB(一)

热门文章

  1. ios两张图片的合并
  2. 关于.cpp文件包含另一个.cpp文件出错的原因以及解决办法
  3. U2NET目标显著性检测,抠图去背景效果倍儿棒
  4. SpringBoot整合Spring Security——第三章异常处理
  5. Docker管理面板Portainer中文汉化教程
  6. Python 实例属性和类属性
  7. python字符串截取:截取yaml文件名后3个字符
  8. Python3类方法和静态方法
  9. Python Django 一对多正向查询示例
  10. Linux man指令