【Linux】File 结构体,和 fd 的关系

来源:互联网 发布:美分知乎 编辑:程序博客网 时间:2024/05/29 16:40

1. struct file
  struct file 结构体在 include/linux/fs.h 中定义。文件结构体代表一个打开的文件,系统中每个打开的文件在内核空间都有一个关联的 struct file。它由内核在打开文件时创
建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核创建和驱动源码中,struct file 的指针通常被命名为 file 或 filp 
 
下面我们来展示 struct file  最重要的成员:

1)mode_t  f_mode
   文件模式确定文件是可读的或者可写的(或者都是),通过 位FMODE_READ和 FMODE_WRITE。 

2)loff_t  f_pos
   当前读写位置, loff_t 在所有平台都是 64 位( 在 gcc术语里是 long long)。 驱动可以读这个值,如果它需要知道文件中的当前位置,但是正常地不应该改变它;读和写应当使用它们作为最后参数而收到的指针来更新一个位置,代替直接作用于 filp->f_pos。这个规则的一个例外是在 lseek  方法中,它的目的就是改变文件位置。

3)unsigned  int  f_flags
   这些是文件标志,例如 O_RDONLY,O_NONBLOCK,和 O_SYNC。驱动应当检查 O_NONBLOCK  标志来看是否是请求非阻塞操作;其他标志很少使用。特别地,应当检查读/写许可,使用 f_mode 而不是 f_flags。所有的标志在头文件 <linux/fcntl.h> 中定义。

4)struct file_operations  *f_op
   和文件关联的操作。内核安排指针作为它的 open  实现的一部分,接着读取它当它需要分派任何的操作时,filp->f_op 中的值从不由内核保存为后面的引用;这意味着你可改变你的文件关联的文件操作,在你返回调用者之后新方法会起作用。例如,关联到主编号 1 (/dev/null,/dev/zero 等等)的 open 代码根据打开的次编号来替代 filp->f_op 中的操作。这个做法允许实现几种行为,在同一个主编号下而不必在每个系统调用中引入开销。替换文件操作的能力是面向对象编程的“方法重载”的内核对等体。

5)void  *private_data
   open  系统调用设置这个指针为  NULL,在为驱动调用 open  方法之前。你可自由使用这个成员或者忽略它;你可以使用这个成员来指向分配的数据,但是接着你必须记住在内核销毁文件结构之前,在 release 方法中释放那个内存。 private_data  是一个有用的资源,在系统调用间保留状态信息,我们大部分例子模块都使用它。

6)struct dentry *f_dentry
   关联到文件的目录入口(dentry)结构。设备驱动编写者正常地不需要关心  dentry 结构,除了作为 filp->f_dentry->d_inode 存取 inode 结构。


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 内核中新的锁机制  #define f_dentry f_path.dentry         //f_path的成员之一,当前文件的 dentry 结构  } f_u;  struct path f_path;                    //包含 dentry 和 mnt 两个成员,用于确定文件路径  const struct file_operations *f_op;    //与该文件相关联的操作函数  #define f_vfsmnt f_path.mnt            //表示当前文件所在文件系统的挂载根目录  atomic_t f_count;                      //文件的引用计数(有多少进程打开该文件)  off_t f_pos;                           //该文件在当前进程中的文件偏移量  unsigned int f_flags;                  //对应于 open 时指定的 flag  mode_t f_mode;                         //读写模式:open 的 mod_t mode 参数  struct file_ra_state f_ra;             //在 linux/include/linux/fs.h 中定义,文件预读相关  struct fown_struct f_owner;            //该结构的作用是通过信号进行 I/O 时间通知的数据。  unsigned int f_uid, f_gid;             //文件所有者 id,所有者组 id  unsigned long f_version;  spinlock_t f_ep_lock;  #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;



2. Linux 文件描述符与 C FILE之间的关系


1. linux 文件描述符

        对于 linux 而言,所有对设备和文件的操作都是使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值,指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数。

       通常,一个进程启动时,都会打开 个文件:标准输入、标准输出和标准出错处理。这 个文件分别对应文件描述符为012(宏 STD_FILENOSTDOUT_FILENO STDERR_FILENO)。


2.内核中的实现细节:
   linux  用一个数组来管理进程打开的文件的 file 对象,数组中的每一个元素都存放了一个指向进程所打开的文件的  file 对象。数组的下标用来访问文件。linux  把数组元素的下标叫做该数组元素所对应文件的文件描述符,这个描述符就是系统对文件的标识,也叫做文件描述符数组。(注:内核可以通过系统调用函数  dup( )dup2( )  和  fctl( )  将数组中的多个元素指向同一个文件的  file  对象。即同一个文件可以有多个文件描述符。)文件描述符表在  files_struct   结构中定义,为  structfile*fd_array[NR_OPEN_DEFAULT]; 进程与文件系统及其所打开文件的关系如图所示:

        

3.进程打开一个文件的具体流程
    进程通过系统调用 open( ) 来打开一个文件,实质上是获得一个文件描述符,以便进程通过文件描述符为连接对文件进行其他操作。进程打开文件时,会为该文件创建一个 file对象,并把该 file 对象存入进程打开文件表中(文件描述符数组),进而确定了所打开文件的文件描述符。

       open( ) 操作在内核里通过 sys_open( ) 实现,sys_open( ) 将创建文件的 dentryinode 和 file 对象,并在 file_struct 结构体的进程打开文件表  fd_array[NR_OPEN_DEFAULT] 中寻找一个空闲表项,然后返回这个表项的下标(索引),即文件描述符。创建文件的 file 对象时,将 file 对象的 f_op 指向了所属文件系统的操作函数集 file_operations,而该函数集又来自具体文件的i节点,于是虚拟文件系统就与实际文件系统的操作衔接起来了。

4. C 标准库中的 FILE 结构和文件描述符、files_struct 和 file 结构之间的关系

    早期的 标准库中,FILE 在 stdio.h 中定义Turbo C 中,参见谭浩强的《C程序设计》,FILE 结构体中包含成员 fd,即文件描述符。在 glibc- 2.9 中,stdio.h 中定义 FILE为 _IO_FILE 类型,而 FILE 为 _IO_FILE 在 /glibc-2.9/libio/libio.h 中定义,如下所示,亦可以在安装的 Ubuntu 系统的 /usr/include/stdio.h 找到:


struct _IO_FILE {  int _flags;          /* High-order word is _IO_MAGIC; restis flags. */#define _IO_file_flags _flags  /* The following pointerscorrespond to the C++ streambuf protocol. */  /* Note:  Tk uses the _IO_read_ptr and _IO_read_endfields directly. */  char* _IO_read_ptr;   /* Current read pointer */  char* _IO_read_end;  /* End of get area. */  char* _IO_read_base; /* Start of putback+get area. */  char* _IO_write_base;       /* Start of put area. */  char* _IO_write_ptr;  /* Current put pointer. */  char* _IO_write_end; /* End of put area. */  char* _IO_buf_base;  /* Start of reserve area. */  char* _IO_buf_end;   /* End of reserve area. */  /* The following fields areused to support backing up and undo. */  char *_IO_save_base; /*Pointer to start of non-current get area. */  char *_IO_backup_base;  /* Pointer to first valid character of backuparea */  char *_IO_save_end; /*Pointer to end of non-current get area. */  struct _IO_marker *_markers;  struct _IO_FILE *_chain;  int _fileno;#if 0  int _blksize;#else  int _flags2;#endif  _IO_off_t _old_offset; /*This used to be _offset but it's too small. */#define __HAVE_COLUMN /* temporary */  /* 1+column number ofpbase(); 0 is unknown. */  unsigned short _cur_column;  signed char _vtable_offset;  char _shortbuf[1];  /*  char* _save_gptr;  char* _save_egptr; */  _IO_lock_t *_lock;#ifdef _IO_USE_OLD_IO_FILE};


   当然,上面的结构体太复杂,我们只关心需要的部分-文件描述符即可文件描述符的分配是从小到大逐个查询文件描述符是否已经使用,然后再分配。 

   file 在 /include/linux/fs.h 中定义,从进程的角度来看,文件就是一个 file 结构的实例,也常常叫 file 对象。file 对象是进程调用open 打开一个文件,linux 虚拟文件系统根据进程请求创建的一个 file 对象。进程通过该文件的 file 结构了解和操作文件。


struct file {       /*        * fu_list becomes invalid after file_free iscalled and queued via        * fu_rcuhead for RCU freeing        */       union{              struct list_head       fu_list;              struct rcu_head      fu_rcuhead;       }f_u;       struct path             f_path;#define f_dentry     f_path.dentry#define f_vfsmnt    f_path.mnt       const struct file_operations    *f_op;       atomic_t         f_count;       unsigned int          f_flags;mode_t                  f_mode;       loff_t                    f_pos;       struct fown_struct   f_owner;       unsigned int           f_uid, f_gid;       struct file_ra_state  f_ra;       u64                f_version;#ifdef CONFIG_SECURITY       void               *f_security;#endif       /*needed for tty driver, and maybe others */       void               *private_data;#ifdef CONFIG_EPOLL       /* Used by fs/eventpoll.c to link all thehooks to this file */       struct list_head       f_ep_links;       spinlock_t              f_ep_lock;#endif /* #ifdef CONFIG_EPOLL */       struct address_space       *f_mapping;#ifdef CONFIG_DEBUG_WRITECOUNT       unsigned long f_mnt_write_state;#endif};


   文件描述符数组中存放了一个进程所打开的所有文件。文件描述符数组包含在进程打开的文件表 files_struct 结构中。在 /include/linux/fdtable.h 中定义,为一个指向 file类型的指针数组---fd_array[NR_OPEN_DEFAULT],其中 NR_OPEN_DEFAULT 也在 fdtable.h 中定义,这是一个和具体的 CPU 体系结构有关的变量,#define NR_OPEN_DEFAULTBITS_PER_LONG。以 x86  体系为例,BITS_PER_LONG  在 /include/asm-x86/types.h 中定义,32 位 CPU 为 32,64 位 CPU 为 64。


struct files_struct {  /*   * read mostly part   */       atomic_t count;       struct fdtable *fdt;       struct fdtable fdtab;  /*   * written part on aseparate cache line in SMP   */       spinlock_t file_lock____cacheline_aligned_in_smp;       int next_fd;       struct embedded_fd_setclose_on_exec_init;       struct embedded_fd_setopen_fds_init;       struct file * fd_array[NR_OPEN_DEFAULT];};


FILE 结构和文件描述符 fdfiles_struct 和 file 结构之间的关系如下图所示:

 


以上就是我关于 File 和 fd 各方面的理解,希望对你有所帮助。





0 0
原创粉丝点击