关联block_device结构
来源:互联网 发布:java qq停止服务 编辑:程序博客网 时间:2024/05/17 21:50
1.5.3 关联block_device结构
接下来是register_disk函数,来自fs/partitions/check.c:
473 /* Not exported, helper to add_disk(). */
474 void register_disk(struct gendisk *disk)
475 {
476 struct block_device *bdev;
477 char *s;
478 int i;
479 struct hd_struct *p;
480 int err;
481
482 strlcpy(disk->kobj.name,disk->disk_name,KOBJ_NAME_LEN);
483 /* ewww... some of these buggers have / in name... */
484 s = strchr(disk->kobj.name, '/');
485 if (s)
486 *s = '!';
487 if ((err = kobject_add(&disk->kobj)))
488 return;
489 err = disk_sysfs_symlinks(disk);
490 if (err) {
491 kobject_del(&disk->kobj);
492 return;
493 }
494 disk_sysfs_add_subdirs(disk);
495
496 /* No minors to use for partitions */
497 if (disk->minors == 1)
498 goto exit;
499
500 /* No such device (e.g., media were just removed) */
501 if (!get_capacity(disk))
502 goto exit;
503
504 bdev = bdget_disk(disk, 0);
505 if (!bdev)
506 goto exit;
507
508 /* scan partition table, but suppress uevents */
509 bdev->bd_invalidated = 1;
510 disk->part_uevent_suppress = 1;
511 err = blkdev_get(bdev, FMODE_READ, 0);
512 disk->part_uevent_suppress = 0;
513 if (err < 0)
514 goto exit;
515 blkdev_put(bdev);
516
517 exit:
518 /* announce disk after possible partitions are already created */
519 kobject_uevent(&disk->kobj, KOBJ_ADD);
520
521 /* announce possible partitions */
522 for (i = 1; i < disk->minors; i++) {
523 p = disk->part[i-1];
524 if (!p || !p->nr_sects)
525 continue;
526 kobject_uevent(&p->kobj, KOBJ_ADD);
527 }
528 }
首先487行这个kobject_add的作用是很直观的,在Sysfs中为这块磁盘建一个子目录,例如我们为的硬盘建立一个块设备驱动,则会在/sys/block/目录中看到一个sdf,要是把这个调用kobject_add函数这行注释掉,肯定就看不到这个sdf目录。这里有两个问题:
第一,为什么kobject_add这么一调用,生成的这个子目录的名字就叫做“sdf”,而不叫做别的呢?其实在sd_probe中做过这么一件事情,通过精心计算得到disk_name的,而这个disk_name正是struct gendisk的一个成员,这里我们看到482行我们把disk_name给了kobj.name,这就是为什么我们调用kobject_add添加一个kobject的时候,它的名字就是我们当时的disk_name。
第二,为什么生成的这个子目录是在/sys/block目录下面,而不是在别的位置呢?还记得在alloc_disk_node中我们申请struct gendisk的情景么?kobj_set_kset_s(disk,block_subsys)做的就是让disk对应的kobject从属于block_subsys对应的kobject下面。这就是为什么我们现在添加这个kobject的时候,它很自然的就会在/sys/block子目录下面建立文件。
继续走,disk_sysfs_symlinks来自fs/partitions/check.c,这个函数虽然不短,但是比较浅显易懂。
static int disk_sysfs_symlinks(struct gendisk *disk){
struct device *target = get_device(disk->driverfs_dev);
int err;
char *disk_name = NULL;
if (target) {
disk_name = make_block_name(disk);
if (!disk_name) {
err = -ENOMEM;
goto err_out;
}
err = sysfs_create_link(&disk->kobj, &target->kobj, "device");
if (err)
goto err_out_disk_name;
err = sysfs_create_link(&target->kobj, &disk->kobj, disk_name);
if (err)
goto err_out_dev_link;
}
err = sysfs_create_link(&disk->kobj, &block_subsys.kobj,
"subsystem");
if (err)
goto err_out_disk_name_lnk;
kfree(disk_name);
return 0;
err_out_disk_name_lnk:
if (target) {
sysfs_remove_link(&target->kobj, disk_name);
err_out_dev_link:
sysfs_remove_link(&disk->kobj, "device");
err_out_disk_name:
kfree(disk_name);
err_out:
put_device(target);
}
return err;
}
我们用实际效果来解读这个函数。首先我们看正常工作的U盘会在/sys/block/sdf下面有哪些内容:
[root@localhost ~]# ls /sys/block/sdf/
capability dev device holders queue range removable size slaves stat subsystem uevent
第一个sysfs_create_link创建的就是这里这个device这个软链接文件。我们来看它链接到哪里去了:
[root@localhost ~]# ls -l /sys/block/sdf/device
lrwxrwxrwx 1 root root 0 Dec 13 07:09
/sys/block/sdf/device
-> ../../devices/pci0000:00/0000:00:1d.7/usb4/4-4/4-4:1.0/host24/target24:0:0/24:0:0:0
第二个sysfs_create_link则从那边又建立一个反链接,又给链接回来了:
[root@localhost~]# ls -l ……
lrwxrwxrwx 1 root root 0 Dec 13 21:16
/sys/devices/pci0000:00/0000:00:1d.7/usb4/4-4/4-4:1.0/host24/target24:0:0/24:0:0:0/block:sdf -> ../../../../../../../../../block/sdf
于是这就等于你中有我我中有你,你那边有一个文件链接到了我这边,我这边有一个文件链接到了你那边。第三个sysfs_create_link,生成的是/sys/block/sdf/subsystem这个软链接文件。
[root@localhost ~]# ls -l /sys/block/sdf/subsystem
lrwxrwxrwx 1 root root 0 Dec 13 07:09 /sys/block/sdf/subsystem -> ../../block
三个链接文件建立好之后,disk_sysfs_symlinks也就结束了它的使命。接下来一个函数是disk_sysfs_add_subdirs。同样来自fs/partitions/check.c:
static inline void disk_sysfs_add_subdirs(struct gendisk *disk){
struct kobject *k;
k = kobject_get(&disk->kobj);
disk->holder_dir = kobject_add_dir(k, "holders");
disk->slave_dir = kobject_add_dir(k, "slaves");
kobject_put(k);
}
这个函数的意图太明显了,无非就是建立holders和slaves两个子目录。
504行接着调用一个内联函数,bdget_disk,《Thinking in C++》告诉我们内联函数最好定义在头文件中,所以这个函数来自include/linux/genhd.h:
static inline struct block_device *bdget_disk(struct gendisk *disk, int index){
return bdget(MKDEV(disk->major, disk->first_minor) + index);
}
bdget来自fs/block_dev.c:
struct block_device *bdget(dev_t dev){
struct block_device *bdev;
struct inode *inode;
inode = iget5_locked(bd_mnt->mnt_sb, hash(dev),
bdev_test, bdev_set, &dev);
if (!inode)
return NULL;
bdev = &BDEV_I(inode)->bdev;
if (inode->i_state & I_NEW) {
bdev->bd_contains = NULL;
bdev->bd_inode = inode;
bdev->bd_block_size = (1 << inode->i_blkbits);
bdev->bd_part_count = 0;
bdev->bd_invalidated = 0;
inode->i_mode = S_IFBLK;
inode->i_rdev = dev;
inode->i_bdev = bdev;
inode->i_data.a_ops = &def_blk_aops;
mapping_set_gfp_mask(&inode->i_data, GFP_USER);
inode->i_data.backing_dev_info = &default_backing_dev_info;
spin_lock(&bdev_lock);
list_add(&bdev->bd_list, &all_bdevs);
spin_unlock(&bdev_lock);
unlock_new_inode(inode);
}
return bdev;
}
这个函数是什么意思呢,还记得前面讲过的struct block_device数据结构,以及我们的老熟人struct inode数据结构。不错,Linux中每一个Block设备都由这么一个结构体变量表示,这玩意儿因此被称作块设备描述符。inode我们不多讲,但是这里一个很重要的结构体是struct bdev_inode:
struct bdev_inode {
struct block_device bdev;
struct inode vfs_inode;
};
bdev_inode好像没出现过,用来干嘛呢?我们来看看BDEV_I函数,这个内联函数来自fs/block_dev.c:
static inline struct bdev_inode *BDEV_I(struct inode *inode){
return container_of(inode, struct bdev_inode, vfs_inode);
}
很显然,从inode得到相应的bdev_inode。于是这个&BDEV_I(inode)->bdev表示的就是inode对应的bdev_inode的成员struct block_device bdev。
但是bdev结构体变量是不会自动来到你的面前,需要的时候你要去申请才会有。iget5_locked就是干这件事情的,这个函数来自fs/inode.c,跟我们前面接触到的iget类似。我们显然不会去深入看它,只能告诉你,这个函数这么一执行,我们就既有inode又有block_device了,而且对于第一次申请的inode,其i_state成员是设置了I_NEW这个flag的,所以bdget()函数中,最后一段if语句是要被执行的。这一段if语句的作用就是初始化inode结构体指针inode以及block_device结构体指针bdev。而函数最终返回的也正是bdev。需要强调一下,bdev正是从这一刻开始正式崭露头角的。
回到register_disk()中,继续往下。下一个重量级的函数是blkdev_get,来自fs/block_dev.c:
static int __blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags,
int for_part){
struct file fake_file = {};
struct dentry fake_dentry = {};
fake_file.f_mode = mode;
fake_file.f_flags = flags;
fake_file.f_path.dentry = &fake_dentry;
fake_dentry.d_inode = bdev->bd_inode;
return do_open(bdev, &fake_file, for_part);
}
int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags){
return __blkdev_get(bdev, mode, flags, 0);
}
看到blkdev_get调用的是__blkdev_get,所以我们两个函数一块贴出来了。很显然,真正需要看的却是do_open,来自同一个文件,我们来详细讨论一下:
1110 static int do_open(struct block_device *bdev, struct file *file, int for_part)
1111 {
1112 struct module *owner = NULL;
1113 struct gendisk *disk;
1114 int ret = -ENXIO;
1115 int part;
1116
1117 file->f_mapping = bdev->bd_inode->i_mapping;
1118 lock_kernel();
1119 disk = get_gendisk(bdev->bd_dev, &part); /* part肯定为0 */
1120 if (!disk) {
1121 unlock_kernel();
1122 bdput(bdev);
1123 return ret;
1124 }
1125 owner = disk->fops->owner;
1126
1127 mutex_lock_nested(&bdev->bd_mutex, for_part);
1128 if (!bdev->bd_openers) {
1129 bdev->bd_disk = disk;
1130 bdev->bd_contains = bdev;
1131 if (!part) {
1132 struct backing_dev_info *bdi;
1133 if (disk->fops->open) {
1134 ret = disk->fops->open(bdev->bd_inode, file);
1135 if (ret)
1136 goto out_first;
1137 }
1138 if (!bdev->bd_openers) {
1139 bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
1140 bdi = blk_get_backing_dev_info(bdev);
1141 if (bdi == NULL)
1142 bdi = &default_backing_dev_info;
1143 bdev->bd_inode->i_data.backing_dev_info = bdi;
1144 }
1145 if (bdev->bd_invalidated)
1146 rescan_partitions(disk, bdev);
1147 } else {
1148 struct hd_struct *p;
1149 struct block_device *whole;
1150 whole = bdget_disk(disk, 0);
1151 ret = -ENOMEM;
1152 if (!whole)
1153 goto out_first;
1154 BUG_ON(for_part);
1155 ret = __blkdev_get(whole, file->f_mode, file->f_flags, 1);
1156 if (ret)
1157 goto out_first;
1158 bdev->bd_contains = whole;
1159 p = disk->part[part - 1];
1160 bdev->bd_inode->i_data.backing_dev_info =
1161 whole->bd_inode->i_data.backing_dev_info;
1162 if (!(disk->flags & GENHD_FL_UP) || !p || !p->nr_sects) {
1163 ret = -ENXIO;
1164 goto out_first;
1165 }
1166 kobject_get(&p->kobj);
1167 bdev->bd_part = p;
1168 bd_set_size(bdev, (loff_t) p->nr_sects << 9);
1169 }
1170 } else {
1171 put_disk(disk);
1172 module_put(owner);
1173 if (bdev->bd_contains == bdev) {
1174 if (bdev->bd_disk->fops->open) {
1175 ret = bdev->bd_disk->fops->open(bdev->bd_inode, file);
1176 if (ret)
1177 goto out;
1178 }
1179 if (bdev->bd_invalidated)
1180 rescan_partitions(bdev->bd_disk, bdev);
1181 }
1182 }
1183 bdev->bd_openers++;
1184 if (for_part)
1185 bdev->bd_part_count++;
1186 mutex_unlock(&bdev->bd_mutex);
1187 unlock_kernel();
1188 return 0;
1189
1190 out_first:
1191 bdev->bd_disk = NULL;
1192 bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info;
1193 if (bdev != bdev->bd_contains)
1194 __blkdev_put(bdev->bd_contains, 1);
1195 bdev->bd_contains = NULL;
1196 put_disk(disk);
1197 module_put(owner);
1198 out:
1199 mutex_unlock(&bdev->bd_mutex);
1200 unlock_kernel();
1201 if (ret)
1202 bdput(bdev);
1203 return ret;
1204 }
一开始的时候,bd_openers是被初始化为了0,所以1128这个if语句是要被执行的。bd_openers为0表示一个文件还没有被打开过。
一开始我们还没有涉及到分区的信息,所以一开始我们只有sda这个概念,而没有sda1,sda2,sda3…这些概念。这时候我们调用get_gendisk得到的part一定是0。所以1131行的if语句也会执行。而disk->fops->open很明显,就是sd_open。(因为我们在sd_probe中曾经设置了gd->fops等于&sd_fops)
但此时此刻我们执行sd_open实际上是不做什么正经事儿的。顶多就是测试一下看看sd_open能不能执行,如果能执行,那么就返回0。如果根本就不能执行,那就赶紧汇报错误。
接下来还有几个函数,主要做一些赋值,暂时不去看它,等到适当的时候需要看了再回来看。
而1146行这个rescan_partitions()显然是我们要看的,首先我们在调用blkdev_get之前把bd_invalidated设置为了1,所以这个函数这次一定会被执行。从这一刻开始分区信息闯入了我们的生活。这个函数来自fs/partitions/check.c:
530 int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
531 {
532 struct parsed_partitions *state;
533 int p, res;
534
535 if (bdev->bd_part_count)
536 return -EBUSY;
537 res = invalidate_partition(disk, 0);
538 if (res)
539 return res;
540 bdev->bd_invalidated = 0;
541 for (p = 1; p < disk->minors; p++)
542 delete_partition(disk, p);
543 if (disk->fops->revalidate_disk)
544 disk->fops->revalidate_disk(disk);
545 if (!get_capacity(disk) || !(state = check_partition(disk, bdev)))
546 return 0;
547 if (IS_ERR(state)) /* I/O error reading the partition table */
548 return -EIO;
549 for (p = 1; p < state->limit; p++) {
550 sector_t size = state->parts[p].size;
551 sector_t from = state->parts[p].from;
552 if (!size)
553 continue;
554 if (from + size > get_capacity(disk)) {
555 printk(" %s: p%d exceeds device capacity/n",
556 disk->disk_name, p);
557 }
558 add_partition(disk, p, from, size, state->parts[p].flags);
559 #ifdef CONFIG_BLK_DEV_MD
560 if (state->parts[p].flags & ADDPART_FLAG_RAID)
561 md_autodetect_dev(bdev->bd_dev+p);
562 #endif
563 }
564 kfree(state);
565 return 0;
566 }
这个函数执行过后,关于分区的信息我们就算都有了。关于分区,前面我们看到是用struct hd_struct这么个结构体来表示的,而struct hd_struct也正是struct gendisk的成员,并且是个二级指针。接着,get_capacity()。没有比这个函数更简单的函数了。来自include/linux/genhd.h:
static inline sector_t get_capacity(struct gendisk *disk){
return disk->capacity;
}
而check_partition就稍微复杂一些了,来自fs/partitions/check.c,我们就不多讲了,这个函数主要是利用parsed_partitions数据结构来记录分区信息的,并且调用check_part来专门指定一个分区表格式,然后我们就来到了add_partition,仍然是来自fs/partitions/check.c:
371 void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, int flags)
372 {
373 struct hd_struct *p;
374
375 p = kmalloc(sizeof(*p), GFP_KERNEL);
376 if (!p)
377 return;
378
379 memset(p, 0, sizeof(*p));
380 p->start_sect = start;
381 p->nr_sects = len;
382 p->partno = part;
383 p->policy = disk->policy;
384
385 if (isdigit(disk->kobj.name[strlen(disk->kobj.name)-1]))
386 snprintf(p->kobj.name,KOBJ_NAME_LEN,"%sp%d",disk->kobj.name,part);
387 else
388 snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->kobj.name,part);
389 p->kobj.parent = &disk->kobj;
390 p->kobj.ktype = &ktype_part;
391 kobject_init(&p->kobj);
392 kobject_add(&p->kobj);
393 if (!disk->part_uevent_suppress)
394 kobject_uevent(&p->kobj, KOBJ_ADD);
395 sysfs_create_link(&p->kobj, &block_subsys.kobj, "subsystem");
396 if (flags & ADDPART_FLAG_WHOLEDISK) {
397 static struct attribute addpartattr = {
398 .name = "whole_disk",
399 .mode = S_IRUSR | S_IRGRP | S_IROTH,
400 .owner = THIS_MODULE,
401 };
402
403 sysfs_create_file(&p->kobj, &addpartattr);
404 }
405 partition_sysfs_add_subdir(p);
406 disk->part[part-1] = p;
407 }
有了之前的经验,现在再看这些kobject相关的,sysfs相关的函数就容易多了。
389行这个p->kobj.parent = &disk->kobj保证了我们接下来生成的东西在刚才的目录之下,即sda1、sda2、…在sda目录下。而395行sysfs_create_link的效果也很显然。而partition_sysfs_add_subdir也没什么好说的,添加了holders子目录。
最后,让我们记住这个函数做过的一件事情,对p的各个成员进行了赋值,而在函数的结尾处把disk->part[part-1]指向了p。也就是说,从此以后,struct hd_struct这个指针数组里就应该有内容了,而不再是空的。
到这里,rescan_partitions()宣告结束,回到do_open()中.1183行,让bd_openers这个引用计数增加1,如果for_part有值,那么就让它对应的引用计数也加1。然后do_open也就华丽丽的结束了,像多米诺骨牌一样,__blkdev_get和blkdev_get相继返回。blkdev_put和blkdev_get做的事情基本相反,我们就不看了,只是需要注意,它把刚才增加上去的这两个引用计数给减了回去。
最后,register_disk()中调用的最后一个函数就是kobject_uevent(),这个函数就是通知用户空间的进程udevd,告诉它有事件发生了,如果你使用的发行版正确配置了udev的配置文件(详见/etc/udev/目录下),那么其效果就是让/dev目录下面有了相应的设备文件。比如:
[root@localhost tedkdb]# ls /dev/sda*
/dev/sda /dev/sda10 /dev/sda12 /dev/sda14 /dev/sda2 /dev/sda5 /dev/sda7 /dev/sda9 /dev/sda1 /dev/sda11 /dev/sda13 /dev/sda15 /dev/sda3 /dev/sda6 /dev/sda8
至于为什么,你可以去阅读关于udev的知识,这是用户空间的程序,咱们就不多说了。
- 关联block_device结构
- 关联block_device结构
- block_device结构体从哪里来?
- gendisk ;block_device; device
- gendisk ;block_device; device
- struct block_device *bdget(dev_t dev)
- 高级关联结构 --- 哈希表
- ffmpeg里的结构关联
- ffmpeg里的结构关联
- IRP和IO_STACK_LOCATION结构的关联
- hibernate树形结构多对一双向关联
- SQL tuning和shared pool结构的关联介绍
- ORACLE 多表关联查询SQL 语法结构梳理
- 十三、用表关联结构实现类目管理
- 数据库表关联关系表结构字段命名
- 用SQL数据库做多表关联应怎样设计库结构
- 用SQL数据库做多表关联应怎样设计库结构
- 收藏一下:各种神经网络结构进化与关联(雷锋网)
- 加快你的Tomcat启动速度,给Tomcat减负
- 谷歌WebM VP8视频压缩格式来袭
- 建立块设备驱动环境
- VP8 第八代的On2视频
- WebM影片格式 一个开放、免费的媒体文件格式用于网页
- 关联block_device结构
- 为设备建立请求队列
- android刷机小白教程
- H.264 高度压缩数字视频编解码器标准
- 块设备I/O调度程序
- 真实的I/O调度层处理
- #pragma的作用
- 虚数的理解
- 半年工作小结