| 
              else   PDEBUGG("%08x:%05i <-- %08x:%05i\n",   ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),   ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source)); 
              /*   * Ok, now the packet is ready for transmission: first simulate a   * receive interrupt on the twin device, then a   * transmission-done on the transmitting device   */   dest = snull_devs + (dev==snull_devs ? 1 : 0);   priv = (struct snull_priv *) dest->priv;   priv->status = SNULL_RX_INTR;   priv->rx_packetlen = len;   priv->rx_packetdata = buf;   snull_interrupt(0, dest, NULL);
    priv = (struct snull_priv *) dev->priv;   priv->status = SNULL_TX_INTR;   priv->tx_packetlen = len;   priv->tx_packetdata = buf;   if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) {    /* Simulate a dropped transmit interrupt */    netif_stop_queue(dev);    PDEBUG("Simulate lockup at %ld, txp %ld\n", jiffies,(unsigned long) priv->stats.tx_packets);   }   else    snull_interrupt(0, dev, NULL);  } 
             | 
         
    
 
   块设备也以与字符设备register_chrdev、unregister_ chrdev 函数类似的方法进行设备的注册与释放。但是,register_chrdev使用一个向 file_operations 结构的指针,而register_blkdev 则使用 block_device_operations 结构的指针,其中定义的open、release 和 ioctl 方法和字符设备的对应方法相同,但未定义 read 或者 write 操作。这是因为,所有涉及到块设备的 I/O 通常由系统进行缓冲处理。 
  块驱动程序最终必须提供完成实际块 I/O 操作的机制,在 Linux中,用于这些 I/O 操作的方法称为"request(请求)"。在块设备的注册过程中,需要初始化request队列,这一动作通过blk_init_queue来完成,blk_init_queue函数建立队列,并将该驱动程序的 request 函数关联到队列。在模块的清除阶段,应调用 blk_cleanup_queue 函数。看看mtdblock的例子: 
    
        
            | 
             static void handle_mtdblock_request(void) {  struct request *req;  struct mtdblk_dev *mtdblk;  unsigned int res;
   for (;;) {   INIT_REQUEST;   req = CURRENT;   spin_unlock_irq(QUEUE_LOCK(QUEUE));    mtdblk = mtdblks[minor(req->rq_dev)];   res = 0;
    if (minor(req->rq_dev) >= MAX_MTD_DEVICES)    panic("%s : minor out of bound", __FUNCTION__); 
             | 
         
    
 
    
        
            | 
               if (!IS_REQ_CMD(req))    goto end_req;
    if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9))    goto end_req;
    // Handle the request   switch (rq_data_dir(req))   {    int err;
     case READ:     down(&mtdblk->cache_sem);     err = do_cached_read (mtdblk, req->sector << 9, req->current_nr_sectors << 9, req->buffer);     up(&mtdblk->cache_sem);     if (!err)      res = 1;      break; 
             | 
         
    
 
 
		    
                       
		      
		      
		   |