加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

文件描述符FD,文件表,vnode节点与inode节点

(2021-05-29 09:48:46)
分类: unix
    文件描述符fd属于进程级别(用uint16_t表示),各个进程的文件描述符可以重复,是一个非负整数,一般0,1,2分别是标准输入,标准输出,标准错误。task_struct里有一个files_struct的指针,里面记录了所有打开的文件描述符和file指针。
     vnode是unix的概念,linux中并没有vnode结构。
    文件表项(内核中用file结构表示),属于系统级别,由内核管理,处于文件表(内核维护一个全局的文件表)中,它包含了打开文件的读写偏移量,文件索引节点inode指针,文件所有者等信息。
     inode是文件的真实信息,也叫索引节点,指向磁盘(如磁盘文件)或内存(如socket),属于内核,在内核中使用inode结构表示。
     对一个文件,只有一个inode,但vnode和fd都可以有多个,如使用dup,可以使用让两个不同的fd指向同一个文件表项。同一进程 (或不同进程)使用open打开同一个文件两次,会有两个fd与两个文件表项(unix中文件表项有两个,但共享一个vnode),但都指向同一个inode,此时,由于文件表项不同,有不同的offset,都写文件时,就会导致文件内容乱掉。
     注意,fd, 文件表,vnode是在内存,只有打开文件时存在,inode属于永久数据,在硬盘(socket,管道等除外)。

对于每个进程,task_struct包含一个files_struct结构(或叫文件描述符表),它里面有个file数组,用来记录文件描述符的使用情况,定义在include/linux/file.h中
struct files_struct
{
  atomic_t count; //使用该表的进程数
  struct fdtable *fdt;
  struct fdtable fdtab;
  spinlock_t file_lock ____cacheline_aligned_in_smp;
  int next_fd; //数值最小的最近关闭文件的文件描述符,下一个可用的文件描述符
  struct embedded_fd_set close_on_exec_init; //执行exec时需要关闭的文件描述符初值集合
  struct embedded_fd_set open_fds_init; //文件描述符的屏蔽字初值集合
  struct file * fd_array[NR_OPEN_DEFAULT]; //默认打开的fd队列(文件描述符作为数组下标直接访问file,file中并没有文件描述符,files_struct中只记录下一个可用的文件描述符;dup或dup2函数即是在数组中增加一个file指针,指向已有描述符指向的file对象;打开新文件,即是创建一个新的file对象,并在数组中用下个一可用fd作下标存放file指针,并向应用返回fd
  };
  struct fdtable {
  unsigned int max_fds;
  struct file ** fd; //指向打开的文件描述符列表的指针,开始的时候指向fd_array,当超过max_fds时,重新分配地址
  fd_set *close_on_exec; //执行exec需要关闭的文件描述符位图(fork,exec即不被子进程继承的文件描述符)
  fd_set *open_fds; //打开的文件描述符位图
  struct rcu_head rcu;
  struct fdtable *next;
};

其中file结构:
struct file {
   
    union {
        struct list_head    fu_list;
        struct rcu_head     fu_rcuhead;
    } f_u;
    struct path     f_path; //文件路径,它里面的成员指向inode
#define f_dentry    f_path.dentry
#define f_vfsmnt    f_path.mnt
    const struct file_operations    *f_op; // 文件操作函数,与文件类型相关,内部都是函数指针,对应系统调用,把系统调用和文件驱动程序关联起来
    spinlock_t      f_lock; 
    atomic_long_t       f_count; // 指向该文件结构的引用计数,例如使用dup复制文件描述符,该计数增加,fork子进程时,计数也增加
    unsigned int        f_flags;  // 表示打开文件的权限
    fmode_t         f_mode;  // 设置对文件的访问模式,例如:只读,只写等。
    loff_t          f_pos;  // 表示当前读写文件的位置,即偏移量,可见读写偏移量不是分开
    struct fown_struct  f_owner;
    const struct cred   *f_cred;
    struct file_ra_state    f_ra;

    u64         f_version;
#ifdef CONFIG_SECURITY
    void            *f_security;
#endif
   
    void            *private_data;

#ifdef CONFIG_EPOLL
   
    struct list_head    f_ep_links;
#endif
    struct address_space    *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
    unsigned long f_mnt_write_state;
#endif
};

file结构中的文件操作结构file_operations

struct file_operations {
    struct module *owner;  //指向拥有该模块的指针;
    loff_t (*llseek) (struct file *, loff_t, int); //改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. 
    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 *);//请求将设备内存映射到进程的地址空间. 如果这个方法是 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 *);//实现文件加锁; 加锁对常规文件是必不可少的特性, 但是设备驱动几乎从不实现它.
    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结构中的path结构:
struct path {
        struct vfsmount *mnt; //该文件已安装的文件系统
        struct dentry *dentry; //与文件相关的目录项对象,内部有个inode成员
};

path结构中的dentry结构:
struct dentry {
  atomic_t d_count; //目录项对象使用计数器,可以有未使用态,使用态和负状态
  unsigned int d_flags; //目录项标志
  struct inode * d_inode; //与文件名关联的索引节点
  struct dentry * d_parent; //父目录的目录项对象
  struct list_head d_hash; //散列表表项的指针
  struct list_head d_lru; //未使用链表的指针
  struct list_head d_child; //父目录中目录项对象的链表的指针
  struct list_head d_subdirs; //对目录而言,表示子目录目录项对象的链表
  struct list_head d_alias; //相关索引节点(别名)的链表
  int d_mounted; //对于安装点而言,表示被安装文件系统根项
  struct qstr d_name; //文件名
  unsigned long d_time;
  struct dentry_operations *d_op; //目录项方法
  struct super_block * d_sb; //文件的超级块对象
  vunsigned long d_vfs_flags;
  void * d_fsdata; //与文件系统相关的数据
  unsigned char d_iname [DNAME_INLINE_LEN]; //存放短文件名
};

索引inode结构:
struct inode {
    struct list_head i_hash;
    struct list_head i_list;
    struct list_head i_dentry;
    struct list_head i_dirty_buffers;

    unsigned long i_ino; //节点号
    atomic_t i_count;
    kdev_t i_dev;
    umode_t i_mode;
    nlink_t i_nlink;
    uid_t i_uid;
    gid_t i_gid;
    kdev_t i_rdev;
    loff_t i_size;
    time_t i_atime;
    time_t i_mtime;
    time_t i_ctime;
    unsigned long i_blksize;
    unsigned long i_blocks;
    unsigned long i_version;
    struct semaphore i_sem;
    struct semaphore i_zombie;
    struct inode_operations *i_op;
    struct file_operations *i_fop;
    struct super_block *i_sb;
    wait_queue_head_t i_wait;
    struct file_lock *i_flock;
    struct address_space *i_mapping;
    struct address_space i_data;
    struct dquot *i_dquot[MAXQUOTAS];
    struct pipe_inode_info *i_pipe;
    struct block_device *i_bdev;

    unsigned long i_dnotify_mask;
    struct dnotify_struct *i_dnotify;

    unsigned long i_state;

    unsigned int i_flags;
    unsigned char i_sock;

    atomic_t i_writecount;
    unsigned int i_attr_flags;
    __u32 i_generation;
    union {
        struct minix_inode_info minix_i;
        struct ext2_inode_info ext2_i;
        struct hpfs_inode_info hpfs_i;
        struct ntfs_inode_info ntfs_i;
        struct msdos_inode_info msdos_i;
        struct umsdos_inode_info umsdos_i;
        struct iso_inode_info isofs_i;
        struct nfs_inode_info nfs_i;
        struct sysv_inode_info sysv_i;
        struct affs_inode_info affs_i;
        struct ufs_inode_info ufs_i;
        struct efs_inode_info efs_i;
        struct romfs_inode_info romfs_i;
        struct shmem_inode_info shmem_i;
        struct coda_inode_info coda_i;
        struct smb_inode_info smbfs_i;
        struct hfs_inode_info hfs_i;
        struct adfs_inode_info adfs_i;
        struct qnx4_inode_info qnx4_i;
        struct bfs_inode_info bfs_i;
        struct udf_inode_info udf_i;
        struct ncp_inode_info ncpfs_i;
        struct proc_inode_info proc_i;
        struct socket socket_i;
        struct usbdev_inode_info        usbdev_i;
        void *generic_ip;
    } u;
};

inode中的union,可见支持不同的文件类型,包括socket.

复制两张图:


0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有