Linux中塊裝置驅動程式分析
阿新 • • 發佈:2019-01-06
struct sbull_dev {
int size; /* Device size in sectors */
u8 *data; /* The data array */
short users; /* How many users */
short media_change; /* Flag a media change? */
spinlock_t lock; /* For mutual exclusion */
struct request_queue *queue; /* The device request queue */
struct gendisk *gd; /* The gendisk structure */
struct timer_list timer; /* For simulated media changes */
};
在這個結構中,struct request_queue與 struct gendisk 是這個結構中的重要成員,
也是塊裝置中的重要結構,這個文章中的大部分闡述都是基於這兩個結構的操作。
一.流程:
塊裝置驅動也是從init函式開始的,所以分析也從這裡開始。
第一步:
register_blkdev(sbull_major, "sbull");
先註冊塊裝置,第一個引數是裝置號,為0表完動態分配,第二個為裝置名。
第二步:
Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
建立這個塊裝置的核心資料結構,也就是這個塊裝置的物件實體,建立了ndevice這樣
的實體。
第三步:
setup_device(Devices + i, i);
說白了,就是初始化實體,並把它新增到系統的block層中去。這個步驟很重要,它完
成以以下一些操作:
1.初始化一個自旋鎖。
spin_lock_init(&dev->lock);
2.分配一個請求佇列,並用1中的自旋鎖來控制對佇列的訪問。
dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
3.分配,初始化及安裝相應的gendisk結構。
dev->gd = alloc_disk(SBULL_MINORS);
if (! dev->gd) {
printk (KERN_NOTICE "alloc_disk failure/n");
goto out_vfree;
}
dev->gd->major = sbull_major;
dev->gd->first_minor = which*SBULL_MINORS;
dev->gd->fops = &sbull_ops;
dev->gd->queue = dev->queue;
dev->gd->private_data = dev;
snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
4.最後add_disk完成整個初始化過程,這步一定要在初始化的最後再呼叫,因為
add_disk後,可能就會呼叫磁碟的操作函式,如果初始化還沒有完成就會出錯。
二.塊裝置操作 struct block_device_operations 結構分析:
sbull模組中的該結構:
static struct block_device_operations sbull_ops = {
.owner = THIS_MODULE,
.open = sbull_open,
.release = sbull_release,
.media_changed = sbull_media_changed,
.revalidate_disk = sbull_revalidate,
.ioctl = sbull_ioctl
};
open與release這兩個函式就不再具體分析,它們有一個重要功能就是增加使用者計數和
減少使用者計數。media_changed 和revalidata_disk即是對可移動介質的支援,像u盤等
等這些可移動,即插即用的裝置就應該實現這兩個函式。media_changed是檢查介質是
否改變,發跡即返回非零,revalidate_disk即介質改變後執行。它們之間如何聯絡我
們不用管,我們主要實現這個函式的實體即可。
ioctl函式,ioctl函式的功能也簡化了,可實際的磁碟裝置大多也主要是實現對磁碟信
息的查詢。
三.請求處理。
塊裝置驅動程式的核心是請求處理部分,是塊裝置驅動的難點。設計得好否直接關
繫到裝置的效能。
我們看在安裝塊裝置實體的時候,初始化了一個請求佇列:
dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
這個操作就是把生成的請求佇列dev->queue與請求函式sbull_full_request繫結在一
起。sbull中的request函式:
static void sbull_full_request(request_queue_t *q)
{
struct request *req;
int sectors_xferred;
struct sbull_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 = sbull_xfer_request(dev, req);
if (! end_that_request_first(req, 1, sectors_xferred)) {
blkdev_dequeue_request(req);
end_that_request_last(req);
}
}
}
req = elv_next_request(q) 獲取佇列中第一個未完成的請求,兩次呼叫而沒有
執行end_that_request_last或者end_request時得到的是相同的結果,因為它不會刪除
佇列中的請求。只有結束該請求,才會得到下一個請求。sbull_xfer_request在這裡即
是實際的資料傳輸。
一個實際的塊裝置請求處理要這些要複雜得多,那就要更深入瞭解request結構,bio結
int size; /* Device size in sectors */
u8 *data; /* The data array */
short users; /* How many users */
short media_change; /* Flag a media change? */
spinlock_t lock; /* For mutual exclusion */
struct request_queue *queue; /* The device request queue */
struct gendisk *gd; /* The gendisk structure */
struct timer_list timer; /* For simulated media changes */
};
在這個結構中,struct request_queue與 struct gendisk
也是塊裝置中的重要結構,這個文章中的大部分闡述都是基於這兩個結構的操作。
一.流程:
塊裝置驅動也是從init函式開始的,所以分析也從這裡開始。
第一步:
register_blkdev(sbull_major, "sbull");
先註冊塊裝置,第一個引數是裝置號,為0表完動態分配,第二個為裝置名。
第二步:
Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
建立這個塊裝置的核心資料結構,也就是這個塊裝置的物件實體,建立了ndevice這樣
的實體。
第三步:
setup_device(Devices + i, i);
說白了,就是初始化實體,並把它新增到系統的block層中去。這個步驟很重要,它完
成以以下一些操作:
1.初始化一個自旋鎖。
spin_lock_init(&dev->lock);
2.分配一個請求佇列,並用1中的自旋鎖來控制對佇列的訪問。
dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
3.分配,初始化及安裝相應的gendisk結構。
dev->gd = alloc_disk(SBULL_MINORS);
if (! dev->gd) {
printk (KERN_NOTICE "alloc_disk failure/n");
goto out_vfree;
}
dev->gd->major = sbull_major;
dev->gd->first_minor = which*SBULL_MINORS;
dev->gd->fops = &sbull_ops;
dev->gd->queue = dev->queue;
dev->gd->private_data = dev;
snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
4.最後add_disk完成整個初始化過程,這步一定要在初始化的最後再呼叫,因為
add_disk後,可能就會呼叫磁碟的操作函式,如果初始化還沒有完成就會出錯。
二.塊裝置操作 struct block_device_operations 結構分析:
sbull模組中的該結構:
static struct block_device_operations sbull_ops = {
.owner = THIS_MODULE,
.open = sbull_open,
.release = sbull_release,
.media_changed = sbull_media_changed,
.revalidate_disk = sbull_revalidate,
.ioctl = sbull_ioctl
};
open與release這兩個函式就不再具體分析,它們有一個重要功能就是增加使用者計數和
減少使用者計數。media_changed 和revalidata_disk即是對可移動介質的支援,像u盤等
等這些可移動,即插即用的裝置就應該實現這兩個函式。media_changed是檢查介質是
否改變,發跡即返回非零,revalidate_disk即介質改變後執行。它們之間如何聯絡我
們不用管,我們主要實現這個函式的實體即可。
ioctl函式,ioctl函式的功能也簡化了,可實際的磁碟裝置大多也主要是實現對磁碟信
息的查詢。
三.請求處理。
塊裝置驅動程式的核心是請求處理部分,是塊裝置驅動的難點。設計得好否直接關
繫到裝置的效能。
我們看在安裝塊裝置實體的時候,初始化了一個請求佇列:
dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
這個操作就是把生成的請求佇列dev->queue與請求函式sbull_full_request繫結在一
起。sbull中的request函式:
static void sbull_full_request(request_queue_t *q)
{
struct request *req;
int sectors_xferred;
struct sbull_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 = sbull_xfer_request(dev, req);
if (! end_that_request_first(req, 1, sectors_xferred)) {
blkdev_dequeue_request(req);
end_that_request_last(req);
}
}
}
req = elv_next_request(q) 獲取佇列中第一個未完成的請求,兩次呼叫而沒有
執行end_that_request_last或者end_request時得到的是相同的結果,因為它不會刪除
佇列中的請求。只有結束該請求,才會得到下一個請求。sbull_xfer_request在這裡即
是實際的資料傳輸。
一個實際的塊裝置請求處理要這些要複雜得多,那就要更深入瞭解request結構,bio結