inode 裝置檔案

來源:互聯網
上載者:User

裝置並不是通過其檔案名稱來標識,而是通過檔案的主、次裝置號標識(檔案名稱和檔案的主次裝置號在裝置檔案的父目錄的inode的資料區中表現出來的,這是fs層的東東)。

 

裝置檔案和普通檔案的區別:

查看裝置檔案的命令為:ls -l /dev/

1:存取權限前面的字母b/c,分別表示塊裝置和字元裝置。

2:裝置檔案沒有檔案長度,而增加了另外兩個值,分別為主裝置號和次裝置號。二者共同形成一個唯一的號碼,核心由此可以尋找到對應的裝置驅動程式。

由於引入了udev機制,/dev不再放置到基於磁碟的檔案系統中,而是使用tmpfs,這是RAM磁碟檔案系統ramfs的一種輕型變體。這意味著裝置結點不是持久性的,系統關機/重啟後就會消失。

 

IOCTL:輸入輸出控制介面,是用於配置和修改特定裝置屬性的通用介面。

 

 

核心如果能瞭解到系統中有哪些字元裝置和塊裝置可用,那自然是很有利的,因而需要維護一個資料庫,此外必須提供一個介面,以便驅動程式開發人員能夠將新項添加到資料庫中。

資料結構:

1、裝置資料庫

儘管塊裝置和字元裝置彼此的行為有很大的不同,但用於跟蹤所有可用裝置的資料庫是相同的。因為字元裝置和塊裝置都是通過唯一的裝置號標識。但是,資料庫會根據塊裝置/字元裝置來跟蹤不同的對象。

。每個字元裝置都表示為一個struct cdev的執行個體

。struct genhd用於管理塊裝置的分區,作用類似於字元裝置的cdev.

有兩個全域數組(bdev_map用於塊裝置,cdev_map用於字元裝置)用來實現散列表,使用主裝置號作為散列鍵。cdev_map和bdev_map都是同一資料結構struct kobj_map的執行個體。散列的方法很簡單:major%255.

struct kobj_map {
    struct probe {
         struct probe *next;
         dev_t dev;
         unsigned long range;
         struct module *owner;
         kobj_probe_t *get;
         int (*lock)(dev_t, void *);
         void *data;
    } *probes[255];
 struct mutex *lock;
};

互斥量lock實現了對散列表訪問的序列化.struct probe的成員如下:

next:將所有散列錶鏈接在一個單鏈表中。(沒有搞明白這個單鏈表中的裝置的主裝置號都是相同的否?

dev: 表示裝置號,包括主次裝置號

rang:從裝置號的連續範圍儲存在range中。那麼與裝置關聯的各個從裝置號的範圍是[MINORS(dev), MINORS(dev)+range-1].

owner:指向提供裝置驅動程式的模組。

get:指向一個函數,可以返回與裝置關聯的kobject執行個體。

data:字元裝置和塊裝置的區別就在於data.對於字元裝置,他指向struct cdev的一個執行個體,而對於塊裝置,則指向struct genhd的執行個體。

                                         

 

2、字元裝置範圍資料庫

第二類資料庫只是用於字元裝置。他是用於管理為驅動程式分配的裝置號範圍。驅動程式可以請求一個動態裝置號(用register_chrdev_region()函數註冊裝置號),或者指定一個範圍,從中擷取(用alloc_chrdev_region()函數分配裝置號)。

再次使用散列表來跟蹤已經分配的裝置號範圍,並同樣使用主裝置號作為散列鍵。資料結構如下:

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];

next:連結同一散列行中的所有散列元素

major:主裝置號

baseminor是包含minorct個從裝置號的連續範圍的最小的從裝置

name為裝置提供了一個標識符

 

與檔案系統關聯

inode中裝置檔案成員

只列出與驅動有關的成員

struct inode{

          ...

          dev_t             i_rdev;

          ...

          umode_t        i_mode;

          ...

          struct file_operations *i_fop;

          ...

          union {

                  struct block_device *i_bdev;

                  struct cdev              *i_cdev;

          };

          ....

};

i_mode:為唯一的標識與一個裝置檔案關聯,核心在i_mode中儲存量檔案類型(面向塊/面向字元

i_rdev中儲存了主次裝置號

i_fop是一組函數指標的集合,包括許多檔案操作(open,read,write等),這些有虛擬檔案系統使用來出來塊裝置

核心會根據inode表示塊裝置還是字元裝置,來使用i_bdev/i_cdev指向更多具體資訊。

 

標準檔案操作

在開啟一個裝置檔案時,各種檔案系統的實現都會調用init_special_inode函數,為字元裝置或者塊裝置檔案建立一個Inode(在檔案系統層).

在此還是有疑問的,在開啟裝置的時候才建立Inode,難道在裝置檔案建立的時候fs系統沒有建立Inode嗎,fs只是把裝置檔案的ID和檔案名稱存於/dev的inode的dentry中嗎?

針對上面的問題需要說明的是:在裝置檔案開啟的時候和建立裝置檔案的時候都建立了inode,但這兩個inode不是同一個,是兩個不同類型的資料。裝置檔案建立的時候產生的Inode是儲存在硬碟中的,他的結構比較簡單,隨裝置檔案的刪除會消失的。而裝置檔案開啟的時候建立的inode是struct inode結構體,他是儲存在ram中的,隨著檔案的關閉會消失的。

對下面的問題“fs只是把裝置檔案的id 和檔案名稱存於/dev的inode的dentry中嗎?”這樣的猜想是錯誤的,有這樣的說法,/dev目錄是一個臨時的檔案夾,他並不在inode塊中存在inode結點,但他目錄下的檔案在inode塊中是存在inode結點的。那麼我們就該想了,那fs是如何找到/dev目錄下的檔案呢?裝置檔案是一類比較特殊的檔案,他的尋找方式跟普通的檔案的尋找方式是不同的。其實裝置檔案根本就不需要尋找他在磁碟上對應的inode結點,因為裝置檔案根本就沒有資料區,我們需要的裝置檔案的裝置操作指標(f_ops).如何找到裝置操作指標的呢?看下面的黑體部分就可以了。(上面的說法對字元裝置檔案是可行的,但沒有看塊裝置,不知道塊裝置是否也是這樣的。其實也同樣存在一個問題,按照上面的解釋,我們完全可以不用為裝置檔案在磁碟上建立一個inode結點,但系統還是建了,可以肯定是這樣的inode結點是有作用的,系統不會乾沒意義的事的)

void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
         inode->i_mode = mode;
         if (S_ISCHR(mode)) {
                   inode->i_fop = &def_chr_fops;
                   inode->i_rdev = rdev;
         } else if (S_ISBLK(mode)) {
                   inode->i_fop = &def_blk_fops;
                   inode->i_rdev = rdev;
         } else if (S_ISFIFO(mode))
                   inode->i_fop = &def_fifo_fops;
         else if (S_ISSOCK(mode))
                   inode->i_fop = &bad_sock_fops;
         else
                   printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o)/n", mode);
}

用於字元裝置的標準操作

fs/device.c

const struct file_operations def_chr_fops = {
          .open = chrdev_open,
};

chrdev_open()函數的主要任務是向該結構(其實不明白"該結構"指的是什麼結構,似乎有點像struct file)填入已開啟裝置的函數指標(file_operation),使得能夠在裝置檔案上執行有意義的操作,並最終能夠操作裝置本身。

chrdev_open()函數的框圖:

假定表示裝置檔案的inode此前沒有開啟過,根據裝置號,kobject_lookup查詢字元裝置的資料庫(cdev_map),並返回與該驅動程式向關聯的kobject執行個體。該傳回值可用於擷取cdev執行個體

擷取了對應於裝置的cdev執行個體,核心通過cdev->ops還可以訪問特定於裝置的file_operations.接下來設定各種資料結構之間的關聯

                 

inode->i_cdev指向所選擇的cdev執行個體。在下一次開啟該inode時,就不必再查詢字元裝置的資料庫,因為可以使用緩衝的值。

該inode將添加到cdev->list

file->f_ops是用於struct file新的file_operations,設定為指向struct cdev給出的file_operations執行個體。         

接下來調用struct file新的file_operations中的open函數(現在是用於特定的裝置,我們針對特定的裝置在驅動層實現的open函數),在裝置上執行所需的初始化任務。    

      

用於塊裝置的標準操作

fs/block_dev.c

const struct file_operations def_blk_fops = {
      .open  = blkdev_open,
      .release = blkdev_close,
      .llseek  = block_llseek,
      .read  = do_sync_read,
      .write  = do_sync_write,
      .aio_read = generic_file_aio_read,
      .aio_write = generic_file_aio_write_nolock,
      .mmap  = generic_file_mmap,
      .fsync  = block_fsync,
      .unlocked_ioctl = block_ioctl,
      #ifdef CONFIG_COMPAT
      .compat_ioctl = compat_blkdev_ioctl,
      #endif
      .splice_read = generic_file_splice_read,
      .splice_write = generic_file_splice_write,
};

儘管file_operations與block_device_operations的結構類似,但兩者不能混淆。file_operations由VFS層用來與使用者空間通訊的,他裡面的函數是有使用者層來調用的。其中的函數會調用block_device_operations中的函數,以實現與塊裝置的通訊。block_device_operations必須針對各種具體的塊裝置分別實現,對裝置的屬性加以抽象,而在此基礎上建立file_operations,是使用者可以使用相同的操作(file_operations提供的函數)即可處理所有的塊裝置。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.