·¢²©ÎÄ
¡°¿ªÔ´Ò»¿Ì¡±¹¤×÷ÊÒ

http://blog.chinaunix.net/space.php?uid=20255102

&nbsp;&nbsp;&nbsp;&nbsp;»¶Ó­¹âÁÙ¡°¿ªÔ´Ò»¿Ì¡±¡£±¾Õ¾µãÖ÷Òª´ÓÊ¿ªÔ´×ÊÁϵĵÄÊÕ¼¯»ã×ܵȵȣ¬Ö÷Òª°üÀ¨²Ù×÷ϵ<br/>ͳ¡¢³ÌÐòÉè¼Æ¡¢×ÀÃæÓ¦Óõȵȸ÷¸ö·½ÃæµÄÐÅÏ¢£¬Ï£Íû¸÷λ¶à¶à¹âÁÙÖ¸½Ì£¬»¥ÏཻÁ÷¡£±¾Õ¾×Ê<br/>Ô´¾ùÀ´×Ô»¥   
¸öÈË×ÊÁÏ
  • ²©¿Í·ÃÎÊ£º551609
  • ²©ÎÄÊýÁ¿£º151
  • ²©¿Í»ý·Ö£º10015
  • ²©¿ÍµÈ¼¶£ºÉϽ«
  • ¹Ø×¢ÈËÆø£º 2
  • ×¢²áʱ¼ä£º2005-07-31 02:15:39
¶©ÔÄÎҵIJ©¿Í
  • ¶©ÔÄ
  • ¶©Ôĵ½Ïʹû
  • ¶©Ôĵ½×¥Ïº
  • ¶©Ôĵ½Google
×ÖÌå´óС£º´ó ÖРС²©ÎÄ

¿éÉ豸Çý¶¯³ÌÐòʵÏÖ
´ÓÐéÄâÎļþϵͳ£¬Îļþϵͳ£¬Âß¼­¾íһ·˳Á÷¶øÏ£¬Ò»Ö±À´µ½ÁËÓ²ÅÌ¿éÉ豸Çý¶¯³ÌÐò¡£´ÓÉϲ㿴ϲ㣬×ÜÊDZ»ÏµÍ³µÄʵÏÖ°áŪµÃÔÆÕÚÎíÕÏ£¬²»Ã÷ËùÒÔ£¬Òò´Ë£¬¸ÉÂ𲻸ɴà´Óµ×²ã¿ªÊ¼ÂýÂýÍùÉÏÅÀ¡£
OK,ÈÃÎÒÃǾʹӿéÉ豸µÄÇý¶¯³ÌÐò¿ªÊ¼°É¡£
Ê×Ïȶ¨ÒåһЩ³£ÓñäÁ¿
£¨ÎªÊ²Ã´ÊÇGK? GK stands for gingko¡£ÕâÊÇGingko Storage SystemµÄÒ»¸ö×é³É²¿·Ö¡£Ê²Ã´ÊÇGingko Storage System? -_-!!!ÊÐÃæÉÏ¿´²»µÀµÄ£¬ºÇºÇ¡££©
#define GKDD_MAJOR 0 //Ö÷É豸ºÅ
#define GKDD_SIZE 2048 //´ÅÅÌ´óС
#define GKDD_BLKSIZE 1024 //´æÈ¡¿éµÄ´óС£¬
#define GKDD_HARDSECT 512 //ÿ¸öÉÈÇøµÄ´óС

Õâ¾ÍÊÇÎÒÃǵÄÓ²Å̵Ļù±¾²ÎÊýÁË£¬ÏÂÃæ»¹ÒªÎªÎÒÃǵÄÇý¶¯É趨¼¸¸ö±äÁ¿
static int gkdd_major = 0;
module_param( gkdd_major, int, 0 );

static int hardsect_size = 512;
module_param( hardsect_size, int, 0 );

static int nsectors = 1024;
module_param( nsectors, int, 0 );

static int ndevices = 4;
module_param( ndevices, int, 0 );

Ä£¿é¿ÉÒÔ´«µÝ4¸öintÐͲÎÊý£¬4¿éÓ²ÅÌ£¬Ã¿¿éÅÌ1024¸öÉÈÇø£¬Ã¿¸öÉÈÇø512×Ö½Ú¡£
enum
{
RM_SIMPLE  = 0,
RM_FULL  = 1,
RM_NOQUEUE = 2,
};

static int request_mode = RM_SIMPLE;
module_param( request_mode, int, 0 );
ʹÓüòµ¥µÄÊý¾ÝÇëÇóģʽ¡£
struct gkdd_dev
{
int size;
u8 *data;
short users;
short media_change;
spinlock_t  lock;
struct request_queue  *queue;
struct gendisk  *gd;
struct timer_list timer;
};

static struct gkdd_dev *Devices = NULL;
ºÃÁË£¬Õâ¾ÍÊÇÔÛµÄÓ²ÅÌÊý¾Ý½á¹¹£¬ÓиÃÓ²ÅÌÔÚÄں˵ıíʾgd, Ó²Å̵IJÙ×÷ÇëÇó¶ÓÁÐqueue£¬É豸²Ù×÷×ÔÐýËø£¬ÒÔ¼°²Ù×÷ÑÓʱµÄ¶¨Ê±Æ÷,ͨ¹ý¶¨Ê±Æ÷Ä£ÄâÒ»¸ö¿É¸ü»»½éÖʵĿìÉ豸£¬ÈçÄãµÄDVD¡£
Ò»ÇÐ×¼±¸¾ÍÐ÷£¬ÏòÄÚºË×¢²áÔÛÃǵÄÓ²Å̰ɡ£
static int gkdd_init_module(void)
{
int i;

gkdd_major = register_blkdev( gkdd_major, "gingko_disk" );
if( gkdd_major <= 0 )
{
  printk( KERN_WARNING "GKDD-disk: unable to get major number\n" );
  return -EBUSY;
}

Devices = kmalloc( ndevices*sizeof( struct gkdd_dev ), GFP_KERNEL );
if( Devices == NULL )
  goto out_unregister;

for( i=0; i< ndevices; i++ )
  setup_device( Devices + i, i );
  
printk( KERN_DEBUG "Module gkdd init\n" );
return 0;

out_unregister:
unregister_blkdev( gkdd_major, "sbd" );
return -ENOMEM;
}
module_init(gkdd_init_module);

µ±È»£¬ÕâÀïÃæÓÐÒ»¸ö¹Ø¼üµÄº¯Êý£¬¾ÍÊÇset_updevice()¡£
static void setup_device( struct gkdd_dev *dev, int which )
{
memset( dev, 0, sizeof( struct gkdd_dev ));
dev->size = nsectors*hardsect_size; //É豸µÄ´óС
dev->data = vmalloc( dev->size ); //É豸µÄÊý¾Ý¿Õ¼ä£¬ÒòΪÊÇRAMDISK£¬Ö±½ÓÉêÇëÈ«²¿µÄ¿Õ¼ä£¬·´ÕýÊÇÐé´æ£¬²»Ì«¹ý·Ö£¬×ÜÄÜÓ¦¸¶¡£
if( dev->data == NULL )
{
  printk( KERN_NOTICE "vmalloc failure.\n" );
  return;
}

spin_lock_init( &dev->lock );

init_timer( &dev->timer );
dev->timer.data = (unsigned long) dev;
dev->timer.function = gkdd_invalidate; //ÑÓ³Ù30Ãëºó£¬¸ÃÉ豸µÄ½éÖʾÍÈÏΪÒѾ­±»ÈËÈ¥ÏÂÀ´ÁË£¬Í¨¹ý¸Ãº¯Êý£¬Ê¹ÆäʧЧ

switch( request_mode )
{
  case RM_NOQUEUE:
   dev->queue = blk_alloc_queue( GFP_KERNEL );
   if( dev->queue == NULL )
    goto out_vfree;
   blk_queue_make_request( dev->queue, gkdd_make_request );
   break;
   
  case RM_FULL:
   dev->queue = blk_init_queue( gkdd_full_request, &dev->lock );
   if( dev->queue == NULL )
    goto out_vfree;
   break;
   
  default:
   printk( KERN_NOTICE "Bad request_mode %d, using simple\n", request_mode );
   
  case RM_SIMPLE:  //ÎÒÃÇ´¦ÀíµÄÊÇÕâÖÖģʽ£¬µ±È»£¬ÄãÒ²¿ÉÒÔ¸Ä³ÉÆäËûģʽ²âÊÔÒÔÏÂ
   dev->queue = blk_init_queue( gkdd_request, &dev->lock );
   if( dev->queue == NULL )
    goto out_vfree;
   break;
}

blk_queue_hardsect_size( dev->queue, hardsect_size );
dev->queue->queuedata = dev;

dev->gd = alloc_disk( GKDD_MINORS ); //»ñµÃÓ²ÅÌÔÚÄں˵ÄÊý¾Ý½á¹¹
if( !dev->gd )
{
  printk( KERN_NOTICE "alloc_disk failure\n" );
  goto out_vfree;
}

dev->gd->major = gkdd_major;
dev->gd->first_minor = which*GKDD_MINORS;
dev->gd->fops = &gkdd_ops; //Õâ¸öfopsºÜÊìϤ°É£¬ÔÚÄĶù¿´µ½¹ýÀàËÆµÄÄ£Ñù£¿
dev->gd->queue = dev->queue; //ÇëÇó¶ÓÁÐ
dev->gd->private_data = dev; //±£´æÎÒÃǵÄÉ豸½á¹¹
snprintf( dev->gd->disk_name, 32, "gkdd_disk_%c", which + 'a' ); //É豸Ãû£¬/dev/gkdd_disk_a, b, c, d
set_capacity( dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE) ); //ÈÝÁ¿£¬ÒÔÉÈÇøÎªµ¥Î»
add_disk( dev->gd ); //¼ÓÈëµ½ÄÚºË
return;

out_vfree:
  if( dev->data )
   vfree( dev->data );
}

Õâ¾ÍÊÇÎÒÃǵÄÓ²ÅÌÌṩµÄ²Ù×÷½è¿ÚÁË£º
static struct block_device_operations gkdd_ops =
{
.owner  = THIS_MODULE, //Õâ¸ö¾ÍÊÇÕâ¸öÖµ£¬Ã»µÃÑ¡Ôñ
.open  = gkdd_open,
.release = gkdd_release,
.media_changed = gkdd_media_changed,
.revalidate_disk = gkdd_revalidate,
.ioctl  = gkdd_ioctl
};

ͨ¹ý¸Ã·½·¨£¬ÏòÄÚºËÖÐ×¢²áÁË4¿éÓ²ÅÌ£¬²»¹ýÏÖÔÚ»¹²»ÄÜfdisk -l¡£
´Óopen¿ªÊ¼£º
static int gkdd_open( struct inode *inode, struct file *filp )
{
struct gkdd_dev *dev = inode->i_bdev->bd_disk->private_data; //ÎÒÃǵÄÉ豸½á¹¹±£´æÔÚgendiskµÄprivate_dataÀï

del_timer_sync( &dev->timer ); //Èç¹ûÉϴιرÕÉèÖÃÁ˶¨Ê±Æ÷£¬¶øÔÚ30ÃëÄÚ£¬ÄãÓÖʹÓÃÁ˸ÃÉ豸£¬¾Í²»Ê¹ËüʵЧÁË
filp->private_data = dev; //°áµ½ÐéÄâÎļþϵͳÀïÈ¥
spin_lock( &dev->lock );
if( !dev->users )  //²é¿´ÊÇ·ñ¸ü»»Á˽éÖÊ£¬Õâ¸ö´¿´âΪÁËÄ£ÄâCD,DVDµÈÀàËÆÉ豸Ìṩ
  check_disk_change( inode->i_bdev );
dev->users++; //Ôö¼ÓÒýÓüÆÊý
spin_unlock( &dev->lock );
return 0;
}

release
static int gkdd_release( struct inode *inode, struct file *filp )
{
struct gkdd_dev *dev = inode->i_bdev->bd_disk->private_data;

spin_lock( &dev->lock );
dev->users--;

if( !dev->users ) //ûÓÐÈËʹÓÃÁË£¬¾ÍÉèÖÃÒ»¸öÆÚÏÞ£¬·ñÔò£¬½éÖʾÍÈÏΪ¶ªÁË
{
  dev->timer.expires = jiffies + INVALIDATE_DELAY;
  add_timer( &dev->timer );
}
spin_unlock( &dev->lock );
return 0;
}

¸ü»»É豸ºó£¬ÐèҪһЩ³õʼ»¯µÄ²Ù×÷£¬
int gkdd_media_changed( struct gendisk *gd )
{
struct gkdd_dev *dev = gd->private_data;
return dev->media_change;
}

¼¤»î³õʼ»¯ºó£¬ÄãµÄÓ²ÅÌÉϵÄÎļþϵͳʲôµÄ¾Í±»memsetΪ0ÁË¡£
int gkdd_revalidate( struct gendisk *gd )
{
struct gkdd_dev *dev = gd->private_data;

if( dev->media_change )
{
  dev->media_change = 0;
  memset( dev->data, 0, dev->size );
}
return 0;
}

ΪÁËÌṩijЩӦÓÃÈçfdiskµÈ£¬ÐèÒªÖªµÀCHSÐÅÏ¢¶øÌṩ¡£ÏÖÔÚ´ó¼Ò¶¼ÓÃLBAÁË£¬ËùÒÔ»¹µÈ½«LBA»»Ëã³ÉCHS£¬Æä¼ÆËã·½·¨ÈçÏ£º
CHS2LBA
LBA = (C*number of heads per cylinder + H)*number of sectors per head + S-1

LBA2CHS
C = LBA / (heads per cylinder * sectors per track)
temp = LBA % (heads per cylinder * sectors per track)
H = temp / sectors per track
S = temp % sectors per track + 1

µ±È»£¬ÕâÖ»ÊÇÒ»¸ö¼òµ¥µÄ»»ËãÁË£¬ÏàÐÅÊÀ¼ÍµÄÓ²ÅÌʵÏÖ£¬ÎªÁË¿¼ÂÇ´ÅÅÌתËÙºÍÊý¾Ý´¦ÀíÒÔ¼°»º´æ´óС֮¼äµÄ¹ØÏµ£¬Êµ¼ÊÉϵĶÔÓ¦¹ØÏµÓ¦¸Ã²»ÊÇÕâÑùµÄ¡£¶øÎÒÃǵÄgkdd¾Í¸ü¼ÙÁË£¬Òâ˼һ϶øÒÑ¡£
int gkdd_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg )
{
long size;
struct hd_geometry geo;
struct gkdd_dev *dev = filp->private_data;

switch( cmd )
{
  case HDIO_GETGEO:
   size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
   geo.cylinders = ( size & ~0x3f ) >> 6;
   geo.heads = 4;
   geo.sectors = 16;
   geo.start = 4;
   if( copy_to_user( (void __user *)arg, &geo, sizeof(geo)))
    return -EFAULT;
   return 0;
}
return -ENOTTY;
}

ÏÖÔÚÓ²ÅÌ¿ÉÒÔ½ÓÊÜÓû§µÄ²Ù×÷ÇëÇóÁË£¬
//¶Áд´«ÊäÇëÇó
static void gkdd_transger( struct gkdd_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write )
{
unsigned long ffset = sector * KERNEL_SECTOR_SIZE;
unsigned long nbytes = nsect * KERNEL_SECTOR_SIZE;

if( (offset + nbytes) > dev->size )
{
  printk( KERN_DEBUG "GKDD-disk: Beyond-end write (%ld %ld)\n", offset, nbytes);
  return;
}
if( write )
  memcpy( dev->data + offset, buffer, nbytes ); //дÇëÇ󣬴ÓÓû§¿Õ¼äдÈëÓ²ÅÌ
else
  memcpy( buffer, dev->data + offset, nbytes ); //¶ÁÇëÇó£¬Ïà·´
}


static void gkdd_request( request_queue_t *q )
{
struct request *req;

while( (req = elv_next_request(q)) != NULL )
{
  struct gkdd_dev *dev = req->rq_disk->private_data;
  if( !blk_fs_request(req) )
  {
   printk( KERN_NOTICE "GKDD-disk: Skip non-fs request\n" );
   end_request( req, 0 );
   continue;
  }
  
  gkdd_transger( dev, req->sector, req->current_nr_sectors, req->buffer, rq_data_dir( req ));
  end_request( req, 1 );
}
}

//ÐèÒª´«ÊäÒ»×éÊý¾Ý¿éʱ£¬¾ÍÒªÓõ½block I/OÊý¾Ý½á¹¹ÁË£¬¼´struct bio£¬ÕâÀï¶Ôû¿éblock»¹ÊÇÂýÂýtransfer°É¡£
static int gkdd_xfer_bio( struct gkdd_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 );
  gkdd_transger( 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;
}
static int gkdd_xfer_request( struct gkdd_dev *dev, struct request *req )
{
struct bio *bio;
int nsect = 0;

rq_for_each_bio( bio, req )
{
  gkdd_xfer_bio( dev, bio );
  nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
}
return nsect;
}
static void gkdd_full_request( request_queue_t *q )
{
struct request *req;
int sectors_xferred;
struct gkdd_dev *dev = q->queuedata;

while( (req = elv_next_request( q )) != NULL )
{
  if( !blk_fs_request( req ) )
  {
   printk( KERN_NOTICE "GKDD-disk: Skip non-fs request\n" );
   end_request( req, 0 );
   continue;
  }
  sectors_xferred = gkdd_xfer_request( dev, req );
  if( !end_that_request_first( req, 1, sectors_xferred ) )
  {
   blkdev_dequeue_request( req );
   end_that_request_last( req, 0 ); //*********need to be rethink ************
  }
}
}
static int gkdd_make_request( request_queue_t *q, struct bio *bio )
{
struct gkdd_dev *dev = q->queuedata;
int status;

status = gkdd_xfer_bio( dev, bio );
bio_endio( bio, bio->bi_size, status );
return 0;
}
//Í˳öÄ£¿é£¬È¥Òª×¢ÏúÉ豸£¬²¢ÊÍ·ÅÏÈǰÉêÇëµÄ×ÊÔ´¡£
static void gkdd_exit_module(void)
{
int i;
for( i=0; i<ndevices; i++ )
{
  struct gkdd_dev *dev = Devices + i;
  
  del_timer_sync( &dev->timer );
  if( dev->gd )
  {
   del_gendisk( dev->gd );
   put_disk( dev->gd );
  }
  if( dev->queue )
  {
   if( request_mode == RM_NOQUEUE )
    blk_put_queue( dev->queue );
   else
    blk_cleanup_queue( dev->queue );
  }
  if( dev->data )
   vfree( dev->data );
}
unregister_blkdev( gkdd_major, "gingko_disk" );
kfree( Devices );
printk( KERN_DEBUG "Module gkdd exit\n" );
}
module_exit(gkdd_exit_module);

ÍêÕûµÄ´úÂëÈçÏ£¨²Î¿¼ldd3 source£©£º
ÔËÐл·¾³linux-2.6.20, ½ÏµÍµÄ°æ±¾¿ÉÄÜ»áÓÐÎÊÌ⣬ÒòΪÄں˶ÔijЩÄÚ´æ·ÖÅäµÄÊý¾Ý½á¹¹ºÍ²Ù×÷×öÁËһЩÐ޸ġ£

²©¿ÍÍÆ¼öÎÄÕÂ
Ç×£¬Äú»¹Ã»ÓеǼ,Çë[µÇ¼]»ò[×¢²á]ºóÔÙ½øÐÐÆÀÂÛ