• 文件描述符fd,struct files_struct


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

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

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

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

    file_struct结构如下

    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_SECURITY
      void *f_security;
      #endif
      
      void *private_data;
      #ifdef CONFIG_EPOLL
      
      struct 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_struct。在这里要区分清楚。

    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_struct)是操作系统用来管理文件的数据结构,当我们创建一个进程时,会创建文件描述符表,进程控制块PCB中的fs指针指向文件描述符表,当我们创建文件时,会为指向该文件的指针FILE*关联一个文件描述符并添加在文件描述符表中。在文件描述符表中fd相当于数组的索引,FILE*相当于数组的内容吗,指向一个文件结构体
    ————————————————

    每个进程用一个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];/* 文件对象指针的初始化数组*/
    };
    /*
     * Open file table structure
     */
    struct files_struct {
      /*
       * read mostly part
       */
        atomic_t count;
        struct fdtable __rcu *fdt;
        struct fdtable fdtab;
      /*
       * written part on a separate cache line in SMP
       */
        spinlock_t file_lock ____cacheline_aligned_in_smp;
        int next_fd;
        unsigned long close_on_exec_init[1];
        unsigned long open_fds_init[1];
        struct file __rcu * fd_array[NR_OPEN_DEFAULT];
    };
    struct fdtable {
        unsigned int max_fds;
        struct file __rcu **fd;      /* current fd array */
        unsigned long *close_on_exec;
        unsigned long *open_fds;
        struct rcu_head rcu;
        struct fdtable *next;
    };

    在struct files_struct中包括一个struct fdtable变量实例和一个struct fdtable类型指针,

    而struct fdtable中的成员变量close_on_exec,open_fds,fd又分别指向struct files_struct中的成员close_on_exec_init,open_fds_init和fd_array。

    即一个结构中嵌套了另一个结构,被嵌套结构中的成员又反过来引用了属主的成员,这样看起来有些重复和让人迷惑。


    在files_struct结构的初始化时,能更清晰的看出这种重复。如内核第一个进程(即进程init)的files_struct静态初始化:
    ————————————————

    struct files_struct init_files = {
        .count      = ATOMIC_INIT(1),
        .fdt        = &init_files.fdtab,
        .fdtab      = {
            .max_fds        = NR_OPEN_DEFAULT,
            .fd               = &init_files.fd_array[0],
            .close_on_exec  = (fd_set *)&init_files.close_on_exec_init,
            .open_fds       = (fd_set *)&init_files.open_fds_init,
            .rcu            = RCU_HEAD_INIT,
        },
        .file_lock  = __SPIN_LOCK_UNLOCKED(init_task.file_lock),
    };
    ————————————————

    参考博文:https://blog.csdn.net/gmy2016wiw/article/details/72594093

         https://blog.csdn.net/bit_clearoff/article/details/54565044

         https://blog.csdn.net/metersun/article/details/80513702

  • 相关阅读:
    oracle
    mysql的必知技巧
    sql_update
    sql查询
    Java 动态页面技术 之 jsp
    Java 会话技术 之 session
    Java 会话技术 之cookie
    Java HttpServletRequest
    Java HttpServletResponse
    Java Servlet接口、web.xml配置、HttpServlet父类
  • 原文地址:https://www.cnblogs.com/xiangtingshen/p/11961434.html
Copyright © 2020-2023  润新知