Linux那些事儿之我是Block层(5)浓缩就是精华?(二)

第二个 ,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 }

如果你不懂 Linux 2.6 的统一设备模型 , 那你要看懂这段代码估计够呛 . 但好在我们在 << 我是 Sysfs>> 中对 kobject 方面的东西做了介绍 . 所以这里我们不会深入到 kobject 相关的函数内部中去 , 也不会深入到 sysfs 提供的函数内部 , 点到为止 .

首先 487 行这个 kobject_add 的作用是很直观的 , Sysfs 中为这块磁盘建一个子目录 . 就比如下面这些目录中的那个 sdf, 就是为我的 U 盘而建立的 , 我要是把这个调用 kobject_add 函数这行注释掉 , 保证你就看不到这个 sdf 目录 .

[root@lfg2 ~]# ls /sys/block/

md0   ram1   ram11  ram13  ram15  ram3  ram5  ram7  ram9  sdb  sdd  sdf ram0  ram10  ram12  ram14  ram2   ram4  ram6  ram8  sda   sdc  sde  sdg

这时候网友 塞翁失身 提出两个问题 :

第一 , 为什么 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, 这个函数虽然不短 , 但是比较浅显易懂 .

     429 static int disk_sysfs_symlinks(struct gendisk *disk)

    430 {

    431          struct device *target = get_device(disk->driverfs_dev);

    432         int err;

    433         char *disk_name = NULL;

    434

    435         if (target) {

    436                 disk_name = make_block_name(disk);

    437                 if (!disk_name) {

    438                         err = -ENOMEM;

    439                         goto err_out;

    440                 }

    441

    442                 err = sysfs_create_link(&disk->kobj, &target->kobj, "device");

    443                 if (err)

    444                         goto err_out_disk_name;

    445

    446                 err = sysfs_create_link(&target->kobj, &disk->kobj, disk_name);

    447                 if (err)

    448                         goto err_out_dev_link;

    449          }

    450

    451         err = sysfs_create_link(&disk->kobj, &block_subsys.kobj,

    452                                 "subsystem");

    453         if (err)

    454                 goto err_out_disk_name_lnk;

    455

    456         kfree(disk_name);

    457

    458         return 0;

    459

    460 err_out_disk_name_lnk:

    461         if (target) {

    462                 sysfs_remove_link(&target->kobj, disk_name);

    463 err_out_dev_link:

    464                 sysfs_remove_link(&disk->kobj, "device");

    465 err_out_disk_name:

    466                 kfree(disk_name);

    467 err_out:

    468                 put_device(target);

    469         }

    470         return err;

    471 }

我们用实际效果来解读这个函数 . 首先我们看正常工作的 U 盘会在 /sys/block/sdf 下面有哪些内容 :

[root@localhost ~]# ls /sys/block/sdf/

capability  dev  device  holders  queue  range  removable  size  slaves  stat  subsystem  uevent

442 行的 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

446 行这个 sysfs_create_link 则从那边又建立一个反链接 , 又给链接回来了 .

[root@localhost~]# ls /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 driver ioerr_cnt model rescan scsi_generic:sg7  timeout bus             generic        iorequest_cnt  power        rev                   scsi_level         type delete iocounterbits  max_sectors    queue_depth  scsi_device:24:0:0:0  state       uevent device_blocked  iodone_cnt     modalias       queue_type   scsi_disk:24:0:0:0    subsystem   vendor

很明显 , 就是这个 block:sdf.

[root@localhost~]# ls -l /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

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

于是这就等于你中有我我中有你 , 你那边有一个文件链接到了我这边 , 我这边有一个文件链接到了你那边 .

然后 451 行再次调用 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:

    342 static inline void disk_sysfs_add_subdirs(struct gendisk *disk)

    343 {

    344         struct kobject *k;

    345

    346         k = kobject_get(&disk->kobj);

    347         disk->holder_dir = kobject_add_dir(k, "holders");

    348         disk->slave_dir = kobject_add_dir(k, "slaves");

    349         kobject_put(k);

    350 }

这个函数的意图太明显了 , 相信虹口足球场外倒卖演唱会门票的黄牛党们都能看懂 , 无非就是建立 holders slaves 两个子目录 .

504 ,bdget_disk, 这是一个内联函数 ,<<Thinking in C++>> 告诉我们内联函数最好定义在头文件中 , 所以这个函数来自 include/linux/genhd.h:

    433 static inline struct block_device *bdget_disk(struct gendisk *disk, int index)

    434 {

    435         return bdget(MKDEV(disk->major, disk->first_minor) + index);

    436 }

又是一次声东击西的调用 .bdget 来自 fs/block_dev.c:

    554 struct block_device *bdget(dev_t dev)

    555 {

    556         struct block_device *bdev;

    557         struct inode *inode;

    558

    559         inode = iget5_locked(bd_mnt->mnt_sb, hash(dev),

    560                         bdev_test, bdev_set, &dev);

    561

    562         if (!inode)

    563                 return NULL;

    564

    565         bdev = &BDEV_I(inode)->bdev;

    566

    567         if (inode->i_state & I_NEW) {

    568                 bdev->bd_contains = NULL;

    569                 bdev->bd_inode = inode;

    570                 bdev->bd_block_size = (1 << inode->i_blkbits);

    571                 bdev->bd_part_count = 0;

    572                 bdev->bd_invalidated = 0;

    573                 inode->i_mode = S_IFBLK;

    574                 inode->i_rdev = dev;

    575                 inode->i_bdev = bdev;

    576                 inode->i_data.a_ops = &def_blk_aops;

     577                 mapping_set_gfp_mask(&inode->i_data, GFP_USER);

    578                 inode->i_data.backing_dev_info = &default_backing_dev_info;

    579                 spin_lock(&bdev_lock);

    580                 list_add(&bdev->bd_list, &all_bdevs);

    581                 spin_unlock(&bdev_lock);

    582                 unlock_new_inode(inode);

    583         }

    584         return bdev;

    585 }

真是祸不单行今日行啊 , 一下子跳出来两个变态的结构体来 .struct block_device struct inode.

include/linux/fs.h 中定义了这么一个结构体 :

     460 struct block_device {

    461         dev_t                   bd_dev;  /* not a kdev_t - it's a search key */

    462         struct inode *          bd_inode;       /* will die */

    463         int                     bd_openers;

    464         struct mutex            bd_mutex;       /* open/close mutex */

    465         struct semaphore        bd_mount_sem;

    466         struct list_head        bd_inodes;

    467         void *                  bd_holder;

    468         int                      bd_holders;

    469 #ifdef CONFIG_SYSFS

    470         struct list_head        bd_holder_list;

    471 #endif

    472         struct block_device *   bd_contains;

    473         unsigned                bd_block_size;

    474         struct hd_struct *      bd_part;

    475         /* number of times partitions within this device have been opened. */

    476         unsigned                bd_part_count;

    477         int                     bd_invalidated;

    478         struct gendisk *        bd_disk;

    479         struct list_head        bd_list;

    480         struct backing_dev_info *bd_inode_backing_dev_info;

    481         /*

    482          * Private data.  You must have bd_claim'ed the block_device

    483          * to use this.  NOTE:  bd_claim allows an owner to claim

    484          * the same device multiple times, the owner must take special

    485          * care to not mess up bd_private for that case.

    486          */

    487         unsigned long           bd_private;

    488 };

很明显 ,Linux 中每一个 Block 设备都由这么一个结构体变量表示 , 这玩意儿因此被称作块设备描述符 .inode 咱们不具体讲 , 但是这里挺逗的一个结构体是 struct bdev_inode,

     29 struct bdev_inode {

     30         struct block_device bdev;

     31         struct inode vfs_inode;

     32 };

把两个变态的结构体组合起来就变成了第三个变态的结构体 .

但是网名为 避孕套一直用雕牌 的哥们儿问我 ,bdev_inode 好像没出现过 , 讲它干嘛 ? 我想说看问题要看本质 , 不要被表面迷惑 , 这个世界上很多事情都不像表面上看起来那样 . 不信你看 BDEV_I, 这个内联函数来自 fs/block_dev.c:

     34 static inline struct bdev_inode *BDEV_I(struct inode *inode)

     35 {

     36         return container_of(inode, struct bdev_inode, vfs_inode);

     37 }

很显然 , inode 得到相应的 bdev_inode. 于是 565 行这个 &BDEV_I(inode)->bdev 表示的就是 inode 对应的 bdev_inode 的成员 struct block_device bdev.

但是结构体变量这东西不像公共汽车 , 只需等待就会自动来到你的面前 , 而需要你去申请才会有 .iget5_locked 就是干这件事情的 , 这个函数来自 fs/inode.c, 我们显然不会去深入看它 , 只能告诉你 , 这个函数这么一执行 , 我们就既有 inode 又有 block_device . 而且对于第一次申请的 inode, i_state 成员是设置了 I_NEW 这个 flag , 所以 bdget() 函数中 ,567 行这一段 if 语句是要被执行的 . 这一段 if 语句的作用就是初始化 inode 结构体指针 inode 以及 block_device 结构体指针 bdev. 而函数最终返回的也正是 bdev. 需要强调一下 ,bdev 正是从这一刻开始正式出现在我们的故事中的 .

回到 register_disk() , 继续往下 . 下一个重量级的函数是 blkdev_get, 来自 fs/block_dev.c:

   1206 static int __blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags,

   1207                         int for_part)

   1208 {

   1209         /*

   1210          * This crockload is due to bad choice of ->open() type.

   1211          * It will go away.

   1212          * For now, block device ->open() routine must _not_

   1213          * examine anything in 'inode' argument except ->i_rdev.

   1214          */

   1215         struct file fake_file = {};

   1216         struct dentry fake_dentry = {};

   1217         fake_file.f_mode = mode;

   1218         fake_file.f_flags = flags;

   1219         fake_file.f_path.dentry = &fake_dentry;

   1220         fake_dentry.d_inode = bdev->bd_inode;

   1221

   1222         return do_open(bdev, &fake_file, for_part);

   1223 }

   1224

   1225 int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags)

   1226 {

   1227         return __blkdev_get(bdev, mode, flags, 0);

   1228 }

看到 blkdev_get 调用的是 __blkdev_get, 所以我们两个函数一块贴出来了 .

很显然 , 真正需要看的却是 do_open, 来自同一个文件 .

   1103 /*

   1104  * bd_mutex locking:

   1105  *

   1106  *  mutex_lock(part->bd_mutex)

   1107  *    mutex_lock_nested(whole->bd_mutex, 1)

   1108  */

   1109

   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);

   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 的成员 , 并且是个二级指针 . 一开始这个指针并无所指 , 或者说一开始我们并没有为 struct hd_struct 申请空间 , 所以我即使不贴出下面这个 delete_partition 函数的代码你也应该知道 , 此时此刻 , 它什么也不会干 .

    352 void delete_partition(struct gendisk *disk, int part)

    353 {

    354         struct hd_struct *p = disk->part[part-1];

    355         if (!p)

    356                 return;

    357         if (!p->nr_sects)

    358                 return;

    359         disk->part[part-1] = NULL;

    360         p->start_sect = 0;

    361         p->nr_sects = 0;

    362         p->ios[0] = p->ios[1] = 0;

    363         p->sectors[0] = p->sectors[1] = 0;

    364         sysfs_remove_link(&p->kobj, "subsystem");

    365         kobject_unregister(p->holder_dir);

    366         kobject_uevent(&p->kobj, KOBJ_REMOVE);

    367         kobject_del(&p->kobj);

    368         kobject_put(&p->kobj);

    369 }

revalidate_disk 指针指向的就是 sd_revalidate_disk, 这个函数我们在讲述 sd 的时候对它做足了文章 . sd_probe 调用 add_disk 之前 , 就已经执行过这个函数 , 这里只不过是再执行一次罢了 .

接着 ,get_capacity(). 没有比这个函数更简单的函数了 . 来自 include/linux/genhd.h:

    254 static inline sector_t get_capacity(struct gendisk *disk)

    255 {

    256         return disk->capacity;

    257 }

check_partition 就稍微复杂一些了 , 来自 fs/partitions/check.c:

    156 static struct parsed_partitions *

    157 check_partition(struct gendisk *hd, struct block_device *bdev)

    158 {

    159         struct parsed_partitions *state;

    160         int i, res, err;

    161

    162         state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL);

    163         if (!state)

     164                 return NULL;

    165

    166         disk_name(hd, 0, state->name);

    167         printk(KERN_INFO " %s:", state->name);

    168         if (isdigit(state->name[strlen(state->name)-1]))

    169                 sprintf(state->name, "p");

    170

    171         state->limit = hd->minors;

    172         i = res = err = 0;

    173         while (!res && check_part[i]) {

    174                 memset(&state->parts, 0, sizeof(state->parts));

    175                 res = check_part[i++](state, bdev);

    176                 if (res < 0) {

    177                         /* We have hit an I/O error which we don't report now.

    178                         * But record it, and let the others do their job.

    179                         */

    180                         err = res;

    181                         res = 0;

    182                 }

    183

    184         }

    185         if (res > 0)

    186                 return state;

    187         if (err)

    188         /* The partition is unrecognized. So report I/O errors if there were any */

    189                 res = err;

    190         if (!res)

    191                 printk(" unknown partition table/n");

    192         else if (warn_no_part)

    193                 printk(" unable to read partition table/n");

    194         kfree(state);

    195         return ERR_PTR(res);

    196 }

首先 ,struct parsed_partitions 结构体定义于 fs/partitions/check.h 这么一个头文件中 :

      8 enum { MAX_PART = 256 };

      9

     10 struct parsed_partitions {

     11         char name[BDEVNAME_SIZE];

     12         struct {

     13                 sector_t from;

     14                 sector_t size;

     15                 int flags;

     16         } parts[MAX_PART];

     17         int next;

     18          int limit;

     19 };

这个结构体是我们用来记录分区信息的 .

173 行这个 check_part 是何许人物 ? fs/partitions/check.c 中找到了它 :

     43 int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/

     44

     45 static int (*check_part[])(struct parsed_partitions *, struct block_device *) = {

     46         /*

     47          * Probe partition formats with tables at disk address 0

     48          * that also have an ADFS boot block at 0xdc0.

     49          */

     50 #ifdef CONFIG_ACORN_PARTITION_ICS

     51         adfspart_check_ICS,

      52 #endif

     53 #ifdef CONFIG_ACORN_PARTITION_POWERTEC

     54         adfspart_check_POWERTEC,

     55 #endif

     56 #ifdef CONFIG_ACORN_PARTITION_EESOX

     57         adfspart_check_EESOX,

     58 #endif

     59

     60         /*

     61           * Now move on to formats that only have partition info at

     62          * disk address 0xdc0.  Since these may also have stale

     63          * PC/BIOS partition tables, they need to come before

     64          * the msdos entry.

     65          */

     66 #ifdef CONFIG_ACORN_PARTITION_CUMANA

     67         adfspart_check_CUMANA,

     68 #endif

     69 #ifdef CONFIG_ACORN_PARTITION_ADFS

     70         adfspart_check_ADFS,

     71 #endif

     72

     73 #ifdef CONFIG_EFI_PARTITION

     74         efi_partition,          /* this must come before msdos */

     75 #endif

     76 #ifdef CONFIG_SGI_PARTITION

     77         sgi_partition,

     78 #endif

     79 #ifdef CONFIG_LDM_PARTITION

     80         ldm_partition,          /* this must come before msdos */

     81 #endif

     82 #ifdef CONFIG_MSDOS_PARTITION

     83         msdos_partition,

     84 #endif

     85 #ifdef CONFIG_OSF_PARTITION

     86         osf_partition,

     87 #endif

     88 #ifdef CONFIG_SUN_PARTITION

     89         sun_partition,

     90 #endif

     91 #ifdef CONFIG_AMIGA_PARTITION

     92         amiga_partition,

     93 #endif

     94 #ifdef CONFIG_ATARI_PARTITION

     95         atari_partition,

     96 #endif

     97 #ifdef CONFIG_MAC_PARTITION

     98         mac_partition,

     99 #endif

    100 #ifdef CONFIG_ULTRIX_PARTITION

    101         ultrix_partition,

    102 #endif

    103 #ifdef CONFIG_IBM_PARTITION

    104         ibm_partition,

    105 #endif

    106 #ifdef CONFIG_KARMA_PARTITION

    107         karma_partition,

    108 #endif

    109 #ifdef CONFIG_SYSV68_PARTITION

    110         sysv68_partition,

    111 #endif

    112         NULL

    113 };

好家伙 , 一下子定义了这么多函数 , 要是每个都要看那我他妈还要不要活了 . 也亏了哥们儿是曾经的复旦大学优秀团员 , 要不然还不被吓死去了 .

不过情况总还没有那么遭 , 我们不用像某些媒体一样每次都把夸大事实 , 以至于每年的洪水或干旱都被认定是百年一遇 , 搞得我们不禁怀疑自己到底活过了几个百年 ? 眼下的情况其实很好对付 , 除非你就是专门研究分区表格式的 , 否则这一堆函数你一个也不用看 . 如果你真是研究分区表格式的 , 那么 fs/partitions 目录下面的文件你就得仔细看看了 , 各种格式的都有 , 你就捡自己需要的看吧 .

localhost:/usr/src/linux-2.6.22.1 # ls fs/partitions/

Kconfig   acorn.h  atari.c  check.h  ibm.c    karma.h  mac.c    msdos.h  sgi.c  sun.h     ultrix.c Makefile  amiga.c  atari.h  efi.c    ibm.h    ldm.c    mac.h    osf.c    sgi.h  sysv68.c  ultrix.h acorn.c   amiga.h  check.c  efi.h    karma.c  ldm.h    msdos.c  osf.h    sun.c  sysv68.h

基本上我想说的是 , 以上那么多个函数其目的就是一个 , 为了找到分区信息 . 而且最终分区信息总是会被记录在那个 struct parsed_partitions 结构体的指针 . 而接下来我们就会用到其中的信息 , 这其中像 size ,from , 这些变量的意思不言自明 .

然后我们就来到了 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 目录下 .

[root@localhost tedkdb]# ls /sys/block/sda/

capability  device   queue  removable  sda10  sda12  sda14  sda2  sda5  sda7  sda9  slaves  subsystem dev         holders  range  sda1       sda11  sda13  sda15  sda3  sda6  sda8  size  stat    uevent

395 sysfs_create_link 的效果也很显然 ,

[root@localhost tedkdb]# ls -l /sys/block/sda/sda1/subsystem

lrwxrwxrwx 1 root root 0 Dec 13 03:15 /sys/block/sda/sda1/subsystem -> ../../../block

partition_sysfs_add_subdir 也没什么好说的 , 来自 fs/partitions/check.c:

    333 static inline void partition_sysfs_add_subdir(struct hd_struct *p)

    334 {

    335         struct kobject *k;

    336

    337         k = kobject_get(&p->kobj);

    338         p->holder_dir = kobject_add_dir(k, "holders");

    339         kobject_put(k);

    340 }

添加了 holders 子目录 .

[root@localhost tedkdb]# ls /sys/block/sda/sda1/

dev  holders  size  start  stat  subsystem  uevent

最后 , 让我们记住这个函数做过的一件事情 , 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 的知识 , 这是用户空间的程序 , 咱们就不多说了 .

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值