文件描述符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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM