<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 16.1.?注冊 塊驅動, 象字符驅動, 必須使用一套注冊接口來使內核可使用它們的設備. 概念是類似的, 但是塊設備注冊的細節是都不同的. 你有一整套新的數據結構和設備操作要學習. ### 16.1.1.?塊驅動注冊 大部分塊驅動采取的第一步是注冊它們自己到內核. 這個任務的函數是 register_blkdev(在 <linux/fs.h> 中定義): ~~~ int register_blkdev(unsigned int major, const char *name); ~~~ 參數是你的設備要使用的主編號和關聯的名子(內核將顯示它在 /proc/devices). 如果 major 傳遞為0, 內核分配一個新的主編號并且返回它給調用者. 如常, 自 register_blkdev 的一個負的返回值指示已發生了一個錯誤. 取消注冊的對應函數是: ~~~ int unregister_blkdev(unsigned int major, const char *name); ~~~ 這里, 參數必須匹配傳遞給 register_blkdev 的那些, 否則這個函數返回 -EINVAL 并且什么都不注銷. 在2.6內核, 對 register_blkdev 的調用完全是可選的. 由 register_blkdev 所進行的功能已隨時間正在減少; 這個調用唯一的任務是 (1) 如果需要, 分配一個動態主編號, 并且 (2) 在 /proc/devices 創建一個入口. 在將來的內核, register_blkdev 可能被一起去掉. 同時, 但是, 大部分驅動仍然調用它; 它是慣例. ### 16.1.2.?磁盤注冊 雖然 register_blkdev 可用來獲得一個主編號, 它不使任何磁盤驅動器對系統可用. 有一個分開的注冊接口你必須使用來管理單獨的驅動器. 使用這個接口要求熟悉一對新結構, 這就是我們的起點. #### 16.1.2.1.?塊設備操作 字符設備通過 file_ 操作結構使它們的操作對系統可用. 一個類似的結構用在塊設備上; 它是 struct block_device_operations, 定義在 <linux/fs.h>. 下面是一個對這個結構中的成員的簡短的概覽; 當我們進入 sbull 驅動的細節時詳細重新訪問它們. 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. 專心的讀者可能已注意到這個列表一個有趣的省略: 沒有實際讀或寫數據的函數. 在塊 I/O 子系統, 這些操作由請求函數處理, 它們應當有它們自己的一節并且在本章后面討論. 在我們談論服務請求之前, 我們必須完成對磁盤注冊的討論. #### 16.1.2.2.?gendisk 結構 struct gendisk (定義于 <linux/genhd.h>) 是單獨一個磁盤驅動器的內核表示. 事實上, 內核還使用 gendisk 來表示分區, 但是驅動作者不必知道這點. struct gedisk 中有幾個成員, 必須被一個塊驅動初始化: 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 直到你的驅動被完全初始化并且準備好響應對那個磁盤的請求. ### 16.1.3.?在 sbull 中的初始化 是時間進入一些例子了. sbull 驅動(從 O'Reilly 的 FTP 網站, 以及其他例子源碼)實現一套內存中的虛擬磁盤驅動器. 對每個驅動器, sbull 分配(使用 vmalloc, 為了簡單)一個內存數組; 它接著使這個數組可通過塊操作來使用. 這個 sbull 驅動可通過分區這個驅動器, 在上面建立文件系統, 以及加載到系統層級中來測試. 象我們其他的例子驅動一樣, sbull 允許一個主編號在編譯或者模塊加載時被指定. 如果沒有指定, 動態分配一個. 因為對 register_blkdev 的調用被用來動態分配, sbull 應當這樣做: ~~~ sbull_major = register_blkdev(sbull_major, "sbull"); if (sbull_major <= 0) { printk(KERN_WARNING "sbull: unable to get major number\n"); return -EBUSY; } ~~~ 同樣, 象我們在本書已展現的其他虛擬設備, sbull 設備由一個內部結構描述: ~~~ 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 */ }; ~~~ 需要幾個步驟來初始化這個結構, 并且使系統可用關聯的設備. 我們從基本的初始化開始, 并且分配底層的內存: ~~~ 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. 一旦所有的都被設置, 我們以對 add_disk 的調用來結束. 我們的幾個方法將在 add_disk 返回時被調用, 因此我們負責做這個調用, 這是初始化我們的設備的最后一步. ### 16.1.4.?注意扇區大小 如同我們之前提到的, 內核對待每個磁盤如同一個 512-字節扇區的數組. 不是所有的硬件都使用那個扇區大小, 但是. 使一個有不同扇區大小的設備工作不是一件很難的事; 只要小心處理幾個細節. sbull 設備輸出一個 hardsect_size 參數, 可被用來改變設備的"硬件"扇區大小. 通過看它的實現, 你可見到如何添加這個支持到你自己的驅動. 這些細節中的第一個是通知內核你的設備支持的扇區大小. 硬件扇區大小是一個在請求隊列的參數, 而不是在 gendisk 結構. 這個大小通過調用 blk_queue_hardsect_size 設置的, 在分配隊列后馬上進行: ~~~ blk_queue_hardsect_size(dev->queue, hardsect_size); ~~~ 一旦完成那個, 內核堅持你的設備的硬件扇區大小. 所有的 I/O 請求被正確對齊到一個硬件扇區的起始, 并且每個請求的長度是一個整數的扇區數. 你必須記住, 但是, 內核一直以 512-字節扇區表述自己; 因此, 有必要相應地轉換所有的扇區號. 因此, 例如, 當 sbull 在它的 gendisk 結構中設置設備的容量時, 這個調用看來象: ~~~ set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE)); ~~~ KERNEL_SECTOR_SIZE 是一個本地定義的常量, 我們用來調整內核的 512-字節和任何我們已被告知要使用的大小. 在我們查看 sbull 請求處理邏輯中會不時看到這類計算出來.
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看