1. 題外話html
在蛻變成蝶的一系列學習當中,咱們已經掌握了大部分Linux驅動的知識,在乾坤合一的分享當中,以綜合實例爲主要講解,在一個月的蛻繭成蝶的學習探索當中,以爲數據結構,指針,鏈表等等佔據了代碼的大部分框架,這些都須要咱們平時多看代碼,而且在相關知識點的時候須要在電腦上進行操做,這也讓本身受益不淺,筆者在這期間受到了幾家IT學院的邀請錄製視頻,當兼職佈道師。但畢竟本身仍是個學生,應該潛心學習,爭取更好的作一個IT的人才,因此都沒有接受,這裏很抱歉,而且會更加努力,好好鑽研,但願和你們一塊兒共同進步~node
2. 塊設備與字符設備I/O口操做異同數據結構
2.1 塊設備只能以塊爲單位接受輸入和返回輸出,而字符設備則以字節爲單位。大多數設備是字符設備,由於它們不須要緩衝並且不以固定塊大小進行操做。 框架
2.2 塊設備對於I/O 請求有對應的緩衝區,所以它們能夠選擇以什麼順序進行響應,字符設備無須緩衝且被直接讀寫。對於存儲設備而言調 讀寫的順序做用巨大,由於在讀寫連續的扇區比分離的扇區更快。 dom
2.3 字符設備只能被順序讀寫,而塊設備能夠隨機訪問。雖然塊設備可隨機訪問,可是對於磁盤這類機械設備而言,順序地組織塊設備的訪問能夠提升性能。函數
3. 塊設備驅動結構性能
3.1 block_device_operations 結構體學習
struct block device operations { int (*open)(struct inode *, struct file*); //打開 int (*release)(struct inode *, struct file*); //釋放 //與字符設備驅動相似,當設備被打開和關閉時將調用它們。 int (*ioctl)(struct inode *,struct file *,unsigned,unsigned long); //ioctl // ioctl()系統調用的實現,塊設備包含大量的標準請求,這些標準請求由Linux 塊設備層處理 long (*unlocked ioctl)(struct file *, unsigned, unsigned long); long (*compat ioctl)(struct file *, unsigned, unsigned long); int (*direct access)(struct block device *, sector t, unsigned long*); int (*media changed)(struct gendisk*); //介質被改變? //被內核調用來檢查是否驅動器中的介質已經改變,若是是,則返回一個非0 值,不然返回0 int (*revalidate disk)(struct gendisk*); //使介質有效 //revalidate_disk()函數被調用來響應一個介質改變,它給驅動一個機會來進行必要的工做以使新介質準備好。 int (*getgeo)(struct block device *, struct hd geometry*);//填充驅動器信息 //根據驅動器的幾何信息填充一個hd_geometry 結構體 struct module *owner; //模塊擁有者 // 一個指向擁有這個結構體的模塊的指針,它一般被初始化爲THIS_MODULE };
3.2 gendisk 結構體ui
struct gendisk { int major; /* 主設備號 */ int first minor; /*第1個次設備號*/ int minors; /* 最大的次設備數,若是不能分區,則爲1*/ char disk name [32]; /* 設備名稱 */ struct hd struct **part; /* 磁盤上的分區信息 */ struct block device operations *fops; /*塊設備操做結構體*/ struct request queue *queue; /*請求隊列*/ void *private data; /*私有數據*/ sector t capacity; /*扇區數,512 字節爲1個扇區*/ int flags; char devfs name[64]; int number; struct device *driverfs dev; struct kobject kobj; struct timer rand state *random; int policy; atomic t sync io; /* RAID */ unsigned long stamp; int in flight; #ifdef CONFIG SMP struct disk stats *dkstats; #else struct disk stats dkstats; #endif };
3.3 gendisk的操做atom
//分配gendisk struct gendisk *alloc disk (int minors); // 增長gendisk void add disk(struct gendisk *gd); // 釋放gendisk void del gendisk (struct gendisk *gd); //gendisk 引用計數 // 設置gendisk 容量 void set capacity (struct gendisk *disk, sector t size);
3.4 request 與bio 結構體
1) 請求
在Linux 塊設備驅動中,使用request 結構體來表徵等待進行的I/O 請求,request 結構體的主要成員包括(只用於內核塊設備層):
sector t hard sector; //第一個還沒有傳輸的扇區 unsigned long hard nr sectors; //尚待完成的扇區數 unsigned int hard cur sectors; //當前I/O 操做中待完成的扇區數
2) 請求隊列
一個塊請求隊列是一個塊I/O 請求的隊列,請求隊列跟蹤的塊I/O 請求,它存儲用於描述這個設備可以支持的請求的類型信息、它們的最大大小、多少不一樣的段可進入一個請求、硬件扇區大小、對齊要求等參數,其結果是:若是請求隊列被配置正確了,它不會交給該設備一個不能處理的請求。
//request 隊列結構體 struct request queue { ... /* 保護隊列結構體的自旋鎖 */ spinlock t queue lock; spinlock t *queue lock; /* 隊列kobject */ struct kobject kobj; /* 隊列設置 */ unsigned long nr requests; /* 最大的請求數量 */ unsigned int nr congestion on; unsigned int nr congestion off; unsigned int nr batching; unsigned short max sectors; /* 最大的扇區數 */ unsigned short max hw sectors; unsigned short max phys segments; /* 最大的段數 */ unsigned short max hw segments; unsigned short hardsect size; /* 硬件扇區尺寸 */ unsigned int max segment size; /* 最大的段尺寸 */ unsigned long seg boundary mask; /* 段邊界掩碼 */ unsigned int dma alignment; /* DMA 傳送的內存對齊限制 */ struct blk queue tag *queue tags; atomic t refcnt; /* 引用計數 */ unsigned int in flight; unsigned int sg timeout; unsigned int sg reserved size; int node; struct list head drain list; struct request *flush rq; unsigned char ordered; };
3) 塊I/O
一般一個bio 對應一個I/O 請求,一個請求能夠包含多個bio。
struct bio { sector t bi sector; /* 要傳輸的第一個扇區 */ //標識這個 bio 要傳送的第一個 (512 字節)扇區。 struct bio *bi next; /* 下一個bio */ struct block device *bi bdev; unsigned long bi flags; /* 狀態、命令等 */ unsigned long bi rw; /* 低位表示READ/WRITE,高位表示優先級*/ unsigned short bi vcnt; /* bio vec 數量 */ unsigned short bi idx; /* 當前bvl vec 索引 */ /*不相鄰的物理段的數目*/ unsigned short bi phys segments; /*物理合並和DMA remap合併後不相鄰的物理段的數目*/ unsigned short bi hw segments; unsigned int bi size; /* 以字節爲單位所需傳輸的數據大小 */ //被傳送的數據大小,以字節爲單位,驅動中可使用bio_sectors(bio)宏得到以扇區爲單位的大小。 /* 爲了明瞭最大的hw 尺寸,咱們考慮這個bio 中第一個和最後一個虛擬的可合併的段的尺寸 */ unsigned int bi hw front size; unsigned int bi hw back size; unsigned int bi max vecs; /* 咱們能持有的最大bvl vecs 數 */ struct bio vec *bi io vec; /* 實際的vec 列表 */ bio end io t *bi end io; atomic t bi cnt; void *bi private; bio destructor t *bi destructor; /* destructor */ };
3.5 塊設備驅動註冊與註銷
首先註冊她們本身到內核,其函數原型以下
int register blkdev (unsigned int major, const char *name);
// major參數是塊設備要使用的主設備號,name爲設備名
與register_blkdev()對應的註銷函數是unregister_blkdev(),其原型爲:
int unregister blkdev (unsigned int major, const char *name); // 傳遞給register_blkdev() 的參數必須與傳遞給register_blkdev() 的參數匹配,不然這個函數返回-EINVAL
4 Linux 塊設備驅動的模塊加載與卸載
4.1 須要完成的工做
4.2 塊設備驅動的模塊加載函數模板 (使用bl k_a llo c_que ue )
static int init xxx init (void) { //分配gendisk xxx disks = alloc disk (1); if (!xxx disks) { goto out; } //塊設備驅動註冊 if (register blkdev (XXX MAJOR, "xxx")) { err = - EIO; goto out; } // 「請求隊列」分配 xxx queue = blk alloc queue (GFP KERNEL); if (!xxx queue) { goto out queue; } blk queue make request(xxx queue, &xxx make request); //綁定「製造請求」函數 blk queue hardsect size (xxx queue, xxx blocksize); //硬件扇區尺寸設置 //gendisk初始化 xxx disks->major = XXX MAJOR; xxx disks->first minor = 0; xxx disks->fops = &xxx op; xxx disks->queue = xxx queue; sprintf(xxx disks->disk name, "xxx%d", i); set capacity (xxx disks, xxx size); //xxx size 以512bytes 爲單位 add disk (xxx disks); //添加gendisk return 0; out queue: unregister blkdev (XXX MAJOR, "xxx"); out: put disk(xxx disks); blk cleanup queue (xxx queue); return - ENOMEM; }
4.3 塊設備驅動的模塊加載函數模板(使用bl k_ i nit_queue )
static int init xxx init (void) { //塊設備驅動註冊 if (register blkdev (XXX MAJOR, "xxx")) { err = - EIO; goto out; } //請求隊列初始化 xxx queue = blk init queue (xxx request, xxx lock); if (!xxx queue) { goto out queue; } blk queue hardsect size (xxx queue, xxx blocksize); //硬件扇區尺寸 設置 //gendisk初始化 xxx disks->major = XXX MAJOR; xxx disks->first minor = 0; xxx disks->fops = &xxx op; xxx disks->queue = xxx queue; sprintf(xxx disks->disk name, "xxx%d", i); set capacity (xxx disks, xxx size *2); add disk (xxx disks); //添加gendisk return 0; out queue: unregister blkdev (XXX MAJOR, "xxx"); out: put disk(xxx disks); blk cleanup queue (xxx queue); return - ENOMEM; }
4.4 在塊設備的open()函數中賦值private_data
static int xxx open (struct inode *inode, struct file *filp) { struct xxx dev *dev = inode->i bdev->bd disk->private data; filp->private data = dev; //賦值file 的private data ... return 0; }
5 塊設備的I/O請求處理
5.1 使用求情隊列
塊設備驅動請求函數的原型爲:
void request (request queue t *queue);
//請求函數能夠在沒有完成請求隊列中的全部請求的狀況下返回,甚至它一個請求不完成均可以返回
下面給出了一個更復雜的請求函數,它進行了3 層遍歷:遍歷請求隊 列中的每一個請求,遍歷請求中的每一個bio,遍歷bio 中的每一個段。請求函數遍歷請求、bio 和段以下:
static void xxx full request (request queue t *q) { struct request *req; int sectors xferred; struct xxx dev *dev = q->queuedata; /* 遍歷每一個請求 */ while ((req = elv next request(q)) != NULL) { if (!blk fs request (req)) { printk (KERN NOTICE "Skip non-fs request\n"); end request (req, 0); continue; } sectors xferred = xxx xfer request (dev, req); if (!end that request first (req, 1, sectors xferred)) { blkdev dequeue request (req); end that request last (req); } } } /* 請求處理 */ static int xxx xfer request (struct xxx dev *dev,struct request *req) { struct bio *bio; int nsect = 0; /* 遍歷請求中的每一個bio */ rq for each bio (bio, req) { xxx xfer bio (dev, bio); nsect += bio->bi size / KERNEL SECTOR SIZE; } return nsect; } /* bio 處理 */ static int xxx xfer bio (struct xxx dev *dev, struct bio *bio) { int i; struct bio vec *bvec; sector t sector = bio->bi sector; /* 遍歷每一段 */ bio for each segment(bvec, bio, i) { char *buffer = bio kmap atomic(bio, i, KM USER0); xxx transfer(dev, sector, bio cur sectors(bio), buffer, bio data dir (bio) == WRITE); sector += bio cur sectors(bio); bio kunmap atomic (bio, KM USER0); } return 0; }
5.2 不適用請求隊列
有些設備不須要使用請求隊列,其函數原型以下:
typedef int (make request fn) (request queue t *q, struct bio *bio); //bio 結構體表示一個或多個要傳送的緩衝區
在處理處理bio完成後應該使用bio_endio()函數通知處理結束,以下所示:
void bio endio (struct bio *bio, unsigned int bytes, int error); //參數bytes 是已經傳送的字節數,它能夠比這個bio 所表明的字節數少
無論對應的I/O 處理成功與否,「製造請求」函數都應該返回0 。若是「製造請求」 函數返回一個非零值,bio 將被再次提交。下面代碼所示爲一個 「製造請求」函數的例子。
static int xxx make request (request queue t *q, struct bio *bio) { struct xxx dev *dev = q->queuedata; int status; status = xxx xfer bio (dev, bio); //處理bio bio endio (bio, bio->bi size, status); //通告結束 return 0;
}
版權全部,轉載請註明轉載地址:http://www.cnblogs.com/lihuidashen/p/4506781.html