目前linux中包含anticipatory、cfq、deadline和noop这4个I/O调度器。2.6.18之前的linux默认使用anticipatory,而之后的默认使用cfq。我们在前面编写简单的ramdisk(有请求队列)中分配请求队列使用了blk_init_queue函数,该函数会默认该请求队列分配一个调度器,这里我们打算不使用该函数默认分配给请求队列的调度器,而是使用noop调度器。noop顾名思义,是一个基本上不干事的调度器。它基本不对请求进行什么附加的处理,仅仅假惺惺地告诉通用块设备层:我处理完了。但与吃空饷的公仆不同,noop的存在还是有不少进步意义的。至少我们现在就需要一个不要没事添乱的I/O调度器。
可以用下面的函数来更换一个请求队列的调度器,该函数定义如下:
1 int elevator_change(struct request_queue *, const char *)
第一个参数是请求队列的指针,第二个参数是需要设定的I/O调度器名称。如果name为NULL,那么内核会首先尝试选择启动参数"elevator="中指定的调度器,不成功的话就去选择编译内核时指定的默认调度器,如果运气太背还是不成功,就去选择"noop"调度器。
对于我们的代码,就是在simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL)后面加上elevator_change(simp_blkdev_queue, "noop");所以我们的代码应该是(颜色为红色的代码是选择调度器代码):
1 static int __init simp_blkdev_init(void) 2 { 3 int ret; 4 struct elevator_queue *elv_old; 5 6 //分配一个请求队列 7 simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL); 8 if(!simp_blkdev_queue) 9 { 10 ret = -ENOMEM; 11 goto blk_init_queue; 12 } 13 //更换IO调度器 14 if (IS_ERR_VALUE(elevator_change(simp_blkdev_queue, "noop"))) 15 printk(KERN_WARNING "Switch elevator failed, using default\n"); 16 17 //分配一个 gendisk 结构,有一个分区 18 simp_blkdev_disk = alloc_disk(1); 19 if(!simp_blkdev_disk) 20 { 21 ret = -ENOMEM; 22 goto err_alloc_disk; 23 } 24 25 //填充 gendisk 主要结构成员 26 strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME); 27 simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR; 28 simp_blkdev_disk->first_minor = 0; 29 simp_blkdev_disk->fops = &simp_blkdev_fops; 30 simp_blkdev_disk->queue = simp_blkdev_queue; 31 set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES >> 9); 32 33 //向内核注册块设备驱动 34 add_disk(simp_blkdev_disk); 35 36 return 0; 37 38 err_alloc_disk: 39 blk_cleanup_queue(simp_blkdev_queue); 40 blk_init_queue: 41 return ret; 42 }
如果要用函数elevator_init来设置调度器,就必须先保存原来blk_init_queue()帮我们申请的默认调度器,然后设置队列中的指向调度器的elevator指针为NULL(注意:设置指针为NULL是必须的,要不然函数elevator_init会不做任何事情直接返回),最后才是调用elevator_init(simp_blkdev_queue, "noop")来设置,如果设置成功则调用elevator_exit来释放原来的调度器。具体代码如下(颜色为红色的代码是选择调度器代码):
1 static int __init simp_blkdev_init(void) 2 { 3 int ret; 4 struct elevator_queue *elv_old; 5 6 //分配一个请求队列 7 simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL); 8 if(!simp_blkdev_queue) 9 { 10 ret = -ENOMEM; 11 goto blk_init_queue; 12 } 13 14 elv_old = simp_blkdev_queue->elevator; 15 simp_blkdev_queue->elevator = NULL; 16 //更换IO调度器 17 if (IS_ERR_VALUE(elevator_init(simp_blkdev_queue, "noop"))) 18 printk(KERN_WARNING "Switch elevator failed, using default\n"); 19 else 20 elevator_exit(elv_old); 21 //分配一个 gendisk 结构,有一个分区 22 simp_blkdev_disk = alloc_disk(1); 23 if(!simp_blkdev_disk) 24 { 25 ret = -ENOMEM; 26 goto err_alloc_disk; 27 } 28 29 //填充 gendisk 主要结构成员 30 strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME); 31 simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR; 32 simp_blkdev_disk->first_minor = 0; 33 simp_blkdev_disk->fops = &simp_blkdev_fops; 34 simp_blkdev_disk->queue = simp_blkdev_queue; 35 set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES >> 9); 36 37 //向内核注册块设备驱动 38 add_disk(simp_blkdev_disk); 39 40 return 0; 41 42 err_alloc_disk: 43 blk_cleanup_queue(simp_blkdev_queue); 44 blk_init_queue: 45 return ret; 46 }