久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

塊設備驅(qū)動初步

 WUCANADA 2012-05-09
塊設備驅(qū)動初步
2010-05-02 19:55
  單擊,,返回主頁,,查看更多內(nèi)容

  本文的素材以及源代碼(稍有改動)均來源于《Linux Device Driver》,因此本文可視為該書塊設備驅(qū)動相關章節(jié)的閱讀理解,。

  一,、體驗塊設備驅(qū)動(單擊下載驅(qū)動源碼)

  本驅(qū)動模擬了一個硬盤,。想想你去中關村(不過我更喜歡去成都@世界)買了一個硬盤,,迫不及待地安裝在你的Linux機器上,你怎么樣才能使用這個新硬盤呢,?當然要先把它驅(qū)動起來,。

  1、make生成sbull.ko后加載驅(qū)動:sudo insmod sbull.ko

  2,、對硬盤分區(qū) 執(zhí)行suod fdisk /dev/sbulla

  輸入x,,進入高級菜單

  輸入h,change number of heads 為4

  輸入c,,change number of cylinders 為4

  輸入r,,退回主菜單

  順次輸入n、p,、1,、1、4,,創(chuàng)建一個主分區(qū)占有cylinders 1-4

  輸入w,,保存并退出

  3、輸入 sudo mkfs.ext2 /dev/sbulla1 在硬盤分區(qū)上格式化ext2文件系統(tǒng)

  4,、掛載新硬盤上的分區(qū) 輸入 mkdir testdir創(chuàng)建空目錄

  輸入mount –t ext2 /dev/sbulla1 ./testdir

  之后,,你就可以通過testdir目錄來訪問新硬盤分區(qū)了。

  二,、塊設備驅(qū)動框架介紹

  1,、驅(qū)動接口簡介 上層接口

  用戶接口

  塊設備 b:/dev/sda、/dev/sda2,、 /dev/ram0

  用戶空間程序

  mkfs,、mount、fdisk,;

  直接調(diào)用open,、release,、ioctl接口

  讀寫時,不直接調(diào)用讀寫接口,,而是將讀寫request提交給OS的block layer層

  操作系統(tǒng)接口

  block layer I/O scheduler決定需要執(zhí)行磁盤I/O時,,調(diào)用驅(qū)動的讀寫接口

  抽象物理設備為 cylinder、header,、sector組成,,視其為編號從0開始的flat型sector集合。(注1)

  總假定物理設備一個sector大小為512byte(注2)

  下層接口

  抽象物理設備為編號從0開始的flat型sector集合,,但轉(zhuǎn)換sector大小為物理設備的實際值,,通過物理寄存器的讀寫,將數(shù)據(jù)寫入指定的sector位置

  由物理設備的中斷(讀寫完成)來喚醒用戶進程(或內(nèi)核線程)

  注1: 借助minor number辨別partition編號(也可能是整個磁盤),,再借助分區(qū)表決定分區(qū)的起始sector號

  注2: #define KERNEL_SECTOR_SHIFT 9 #define KERNEL_SECTOR_SIZE (1 << KERNEL_SECTOR_SHIFT)

  2,、塊設備結(jié)構體

  塊設備結(jié)構體 struct gendisk 是塊設備驅(qū)動中最重要的數(shù)據(jù)結(jié)構,它在操作系統(tǒng)和驅(qū)動中代表一個物理磁盤,。其主要字段有: major:磁盤對應的主設備號,,出現(xiàn)在/proc/devices中。register_blkdev(sbull_major, "sbull");

  first_minor:磁盤對應的第1個次設備號,。dev->gd->first_minor = which*SBULL_MINORS,;

  minors :磁盤擁有的次設備號的總數(shù)。dev->gd = alloc_disk(SBULL_MINORS);

  disk_name:磁盤的名稱,。出現(xiàn)在/proc/partitions中

  fops:塊設備驅(qū)動中的功能函數(shù),。包括:open\release\media_change\revalidate_disk\ioctl等

  queue:OS回調(diào)讀寫函數(shù)時使用的request queue隊列(它綁定了讀寫函數(shù))

  private_data:私有數(shù)據(jù),常用于存放包裹設備結(jié)構體

  capacity:512字節(jié)大小的sector的總數(shù)量

  set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));

  3,、塊設備結(jié)構體注冊 申請major number

  sbull_major = register_blkdev(sbull_major, "sbull"); //sbull出現(xiàn)在/proc/devices中

  分配(初始生成)塊設備結(jié)構體

  dev->gd = alloc_disk(SBULL_MINORS); //同時也關聯(lián)設備號數(shù)量

  將塊設備結(jié)構體與設備號,、設備操作函數(shù)、讀寫隊列關聯(lián)

  dev->gd->major = sbull_major; //關聯(lián)主設備號

  dev->gd->first_minor = which*SBULL_MINORS; //關聯(lián)次設備號

  dev->gd->fops = &sbull_ops; //關聯(lián)功能函數(shù)

  dev->queue = blk_init_queue(sbull_request, &dev->lock);

  dev->gd->queue = dev->queue; //關聯(lián)讀寫隊列

  將塊設備結(jié)構體注冊進OS

  add_disk(dev->gd);

  4,、塊設備結(jié)構體注銷 將塊設備結(jié)構體從OS中注銷

  del_gendisk(dev->gd);

  銷毀塊設備結(jié)構體(移除對kobject的最后ref.,,釋放結(jié)構體內(nèi)存)

  put_disk(dev->gd);

  釋放major number

  unregister_blkdev(sbull_major, "sbull");

  5、塊設備的簡單讀寫-原理 用戶程序或內(nèi)核組件提出讀寫request時,,會將該request提交給block layer層

  block layer層構造request結(jié)構,,并將其鏈入塊設備結(jié)構體綁定的request queue中

  在適當?shù)臅r候,block layer層回調(diào)request queue中綁定的驅(qū)動中的讀寫函數(shù),,并將request queue的指針作為參數(shù)傳給驅(qū)動中的讀寫函數(shù)

  驅(qū)動中 的讀寫函數(shù)根據(jù)傳給它的request queue的指針,,從request queue中取出一個request,根據(jù)request中指定的方向(讀或?qū)懀?、?shù)據(jù)在內(nèi)存中的位置,、在設備上的位置(起始sector編號)、傳輸數(shù) 據(jù)量的大?。╯ector數(shù)量),,完成物理讀寫

  驅(qū)動通知block layer層實際完成的讀寫量,,block layer層據(jù)此更新其內(nèi)部各個數(shù)據(jù)結(jié)構;并告知驅(qū)動是否整個request已經(jīng)處理完成,,若是,,驅(qū)動則負責將request從request queue中摘下,并釋放request結(jié)構的內(nèi)存,,喚醒等待該request完成的所有進程

  6,、讀寫隊列結(jié)構體-注冊與注銷 分配(初始生成)讀寫隊列結(jié)構體,并與讀寫函數(shù)綁定

  dev->queue = blk_init_queue(sbull_request, &dev->lock);

  block layer在回調(diào)讀寫函數(shù)sbull_request時,,會先獲得自旋鎖dev->lock,,這樣block layer就可以與驅(qū)動的其它函數(shù)共享相同的臨界區(qū)

  blk_queue_hardsect_size(dev->queue, hardsect_size);

  設置實際設備的扇區(qū)大小,這樣block layer層就會根據(jù)實際設備能夠處理的扇區(qū)大小來構造request結(jié)構體,,從而不會出現(xiàn)實際設備處理不了的request

  dev->queue->queuedata = http://blog.soso.com/qz.q/dev

  將塊設備結(jié)構體與讀寫隊列關聯(lián)

  dev->gd->queue = dev->queue;

  將讀寫隊列結(jié)構體間接注冊進OS

  add_disk(dev->gd);

  將讀寫隊列結(jié)構體間接從OS中注銷

  del_gendisk(dev->gd);

  銷毀讀寫隊列結(jié)構體(移除對kobject的最后ref.,,釋放結(jié)構體內(nèi)存)

  blk_cleanup_queue(dev->queue);

  7、塊設備的簡單讀寫-實現(xiàn)

  108 static void sbull_request(request_queue_t *q)

  109 {

  110 struct request *req;

  112 while ((req = elv_next_request(q)) != NULL) { //從request queue中取出一個request,循環(huán)直到request queue中的所有request被傳送完

  //因為讀寫隊列與塊設備結(jié)構體已關聯(lián),,所以block layer層在將讀寫請求鏈入request queue時,,能將rq_disk字段指向塊設備結(jié)構體

  113 struct sbull_dev *dev = req->rq_disk->private_data;

  114 if (! blk_fs_request(req)) {

  116 end_request(req, 0);

  117 continue;

  118 }

   123 sbull_transfer(dev, req->sector, req->current_nr_sectors, //根據(jù)request中指定的方向(rq_data_dir)、數(shù)據(jù)在內(nèi)存中的位置( req->buffer ),、

  124 req->buffer, rq_data_dir(req)); //在設備上的位置( req->sector ),、傳輸數(shù)據(jù)量的大?。?req->current_nr_sectors ),,完成物理讀寫

  125 end_request(req, 1); //通知block layer層;將request從request queue中摘下;釋放request結(jié)構的內(nèi)存,喚醒等待該request完成的所有進程

  126 }

  127 }

  void end_request(struct request *req, int uptodate)

  {

  //驅(qū)動通知block layer層實際完成的讀寫量,,block layer層據(jù)此更新其內(nèi)部各個數(shù)據(jù)結(jié)構,;并告知驅(qū)動是否整個request已經(jīng)處理完成

  if (!end_that_request_first(req, uptodate, req->hard_cur_sectors)) {

  add_disk_randomness(req->rq_disk);

  blkdev_dequeue_request(req); //若是,驅(qū)動則負責將request從request queue中摘下

  end_that_request_last(req); //釋放request結(jié)構的內(nèi)存,,喚醒等待該request完成的所有進程

  }

  }

  8,、簡單讀寫的不足 一次只命令硬件傳輸1個數(shù)據(jù)塊segment(內(nèi)存中不超過1page的連續(xù)單元),其只是request中的一小部分而已

  雖然簡單讀寫采用while循環(huán)請求elv_next_request,,但block層會認為硬件可能由于某種原因不能一次性完成一個完整request的傳輸,,因此相鄰2次elv_next_request極有可能返回的是不同的request

  block layer盡了很大努力,實施電梯調(diào)度算法(drivers/block/ll_rw_block.c and elevator.c ),,使得一個request結(jié)構體中包含多個在內(nèi)存中離散,,但在物理設備上卻連續(xù)的數(shù)據(jù)塊。

  先后2次elv_next_request的簡單讀寫,,使得磁頭必須尋道,,產(chǎn)生較大延遲

  簡單讀寫忽視block layer層的工作,對同一個request結(jié)構體中包含的多個segment在物理設備上連續(xù),,不予理睬,,實在是暴殄天物

  9,、請求隊列 一個塊設備的I/O請求的序列

  跟蹤未完成的塊I/O請求

  允許使用多I/O調(diào)度器,以最大化性能的方式提交I/O請求給你的驅(qū)動,,I/O調(diào)度器還負責合并鄰近的請求

  請求隊列的實現(xiàn)

  drivers/block/Ll_rw_block.c和elevator.c

  10,、塊設備的高效讀寫-原理與實現(xiàn)

  一個request結(jié)構體中包含多個在內(nèi)存中離散,但在物理設備上卻連續(xù)的數(shù)據(jù)塊,,由bio和bio_vec結(jié)構體來表示

  

  

  337 static void setup_device(struct sbull_dev *dev, int which)

  362 switch (request_mode) {

  370 case RM_FULL:

  371 dev->queue = blk_init_queue(sbull_full_request, &dev->lock);

  374 break;

  385 }

  387 dev->queue->queuedata = http://blog.soso.com/qz.q/dev;

  409 }

  175 static void sbull_full_request(request_queue_t *q)

  176 {

  177 struct request *req;

  178 int sectors_xferred;

  179 struct sbull_dev *dev = q->queuedata;

  181 while ((req = elv_next_request(q)) != NULL) { //每次取出讀寫隊列中的一個request

  187 sectors_xferred = sbull_xfer_request(dev, req);

  188 if (! end_that_request_first(req, 1, sectors_xferred)) {

  189 blkdev_dequeue_request(req);

  191 end_that_request_last(req, 1);

  192 }

  193 }

  194 }

  #define rq_for_each_bio(_bio, rq) \

  if ((rq->bio)) \

  for (_bio = (rq)->bio; _bio; _bio = _bio->bi_next)

  157 static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)

  158 {

  159 struct bio *bio;

  160 int nsect = 0;

  162 rq_for_each_bio(bio, req) { //while循環(huán),,每次取出req中的一個bio

  163 sbull_xfer_bio(dev, bio);

  164 nsect += bio->bi_size/KERNEL_SECTOR_SIZE; //bi_size記錄一個bio中數(shù)據(jù)的總字節(jié)數(shù)

  165 }

  167 return nsect;

  168 }

  #define bio_for_each_segment(bvl, bio, i) \

  __bio_for_each_segment(bvl, bio, i, (bio)->bi_idx)

  #define __bio_for_each_segment(bvl, bio, i, start_idx) \

  for (bvl = bio_iovec_idx((bio), (start_idx)), i = (start_idx); \

  i < (bio)->bi_vcnt; \

  bvl++, i++)

  133 static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)

  134 {

  135 int i;

  136 struct bio_vec *bvec;

  137 sector_t sector = bio->bi_sector; //bi_sector記錄bio中首字節(jié)應位于硬件的哪個扇區(qū)。bio中的所有segment在硬件上的sector位置全部連續(xù)

  139 /* Do each segment independently. */

  140 bio_for_each_segment(bvec, bio, i) { //while循環(huán),,每次取出bio中的一個bio_vec來傳輸

  141 char *buffer = __bio_kmap_atomic(bio, i, KM_USER0); //獲取bv_page的內(nèi)核virtual address

  143 sbull_transfer(dev, sector, bio_cur_sectors(bio), //bio_cur_sectors(bio)求出bio當前segment的大?。╯ector數(shù)目)

  144 buffer, bio_data_dir(bio) == WRITE);

  145 sector += bio_cur_sectors(bio); //累進數(shù)據(jù)在硬件上的位置(sector)

  147 __bio_kunmap_atomic(bio, KM_USER0);

  149 }

  151 return 0; /* Always "succeed" */

  152 }

  11、塊設備的其它操作接口fops(open,、release,、media_change、revalidate_disk,、ioctl)

  1) open與release(本驅(qū)動可以模擬光盤從光驅(qū)中更換)

  216 static int sbull_open(struct inode *inode, struct file *filp)

  217 {

   218 struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data; //inode->i_bdev->bd_disk指向關聯(lián)的gendisk structure

  223 if (! dev->users)

  224 check_disk_change(inode->i_bdev); //check_disk_change將導致對media_change的調(diào)用,,若介質(zhì)已改變,將導致調(diào)用revalidate_disk

  225 dev->users++;

  228 } media_changed不為NULL時,,則會被內(nèi)核API check_disk_change所調(diào)用,;

  在media_changed返回true的情況下, revalidate_disk會被內(nèi)核API check_disk_change所調(diào)用

  int check_disk_change(struct block_device *bdev)

  {

  struct gendisk *disk = bdev->bd_disk;

  struct block_device_operations * bdops = disk->fops;

  if (!bdops->media_changed) //media_changed為NULL

  return 0;

  if (!bdops->media_changed(bdev->bd_disk)) //media_changed不為NULL時,,則會被內(nèi)核API check_disk_change所調(diào)用

  return 0;

  if (__invalidate_device(bdev))

  printk("VFS: busy inodes on changed media.\n");

  if (bdops->revalidate_disk) // 在media_changed返回true的情況下,,revalidate_disk不為NULL時,則會被內(nèi)核API check_disk_change所調(diào)用

  bdops->revalidate_disk(bdev->bd_disk);

  if (bdev->bd_disk->minors > 1)

  bdev->bd_invalidated = 1;

  return 1;

  }

  230 static int sbull_release(struct inode *inode, struct file *filp)

  231 {

  232 struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;

  235 dev->users--;

  244 } 12,、media_change與revalidate_disk media_changed不為NULL時,,則會被內(nèi)核API check_disk_change所調(diào)用

  若磁盤介質(zhì)已更改,應返回true,;否則返回false

  在media_changed返回true的情況下,, revalidate_disk會被內(nèi)核API check_disk_change所調(diào)用

  應完成對新磁盤介質(zhì)進行操作的準備工作

  249 int sbull_media_changed(struct gendisk *gd)

  250 {

  251 struct sbull_dev *dev = gd->private_data;

  253 return dev->media_change;

  254 } 260 int sbull_revalidate(struct gendisk *gd)

  261 {

  262 struct sbull_dev *dev = gd->private_data;

  264 if (dev->media_change) {

  265 dev->media_change = 0;

  266 // memset (dev->data, 0, dev->size);

  267 }

  269 } 13、ioctl 大部分的ioctl命令都已經(jīng)被block layer層所截獲并處理,,到達不了驅(qū)動程序

  驅(qū)動ioctl函數(shù)中需要處理的命令是HDIO_GETGEO (用戶,,例如fdisk,要求獲得磁盤的幾何參數(shù))(測試結(jié)果似乎OS并未調(diào)用ioctl,,原因待查)

  291 int sbull_ioctl (struct inode *inode, struct file *filp,

  292 unsigned int cmd, unsigned long arg)

  293 {

  294 long size;

  295 struct hd_geometry geo;

  296 struct sbull_dev *dev = filp->private_data;

  298 switch(cmd) {

  299 case HDIO_GETGEO:

  301 /*

  302 * Get geometry: since we are a virtual device, we have to make

  303 * up something plausible. So we claim 16 sectors, four heads,

  304 * and calculate the corresponding number of cylinders. We set the

  305 * start of data at sector four.

  306 */

  307 // size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);

  308 size = dev->size / KERNEL_SECTOR_SIZE;

  309 geo.cylinders = (size & ~0x3f) >> 6;

  310 geo.heads = 4;

  311 geo.sectors = 16;

  312 geo.start = 4;

  313 if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))

  314 return -EFAULT;

  315 return 0;

  316 }

  318 return -ENOTTY; /* unknown command */

  319 } 單擊,,與作者交流

more http://www.

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙,。如發(fā)現(xiàn)有害或侵權內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多