ext4文件系统 文件/目录创建ext4_create/ext4_mkdir 函数源码解析

  • Post author:
  • Post category:其他


网上讲解ext4的文章挺多的,但是讲解ext4源码的文章很少。如果想要深入理解ext4,还是需要研究源码的。本文在上篇文章《

ext4文件系统之文件查找ext4_lookup函数源码解析

》基础上,讲解文件/目录创建函数ext4_create /ext4_mkdir源码。这个过程也会讲解ext4_add_entry、__ext4_new_inode、find_group_orlov、find_group_other、ext4_getblk、ext4_mark_inode_dirty函数源码。

本文内核源码版本3.10.96,详细内核详细源码注释见

GitHub – dongzhiyan-stack/kernel-code-comment: 3.10.96 内核源代码注释

。文中如有错误请指出。

1:ext4_create文件创建函数源码解析

先把ext4_create函数源码贴下:



  1. static


    int





    ext4_create


    (



    struct


    inode



    *



    dir



    ,



    struct


    dentry



    *



    dentry



    ,



    umode_t mode



    ,




  2. bool


    excl



    )



    //dentry


    是待创建文件


    dentry




  3. {





  4. handle_t



    *



    handle



    ;




  5. struct


    inode



    *



    inode



    ;




  6. int


    err



    ,



    credits



    ,



    retries



    =



    0



    ;





  7. …………..




  8. retry



    :




  9. /*


    为新创建的文件分配一个


    inode


    结构,接着为该文件找一个有空闲


    inode


    和空闲


    block


    的块组


    group


    ,然后在该块组的


    inode bitmap


    找一个空闲


    inode


    编号,最后把该


    inode


    编号赋值给


    inode->i_ino*/



  10. inode



    =



    ext4_new_inode_start_handle



    (



    dir



    ,



    mode



    ,




    &



    dentry



    ->



    d_name



    ,



    0



    ,





  11. NULL




    ,



    EXT4_HT_DIR



    ,



    credits



    );




  12. handle



    =



    ext4_journal_current_handle



    ();




  13. err



    =



    PTR_ERR



    (



    inode



    );





  14. if




    (!



    IS_ERR



    (



    inode



    ))




    {




    //


    为文件分配


    inode


    成功



  15. //





    inode





    i_op





    i_fop


    赋值



  16. inode



    ->



    i_op



    =




    &



    ext4_file_inode_operations



    ;




  17. inode



    ->



    i_fop



    =




    &



    ext4_file_operations



    ;




  18. ext4_set_aops



    (



    inode



    );




  19. //





    dentry





    inode


    对应的文件或目录添加到它父目录的


    ext4_dir_entry_2






  20. err



    =



    ext4_add_nondir



    (



    handle



    ,



    dentry



    ,



    inode



    );





  21. if




    (!



    err



    &&



    IS_DIRSYNC



    (



    dir



    ))




  22. ext4_handle_sync



    (



    handle



    );





  23. }





  24. …………..





  25. return



    err



    ;





  26. }




  27. #define ext4_new_inode_start_handle(dir, mode, qstr, goal, owner, \



  28. type, nblocks)          \



  29. __ext4_new_inode(NULL, (dir), (mode), (qstr), (goal), (owner), \



  30. (type), __LINE__, (nblocks))

里边主要调用了ext4_new_inode_start_handle和ext4_add_nondir两个函数,而ext4_new_inode_start_handle主要是调用__ext4_new_inode,__ext4_new_inode是创建文件或目录的关键入口函数,这些函数我们依次讲解。

1.1__ext4_new_inode函数源码解析

__ext4_new_inode源码比较复杂,看下源码:



  1. //


    找到一个合适的块组,从这个块组分配一个空闲


    inode



  2. struct


    inode



    *


    __ext4_new_inode


    (



    handle_t



    *



    handle



    ,



    struct


    inode



    *



    dir



    ,




  3. umode_t mode



    ,



    const


    struct


    qstr



    *



    qstr



    ,



    //qstr


    是待创建的目录或文件名字



  4. __u32 goal



    ,



    uid_t



    *



    owner



    ,



    int


    handle_type



    ,



    //


    创建目录和文件时


    goal


    都是


    0



  5. unsigned


    int


    line_no



    ,



    int


    nblocks



    )





  6. {





  7. struct


    super_block



    *



    sb



    ;




  8. struct


    buffer_head



    *



    inode_bitmap_bh



    =




    NULL




    ;




  9. struct


    buffer_head



    *



    group_desc_bh



    ;




  10. ext4_group_t ngroups



    ,



    group



    =



    0



    ;




  11. unsigned


    long


    ino



    =



    0



    ;




  12. struct


    inode



    *



    inode



    ;




  13. struct


    ext4_group_desc



    *



    gdp



    =




    NULL




    ;




  14. struct


    ext4_inode_info



    *



    ei



    ;




  15. struct


    ext4_sb_info



    *



    sbi



    ;




  16. int


    ret2



    ,



    err



    =



    0



    ;




  17. struct


    inode



    *



    ret



    ;




  18. ext4_group_t i



    ;




  19. ext4_group_t flex_group



    ;




  20. /* Cannot create files in a deleted directory */




  21. if




    (!



    dir



    ||




    !



    dir



    ->



    i_nlink



    )





  22. return



    ERR_PTR



    (-



    EPERM



    );




  23. sb



    =



    dir



    ->



    i_sb



    ;




  24. //


    总块组个数



  25. ngroups



    =



    ext4_get_groups_count



    (



    sb



    );




  26. trace_ext4_request_inode



    (



    dir



    ,



    mode



    );




  27. //


    分配


    ext4_inode_info


    结构并返回它的成员


    struct inode vfs_inode


    的地址



  28. inode



    =



    new_inode



    (



    sb



    );





  29. if




    (!



    inode



    )





  30. return



    ERR_PTR



    (-



    ENOMEM



    );




  31. //





    inode


    得到


    ext4_inode_info



  32. ei



    =



    EXT4_I



    (



    inode



    );




  33. //





    sb


    得到


    ext4_sb_info



  34. sbi



    =



    EXT4_SB



    (



    sb



    );





  35. ……………




  36. /*


    下边为新创建的文件或目录先找到一个有空闲


    inode


    和空闲


    block


    的块组,优先查找父目录所属块组。查找失败则遍历所有块组,看哪个有空闲


    inode





    block


    。找到合适块组把块组号赋值给


    group


    ,接着会在该块组分配一个空闲的


    inode


    编号


    */




  37. if




    (



    S_ISDIR



    (



    mode



    ))



    //


    创建的是目录


    inode



  38. ret2



    =



    find_group_orlov



    (



    sb



    ,



    dir



    ,




    &



    group



    ,



    mode



    ,



    qstr



    );





  39. else



    //


    创建的是文件


    inode



  40. ret2



    =



    find_group_other



    (



    sb



    ,



    dir



    ,




    &



    group



    ,



    mode



    );




  41. got_group



    :




  42. //


    记录最近一次分配的


    inode


    所属的块组。到这里


    group


    就是本次要分配的


    inode


    所属的块组编号



  43. EXT4_I



    (



    dir



    )->



    i_last_alloc_group



    =



    group



    ;




  44. err



    =








    ENOSPC



    ;





  45. if




    (



    ret2



    ==








    1



    )





  46. goto



    out



    ;





  47. for




    (



    i



    =



    0



    ;



    i



    <



    ngroups



    ;



    i



    ++,



    ino



    =



    0



    )




    {




    //ngroups





    ext4


    文件系统总的块组数



  48. err



    =








    EIO



    ;




  49. /*


    由块组编号


    group


    得到块组描述符结构


    ext4_group_desc


    ,并且令


    group_desc_bh


    指向保存块组描述符数据的物理块映射的


    bh*/



  50. gdp



    =



    ext4_get_group_desc



    (



    sb



    ,



    group



    ,




    &



    group_desc_bh



    );





  51. if




    (!



    gdp



    )





  52. goto



    out



    ;




  53. //


    块组要是空闲


    inode


    不够了,


    group





    1


    指向下一个块组,如果


    group


    是最后一个块组则从



  54. //


    第一个块组开始




  55. if




    (



    ext4_free_inodes_count



    (



    sb



    ,



    gdp



    )




    ==



    0



    )




    {






  56. if




    (++



    group



    ==



    ngroups



    )




  57. group



    =



    0



    ;





  58. continue




    ;





  59. }




  60. brelse



    (



    inode_bitmap_bh



    );




  61. /*


    先根据块组号


    group


    得到块组描述符


    ext4_group_desc


    ,再由块组描述符得到保存


    inode bitmap


    数据的物理块号,最后读取该


    inode bitmap


    物理块的


    4K


    数据到


    inode_bitmap_bh


    并返回。注意,每个块组的


    inode bitmap


    应该只占一个物理块,最大数据量是


    4K*/



  62. inode_bitmap_bh



    =



    ext4_read_inode_bitmap



    (



    sb



    ,



    group



    );





  63. if




    (!



    inode_bitmap_bh



    )





  64. goto



    out



    ;




  65. repeat_in_this_group



    :




  66. /*


    一个块组内,


    inode bitmap


    占一个物理块,


    4K


    大小,总计有


    4k*8





    bit


    。因此,理论上一个块组内最多可以容纳


    4k*8





    inode


    ,但实际上只有


    EXT4_INODES_PER_GROUP(sb)


    个,即


    8192


    个,这个应该是综合考虑的结果,一个块组实际容纳不了


    4k*8





    inode


    。这里在


    inode bitmap


    对应的


    inode_bitmap_bh->b_data[]





    4K


    数据中,找一个空闲的


    inode


    号。每在


    inode bitmap


    找一个空闲


    inode


    号,在对应的


    inode bitmap





    bit


    位置


    1


    。比如


    inode bitmap





    buf





    inode_bitmap_bh->b_data[]


    的第


    1


    个字节的第


    1





    bit





    0


    ,则给本次的


    inode


    分配的


    inode


    编号就是


    0


    ,然后下边把这个


    bit


    位置


    1


    。下次分配新的


    inode


    ,找到


    inode_bitmap_bh->b_data[]


    的第


    1


    个字节的第


    2





    bit


    ,则为该新


    inode


    分配的编号是


    1.inode_bitmap_bh->b_data[]


    某个


    bit


    位是


    1


    表示对应编号


    inode


    分配了,为


    0


    表示该


    bit


    位对应的


    inode


    空闲


    */



  67. ino



    =



    ext4_find_next_zero_bit



    ((



    unsigned


    long



    *)




  68. inode_bitmap_bh



    ->



    b_data



    ,




  69. EXT4_INODES_PER_GROUP



    (



    sb



    ),



    ino



    );




  70. //


    新分配的


    inode


    编号大于最大值,则说明当前块组


    inode


    用完了,则去下一个块组分配


    inode




  71. if




    (



    ino



    >=



    EXT4_INODES_PER_GROUP



    (



    sb



    ))





  72. goto



    next_group



    ;





  73. ……………………….




  74. /*





    inode bitmap





    buf





    inode_bitmap_bh->b_data[]


    数组的


    ino


    对应的


    bit


    位置


    1


    ,表示该


    bit


    位对应的


    inode


    已经分配了,下次再分配


    inode


    就跳过该


    bit


    位,找一个新的是


    0





    bit


    位。


    */



  75. ret2



    =



    ext4_test_and_set_bit



    (



    ino



    ,



    inode_bitmap_bh



    ->



    b_data



    );




  76. ext4_unlock_group



    (



    sb



    ,



    group



    );




  77. //


    搞不清楚为什么这里要加


    1?


    我猜测应该是以


    1


    为最小


    inode


    编号,以


    1





    base



  78. ino



    ++;





  79. if




    (!



    ret2



    )



    //





    group


    块组成功分配一个


    inode


    编号是


    ino





    inode(


    空闲的


    inode)


    ,跳出循环




  80. goto



    got



    ;




  81. /*


    执行到这里,说明没有找一个空闲的


    inode


    编号,则跳到


    repeat_in_this_group


    分支重新在


    inode bitmap





    buf





    inode_bitmap_bh->b_data[]


    数组重新找一个空闲的


    inode


    编号


    */




  82. if




    (



    ino



    <



    EXT4_INODES_PER_GROUP



    (



    sb



    ))





  83. goto



    repeat_in_this_group



    ;




  84. next_group



    :




  85. //


    到这里说明已经遍历到最后一个块组,还是没找到有空闲


    inode


    的块组,那就从第一个块组中找一个空闲的


    inode




  86. if




    (++



    group



    ==



    ngroups



    )




  87. group



    =



    0



    ;





  88. }




  89. err



    =








    ENOSPC



    ;





  90. goto



    out



    ;




  91. got



    :




  92. /*inode bitmap





    buf





    inode_bitmap_bh->b_data[]


    数组的数据脏了,因为上边分配一个空闲的


    inode


    ,把该


    buf





    inode


    编号对应的


    bit


    位置


    1*/



  93. err



    =



    ext4_handle_dirty_metadata



    (



    handle



    ,




    NULL




    ,



    inode_bitmap_bh



    );





  94. if




    (



    err



    )




    {





  95. ext4_std_error



    (



    sb



    ,



    err



    );





  96. goto



    out



    ;





  97. }





  98. …………




  99. //





    gdp


    对应的块组空闲的


    inode


    数减


    1



  100. ext4_free_inodes_set



    (



    sb



    ,



    gdp



    ,



    ext4_free_inodes_count



    (



    sb



    ,



    gdp



    )








    1



    );





  101. if




    (



    S_ISDIR



    (



    mode



    ))




    {





  102. //


    设置块组的已分配的目录


    inode


    个数加


    1



  103. ext4_used_dirs_set



    (



    sb



    ,



    gdp



    ,



    ext4_used_dirs_count



    (



    sb



    ,



    gdp



    )




    +



    1



    );





  104. if




    (



    sbi



    ->



    s_log_groups_per_flex



    )




    {





  105. ext4_group_t f



    =



    ext4_flex_group



    (



    sbi



    ,



    group



    );




  106. atomic_inc



    (&



    sbi



    ->



    s_flex_groups



    [



    f



    ].



    used_dirs



    );





  107. }





  108. }





  109. …………………




  110. //


    前边修改了块组描述符的数据,比如块组空闲


    inode


    数,现在使块组描述符的


    buffer_head


    标记脏



  111. err



    =



    ext4_handle_dirty_metadata



    (



    handle



    ,




    NULL




    ,



    group_desc_bh



    );





  112. if




    (



    err



    )




    {





  113. ext4_std_error



    (



    sb



    ,



    err



    );





  114. goto



    out



    ;





  115. }




  116. //


    空闲


    inode


    数减


    1



  117. percpu_counter_dec



    (&



    sbi



    ->



    s_freeinodes_counter



    );





  118. if




    (



    S_ISDIR



    (



    mode



    ))




  119. percpu_counter_inc



    (&



    sbi



    ->



    s_dirs_counter



    );



    //


    当前要创建的是目录时减


    1




  120. if




    (



    sbi



    ->



    s_log_groups_per_flex



    )




    {




    //4



  121. flex_group



    =



    ext4_flex_group



    (



    sbi



    ,



    group



    );




  122. atomic_dec



    (&



    sbi



    ->



    s_flex_groups



    [



    flex_group



    ].



    free_inodes



    );





  123. }




  124. //


    根据为


    inode


    分配的块组编号


    group


    和在块组内找到的一个空闲


    inode


    号,计算最终的


    inode


    编号



  125. inode



    ->



    i_ino



    =



    ino



    +



    group



    *



    EXT4_INODES_PER_GROUP



    (



    sb



    );




  126. inode



    ->



    i_blocks



    =



    0



    ;




  127. //


    计算当前


    inode


    的创建和修改时间



  128. inode



    ->



    i_mtime



    =



    inode



    ->



    i_atime



    =



    inode



    ->



    i_ctime



    =



    ei



    ->



    i_crtime



    =




  129. ext4_current_time



    (



    inode



    );




  130. //





    struct ext4_inode_info *ei


    赋值



  131. memset



    (



    ei



    ->



    i_data



    ,



    0



    ,




    sizeof




    (



    ei



    ->



    i_data



    ));




  132. ei



    ->



    i_dir_start_lookup



    =



    0



    ;




  133. ei



    ->



    i_disksize



    =



    0



    ;





  134. …………………





  135. return



    ERR_PTR



    (



    err



    );





  136. }


把__ext4_new_inode函数关键点总结下:

1:执行new_inode函数分配ext4_inode_info和inode结构

2:执行find_group_orlov或find_group_other为当前创建的文件查找一个有空闲inode和block的块组,有固定的规则。

3:接着是for循环里,找到该块组的块组描述符结构gdp,然后通过gdp找到该块组的inode bitmap,接着在inode bitmap为当前要创建的文件分配一个空闲的inode号,并令inode bitmap对应的bit位置1,表示这个inode已经分配走了。最后,再标记inode bitmap对应的物理块bh脏。

4:令当前块组空闲的inode数减1,ext4总的空闲inode数减1

5:为本次新创建inode赋值inode号、inode创建时间等等。

好的,__ext4_new_inode函数源码主体讲解过了,下边把里边涉及的主要函数源码讲解一下

1.1.1 find_group_orlov函数源码解析

find_group_orlov函数就是为当前创建的文件找一个有空闲inode的块组,先看下源码:



  1. static


    int





    find_group_other


    (



    struct


    super_block



    *



    sb



    ,



    struct


    inode



    *



    parent



    ,




  2. ext4_group_t



    *



    group



    ,



    umode_t mode



    )





  3. {





  4. //


    父目录所在块组



  5. ext4_group_t parent_group



    =



    EXT4_I



    (



    parent



    )->



    i_block_group



    ;




  6. ext4_group_t i



    ,



    last



    ,



    ngroups



    =



    ext4_get_groups_count



    (



    sb



    );




  7. struct


    ext4_group_desc



    *



    desc



    ;




  8. int


    flex_size



    =



    ext4_flex_bg_size



    (



    EXT4_SB



    (



    sb



    ));






  9. if




    (



    flex_size



    >



    1



    )



    //


    使用了


    flex group


    块组




  10. {





  11. int


    retry



    =



    0



    ;




  12. try_again



    :




  13. //parent_group


    是父目录,这里计算的


    parent_group


    是父目录所属


    flex group


    块组里的第一个块组号



  14. parent_group



    &=




    ~(



    flex_size







    1



    );




  15. //last





    parent_group


    所属


    flex group


    块组里最后一个块组号



  16. last



    =



    parent_group



    +



    flex_size



    ;





  17. if




    (



    last



    >



    ngroups



    )




  18. last



    =



    ngroups



    ;




  19. //





    flex group


    块组里第


    1


    个块组搜索到最后


    1


    个块组




  20. for




    (



    i



    =



    parent_group



    ;



    i



    <



    last



    ;



    i



    ++)




    {





  21. //


    取出该块组的描述符



  22. desc



    =



    ext4_get_group_desc



    (



    sb



    ,



    i



    ,




    NULL




    );




  23. //


    该块组有空闲的


    inode


    ,那它就是选中的块组




  24. if




    (



    desc



    &&



    ext4_free_inodes_count



    (



    sb



    ,



    desc



    ))




    {






  25. *



    group



    =



    i



    ;





  26. return



    0



    ;





  27. }





  28. }




  29. //


    这里是取出父目录分配


    inode


    所属块组号


    i_last_alloc_group


    ,再尝试一次




  30. if




    (!



    retry



    &&



    EXT4_I



    (



    parent



    )->



    i_last_alloc_group



    !=




    ~



    0



    )




    {





  31. retry



    =



    1



    ;




  32. parent_group



    =



    EXT4_I



    (



    parent



    )->



    i_last_alloc_group



    ;





  33. goto



    try_again



    ;





  34. }




  35. /*


    到这里说明在父目录所属


    flex group


    中的所


    16


    个块组,都没有空闲


    inode


    ,于是下边执行


    find_group_orlov()


    再找一个合适的


    flex group


    块组


    */




  36. *



    group



    =



    parent_group



    +



    flex_size



    ;





  37. if




    (*



    group



    >



    ngroups



    )





  38. *



    group



    =



    0



    ;





  39. //


    找一个空闲


    inode


    数充裕、空闲


    block


    数充裕、已使用的目录很少的


    flex group


    块组,把找到的


    flex group


    块组号赋值给


    group




  40. return



    find_group_orlov



    (



    sb



    ,



    parent



    ,



    group



    ,



    mode



    ,




    NULL




    );





  41. }




  42. /*


    执行到这里,说明没有使用


    flex group*/



  43. //


    如果父目录所属块组有空闲


    inode





    block


    ,那这个块组就是本次选中的块组




  44. *



    group



    =



    parent_group



    ;




  45. desc



    =



    ext4_get_group_desc



    (



    sb



    ,




    *



    group



    ,




    NULL




    );





  46. if




    (



    desc



    &&



    ext4_free_inodes_count



    (



    sb



    ,



    desc



    )




    &&




  47. ext4_free_group_clusters



    (



    sb



    ,



    desc



    ))





  48. return



    0



    ;




  49. /*


    执行到这里,说明没有使用


    flex group


    ,并且父目录所属块组没有空闲的


    inode





    block*/




  50. //


    这里计算后


    group


    应该是父目录所在块组的下一个块组号




  51. *



    group



    =




    (*



    group



    +



    parent



    ->



    i_ino



    )




    %



    ngroups



    ;




  52. //





    group


    对应的块组一直向后搜索




  53. for




    (



    i



    =



    1



    ;



    i



    <



    ngroups



    ;



    i



    <<=



    1



    )




    {




    //


    搞不清楚


    i <<= 1


    是什么意思


    ???????



  54. //group


    块组号每次增加


    1





    2





    4





    8





    16…….




  55. *



    group



    +=



    i



    ;





  56. if




    (*



    group



    >=



    ngroups



    )





  57. *



    group



    -=



    ngroups



    ;




  58. desc



    =



    ext4_get_group_desc



    (



    sb



    ,




    *



    group



    ,




    NULL




    );




  59. //


    新找到的块组


    group


    有空闲的


    inode





    block


    ,那它就是要找到的块组




  60. if




    (



    desc



    &&



    ext4_free_inodes_count



    (



    sb



    ,



    desc



    )




    &&




  61. ext4_free_group_clusters



    (



    sb



    ,



    desc



    ))





  62. return



    0



    ;





  63. }




  64. /*


    执行到这里,还没找到合适的块组,于是下边放宽查找块组的限制条件


    */




  65. *



    group



    =



    parent_group



    ;





  66. for




    (



    i



    =



    0



    ;



    i



    <



    ngroups



    ;



    i



    ++)




    {





  67. //group


    块组号每次只增加


    1




  68. if




    (++*



    group



    >=



    ngroups



    )





  69. *



    group



    =



    0



    ;




  70. desc



    =



    ext4_get_group_desc



    (



    sb



    ,




    *



    group



    ,




    NULL




    );




  71. //


    新找到的块组


    group


    有空闲的


    inode


    ,那它就是要找到的块组,这里不再看是否有空闲


    block


    限制




  72. if




    (



    desc



    &&



    ext4_free_inodes_count



    (



    sb



    ,



    desc



    ))





  73. return



    0



    ;





  74. }





  75. return








    1



    ;





  76. }


总结一个这个函数的工作流程:使用flex group时,先在父目录所属flex group的16个块组里找一个有空闲inode的块组,找不到就执行find_group_orlov()找一个有充裕空闲inode和block的flex group块组。不使用flex group时,先看父目录所属块组有没有空闲的inode和block,有就返回父目录的块组号。没有就遍历一个个块组,看哪个块组有空闲的inode。

1.1.2 find_group_orlov函数源码解析

find_group_orlov函数跟find_group_other比较像,但它是为当前创建的目录找一个空闲inode数充裕、空闲block数充裕、已使用的目录很少的块组,最后把找到的块组号赋值给group,流程更复杂点。这里先把源码贴下,然后慢慢讲解:



  1. static


    int





    find_group_orlov


    (



    struct


    super_block



    *



    sb



    ,



    struct


    inode



    *



    parent



    ,




  2. ext4_group_t



    *



    group



    ,



    umode_t mode



    ,




  3. const


    struct


    qstr



    *



    qstr



    )



    //qstr


    是创建的目录名字




  4. {





  5. //





    inode


    所属的块组编号



  6. ext4_group_t parent_group



    =



    EXT4_I



    (



    parent



    )->



    i_block_group



    ;




  7. struct


    ext4_sb_info



    *



    sbi



    =



    EXT4_SB



    (



    sb



    );




  8. //


    块组个数



  9. ext4_group_t real_ngroups



    =



    ext4_get_groups_count



    (



    sb



    );




  10. //


    每个块组的最多的


    inode






  11. int


    inodes_per_group



    =



    EXT4_INODES_PER_GROUP



    (



    sb



    );




  12. unsigned


    int


    freei



    ,



    avefreei



    ,



    grp_free



    ;




  13. ext4_fsblk_t freeb



    ,



    avefreec



    ;




  14. unsigned


    int


    ndirs



    ;




  15. int


    max_dirs



    ,



    min_inodes



    ;




  16. ext4_grpblk_t min_clusters



    ;




  17. ext4_group_t i



    ,



    grp



    ,



    g



    ,



    ngroups



    ;




  18. struct


    ext4_group_desc



    *



    desc



    ;




  19. struct


    orlov_stats stats



    ;




  20. //flex group


    包含的块组个数,


    16



  21. int


    flex_size



    =



    ext4_flex_bg_size



    (



    sbi



    );




  22. struct


    dx_hash_info hinfo



    ;




  23. ngroups



    =



    real_ngroups



    ;





  24. if




    (



    flex_size



    >



    1



    )




    {





  25. ngroups



    =




    (



    real_ngroups



    +



    flex_size







    1



    )




    >>




  26. sbi



    ->



    s_log_groups_per_flex



    ;




  27. parent_group



    >>=



    sbi



    ->



    s_log_groups_per_flex



    ;





  28. }




  29. //


    空闲


    inode






  30. freei



    =



    percpu_counter_read_positive



    (&



    sbi



    ->



    s_freeinodes_counter



    );




  31. //


    平均每个块组的空闲


    inode






  32. avefreei



    =



    freei



    /



    ngroups



    ;




  33. //


    空闲


    block






  34. freeb



    =



    EXT4_C2B



    (



    sbi



    ,




  35. percpu_counter_read_positive



    (&



    sbi



    ->



    s_freeclusters_counter



    ));




  36. avefreec



    =



    freeb



    ;




  37. //avefreec = avefreec/ngroups


    平均每个块组空闲


    block






  38. do_div



    (



    avefreec



    ,



    ngroups



    );




  39. ndirs



    =



    percpu_counter_read_positive



    (&



    sbi



    ->



    s_dirs_counter



    );




  40. /*


    这个


    if


    成立应该说明父目录是根目录或者顶层目录


    */




  41. if




    (



    S_ISDIR



    (



    mode



    )




    &&





  42. ((



    parent



    ==



    sb



    ->



    s_root



    ->



    d_inode



    )




    ||



    //


    父目录是根文件系统根目录




  43. (



    ext4_test_inode_flag



    (



    parent



    ,



    EXT4_INODE_TOPDIR



    ))))



    //


    顶层目录




  44. {





  45. //


    每个块组最多的


    inode






  46. int


    best_ndir



    =



    inodes_per_group



    ;




  47. int


    ret



    =








    1



    ;




  48. //


    下边这是根据各种规则计算一个初始块组号赋于


    grp




  49. if




    (



    qstr



    )




    {





  50. hinfo



    .



    hash_version



    =



    DX_HASH_HALF_MD4



    ;




  51. hinfo



    .



    seed



    =



    sbi



    ->



    s_hash_seed



    ;




  52. ext4fs_dirhash



    (



    qstr



    ->



    name



    ,



    qstr



    ->



    len



    ,




    &



    hinfo



    );




  53. grp



    =



    hinfo



    .



    hash



    ;





  54. }




    else




  55. get_random_bytes



    (&



    grp



    ,




    sizeof




    (



    grp



    ));




  56. //parent_group


    就是上边的初始块组号


    grp



  57. parent_group



    =




    (



    unsigned



    )



    grp



    %



    ngroups



    ;




  58. /*





    0


    号块组依次向后查找,看哪个块组有充裕的


    inode





    block


    有个疑问,如果使用


    flex group


    块组的情况下,


    ngroups


    应该是


    flex group


    组个数,一个


    flex group


    组有


    16


    个真正的块组


    */




  59. for




    (



    i



    =



    0



    ;



    i



    <



    ngroups



    ;



    i



    ++)




    {





  60. g



    =




    (



    parent_group



    +



    i



    )




    %



    ngroups



    ;




  61. //


    得到块组或者


    flex group


    块组的空闲


    inode


    数、空闲


    block


    数、已使用的目录数



  62. get_orlov_stats



    (



    sb



    ,



    g



    ,



    flex_size



    ,




    &



    stats



    );




  63. //


    当前块组


    (


    或者


    flex group


    块组


    )


    ,没有空闲


    inode


    ,跳过




  64. if




    (!



    stats



    .



    free_inodes



    )





  65. continue




    ;




  66. //


    这个成立说明当前块组


    (


    或者


    flex group


    块组


    )


    已使用的目录太多,高于每个块组最大


    inode


    数,跳过




  67. if




    (



    stats



    .



    used_dirs



    >=



    best_ndir



    )





  68. continue




    ;




  69. //


    这个成立说明当前块组


    (


    或者


    flex group


    块组


    )


    空闲


    inode


    太少,低于平均值,跳过




  70. if




    (



    stats



    .



    free_inodes



    <



    avefreei



    )





  71. continue




    ;




  72. //


    这个成立说明当前块组


    (


    或者


    flex group


    块组


    )


    空闲


    block


    太少,低于平均值,跳过




  73. if




    (



    stats



    .



    free_clusters



    <



    avefreec



    )





  74. continue




    ;





  75. /*


    到这里很奇怪,明明已经找到


    inode





    block


    等充足的块组


    g


    ,但却没有跳转


    for


    循环,而是继续


    for


    循环查找下一个


    inode


    充足的块组,直到遍历到最后一个块组,搞不清楚


    */



  76. grp



    =



    g



    ;




  77. ret



    =



    0



    ;




  78. //


    这里更新


    best_ndir!!!!!!!!!



  79. best_ndir



    =



    stats



    .



    used_dirs



    ;





  80. }




  81. /*


    如果上边


    for


    循环找到


    inode


    充足等的块组,则


    ret





    0


    。否则没有找到


    inode





    block


    充足等的块组,


    ret





    -1


    ,直接跳转到


    fallback


    分支


    */




  82. if




    (



    ret



    )





  83. goto



    fallback



    ;




  84. found_flex_bg



    :




  85. //


    没有使用


    flex group


    块组则直接使用


    grp


    作为本次找到的块组号




  86. if




    (



    flex_size



    ==



    1



    )




    {






  87. *



    group



    =



    grp



    ;





  88. return



    0



    ;





  89. }




  90. /*


    到这里说明


    grp





    flex group


    块组的编号令


    grp


    乘以


    flex_size(16)


    ,之后


    grp


    应该就是真正的块组号,一个


    flex group


    组有


    16


    个块组


    */



  91. grp



    *=



    flex_size



    ;




  92. /*


    这里应该是在选中的


    flex group


    组里的


    16


    个块组里,找一个空闲


    inode


    充足的块组,然后执行


    *group = grp+i





    return


    ,这就是最终选中的块组


    */




  93. for




    (



    i



    =



    0



    ;



    i



    <



    flex_size



    ;



    i



    ++)




    {






  94. if




    (



    grp



    +



    i



    >=



    real_ngroups



    )



    //real_ngroups


    是最大块组树




  95. break




    ;




  96. desc



    =



    ext4_get_group_desc



    (



    sb



    ,



    grp



    +



    i



    ,




    NULL




    );





  97. if




    (



    desc



    &&



    ext4_free_inodes_count



    (



    sb



    ,



    desc



    ))




    {






  98. *



    group



    =



    grp



    +



    i



    ;





  99. return



    0



    ;





  100. }





  101. }





  102. goto



    fallback



    ;





  103. }




  104. /*


    到这里,一般情况是,父目录不是顶层目录或者根目录


    */



  105. //


    计算块组最大目录个数上限


    max_dirs



  106. max_dirs



    =



    ndirs



    /



    ngroups



    +



    inodes_per_group



    /



    16



    ;




  107. //


    减少


    avefreei


    ,块组空闲


    inode


    数下限



  108. min_inodes



    =



    avefreei







    inodes_per_group



    *



    flex_size



    /



    4



    ;





  109. if




    (



    min_inodes



    <



    1



    )




  110. min_inodes



    =



    1



    ;




  111. //


    减少


    avefreec


    ,块组空闲


    block


    数下限



  112. min_clusters



    =



    avefreec







    EXT4_CLUSTERS_PER_GROUP



    (



    sb



    )*



    flex_size



    /



    4



    ;





  113. if




    (



    EXT4_I



    (



    parent



    )->



    i_last_alloc_group



    !=




    ~



    0



    )




    {





  114. //


    取出父目录上一次分配


    inode


    所属的块组号给


    parent_group


    ,作为本次查找块组的基准值



  115. parent_group



    =



    EXT4_I



    (



    parent



    )->



    i_last_alloc_group



    ;





  116. if




    (



    flex_size



    >



    1



    )




  117. parent_group



    >>=



    sbi



    ->



    s_log_groups_per_flex



    ;





  118. }




  119. //





    0


    号块组向后遍历,找到一个


    inode


    充足的块组




  120. for




    (



    i



    =



    0



    ;



    i



    <



    ngroups



    ;



    i



    ++)




    {





  121. //


    其实就是父目录所在


    group





    i



  122. grp



    =




    (



    parent_group



    +



    i



    )




    %



    ngroups



    ;




  123. //


    得到块组或者


    flex group


    块组的空闲


    inode


    数、空闲


    block


    数、已使用的目录数



  124. get_orlov_stats



    (



    sb



    ,



    grp



    ,



    flex_size



    ,




    &



    stats



    );




  125. //


    这个成立说明当前块组


    (


    或者


    flex group


    块组


    )


    已使用的目录太多,高于每个块组最大


    inode


    数,跳过




  126. if




    (



    stats



    .



    used_dirs



    >=



    max_dirs



    )





  127. continue




    ;




  128. //


    这个成立说明当前块组


    (


    或者


    flex group


    块组


    )


    空闲


    inode


    太少,低于平均值,跳过




  129. if




    (



    stats



    .



    free_inodes



    <



    min_inodes



    )





  130. continue




    ;




  131. //


    这个成立说明当前块组


    (


    或者


    flex group


    块组


    )


    空闲


    block


    太少,低于平均值,跳过




  132. if




    (



    stats



    .



    free_clusters



    <



    min_clusters



    )





  133. continue




    ;




  134. /*


    到这里说明找到一个空闲


    inode


    充裕的块组


    (


    或者


    flex group


    块组


    )


    ,块组号是


    grp


    。则跳到


    found_flex_bg


    分支,如果是


    flex group


    组则从该


    flex group


    找一个空闲


    inode


    充裕的块组


    */




  135. goto



    found_flex_bg



    ;





  136. }




  137. fallback



    :




  138. ngroups



    =



    real_ngroups



    ;




  139. //


    平均每个块组空闲


    inode






  140. avefreei



    =



    freei



    /



    ngroups



    ;





  141. /*


    到这里,说明上边按照





    块组或者


    flex group


    块组的空闲


    inode


    数、空闲


    block


    数、已使用的目录数





    的规则,找不到合适的块组。于是下边放松条件,重新找一个空闲


    inode


    充裕的块组


    */



  142. fallback_retry



    :




  143. //


    父目录所属块组号



  144. parent_group



    =



    EXT4_I



    (



    parent



    )->



    i_block_group



    ;




  145. //





    0


    号块组或者


    flex group


    块组向后遍历,找到一个


    inode


    充足的块组或者


    flex group


    块组




  146. for




    (



    i



    =



    0



    ;



    i



    <



    ngroups



    ;



    i



    ++)




    {





  147. //


    其实就是父目录所在块组号或者


    flex group


    块组号加


    i


    得到快组号


    grp



  148. grp



    =




    (



    parent_group



    +



    i



    )




    %



    ngroups



    ;




  149. desc



    =



    ext4_get_group_desc



    (



    sb



    ,



    grp



    ,




    NULL




    );





  150. if




    (



    desc



    )




    {





  151. //


    如果


    grp


    这个块组或者


    flex group


    块组空闲


    inode


    数大于


    avefreei(


    平均每个块组空闲


    inode





    )


    ,那它就是本次选中的块组



  152. grp_free



    =



    ext4_free_inodes_count



    (



    sb



    ,



    desc



    );





  153. if




    (



    grp_free



    &&



    grp_free



    >=



    avefreei



    )




    {






  154. *



    group



    =



    grp



    ;





  155. return



    0



    ;





  156. }





  157. }





  158. }





  159. …………….





  160. return








    1



    ;





  161. }


这里说一下这个函数的工作细节:

1:如果父目录是顶层目录或者根目录,则以best_ndir、avefreei、avefreec 为阈值,从0号块组或者flex group块组向后一直查找,找到块组已使用目录数、块组空闲inode数、块组空闲block数符合阈值的块组。找到后,如果不是flex group块组,直接返回这个块组号。如果是flex group块组,则从该flex group块组找一个空闲inode充足的块组。

2:如果父目录不是顶层目录或者根目录,则以max_dirs、min_inodes、min_clusters为阈值,从0号块组或者flex group块组向后一直查找,找到块组已使用目录数、块组空闲inode数、块组空闲block数符合阈值的块组。找到后,如果不是flex group块组,如果是flex group块组,则从该flex group块组找一个空闲inode充足的块组。

3:如果按照步骤1和2找不到合适的块组,则从0号块组或者flex group块组向后一直查找,只要块组空闲inode数充裕,直接返回该块组号。

最后做个总结: parent_group 是个关键,它是搜索空闲块组(有空闲inode和block)的基准块组号

1:是顶层目录或者根目录时,则采用分散形式查找空闲块组,因为此时parent_group = (unsigned)grp % ngroups,parent_group是个随机值。

2:如果不是顶层目录或根目录,才在父目录所属块组附近查找空闲块组,因为此时parent_group = EXT4_I(parent)->i_block_group,就是父目录所属的块组编号

3:如果最后找不到合适的空闲块组,那就没那么多限制条件了,从0号块组开始遍历,谁有空闲inode就选中谁作为最终的块组

1.1.3 ext4_get_group_desc函数源码解析

ext4_get_group_desc函数主要作用是:由传入的块组编号block_group得到块组描述符结构ext4_group_desc,并且令group_desc_bh指向保存块组描述符数据的物理块映射的bh,下边把源码贴下:



  1. struct


    ext4_group_desc



    *




    ext4_get_group_desc


    (



    struct


    super_block



    *



    sb



    ,




  2. ext4_group_t block_group



    ,




  3. struct


    buffer_head



    **



    bh



    )





  4. {





  5. unsigned


    int


    group_desc



    ;




  6. unsigned


    int


    offset



    ;




  7. ext4_group_t ngroups



    =



    ext4_get_groups_count



    (



    sb



    );




  8. struct


    ext4_group_desc



    *



    desc



    ;




  9. struct


    ext4_sb_info



    *



    sbi



    =



    EXT4_SB



    (



    sb



    );





  10. ……………….




  11. group_desc



    =



    block_group



    >>



    EXT4_DESC_PER_BLOCK_BITS



    (



    sb



    );




  12. offset



    =



    block_group



    &




    (



    EXT4_DESC_PER_BLOCK



    (



    sb



    )








    1



    );





  13. …………..




  14. /*sbi->s_group_desc[group_desc]->b_data


    保存的数据是目标块组


    block_group


    这个块组所在物理块的


    4K


    数据,都是块组描述符结构。


    offset*EXT4_DESC_SIZE(sb)


    是目标块组


    block_group





    64


    字节块组描述符数据在这个物理块


    4K


    数据中的的偏移。最终得到


    block_group


    这个块组的块组描述符结构


    */



  15. desc



    =




    (



    struct


    ext4_group_desc



    *)(





  16. (



    __u8



    *)



    sbi



    ->



    s_group_desc



    [



    group_desc



    ]->



    b_data



    +




  17. offset



    *



    EXT4_DESC_SIZE



    (



    sb



    ));





  18. if




    (



    bh



    )



    //bh


    指向保存块组描述符数据的


    buffer_head




  19. *



    bh



    =



    sbi



    ->



    s_group_desc



    [



    group_desc



    ];





  20. return



    desc



    ;





  21. }


主要说一下group_desc和offset的计算过程。ext4文件系统的组成是 超级块(1个block)+块组描述符(N个block)+预留块(N个block)+Data Block Bitmap(1个block)+ inode Bitmap(1个block)+inode table(N个block)+ data block(N个block)。ext4一个物理block大小4k,一个块组描述符ext4_group_desc结构64B,一个block可以容纳64个块组描述符。group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb)就是group_desc=block_group/64,计算当前的块组号block_group落在第几个物理块(即第group_desc个物理块),offset = block_group & (EXT4_DESC_PER_BLOCK(sb) – 1)是计算当前的块组号block_group对应的块组描述符在第group_desc个物理块的偏移,准确说是group_desc物理块里的第offset个块组描述符。说着比较抽象,举个例子,一个2个物理块,2*64个块组,那块组号65的块组描述符在哪里?group_desc=65/64=1说明在第2个物理块,offset=65%64=1说明在第2个物理块的第2个块组描述符哪里。

ok,__ext4_new_inode函数讲解过了,下边回到ext4_create函数讲解ext4_add_nondir函数。

1.2 ext4_add_nondir、ext4_add_entry函数源码解析

这个函数简单说,就是把刚才为新文件分配的inode添加到它的父目录里,具体怎么操作呢?看下源码:



  1. static


    int





    ext4_add_nondir


    (



    handle_t



    *



    handle



    ,




  2. struct


    dentry



    *



    dentry



    ,



    struct


    inode



    *



    inode



    )





  3. {





  4. //





    dentry





    inode


    对应的文件或目录添加到它父目录



  5. int


    err



    =



    ext4_add_entry



    (



    handle



    ,



    dentry



    ,



    inode



    );





  6. if




    (!



    err



    )




    {





  7. //


    标记


    inode


    脏,重点是


    根据


    inode


    编号得到它在


    所属的块组的


    inode table


    的物理块号



  8. ext4_mark_inode_dirty



    (



    handle



    ,



    inode



    );




  9. unlock_new_inode



    (



    inode



    );




  10. //


    建立


    dentry





    inode


    联系



  11. d_instantiate



    (



    dentry



    ,



    inode



    );





  12. return



    0



    ;





  13. }





  14. …………..





  15. return



    err



    ;





  16. }


显然主要是执行ext4_add_entry把新创建的文件inode添加到父目录。并且,还执行ext4_mark_inode_dirty标记这个inode脏,ext4_mark_inode_dirty里还有一个重点操作是从inode table为该inode分配一席之地。

1.2.1 ext4_add_entry函数源码解析

这个函数就是把新创建的文件inode添加到父目录里。



  1. static


    int





    ext4_add_entry


    (



    handle_t



    *



    handle



    ,



    struct


    dentry



    *



    dentry



    ,




  2. struct


    inode



    *



    inode



    )



    //dentry





    inode


    都是待创建的目录或文件的




  3. {





  4. //


    父目录


    inode



  5. struct


    inode



    *



    dir



    =



    dentry



    ->



    d_parent



    ->



    d_inode



    ;




  6. struct


    buffer_head



    *



    bh



    =




    NULL




    ;




  7. struct


    ext4_dir_entry_2



    *



    de



    ;




  8. struct


    ext4_dir_entry_tail



    *



    t



    ;




  9. struct


    super_block



    *



    sb



    ;




  10. int


    retval



    ;




  11. int


    dx_fallback



    =



    0



    ;




  12. unsigned


    blocksize



    ;




  13. ext4_lblk_t block



    ,



    blocks



    ;




  14. int


    csum_size



    =



    0



    ;





  15. if




    (



    EXT4_HAS_RO_COMPAT_FEATURE



    (



    inode



    ->



    i_sb



    ,




  16. EXT4_FEATURE_RO_COMPAT_METADATA_CSUM



    ))




  17. csum_size



    =




    sizeof




    (



    struct


    ext4_dir_entry_tail



    );




  18. sb



    =



    dir



    ->



    i_sb



    ;




  19. blocksize



    =



    sb



    ->



    s_blocksize



    ;



    //ext4


    文件系统一个物理块


    4K







  20. if




    (!



    dentry



    ->



    d_name



    .



    len



    )





  21. return








    EINVAL



    ;





  22. …………




  23. //dir->i_size


    是父目录的数据量大小,


    blocks


    是父目录数据占的


    block


    个数



  24. blocks



    =



    dir



    ->



    i_size



    >>



    sb



    ->



    s_blocksize_bits



    ;




  25. /*


    这个


    for


    循环是根据父目录逻辑块地址


    0~blocks


    ,依次读取这些逻辑块映射的物理块的数据,然后在这些物理块数据中查找一个空闲的


    ext4_dir_entry_2


    结构,最后把本次添加的文件子文件或子目录的名字等信息赋值给


    ext4_dir_entry_2


    。这就相当于把该子目录或子文件添加到了父目录


    */




  26. for




    (



    block



    =



    0



    ;



    block



    <



    blocks



    ;



    block



    ++)




    {





  27. /*


    根据父目录的逻辑地址


    block





    ext4


    文件系统的


    data block


    区分配


    1


    个物理块,并与逻辑地址


    block


    构成映射,最后返回这物理块的


    bh


    。注意,


    bh->b_data


    就保存了该父目录的一个物理块数据,是一个个包含子目录或者子文件名字等信息的


    ext4_dir_entry_2


    结构


    */



  28. bh



    =



    ext4_read_dirblock



    (



    dir



    ,



    block



    ,



    DIRENT



    );





  29. if




    (



    IS_ERR



    (



    bh



    ))





  30. return



    PTR_ERR



    (



    bh



    );




  31. /*


    在父目录的


    block


    块数据中查找一个空闲的


    ext4_dir_entry_2


    结构并赋值给


    de


    ,然后对


    de


    这个


    ext4_dir_entry_2


    结构赋值待添加的文件或目录名字、


    inode


    编号、文件长度等信息。这里就相当于把新的文件或目录添加到了父目录


    */



  32. retval



    =



    add_dirent_to_buf



    (



    handle



    ,



    dentry



    ,



    inode



    ,




    NULL




    ,



    bh



    );





  33. if




    (



    retval



    !=








    ENOSPC



    )





  34. goto



    out



    ;





  35. ……………..





  36. }




  37. //


    执行到这里,应该是说,


    dir


    父目录数据块被占满了,则需要增加一个物理块,并返回它的


    bh


    ,最后把本次的子目录或子文件添加到这个父目录新的物理块



  38. bh



    =



    ext4_append



    (



    handle



    ,



    dir



    ,




    &



    block



    );





  39. if




    (



    IS_ERR



    (



    bh



    ))





  40. return



    PTR_ERR



    (



    bh



    );




  41. de



    =




    (



    struct


    ext4_dir_entry_2



    *)



    bh



    ->



    b_data



    ;




  42. de



    ->



    inode



    =



    0



    ;




  43. de



    ->



    rec_len



    =



    ext4_rec_len_to_disk



    (



    blocksize







    csum_size



    ,



    blocksize



    );





  44. if




    (



    csum_size



    )




    {





  45. t



    =



    EXT4_DIRENT_TAIL



    (



    bh



    ->



    b_data



    ,



    blocksize



    );




  46. initialize_dirent_tail



    (



    t



    ,



    blocksize



    );





  47. }




  48. retval



    =



    add_dirent_to_buf



    (



    handle



    ,



    dentry



    ,



    inode



    ,



    de



    ,



    bh



    );




  49. out



    :




  50. brelse



    (



    bh



    );





  51. if




    (



    retval



    ==



    0



    )




  52. ext4_set_inode_state



    (



    inode



    ,



    EXT4_STATE_NEWENTRY



    );





  53. return



    retval



    ;





  54. }


首先执行ext4_read_dirblock函数从保存父目录数据的物理块中依次读取数据到这些物理块映射的bh。然后执行add_dirent_to_buf函数,在父目录的物理块数据里,为新创建的文件查找一个空闲的ext4_dir_entry_2结构,还不能有重名的子文件或目录,看下它的源码:



  1. static


    int





    add_dirent_to_buf


    (



    handle_t



    *



    handle



    ,



    struct


    dentry



    *



    dentry



    ,






  2. //dentry





    inode


    都是待创建的目录或文件的



  3. struct


    inode



    *



    inode



    ,



    struct


    ext4_dir_entry_2



    *



    de



    ,




  4. //bh


    是保存父目录的数据物理块映射的


    bh



  5. struct


    buffer_head



    *



    bh



    )





  6. {





  7. //


    父目录



  8. struct


    inode



    *



    dir



    =



    dentry



    ->



    d_parent



    ->



    d_inode



    ;




  9. //


    本次创建的新文件或目录的名字



  10. const


    char



    *



    name



    =



    dentry



    ->



    d_name



    .



    name



    ;




  11. //


    本次创建的新文件或目录的名字长度



  12. int


    namelen



    =



    dentry



    ->



    d_name



    .



    len



    ;




  13. unsigned


    int


    blocksize



    =



    dir



    ->



    i_sb



    ->



    s_blocksize



    ;




  14. int


    csum_size



    =



    0



    ;




  15. int


    err



    ;





  16. if




    (



    EXT4_HAS_RO_COMPAT_FEATURE



    (



    inode



    ->



    i_sb



    ,




  17. EXT4_FEATURE_RO_COMPAT_METADATA_CSUM



    ))




  18. csum_size



    =




    sizeof




    (



    struct


    ext4_dir_entry_tail



    );





  19. if




    (!



    de



    )




    {




    //


    一般


    de





    NULL



  20. //


    在父目录的数据中查找一个空闲的


    ext4_dir_entry_2



  21. err



    =



    ext4_find_dest_de



    (



    dir



    ,



    inode



    ,




  22. bh



    ,



    bh



    ->



    b_data



    ,



    blocksize







    csum_size



    ,




  23. name



    ,



    namelen



    ,




    &



    de



    );





  24. if




    (



    err



    )





  25. return



    err



    ;





  26. }





  27. ……………..




  28. //





    de


    这个


    ext4_dir_entry_2


    赋值待添加的文件或目录名字、


    inode


    编号、文件长度等等



  29. ext4_insert_dentry



    (



    inode



    ,



    de



    ,



    blocksize



    ,



    name



    ,



    namelen



    );




  30. //


    更新父目录修改时间



  31. dir



    ->



    i_mtime



    =



    dir



    ->



    i_ctime



    =



    ext4_current_time



    (



    dir



    );




  32. ext4_update_dx_flag



    (



    dir



    );




  33. dir



    ->



    i_version



    ++;




  34. ext4_mark_inode_dirty



    (



    handle



    ,



    dir



    );




  35. BUFFER_TRACE



    (



    bh



    ,



    “call ext4_handle_dirty_metadata”



    );




  36. err



    =



    ext4_handle_dirty_dirent_node



    (



    handle



    ,



    dir



    ,



    bh



    );





  37. if




    (



    err



    )




  38. ext4_std_error



    (



    dir



    ->



    i_sb



    ,



    err



    );





  39. return



    0



    ;





  40. }


这个函数就是在父目录的数据中查找一个空闲并且不能重名的的ext4_dir_entry_2赋值给de,然后对de这个ext4_dir_entry_2赋值待添加的文件或目录名字、inode编号、文件长度等等。关键是调用ext4_find_dest_de函数。



  1. int





    ext4_find_dest_de


    (



    struct


    inode



    *



    dir



    ,



    struct


    inode



    *



    inode



    ,



    //inode


    都是新创建的目录或文件的



  2. struct


    buffer_head



    *



    bh



    ,



    //bh


    是保存父目录的数据物理块映射的


    bh



  3. void



    *



    buf



    ,



    int


    buf_size



    ,



    //buf





    bh->b_data





    buf_size





    bh->b_data


    这片


    buf


    大小,是


    4k



  4. const


    char



    *



    name



    ,



    int


    namelen



    ,



    //name





    namelen


    是待创建文件或目录的名字和长度



  5. struct


    ext4_dir_entry_2



    **



    dest_de



    )





  6. {





  7. struct


    ext4_dir_entry_2



    *



    de



    ;




  8. //reclen





    namelen


    稍大,容纳一些冗余信息吧



  9. unsigned


    short


    reclen



    =



    EXT4_DIR_REC_LEN



    (



    namelen



    );




  10. int


    nlen



    ,



    rlen



    ;




  11. unsigned


    int


    offset



    =



    0



    ;




  12. char



    *



    top



    ;




  13. //buf


    是保存父目录的数据物理块映射的


    bh





    buf





    de


    指向这片内存首地址



  14. de



    =




    (



    struct


    ext4_dir_entry_2



    *)



    buf



    ;




  15. //top


    指向这片


    buf


    的顶端



  16. top



    =



    buf



    +



    buf_size







    reclen



    ;





  17. /*


    父目录的数据是一个个


    ext4_dir_entry_2


    结构,保存了子文件或者子目录的名字等关键信息。这个


    while


    循环是从保存父目录的数据的


    buf


    头开始,遍历一个个


    ext4_dir_entry_2


    结构


    */




  18. while




    ((



    char



    *)



    de



    <=



    top



    )




    {






  19. if




    (



    ext4_check_dir_entry



    (



    dir



    ,




    NULL




    ,



    de



    ,



    bh



    ,




  20. buf



    ,



    buf_size



    ,



    offset



    ))





  21. return








    EIO



    ;




  22. //


    如果父目录已经有了名字是


    name


    的文件或目录,返回


    EEXIST


    ,不能重名




  23. if




    (



    ext4_match



    (



    namelen



    ,



    name



    ,



    de



    ))





  24. return








    EEXIST



    ;




  25. //nlen





    de->name_len


    大几个字节



  26. nlen



    =



    EXT4_DIR_REC_LEN



    (



    de



    ->



    name_len



    );




  27. //rlen = de->rec_len



  28. rlen



    =



    ext4_rec_len_from_disk



    (



    de



    ->



    rec_len



    ,



    buf_size



    );




  29. /*


    如果当前的


    de


    没被使用,


    de->inode


    应该是


    0


    ,此时只要


    rlen>=reclen


    ,则当前的


    de


    就是选中的


    ext4_dir_entry_2





    rlen





    de


    的空间大小,


    reclen


    是本次创建的子目录或者子文件的名字的长度,


    rlen>=reclen


    说明


    de


    可以容纳下本次创建的子目录或者子文件


    */




  30. if




    ((



    de



    ->



    inode



    ?



    rlen







    nlen



    :



    rlen



    )




    >=



    reclen



    )





  31. break




    ;




  32. //de


    指向下一个


    ext4_dir_entry_2


    结构



  33. de



    =




    (



    struct


    ext4_dir_entry_2



    *)((



    char



    *)



    de



    +



    rlen



    );




  34. offset



    +=



    rlen



    ;





  35. }




  36. //de


    超过保存父目录的数据物理块映射


    bh





    buf


    尾部,说明空间不够了




  37. if




    ((



    char



    *)



    de



    >



    top



    )





  38. return








    ENOSPC



    ;





  39. //de


    就是为本次的子文件或子目录找到的


    ext4_dir_entry_2


    结构




  40. *



    dest_de



    =



    de



    ;





  41. return



    0



    ;





  42. }


注释写的比较清晰,主要就是在父目录里找一个没有重名并且空闲的ext4_dir_entry_2给新创建的文件。下边重点讲解ext4_add_entry函数里执行的ext4_read_dirblock函数。

1.2.2 ext4_read_dirblock和__ext4_read_dirblock函数源码解析

该函数重点是读取父目录的一个物理块数据并返回这个物理块映射的bh,看下源码:



  1. #define ext4_read_dirblock(inode, block, type) \



  2. __ext4_read_dirblock((inode), (block), (type), __LINE__)



  3. static


    struct


    buffer_head



    *


    __ext4_read_dirblock


    (



    struct


    inode



    *



    inode



    ,



    //inode


    是父目录的



  4. ext4_lblk_t block



    ,




  5. dirblock_type_t type



    ,




  6. unsigned


    int


    line



    )





  7. {





  8. struct


    buffer_head



    *



    bh



    ;




  9. struct


    ext4_dir_entry



    *



    dirent



    ;




  10. int


    err



    =



    0



    ,



    is_dx_block



    =



    0



    ;




  11. //


    根据传入的目录


    inode


    的逻辑地址


    block





    ext4


    文件系统的


    data block


    区分配


    1


    个物理块,并与逻辑地址


    block


    构成映射,最后返回这物理块的


    bh



  12. bh



    =



    ext4_bread



    (




    NULL




    ,



    inode



    ,



    block



    ,



    0



    ,




    &



    err



    );





  13. ………….





  14. return



    bh



    ;





  15. }




  16. struct


    buffer_head



    *


    ext4_bread


    (



    handle_t



    *



    handle



    ,



    struct


    inode



    *



    inode



    ,



    //inode


    是父目录的



  17. ext4_lblk_t block



    ,



    int


    create



    ,



    int



    *



    err



    )





  18. {





  19. struct


    buffer_head



    *



    bh



    ;




  20. //


    根据传入的文件或目录


    inode


    的逻辑地址


    block





    ext4


    文件系统的


    data block


    区分配


    1


    个物理块,并与逻辑地址


    block


    构成映射,最后返回这物理块的


    bh



  21. bh



    =



    ext4_getblk



    (



    handle



    ,



    inode



    ,



    block



    ,



    create



    ,



    err



    );





  22. if




    (!



    bh



    )





  23. return



    bh



    ;





  24. if




    (



    buffer_uptodate



    (



    bh



    ))





  25. return



    bh



    ;




  26. //


    读取


    bh


    映射的物理块的数据到


    bh



  27. ll_rw_block



    (



    READ



    |



    REQ_META



    |



    REQ_PRIO



    ,



    1



    ,




    &



    bh



    );




  28. //


    等待


    bh


    物理块的数据读取到


    bh



  29. wait_on_buffer



    (



    bh



    );





  30. if




    (



    buffer_uptodate



    (



    bh



    ))





  31. return



    bh



    ;




  32. put_bh



    (



    bh



    );





  33. *



    err



    =








    EIO



    ;





  34. return




    NULL




    ;





  35. }


可以发现最终还是调用经典的ext4_getblk函数,这个函数根据传入的逻辑块地址从该文件inode所在块组的Data block区分配指定个数的连续物理块,然后完成传入的逻辑块地址与这些物理块的映射,最终执行ll_rw_block函数读取这个物理块的数据的其映射的bh。我们重点看看ext4_getblk函数

1.2.3 ext4_getblk函数源码解析



  1. struct


    buffer_head



    *


    ext4_getblk


    (



    handle_t



    *



    handle



    ,



    struct


    inode



    *



    inode



    ,




  2. ext4_lblk_t block



    ,



    int


    create



    ,



    int



    *



    errp



    )





  3. {





  4. struct


    ext4_map_blocks map



    ;




  5. struct


    buffer_head



    *



    bh



    ;




  6. int


    fatal



    =



    0



    ,



    err



    ;




  7. map



    .



    m_lblk



    =



    block



    ;




  8. map



    .



    m_len



    =



    1



    ;




  9. //


    根据传入的文件或目录


    inode


    的逻辑地址


    map->m_lblk





    ext4


    文件系统的


    data block


    区分配


    1


    个物理块,并与逻辑地址


    map->m_lblk


    构成映射,并把映射关系保存到


    ext4 extent


    结构



  10. err



    =



    ext4_map_blocks



    (



    handle



    ,



    inode



    ,




    &



    map



    ,




  11. create



    ?



    EXT4_GET_BLOCKS_CREATE



    :



    0



    );





  12. ………..




  13. //map.m_pblk


    就是上边为文件


    inode


    分配的起始物理块,这里是找到它的


    bh



  14. bh



    =



    sb_getblk



    (



    inode



    ->



    i_sb



    ,



    map



    .



    m_pblk



    );





  15. return



    bh



    ;





  16. }


ext4_getblk函数主要作用是:根据传入的文件或目录inode的逻辑地址block从ext4文件系统的data block区分配1个物理块,并与逻辑地址block构成映射,最后返回这物理块的bh。里边主要是调用ext4_map_blocks->ext4_ext_map_blocks函数。

1.2.4 ext4_ext_map_blocks函数源码解析

ext4_ext_map_blocks()函数根据传入的文件或目录inode的逻辑地址map->m_lblk从ext4文件系统的data block区分配map->m_len个物理块,并与逻辑地址map->m_lblk构成映射,并把映射关系保存到ext4 extent结构。里边牵涉到了ext4 extent,ext4 extent会在稍后发出的文章中详细介绍,这里只会简单介绍一下。



  1. int





    ext4_ext_map_blocks


    (



    handle_t



    *



    handle



    ,



    struct


    inode



    *



    inode



    ,




  2. struct


    ext4_map_blocks



    *



    map



    ,



    int


    flags



    )





  3. {





  4. /*





    ext4 extent B+


    树每一层索引节点


    (


    包含根节点


    )


    中找到起始逻辑块地址最接近传入的起始逻辑块地址


    map->m_lblk





    ext4_extent_idx


    结构保存到


    path[ppos]->p_idx.


    然后找到最后一层的叶子节点中最接近传入的起始逻辑块地址


    map->m_lblk





    ext4_extent


    结构,保存到


    path[ppos]->p_ext


    。这个


    ext4_extent


    才包含了逻辑块地址和物理块地址的映射关系。


    */



  5. path



    =



    ext4_ext_find_extent



    (



    inode



    ,



    map



    ->



    m_lblk



    ,




    NULL




    );





  6. …………




  7. /*


    注意,执行到这里说明没有从


    ext4 extent


    找到本次逻辑地址


    map->m_lblk


    映射的物理块,于是就要从


    ext4


    文件系统分配


    map->m_len


    个物理块,然后与逻辑地址


    map->m_lblk


    构成映射。


    ext4_ext_find_goal()


    是先找一个目标物理块号


    ar.goal


    ,然后执行


    ext4_mb_new_blocks():





    ar.goal


    为基准,搜索分配


    map->m_len


    个物理块。最后,再构成与逻辑地址


    map->m_lblk


    的映射


    */




  8. ar



    .



    inode



    =



    inode



    ;




  9. /*


    要为文件


    inode


    分配保存数据的物理块了,该函数是从


    inode


    所属块组先找一个理想的空闲物理块,后续从这个物理块开始搜索,最终查找本次要分配的物理块。简单说,找到


    map->m_lblk


    逻辑块地址映射的目标


    起始物理块地址并返回给


    ar.goal*/



  10. ar



    .



    goal



    =



    ext4_ext_find_goal



    (



    inode



    ,



    path



    ,



    map



    ->



    m_lblk



    );




  11. //ar.logical


    是逻辑块地址



  12. ar



    .



    logical



    =



    map



    ->



    m_lblk



    ;




  13. offset



    =



    EXT4_LBLK_COFF



    (



    sbi



    ,



    map



    ->



    m_lblk



    );



    //offset


    测试时


    0



  14. //


    分配的物理块个数



  15. ar



    .



    len



    =



    EXT4_NUM_B2C



    (



    sbi



    ,



    offset



    +



    allocated



    );





  16. …………




  17. /*


    分配


    map->m_len


    个物理块,这就是


    map->m_lblk


    逻辑块地址映射的


    map->m_len


    个物理块,返回这


    map->m_len


    个物理块的起始物理块号


    newblock


    。测试结果


    newblock





    ar.goal


    有时相等,有时不相等。本次映射的起始逻辑块地址是


    map->m_lblk


    ,映射物理块个数


    map->m_len





    ext4_mb_new_blocks()


    除了要找到


    newblock


    这个起始逻辑块地址,还得保证找到


    newblock


    打头的连续


    map->m_len


    个物理块,必须是连续的,这才是更重要的。


    */



  18. newblock



    =



    ext4_mb_new_blocks



    (



    handle



    ,




    &



    ar



    ,




    &



    err



    );





  19. ………….




  20. map



    ->



    m_flags



    |=



    EXT4_MAP_MAPPED



    ;




  21. //


    本次起始逻辑块地址


    map->m_lblk


    映射的起始物理块号



  22. map



    ->



    m_pblk



    =



    newblock



    ;




  23. //


    本次逻辑块地址完成映射的物理块数,并不能保证


    allocated


    等于传入的


    map->m_len


    ,还有可能小于



  24. map



    ->



    m_len



    =



    allocated



    ;





  25. return



    err



    ?



    err



    :



    allocated



    ;





  26. }


ext4_ext_map_blocks里调用的ext4_ext_find_extent是从ext4 extent缓存中查找传入的逻辑块地址是否已经映射了物理块,没有的话那就需要分配新的物理块了。于是会先调用ext4_ext_find_goal函数查找目标物理块ar.goal,然后以它为基准执行ext4_mb_new_blocks函数最终分配物理块。下边看下源码:

1.2.5 ext4_ext_find_goal 和 ext4_inode_to_goal_block函数源码解析

ext4_ext_find_goal函数里主要调用了ext4_inode_to_goal_block,这里只看后者。



  1. ext4_fsblk_t



    ext4_inode_to_goal_block


    (



    struct


    inode



    *



    inode



    )





  2. {





  3. struct


    ext4_inode_info



    *



    ei



    =



    EXT4_I



    (



    inode



    );




  4. ext4_group_t block_group



    ;




  5. ext4_grpblk_t colour



    ;




  6. //


    实际测试


    flex





    16



  7. int


    flex_size



    =



    ext4_flex_bg_size



    (



    EXT4_SB



    (



    inode



    ->



    i_sb



    ));




  8. ext4_fsblk_t bg_start



    ;




  9. ext4_fsblk_t last_block



    ;




  10. //


    取出


    inode


    所属块组号


    block_group



  11. block_group



    =



    ei



    ->



    i_block_group



    ;





  12. if




    (



    flex_size



    >=



    EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME



    )




    {





  13. //


    这是令


    block_group


    除以


    16


    ,得到


    flex group


    的编号,一个


    flex group





    16


    个块组



  14. block_group



    &=




    ~(



    flex_size







    1



    );





  15. if




    (



    S_ISREG



    (



    inode



    ->



    i_mode



    ))




  16. block_group



    ++;





  17. }




  18. //bg_start:


    得到


    block_group


    这个块组第一个物理块号,就是该块组的起始物理块号



  19. bg_start



    =



    ext4_group_first_block_no



    (



    inode



    ->



    i_sb



    ,



    block_group



    );




  20. last_block



    =



    ext4_blocks_count



    (



    EXT4_SB



    (



    inode



    ->



    i_sb



    )->



    s_es



    )








    1



    ;





  21. if




    (



    test_opt



    (



    inode



    ->



    i_sb



    ,



    DELALLOC



    ))





  22. return



    bg_start



    ;




  23. //


    根据进程


    ID


    计算一个偏移值




  24. if




    (



    bg_start



    +



    EXT4_BLOCKS_PER_GROUP



    (



    inode



    ->



    i_sb



    )




    <=



    last_block



    )




  25. colour



    =




    (



    current



    ->



    pid



    %



    16



    )




    *





  26. (



    EXT4_BLOCKS_PER_GROUP



    (



    inode



    ->



    i_sb



    )




    /



    16



    );





  27. else




  28. colour



    =




    (



    current



    ->



    pid



    %



    16



    )




    *




    ((



    last_block







    bg_start



    )




    /



    16



    );





  29. //bg_start+


    偏移值


    colour


    得到理想的要分配的物理块号




  30. return



    bg_start



    +



    colour



    ;





  31. }


执行到该函数,是要为文件inode分配保存数据的物理块了。该函数是从inode所属块组先找一个理想的空闲物理块,后续从这个物理块开始搜索,最终查找本次要分配的物理块。下边接着看ext4_mb_new_blocks函数源码

1.2.6 ext4_mb_new_blocks函数源码解析

该函数主要是分配ar->len个连续的物理块并返回起始物理块号,直接看源码:



  1. ext4_fsblk_t



    ext4_mb_new_blocks


    (



    handle_t



    *



    handle



    ,




  2. struct


    ext4_allocation_request



    *



    ar



    ,



    int



    *



    errp



    )





  3. {






  4. …………..




  5. //


    计算


    ac->ac_b_ex.fe_logical





    ac->ac_inode





    ac->ac_o_ex.fe_logical





    ac->ac_o_ex.fe_group





    ac->ac_o_ex.fe_start





    ac->ac_o_ex.fe_len


    初值




  6. *



    errp



    =



    ext4_mb_initialize_context



    (



    ac



    ,



    ar



    );





  7. …………..





  8. if




    (!



    ext4_mb_use_preallocated



    (



    ac



    ))




    {




    //


    先使用之前预分配的物理块



  9. ac



    ->



    ac_op



    =



    EXT4_MB_HISTORY_ALLOC



    ;




  10. /*


    主要是依照


    ar->goal


    这个


    分配物理块时的基准物理块号,计算出本次要分配的物理块所在的


    ac->ac_f_ex.fe_group


    和计算它所在


    ac->ac_f_ex.fe_group


    块组的物理块号赋于


    ac->ac_f_ex.fe_start


    。注意,


    ac->ac_f_ex.fe_start


    就是本次在


    ac->ac_f_ex.fe_group


    块组找到的起始空闲物理块号,并且是在这个基础上连续分配了


    ac->ac_g_ex.fe_len


    个空闲物理块号


    */



  11. ext4_mb_normalize_request



    (



    ac



    ,



    ar



    );



    //


    分配失败则在这里正常分配物理块



  12. repeat



    :





  13. *



    errp



    =



    ext4_mb_regular_allocator



    (



    ac



    );





  14. ……………





  15. }





  16. if




    (



    likely



    (



    ac



    ->



    ac_status



    ==



    AC_STATUS_FOUND



    ))




    {





  17. /*


    执行到该函数,


    ac->ac_f_ex.fe_group


    是本次分配的物理块所在块组号,


    ac->ac_f_ex.fe_start


    是在


    ac->ac_f_ex.fe_group


    块组分配


    ac->ac_g_ex.fe_len


    个空闲物理块的起始物理块号。这里是令


    ext4


    总的空闲


    block


    个数和块组空闲的物理块个数减少


    ac->ac_b_ex.fe_len


    个,因为从


    ac->ac_b_ex.fe_group


    块组分配了


    ac->ac_b_ex.fe_len


    个物理块,则标记该块组的


    data block bitmap


    的对应


    bit





    1


    ,表示这些物理块已分配


    */




  18. *



    errp



    =



    ext4_mb_mark_diskspace_used



    (



    ac



    ,



    handle



    ,



    reserv_clstrs



    );





  19. …………..





  20. }




    else




    {






  21. …………..





  22. }




  23. //


    返回起始物理块




  24. return



    block



    ;





  25. }


大部分源码注释写的比较清楚,这里只再介绍下ext4_mb_mark_diskspace_used函数源码:



  1. static


    noinline_for_stack


    int




  2. ext4_mb_mark_diskspace_used


    (



    struct


    ext4_allocation_context



    *



    ac



    ,




  3. handle_t



    *



    handle



    ,



    unsigned


    int


    reserv_clstrs



    )





  4. {





  5. //


    读取


    ac->ac_b_ex.fe_group


    块组的


    data block bitmap


    ,就是


    ext4


    文件系统块组的


    data block


    区的


    bitmap



  6. bitmap_bh



    =



    ext4_read_block_bitmap



    (



    sb



    ,



    ac



    ->



    ac_b_ex



    .



    fe_group



    );





  7. ………….




  8. //


    得到


    ac->ac_b_ex.fe_group


    的块组描述符赋于


    gdp



  9. gdp



    =



    ext4_get_group_desc



    (



    sb



    ,



    ac



    ->



    ac_b_ex



    .



    fe_group



    ,




    &



    gdp_bh



    );





  10. ………….




  11. /*


    本次是在


    ac->ac_f_ex.fe_group


    块组分配


    ac->ac_g_ex.fe_len


    个空闲物理块的起始物理块号,


    ac->ac_f_ex.fe_start


    是个起始物理块号。因此这是在


    ext4


    文件系统块组的


    data block


    区的


    bitmap


    对应位置的


    bit


    位置


    1


    ,表示这些


    data block


    区的物理块被分配了


    */



  12. ext4_set_bits



    (



    bitmap_bh



    ->



    b_data



    ,



    ac



    ->



    ac_b_ex



    .



    fe_start



    ,



    ac



    ->



    ac_b_ex



    .



    fe_len



    );





  13. ………….




  14. len



    =



    ext4_free_group_clusters



    (



    sb



    ,



    gdp



    )








    ac



    ->



    ac_b_ex



    .



    fe_len



    ;




  15. //


    块组空闲的物理块个数减少


    ac->ac_b_ex.fe_len



  16. ext4_free_group_clusters_set



    (



    sb



    ,



    gdp



    ,



    len



    );





  17. ………




  18. //ext4


    总的空闲物理块个数减少


    ac->ac_b_ex.fe_len






  19. percpu_counter_sub



    (&



    sbi



    ->



    s_freeclusters_counter



    ,



    ac



    ->



    ac_b_ex



    .



    fe_len



    );





  20. ………….





  21. }


简单总结下:执行到该函数,ac->ac_f_ex.fe_group是本次分配的物理块所在块组号,ac->ac_f_ex.fe_start是在ac->ac_f_ex.fe_group块组分配ac->ac_g_ex.fe_len个空闲物理块的起始物理块号。这里是令ext4总的空闲block个数和块组空闲的物理块个数减少ac->ac_b_ex.fe_len个。并且因为从ac->ac_b_ex.fe_group块组分配了ac->ac_b_ex.fe_len个物理块,则标记该块组的data block bitmap的对应bit位1,表示这些物理块已分配。

最后回到ext4_add_entry函数,在调用ext4_add_entry把新创建的文件inode添加到父目录后,然后执行ext4_mark_inode_dirty标记这个inode脏,这里边还有一个重点是从该inode所在块组的inode  table分配inode结构,用来保存这个ext4 inode结构,看下源码。

1.2.7 ext4_mark_inode_dirty函数源码解析



  1. int





    ext4_mark_inode_dirty


    (



    handle_t



    *



    handle



    ,



    struct


    inode



    *



    inode



    )





  2. {





  3. /*


    建立


    bh





    jh


    的联系,二者一一对应,把


    jh


    添加到


    handle->h_transaction


    指向的


    transaction





    BJ_Reserved


    链表。根据


    inode


    编号得到它在


    所属的块组的


    inode table


    的物理块号,这个物理块保存的是该块组的


    inode table


    ,这个


    inode table


    保存了这个


    inode


    结构最后得到


    inode


    元数据所在物理块的


    bh,


    存于


    iloc->bh*/



  4. err



    =



    ext4_reserve_inode_write



    (



    handle



    ,



    inode



    ,




    &



    iloc



    );





  5. }




  6. int





    ext4_reserve_inode_write


    (



    handle_t



    *



    handle



    ,



    struct


    inode



    *



    inode



    ,




  7. struct


    ext4_iloc



    *



    iloc



    )





  8. {





  9. int


    err



    ;




  10. /*


    根据


    inode


    编号得到它在


    所属的块组的


    inode table


    的物理块号,这个物理块保存的是该块组的


    inode table


    ,这个


    inode table


    保存了这个


    inode


    结构最后得到


    inode


    元数据所在物理块的


    bh,


    存于


    iloc->bh*/



  11. err



    =



    ext4_get_inode_loc



    (



    inode



    ,



    iloc



    );





  12. if




    (!



    err



    )




    {





  13. BUFFER_TRACE



    (



    iloc



    ->



    bh



    ,



    “get_write_access”



    );




  14. //


    建立


    bh





    jh


    的联系,二者一一对应,把


    jh


    添加到


    handle->h_transaction


    指向的


    transaction





    BJ_Reserved


    链表



  15. err



    =



    ext4_journal_get_write_access



    (



    handle



    ,



    iloc



    ->



    bh



    );





  16. if




    (



    err



    )




    {





  17. brelse



    (



    iloc



    ->



    bh



    );




  18. iloc



    ->



    bh



    =




    NULL




    ;





  19. }





  20. }




  21. ext4_std_error



    (



    inode



    ->



    i_sb



    ,



    err



    );





  22. return



    err



    ;





  23. }




  24. int





    ext4_get_inode_loc


    (



    struct


    inode



    *



    inode



    ,



    struct


    ext4_iloc



    *



    iloc



    )





  25. {






  26. return



    __ext4_get_inode_loc



    (



    inode



    ,



    iloc



    ,





  27. !



    ext4_test_inode_state



    (



    inode



    ,



    EXT4_STATE_XATTR



    ));





  28. }


这里边还有一些ext4 jbd2操作,这里先别理会。可以发现,最终调用的是__ext4_get_inode_loc函数,看下它的源码:



  1. static


    int





    __ext4_get_inode_loc


    (



    struct


    inode



    *



    inode



    ,




  2. struct


    ext4_iloc



    *



    iloc



    ,



    int


    in_mem



    )





  3. {





  4. struct


    ext4_group_desc



    *



    gdp



    ;




  5. struct


    buffer_head



    *



    bh



    ;




  6. struct


    super_block



    *



    sb



    =



    inode



    ->



    i_sb



    ;




  7. ext4_fsblk_t        block



    ;




  8. int


    inodes_per_block



    ,



    inode_offset



    ;




  9. iloc



    ->



    bh



    =




    NULL




    ;





  10. if




    (!



    ext4_valid_inum



    (



    sb



    ,



    inode



    ->



    i_ino



    ))





  11. return








    EIO



    ;




  12. //inode


    的编号号除以每块组


    inode


    个数,计算出该


    inode


    在第几个块组,每个块组容纳的最大


    inode


    个数是一致的



  13. iloc



    ->



    block_group



    =




    (



    inode



    ->



    i_ino







    1



    )




    /



    EXT4_INODES_PER_GROUP



    (



    sb



    );




  14. //


    根据块组号得到该


    inode


    所属的块组描述符



  15. gdp



    =



    ext4_get_group_desc



    (



    sb



    ,



    iloc



    ->



    block_group



    ,




    NULL




    );





  16. if




    (!



    gdp



    )





  17. return








    EIO



    ;




  18. //


    每个物理块容纳的


    inode


    个数



  19. inodes_per_block



    =



    EXT4_SB



    (



    sb



    )->



    s_inodes_per_block



    ;




  20. //inode_offset


    是该


    inode


    编号在块组内的偏移



  21. inode_offset



    =




    ((



    inode



    ->



    i_ino







    1



    )




    %




  22. EXT4_INODES_PER_GROUP



    (



    sb



    ));




  23. /*ext4_inode_table(sb, gdp)


    是得到


    inode


    所属块组


    inode table


    所在的起始物理块号,


    (inode_offset / inodes_per_block)


    是根据


    inode


    号在块组内的偏移计算该


    inode





    inode table


    的偏移,二者相加就是该


    inode


    在该块组的


    inode table


    的物理块号。


    ext4_inode_table(sb, gdp)


    是块组的


    inode table


    所在的起始物理块号,这里计算的是


    inode table


    的物理块,里边保存了该


    inode


    结构


    */



  24. block



    =



    ext4_inode_table



    (



    sb



    ,



    gdp



    )




    +




    (



    inode_offset



    /



    inodes_per_block



    );




  25. //





    inode


    所在


    inode table


    的那个物理块里的偏移



  26. iloc



    ->



    offset



    =




    (



    inode_offset



    %



    inodes_per_block



    )




    *



    EXT4_INODE_SIZE



    (



    sb



    );




  27. //


    这应该是得到


    inode


    元数据所在的物理块对应的


    bh


    吧,注意,这不是


    inode


    对应的文件的数据所在的物理块的


    bh



  28. bh



    =



    sb_getblk



    (



    sb



    ,



    block



    );





  29. ………………




  30. //inode


    元数据所在的物理块的


    bh


    赋予


    iloc->bh



  31. iloc



    ->



    bh



    =



    bh



    ;





  32. return



    0



    ;





  33. }


简单说,该函数是根据inode编号得到它在所属的块组的inode table的物理块(这个物理块映射的bh是iloc->bh)和在该物理块里的偏移(iloc->offset)。这个物理块保存的是该块组的inode table,inode table保存了ext4 inode结构。可能比较抽象,我们举个例子就好点了。

举个例子,inode在块组5,块组5的inode table所在物理块号是1000,inode table占了6个物理块。一个ext4_inode大小256B,一个物理块容纳16个ext4_inode,一个块组容纳16*6=96个ext4_inode。假设inode编号是96*6+18,保存在inode table区的第2个物理块。则ext4_inode_table(sb, gdp) = 1000,inode_offset = (96*6+18 -1)%96=17,block=ext4_inode_table(sb, gdp)+(inode_offset/inodes_per_block)=1000 + 17/16=1001,就是说该inode保存在inode table区的第2个物理块。iloc->offset = (17 % 16)*256B,这就是说该inode在inode table 区的第2个物理块的第2个ext4_inode位置,乘以256B就是指向具体字节位置处。

我们再来看下怎么使用iloc->bh 和 iloc->offset得到该inode对应的ext4 inode结构的:



  1. static


    inline


    struct


    ext4_inode



    *


    ext4_raw_inode


    (



    struct


    ext4_iloc



    *



    iloc



    )





  2. {






  3. return




    (



    struct


    ext4_inode



    *)




    (



    iloc



    ->



    bh



    ->



    b_data



    +



    iloc



    ->



    offset



    );





  4. }


iloc->bh是文件ext4 inode所在物理块对应的bh,iloc->bh->b_data是该物理块的数据保存到内存的首地址,iloc->offset是文件的ext4 inode结构在这个物理块的偏移。

2:ext4_mkdir目录创建函数源码解析

先把源码贴下:



  1. static


    int





    ext4_mkdir


    (



    struct


    inode



    *



    dir



    ,



    struct


    dentry



    *



    dentry



    ,



    umode_t mode



    )





  2. {





  3. handle_t



    *



    handle



    ;




  4. struct


    inode



    *



    inode



    ;




  5. int


    err



    ,



    credits



    ,



    retries



    =



    0



    ;





  6. …………..




  7. retry



    :




  8. //


    为当前的目录分配一个


    inode



  9. inode



    =



    ext4_new_inode_start_handle



    (



    dir



    ,



    S_IFDIR



    |



    mode



    ,





  10. &



    dentry



    ->



    d_name



    ,




  11. 0



    ,




    NULL




    ,



    EXT4_HT_DIR



    ,



    credits



    );




  12. handle



    =



    ext4_journal_current_handle



    ();




  13. err



    =



    PTR_ERR



    (



    inode



    );





  14. if




    (



    IS_ERR



    (



    inode



    ))





  15. goto



    out_stop



    ;




  16. //inode->i_op





    inode->i_fop


    赋值



  17. inode



    ->



    i_op



    =




    &



    ext4_dir_inode_operations



    ;




  18. inode



    ->



    i_fop



    =




    &



    ext4_dir_operations



    ;




  19. //


    初始化目录


    inode



  20. err



    =



    ext4_init_new_dir



    (



    handle



    ,



    dir



    ,



    inode



    );





  21. if




    (



    err



    )





  22. goto



    out_clear_inode



    ;




  23. err



    =



    ext4_mark_inode_dirty



    (



    handle



    ,



    inode



    );





  24. if




    (!



    err



    )




  25. //





    dentry





    inode


    对应的文件或目录添加到它父目录



  26. err



    =



    ext4_add_entry



    (



    handle



    ,



    dentry



    ,



    inode



    );





  27. …………..




  28. ext4_inc_count



    (



    handle



    ,



    dir



    );




  29. ext4_update_dx_flag



    (



    dir



    );




  30. err



    =



    ext4_mark_inode_dirty



    (



    handle



    ,



    dir



    );





  31. …………..





  32. return



    err



    ;





  33. }


可以发现它跟文件创建函数ext4_create流程很接近,也是先分配一个inode,然后把该inode添加到父目录,关键函数前边都讲解过,这里不再赘述。



版权声明:本文为hu1610552336原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。