1. 程式人生 > >儲存筆記--塊裝置驅動的註冊

儲存筆記--塊裝置驅動的註冊

塊裝置的註冊

1。塊裝置模型示意圖

這裡寫圖片描述

一個塊是一個固定大小的資料塊, 大小由核心決定. 塊常常是 4096 位元組, 但是這個值可依賴體系和使用的檔案系統而變化. 一個扇區, 相反, 是一個小塊, 它的大小常常由底層的硬體決定. 核心期望處理實現 512-位元組扇區的裝置。

2。註冊

  • 2.1 塊驅動的註冊
    int register_blkdev(unsigned int major, const char *name);
    引數是你的裝置要使用的主編號和關聯的名子(核心將顯示它在 /proc/devices). 如果 major 傳遞為0, 核心分配一個新的主編號並且返回它給呼叫者. 如常, 自 register_blkdev 的一個負的返回值指示已發生了一個錯誤.
    取消註冊的對應函式是:
    int unregister_blkdev(unsigned int major, const char *name);

  • 2.2 磁碟註冊

    磁碟操作函式 struct block_device_operations。

    int (*open)(struct inode *inode, struct file *filp);
    int (*release)(struct inode *inode, struct file *filp);
    就像它們的字元驅動對等體一樣工作的函式; 無論何時裝置被開啟和關閉都呼叫它們. 一個字元驅動可能通過啟動裝置或者鎖住門(為可移出的介質)來響應一個 open 呼叫. 如果你將介質鎖入裝置, 你當然應當在 release 方法中解鎖.

    int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
    實現 ioctl 系統呼叫的方法. 但是, 塊層首先解釋大量的標準請求; 因此大部分的塊驅動 ioctl 方法相當短.

    int (*media_changed) (struct gendisk *gd);
    被核心呼叫來檢查是否使用者已經改變了驅動器中的介質的方法, 如果是這樣返回一個非零值. 顯然, 這個方法僅適用於支援可移出的介質的驅動器(並且最好給驅動一個”介質被改變”標誌); 在其他情況下可被忽略.
    struct gendisk 引數是核心任何表示單個磁碟; 我們將在下一節檢視這個結構.

    int (*revalidate_disk) (struct gendisk *gd);
    revalidate_disk 方法被呼叫來響應一個介質改變; 它給驅動一個機會來進行需要的任何工作使新介質準備好使用. 這個函式返回一個 int 值, 但是值被核心忽略.

    struct module *owner;
    一個指向擁有這個結構的模組的指標; 它應當常常被初始化為 THIS_MODULE.

    磁碟結構體 gendisk
    struct gendisk 是單獨一個磁碟驅動器的核心表示. 事實上, 核心還使用 gendisk 來表示分割槽, 必須被一個塊驅動初始化:
    int major;
    int first_minor;
    int minors;
    描述被磁碟使用的裝置號的成員. 至少, 一個驅動器必須使用最少一個次編號. 如果你的驅動會是可分割槽的, 但是(並且大部分應當是), 你要分配一個次編號給每個可能的分割槽. 次編號的一個普通的值是 16, 它允許”全磁碟”裝置盒 15 個分割槽. 一些磁碟驅動使用 64 個次編號給每個裝置.

    char disk_name[32];
    應當被設定為磁碟驅動器名子的成員. 它出現在 /proc/partitions 和 sysfs.

    struct block_device_operations *fops;
    來自前一節的裝置操作集合.

    struct request_queue *queue;
    被核心用來管理這個裝置的 I/O 請求的結構; 我們在”請求處理”一節中檢查它.

    int flags;
    一套標誌(很少使用), 描述驅動器的狀態. 如果你的裝置有可移出的介質, 你應當設定 GENHD_FL_REMOVABLE. CD-ROM 驅動器可設定 GENHD_FL_CD. 如果, 由於某些原因, 你不需要分割槽資訊出現在 /proc/partitions, 設定 GENHD_FL_SUPPRESS_PARTITIONS_INFO.

    sector_t capacity;
    這個驅動器的容量, 以512-位元組扇區來計. sector_t 型別可以是 64 位寬. 驅動不應當直接設定這個成員; 相反, 傳遞扇區數目給 set_capacity.

    void *private_data;
    塊驅動可使用這個成員作為一個指向它們自己內部資料的指標.
    核心提供了一小部分函式來使用 gendisk 結構. 我們在這裡介紹它們, 接著看 sbull 如何使用它們來使系統可使用它的磁碟驅動器.

    struct gendisk 是一個動態分配的結構, 它需要特別的核心操作來初始化; 驅動不能自己分配這個結構. 相反, 你必須呼叫:
    struct gendisk *alloc_disk(int minors);
    minors 引數應當是這個磁碟使用的次編號數目; 注意你不能在之後改變 minors 成員並且期望事情可以正確工作. 當不再需要一個磁碟時, 它應當被釋放, 使用:

    void del_gendisk(struct gendisk *gd);
    一個 gendisk 是一個被引用計數的結構(它含有一個 kobject). 有 get_disk 和 put_disk 函式用來操作引用計數, 但是驅動應當從不需要做這個. 正常地, 對 del_gendisk 的呼叫去掉了最一個 gendisk 的最終的引用, 但是不保證這樣. 因此, 這個結構可能繼續存在(並且你的方法可能被呼叫)在呼叫 del_gendisk 之後. 但是, 如果你刪除這個結構當沒有使用者時(即, 在最後的釋放之後, 或者在你的模組清理函式), 你可確信你不會再收到它的資訊.

    分配一個 gendisk 結構不能使系統可使用這個磁碟. 要做到這點, 你必須初始化這個結構並且呼叫 add_disk:
    void add_disk(struct gendisk *gd);

這裡記住一件重要的事情:一旦你呼叫add_disk, 這個磁碟是”活的”並且它的方法可被在任何時間被呼叫. 實際上, 這樣的第一個呼叫將可能發生, 即便在 add_disk 返回之前; 核心將讀前幾個位元組以試圖找到一個分割槽表. 因此你不應當呼叫 add_disk 直到你的驅動被完全初始化並且準備好響應對那個磁碟的請求.

3。初始化

  • 虛擬塊裝置
    struct sbull_dev {
    int size; /* Device size in sectors */
    u8 data; / The data array */
    short users; /* How many users */
    short media_change; /* Flag a media change? */
    spinlock_t lock; /* For mutual exclusion */
    struct request_queue queue; / The device request queue */
    struct gendisk gd; / The gendisk structure */
    struct timer_list timer; /* For simulated media changes */
    };
  • 註冊
    sbull_major = register_blkdev(sbull_major, “sbull”);
    if (sbull_major <= 0)
    {
    printk(KERN_WARNING “sbull: unable to get major number\n”);
    return -EBUSY;
    }

  • 分配記憶體
    memset (dev, 0, sizeof (struct sbull_dev));
    dev->size = nsectors*hardsect_size;
    dev->data = vmalloc(dev->size);
    if (dev->data == NULL)
    {
    printk (KERN_NOTICE “vmalloc failure.\n”);
    return;
    }

  • 分配請求佇列
    spin_lock_init(&dev->lock);
    dev->queue = blk_init_queue(sbull_request, &dev->lock);
    這裡, sbull_request 是我們的請求函式 – 實際進行塊讀和寫請求的函式. 當我們分配一個請求佇列時, 我們必須提供一個自旋鎖來控制對那個佇列的存取. 這個鎖由驅動提供而不是核心通常的部分, 因為, 常常, 請求佇列和其他的驅動資料結構在相同的臨界區; 它們可能被同時存取. 如同任何分配記憶體的函式, blk_init_queue 可能失敗, 因此你必須在繼續之前檢查返回值.
  • 並且安裝對應的 gendisk
    dev->gd = alloc_disk(SBULL_MINORS);
    if (! dev->gd)
    {
    printk (KERN_NOTICE “alloc_disk failure\n”);
    goto out_vfree;
    }
    dev->gd->major = sbull_major;
    dev->gd->first_minor = which*SBULL_MINORS;
    dev->gd->fops = &sbull_ops;
    dev->gd->queue = dev->queue;
    dev->gd->private_data = dev;
    snprintf (dev->gd->disk_name, 32, “sbull%c”, which + ‘a’);
    set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
    add_disk(dev->gd);

這裡, SBULL_MINORS 是每個 sbull 裝置所支援的次編號的數目. 當我們設定第一個次編號給每個裝置, 我們必須考慮被之前的裝置所用的全部編號. 磁碟的名子被設定, 這樣第一個是 sbulla, 第二個是 sbullb, 等等. 使用者空間可接著新增分割槽號以便它們在第 2 個裝置上的分割槽可能是 /dev/sbull3.