Linux字符設備中的兩個重要結構體(file、inode)


對於Linux系統中,一般字符設備和驅動之間的函數調用關系如下圖所示

上圖描述了用戶空間應用程序通過系統調用來調用程序的過程。一般而言在驅動程序的設計中,會關系 struct file 和 struct inode 這兩個結構體。

用戶空間使用open()系統調用函數打開一個字符設備時( int fd = open("dev/demo", O_RDWR) )大致有以下過程:

  1. 在虛擬文件系統VFS中的查找對應與字符設備對應 struct inode節點
  2. 遍歷字符設備列表(chardevs數組),根據inod節點中的 cdev_t設備號找到cdev對象
  3. 創建struct file對象(系統采用一個數組來管理一個進程中的多個被打開的設備,每個文件秒速符作為數組下標標識了一個設備對象)
  4. 初始化struct file對象,將 struct file對象中的 file_operations成員指向 struct cdev對象中的 file_operations成員(file->fops =  cdev->fops)
  5. 回調file->fops->open函數

一、inode結構體

         VFS inode 包含文件訪問權限、屬主、組、大小、生成時間、訪問時間、最后修改時間等信息。它是Linux 管理文件系統的最基本單位,也是文件系統連接任何子目錄、文件的橋梁。

        內核使用inode結構體在內核內部表示一個文件。因此,它與表示一個已經打開的文件描述符的結構體(即file 文件結構)是不同的,我們可以使用多個file 文件結構表示同一個文件的多個文件描述符,但此時,所有的這些file文件結構全部都必須只能指向一個inode結構體

      inode結構體包含了一大堆文件相關的信息,但是就針對驅動代碼來說,我們只要關心其中的兩個域即可:

  1. dev_t i_rdev;     表示設備文件的結點,這個域實際上包含了設備號。
  2. struct cdev *i_cdev;  struct cdev是內核的一個內部結構,它是用來表示字符設備的,當inode結點指向一個字符設備文件時,此域為一個指向inode結構的指針。

下面是源代碼:

struct inode {  
  struct hlist_node i_hash;  
  struct list_head i_list;  
  struct list_head i_sb_list;  
  struct list_head i_dentry;  
  unsigned long  i_ino;  
  atomic_t  i_count;  
  unsigned int  i_nlink;  
  uid_t   i_uid;//inode擁有者id  
  gid_t   i_gid;//inode所屬群組id  
  dev_t   i_rdev;//若是設備文件,表示記錄設備的設備號  
  u64   i_version;  
  loff_t   i_size;//inode所代表大少  
#ifdef __NEED_I_SIZE_ORDERED  
  seqcount_t  i_size_seqcount;  
#endif  
  struct timespec  i_atime;//inode最近一次的存取時間  
  struct timespec  i_mtime;//inode最近一次修改時間  
  struct timespec  i_ctime;//inode的生成時間  
  unsigned int  i_blkbits;  
  blkcnt_t  i_blocks;  
  unsigned short          i_bytes;  
  umode_t   i_mode;  
  spinlock_t  i_lock;   
  struct mutex  i_mutex;  
  struct rw_semaphore i_alloc_sem;  
  const struct inode_operations *i_op;  
  const struct file_operations *i_fop;   
  struct super_block *i_sb;  
  struct file_lock *i_flock;  
  struct address_space *i_mapping;  
  struct address_space i_data;  
#ifdef CONFIG_QUOTA  
  struct dquot  *i_dquot[MAXQUOTAS];  
#endif  
  struct list_head i_devices;  
  union {  
    struct pipe_inode_info *i_pipe;  
    struct block_device *i_bdev;  
    struct cdev  *i_cdev;//若是字符設備,對應的為cdev結構  
  }; 
}; 
struct inode{...}

 inode的相關操作函數

/* 內核函數從inode中提取設備號 */

/* 提取主設備號 */
static inline unsigned imajor(const struct inode *inode)
{
  
return MAJOR(inode->i_rdev); } /* 提取次設備號 */ static inline unsigned iminor(const struct inode *inode) {   return MINOR(inode->i_rdev); }

  二、file 文件結構體

       在設備驅動中,這也是個非常重要的數據結構,必須要注意一點,這里的file與用戶空間程序中的FILE指針是不同的,用戶空間FILE是定義在C庫中,從來不會出現在內核中。而struct file,卻是內核當中的數據結構,因此,它也不會出現在用戶層程序中。

       file結構體指示一個已經打開的文件(設備對應於設備文件),其實系統中的每個打開的文件在內核空間都有一個相應的struct file結構體,它由內核在打開文件時創建,並傳遞給在文件上進行操作的任何函數,直至文件被關閉。如果文件被關閉,內核就會釋放相應的數據結構。

     在內核源碼中,struct file要么表示為file,或者為filp(意指“file pointer”), 注意區分一點,file指的是struct file本身,而filp是指向這個結構體的指針。

下面是幾個重要成員:

1、fmode_t f_mode;

      此文件模式通過 FMODE_READ ,  FMODE_WRITE 識別了文件為可讀的,可寫的,或者是二者。在open或ioctl函數中可能需要檢查此域以確認文件的讀/寫權限,你不必直接去檢測讀或寫權限,因為在進行octl等操作時內核本身就需要對其權限進行檢測。

2、 loff_t f_pos;

     當前讀寫文件的位置。為64位。如果想知道當前文件當前位置在哪,驅動可以讀取這個值而不會改變其位置。對read,write來說,當其接收到一個loff_t型指針作為其最后一個參數時,他們的讀寫操作便作更新文件的位置,而不需要直接執行filp ->f_pos操作。而

3、unsigned int f_flags;

     文件標志,如 O_RDONLY ,  O_NONBLOCK 以及 O_SYNC 。在驅動中還可以檢查O_NONBLOCK標志查看是否有非阻塞請求。其它的標志較少使用。特別地注意的是,讀寫權限的檢查是使用f_mode而不是f_flog。所有的標量定義在頭文件中

4、struct file_operations *f_op;

    與文件相關的各種操作。當文件需要迅速進行各種操作時,內核分配這個指針作為它實現文件打開,讀,寫等功能的一部分。filp->f_op 其值從未被內核保存作為下次的引用,即你可以改變與文件相關的各種操作,這種方式效率非常高。

    file_operation 結構體解析如下:Linux字符設備驅動file_operations

5、 void *private_data;

      在驅動調用open方法之前,open系統調用設置此指針為NULL值。你可以很自由的將其做為你自己需要的一些數據域或者不管它,如,你可以將其指向一個分配好的數據,但是你必須記得在file struct被內核銷毀之前在release方法中釋放這些數據的內存空間。private_data用於在系統調用期間保存各種狀態信息是非常有用的。

三、chardevs 數組

     前面對用戶層open()的分析提到,通過數據結構 struct inode{...} 中的 i_cdev 成員可以找到cdev,而所有的字符設備都在 chrdevs 數組中,chrdevs具體是什么樣的呢

下面先看一下 chrdevs 的定義: 

#define CHRDEV_MAJOR_HASH_SIZE 255  
static DEFINE_MUTEX(chrdevs_lock);

static struct char_device_struct {   struct char_device_struct *next; // 結構體指針   unsigned int major; // 主設備號   unsigned int baseminor; // 次設備起始號   int minorct; // 次備號個數   char name[64];   struct cdev *cdev; /* will die */ } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];// 只能掛255個字符主設備<span style="font-family: Arial, Helvetica, sans-serif; rgb(255, 255, 255);"> </span>

         可以看到全局數組 chrdevs 包含了255(CHRDEV_MAJOR_HASH_SIZE 的值)個 struct char_device_struct的元素,每一個對應一個相應的主設備號。

       如果分配了一個設備號,就會創建一個 struct char_device_struct 的對象,並將其添加到 chrdevs 中;這樣,通過chrdevs數組,我們就可以知道分配了哪些設備號。

相關函數,(這些函數在上篇已經介紹過,現在回顧一下:

  register_chrdev_region( ) 分配指定的設備號范圍

  alloc_chrdev_region( ) 動態分配設備范圍

他們都主要是通過調用函數 __register_chrdev_region() 來實現的;要注意,這兩個函數僅僅是注冊設備號!如果要和cdev關聯起來,還要調用cdev_add()。

  register_chrdev( )申請指定的設備號,並且將其注冊到字符設備驅動模型中.

  它所做的事情為:

  1. 注冊設備號, 通過調用 __register_chrdev_region() 來實現
  2. 分配一個cdev, 通過調用 cdev_alloc() 來實現
  3. 將cdev添加到驅動模型中, 這一步將設備號和驅動關聯了起來. 通過調用 cdev_add() 來實現
  4. 將第一步中創建的 struct char_device_struct 對象的 cdev 指向第二步中分配的cdev. 由於register_chrdev()是老的接口,這一步在新的接口中並不需要。

四、cdev 結構體

        Linux內核中,使用 struct cdev 來描述一個字符設備

五、文件系統中對字符設備文件的訪問

        下面看一下上層應用open() 調用系統調用函數的過程

        對於一個字符設備文件, 其inode->i_cdev 指向字符驅動對象cdev, 如果i_cdev為 NULL ,則說明該設備文件沒有被打開.

  由於多個設備可以共用同一個驅動程序.所以,通過字符設備的inode 中的i_devices 和 cdev中的list組成一個鏈表

首先,系統調用open打開一個字符設備的時候, 通過一系列調用,最終會執行到 chrdev_open

  (最終是通過調用到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 這一系列的調用過程,本文暫不討論)

  int chrdev_open(struct inode * inode, struct file * filp)

chrdev_open()所做的事情可以概括如下:

  1. 根據設備號(inode->i_rdev), 在字符設備驅動模型中查找對應的驅動程序, 這通過kobj_lookup() 來實現, kobj_lookup()會返回對應驅動程序cdev的kobject.

  2. 設置inode->i_cdev , 指向找到的cdev.

  3. 將inode添加到cdev->list 的鏈表中.

  4. 使用cdev的ops 設置file對象的f_op

  5. 如果ops中定義了open方法,則調用該open方法

  6. 返回

執行完 chrdev_open()之后,file對象的f_op指向cdev的ops,因而之后對設備進行的read, write等操作,就會執行cdev的相應操作。

 

得到進程號:
asm/current.h
linux/sched.h
#define current get_current()

得到進程號:current->pid
得到進程名:current->comm

 


免責聲明!

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



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