缓存同步操作--sys_sync系统调用

本文详细剖析了Linux内核中的sys_sync系统调用,该调用负责将缓存中的数据同步到块设备。文章深入介绍了sys_sync背后的实现机制,包括do_sync、sync_inodes、sync_inodes_sb、__writeback_single_inode等关键函数的工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

sys_sync系统调用被用户空间函数调用,用来将缓存中的数据写入块设备,sys_sync系统调用将buffer、inode和super在缓存中的数据写入设备。sys_sync函数在fs/buffer.c中,现分析如下:

  1. asmlinkage long sys_sync(void)
  2. {
  3. do_sync(1);
  4. return 0;
  5. }
  函数do_sync各种数据,开始唤醒pdflush,因为它并行地把所有队列写回设备。函数分析如下:
  1. static void do_sync(unsigned long wait)
  2. {
  3. wakeup_bdflush(0);
  4. sync_inodes(0); /* All mappings, inodes and their blockdevs */
  5. DQUOT_SYNC(NULL);
  6.   // 最终调用sb->s_op->write_super(sb)即具体文件系统的函数来实现写超级块
  7. sync_supers(); /* Write the superblocks */
  8. sync_filesystems(0); /* Start syncing the filesystems */
  9. sync_filesystems(wait); /* Waitingly sync the filesystems */
  10. sync_inodes(wait); /* Mappings, inodes and blockdevs, again. */
  11. if (!wait)
  12. printk("Emergency Sync complete\n");
  13. if (unlikely(laptop_mode))
  14. laptop_sync_completion();
  15. }
多个节点同步回写操作函数sync_inodes
Linux kernel virtual filesystem 09.gif
函数sync_inodes调用层次图

函数sync_inodes遍历每个超级块的脏节点链表,把节点写回到块设备,并等待回写操作的完成,写完成后把节点放回到正常的链表中。函数sync_inodes是给系统调用sys_sync用的,函数fsync_dev使用同样的算法。sync函数的精细地方是块设备"超级块"最后被处理。这是因为函数write_inode是典型的文件系统操作函数,它不执行I/O,而是在块设备映射的地址空间把buffer标识为脏。我们所想做的是先执行所有的标识脏的操作,接着,在一次扫描中通过块设备映射地址空间写回所有的节点块。这样附加的(在某种程度上说是冗余的)sync_blockdev函数在这儿调用来确认这个操作真正发生。因为如果我们对明显的脏节点调用函数sync_inodes_sb(wait=1),回写操作将在文件系统的函数write_inode里有时进入堵塞,这种情况下将运行极慢。

函数sync_inodes的调用层次图如上图,函数sync_inodes分析如下(在fs/fs-writeback.c中):
单个节点同步回写操作函数sync_inodes_sb
  1. void sync_inodes(int wait)
  2. {
  3. struct super_block *sb;
  4. set_sb_syncing(0);//设置每个超级块sb->s_syncing = 0
  5. while ((sb = get_super_to_sync()) != NULL) {//得到脏的超级块结构
  6. sync_inodes_sb(sb, 0);
  7.     //写与块设备对应的所有脏数据,并等待写操作完成
  8. sync_blockdev(sb->s_bdev); 
  9. drop_super(sb);//使用计数减1,即sb->s_count-1
  10. }
  11. if (wait) {
  12. set_sb_syncing(0);
  13. while ((sb = get_super_to_sync()) != NULL) {
  14. sync_inodes_sb(sb, 1);
  15. sync_blockdev(sb->s_bdev);
  16. drop_super(sb);
  17. }
  18. }
  19. }

函数sync_inodes_sb执行回写操作并等待在文件系统的脏节点上。调用者将有两个方式调用这个函数,一个是写,一个是等待写。对于等待写方式来说,WB_SYNC_HOLD标识用来把写的节点停在sb->s_dirty上等待。为了防止函数sys_sync的死锁,将写的页数给了一个限制。限制被加到潜在的脏节点里,因为每个节点写操作能弄脏块设备的页缓存。

函数sync_inodes_sb分析如下(在fs/fs-writeback.c中):
  1. void sync_inodes_sb(struct super_block *sb, int wait)
  2. {
  3. struct writeback_control wbc = {
  4. .sync_mode = wait ? WB_SYNC_ALL : WB_SYNC_HOLD,
  5. };
  6. unsigned long nr_dirty = read_page_state(nr_dirty);//得到脏页数
  7. unsigned long nr_unstable = read_page_state(nr_unstable);//不稳定的页数
  8. wbc.nr_to_write = nr_dirty + nr_unstable +
  9. (inodes_stat.nr_inodes - inodes_stat.nr_unused) +
  10. nr_dirty + nr_unstable;
  11. wbc.nr_to_write += wbc.nr_to_write / 2; /* Bit more for luck */
  12. spin_lock(&inode_lock);
  13. sync_sb_inodes(sb, &wbc);
  14. spin_unlock(&inode_lock);
  15. }
函数sync_sb_inodes写回一个超级块的脏节点链表到块设备。根据sync_mode方式的不同,一个回写等待可在没有节点、所有节点或最后的节点上被执行。如果older_than_this非空,那么仅写回比older_than_this早些时候被弄脏的节点。

如果是一个pdlfush线程,就对整个链表使用pdflush的防冲突措施。对于函数sys_sync来说,WB_SYNC_HOLD标识是一个hack,它重绑定inode到sb->s_dirty,以便inode能被在__writeback_single_inode()上的线程定位。

函数sync_sb_inodes应在inode_lock情况下被调用。如果bdi结构非空,表示我们正被请求回写一个特定的队列,这个函数假定块设备超级块的节点被各种队列支持。因而所有的节点被搜索。对其它超级块来说,假定所有的节点被同一队列支持。

被写的节点被停在sb->s_io上,当他们被选择写时,它们被移回到sb->s_dirty上。这样,在写者控制的途中就不会有丢失。并且得到在多个调节线程之间的相当好的平衡:我们不想它们所有的堆积在__wait_on_inode上。

函数sync_sb_inodes分析如下(在fs/fs-writeback.c中):
  1. static void sync_sb_inodes(struct super_block *sb,
  2. struct writeback_control *wbc)
  3. {
  4. const unsigned long start = jiffies; /* livelock avoidance */
  5. if (!wbc->for_kupdate || list_empty(&sb->s_io))
  6.     //将s_dirty链表加到s_io链表中,并初始化了s_dirty链表
  7. list_splice_init(&sb->s_dirty, &sb->s_io); 
  8. while (!list_empty(&sb->s_io)) {
  9. struct inode *inode = list_entry(sb->s_io.prev,
  10. struct inode, i_list);
  11. struct address_space *mapping = inode->i_mapping;
  12. struct backing_dev_info *bdi = mapping->backing_dev_info;
  13. long pages_skipped;
  14.     //内存支持的文件系统,不能用writepage刷新页
  15. if (bdi->memory_backed) { 
  16.       //将节点inode从i_list中移到s_dirty链表中
  17. list_move(&inode->i_list, &sb->s_dirty);
  18. if (sb == blockdev_superblock) {
  19. //内存支持的块设备脏:ramdisk驱动程序做这,仅跳过这个节点
  20. continue;
  21. }
  22.       //内存支持文件系统的节点脏,而不是块设备支持的文件系统的节点脏,
  23.       //跳过整个超级块。
  24. break;
  25. }
  26.     //没用阻塞在请求队列上,且块设备写操作过多,跳过壅塞的块设备
  27. if (wbc->nonblocking && bdi_write_congested(bdi)) {
  28. wbc->encountered_congestion = 1;
  29. if (sb != blockdev_superblock)//不是块设备的超级块
  30. break; //* Skip a congested fs */
  31. //将inode从i_list队列移到s_dirty队列上
  32. list_move(&inode->i_list, &sb->s_dirty);
  33. continue;
  34. }
  35. if (wbc->bdi && bdi != wbc->bdi) {//块设备有错误的队列
  36. if (sb != blockdev_superblock) //不是块设备的超级块
  37. break; /* fs has the wrong queue */
  38. list_move(&inode->i_list, &sb->s_dirty);
  39. continue;
  40. }
  41. //在sync_sb_inodes函数被调用后,才变成脏节点?
  42. if (time_after(inode->dirtied_when, start))
  43. break;
  44. //节点最近被弄脏的?
  45. if (wbc->older_than_this && time_after(inode->dirtied_when,
  46. *wbc->older_than_this))
  47. break;
  48. //另外的 pdflush线程已在刷新这个队列或不能获得设备回写操作
  49. if (current_is_pdflush() && !writeback_acquire(bdi))
  50. break;
  51. BUG_ON(inode->i_state & I_FREEING);
  52. __iget(inode);
  53. pages_skipped = wbc->pages_skipped;//得到不被写的页数
  54. __writeback_single_inode(inode, wbc);//写回一个节点到设备
  55. // WB_SYNC_HOLD表示为sys_sys()正持有在sb_dirty上的节点  
  56. if (wbc->sync_mode == WB_SYNC_HOLD) {
  57. inode->dirtied_when = jiffies;
  58. list_move(&inode->i_list, &sb->s_dirty);
  59. }
  60. if (current_is_pdflush()) //当前正在pdflush线程
  61. writeback_release(bdi);//释放了bdi上BDI_pdflush状态位
  62. if (wbc->pages_skipped != pages_skipped) {
  63. //因为buffer锁住,writeback不进行处理,现在跳过这个节点
  64. list_move(&inode->i_list, &sb->s_dirty);
  65. }
  66. spin_unlock(&inode_lock);
  67. iput(inode);
  68. spin_lock(&inode_lock);
  69. if (wbc->nr_to_write <= 0)
  70. break;
  71. }
  72. return; //留下任何没写的节点在s_io链表上
  73. }
函数__writeback_single_inode写回一个节点的脏页到块设备,它在inode_lock下被调用。函数__writeback_single_inode分析如下(在fs/fs-writeback.c中):
  1. static int __writeback_single_inode(struct inode *inode,
  2.            struct writeback_control *wbc)
  3. {
  4.   //如果不是同步所有的且节点是锁住的,将节点移到s_dirty链表中
  5. if ((wbc->sync_mode != WB_SYNC_ALL) && (inode->i_state & I_LOCK)) {
  6. list_move(&inode->i_list, &inode->i_sb->s_dirty);
  7. return 0;
  8. }
  9. //它是一个数据一致性同步,必须等待
  10. while (inode->i_state & I_LOCK) {
  11. __iget(inode);
  12. spin_unlock(&inode_lock);
  13. __wait_on_inode(inode);
  14. iput(inode);
  15. spin_lock(&inode_lock);
  16. }
  17. return __sync_single_inode(inode, wbc);
  18. }

函数__sync_single_inode写单个节点的脏页和节点数据到硬盘上,如果wait被设备,则在这个节点上等待。整个回写的设计很复杂且脆弱,我们想避免当其它节点再次变脏时特殊节点存在饥饿的现象,防止死锁等。 函数__sync_single_inode分析如下(在fs/fs-writeback.c中):
  1. static int __sync_single_inode(struct inode *inode,
  2. struct writeback_control *wbc)
  3. {
  4. unsigned dirty;
  5. struct address_space *mapping = inode->i_mapping;
  6. struct super_block *sb = inode->i_sb;
  7. int wait = wbc->sync_mode == WB_SYNC_ALL;
  8. int ret;
  9. BUG_ON(inode->i_state & I_LOCK);
  10. //设置I_LOCK, 重设置I_DIRTY标识
  11. dirty = inode->i_state & I_DIRTY;
  12. inode->i_state |= I_LOCK;
  13. inode->i_state &= ~I_DIRTY;
  14. spin_unlock(&inode_lock);
  15. ret = do_writepages(mapping, wbc);//将节点对应的地址空间的数据写回设备
  16. //如果仅I_DIRTY_PAGES 被设置就不写节点到块设备 
  17. if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
  18. int err = write_inode(inode, wait);//写回节点到设备
  19. if (ret == 0)
  20. ret = err;
  21. }
  22.   //遍历所给地址空间的回写页的链表,等待他们所有的脏页写完
  23. if (wait) {
  24. int err = filemap_fdatawait(mapping);
  25. if (ret == 0)
  26. ret = err;
  27. }
  28. spin_lock(&inode_lock);
  29. inode->i_state &= ~I_LOCK;
  30. if (!(inode->i_state & I_FREEING)) {
  31. //节点非脏且mapping中的页有PAGECACHE_TAG_DIRTY标识
  32. if (!(inode->i_state & I_DIRTY) &&
  33. mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
  34. //我们不写回所有的页,nfs_writepages()有时没做任何事却运行。
  35. //重新把节点设置为脏,节点还在sb->s_io链表中
  36.       if (wbc->for_kupdate) {
  37. //对kupdate函数,我们留节点在sb_dirty的头部,
  38.         //以便在队列变成不壅塞时它得到更多写机会,
  39. inode->i_state |= I_DIRTY_PAGES;
  40. list_move_tail(&inode->i_list, &sb->s_dirty);
  41. } else {
  42. //完全重把节点设置成脏,以便在这个超级块上其它节点得到回写机会。
  43.      //否则,一个文件的较重的写操作将会把其它所有的文件的回写操作挂起
  44. inode->i_state |= I_DIRTY_PAGES;
  45. inode->dirtied_when = jiffies;
  46. list_move(&inode->i_list, &sb->s_dirty);//移到s_dirty链表上
  47. }
  48. } else if (inode->i_state & I_DIRTY) {
  49. //当正回写这些页时,有人重把节点设置成脏 
  50. list_move(&inode->i_list, &sb->s_dirty);
  51. } else if (atomic_read(&inode->i_count)) {
  52. //节点中干净的,在使用的 
  53. list_move(&inode->i_list, &inode_in_use);
  54. } else {
  55. //节点中干净的,没使用的
  56. list_move(&inode->i_list, &inode_unused);
  57. inodes_stat.nr_unused++;
  58. }
  59. }
  60. wake_up_inode(inode);//唤醒节点上的等待队列
  61. return ret;
  62. }

节点地址空间数据回写操作函数do_writepages
Linux kernel virtual filesystem 03.gif
函数do_writepages调用层次图函数do_writepages写回地址空间mapping中的数据到设备,函数do_writepages调用层次图如上图,函数分析如下(在mm/page-writeback.c中):
  1. int do_writepages(struct address_space *mapping,
  2.              struct writeback_control *wbc)
  3. {
  4. if (wbc->nr_to_write <= 0)
  5. return 0;
  6. if (mapping->a_ops->writepages)//使用指定的函数
  7. return mapping->a_ops->writepages(mapping, wbc);
  8. return generic_writepages(mapping, wbc);//使用通用函数
  9. }

函数mpage_writepages遍历所给地址空间的脏页链表并把它们所有的写回块设备。其参数为:

  mapping: 要写的地址空间结构

  wbc: 回写所有页的信息结构,从wbc->nr_to_write 减去已写的页数

  get_block: 文件系统的块映射函数指针,如果这是NULL,那么使用 a_ops->writepage,否则使用direct-to-BIO。

函数mpage_writepages是一个库函数,它利用地址空间结构里操作函数writepages()。如果一页已在I/O,使用generic_writepages()跳过它,即使它是脏的。对于内存清理的回写操作来说这是理想的行为,但它对调用如fsync()来进行数据一致性同步来说是不正确的。fsync()和msync()需要保证脏数据得到针对它们已开始的新I/O,所有的这些脏数据在调用时必须已准备好。如果wbc->sync_mode是WB_SYNC_ALL,那么函数mpage_writepages被调用来进行数据一致性回写,并且它必须等待正存在的I/O完成。

函数mpage_writepages分析如下(在fs/mpage.c中):
  1. int mpage_writepages(struct address_space *mapping,
  2. struct writeback_control *wbc, get_block_t get_block)
  3. {
  4. struct backing_dev_info *bdi = mapping->backing_dev_info;
  5. struct bio *bio = NULL;
  6. sector_t last_block_in_bio = 0;
  7. int ret = 0;
  8. int done = 0;
  9. int (*writepage)(struct page *page, struct writeback_control *wbc);
  10. //在许多地方,把多个页放在一起成一束来进行操作是有效的,
  11. //pagevec是这样的多页的容器结构。
  12. struct pagevec pvec; 
  13. int nr_pages;
  14. pgoff_t index;
  15. pgoff_t end = -1; /* Inclusive */
  16. int scanned = 0;
  17. int is_range = 0;
  18. //块设备I/O是壅塞的
  19.   if (wbc->nonblocking && bdi_write_congested(bdi)) {
  20. wbc->encountered_congestion = 1;
  21. return 0;
  22. }
  23. writepage = NULL;
  24. if (get_block == NULL)
  25. writepage = mapping->a_ops->writepage;
  26. //初始化pagevec结构pvec
  27. pagevec_init(&pvec, 0);
  28.   //没有正在运行的回写同步,不需要等待
  29. if (wbc->sync_mode == WB_SYNC_NONE) {
  30. index = mapping->writeback_index; //从上次的偏移页序号开始 
  31. } else {
  32. index = 0; //需要整个文件的扫描,从0页序号开始
  33. scanned = 1;
  34. }
  35. if (wbc->start || wbc->end) {//计算开始与结束的页序号
  36. index = wbc->start >> PAGE_CACHE_SHIFT;
  37. end = wbc->end >> PAGE_CACHE_SHIFT;
  38. is_range = 1;
  39. scanned = 1;
  40. }
  41. retry:
  42. while (!done && (index <= end) &&
  43. //计算脏页数nr_pages,把脏页放在pvec中
  44. (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
  45. PAGECACHE_TAG_DIRTY,
  46. min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1))) {
  47. unsigned i;
  48. scanned = 1;
  49. for (i = 0; i < nr_pages; i++) {
  50. struct page *page = pvec.pages[i];//得到脏页
  51. //在这个地方,我们不持有mapping->tree_lock锁和页本身的锁:这页可能被剪除或无效(改变page->mapping到NULL),或者甚至从交换空间欺骗地回到tmpfs文件映射中。
  52. lock_page(page);
  53. if (unlikely(page->mapping != mapping)) {//页的地址空间不对
  54. unlock_page(page);
  55. continue;
  56. }
  57. if (unlikely(is_range) && page->index > end) {//所有的页已完成
  58. done = 1;
  59. unlock_page(page);
  60. continue;
  61. }
  62. if (wbc->sync_mode != WB_SYNC_NONE)//正在同步操作
  63. wait_on_page_writeback(page);
  64.     //页在回写或不是以前的脏页
  65. if (PageWriteback(page) ||
  66. !clear_page_dirty_for_io(page)) {
  67. unlock_page(page);
  68. continue;
  69. }
  70. if (writepage) {
  71. ret = (*writepage)(page, wbc);
  72. if (ret) {//返回有错误,设置到mapping->flags
  73. if (ret == -ENOSPC)
  74. set_bit(AS_ENOSPC,
  75. &mapping->flags);
  76. else
  77. set_bit(AS_EIO,
  78. &mapping->flags);
  79. }
  80. } else {
  81. bio = mpage_writepage(bio, page, get_block,
  82. &last_block_in_bio, &ret, wbc);
  83. }
  84. if (ret || (--(wbc->nr_to_write) <= 0))
  85. done = 1;
  86. if (wbc->nonblocking && bdi_write_congested(bdi)) {
  87. wbc->encountered_congestion = 1;
  88. done = 1;
  89. }
  90. }
  91. pagevec_release(&pvec);
  92. cond_resched();//进行调度
  93. }
  94. if (!scanned && !done) {
  95. //命中最后一页,有更多的工作要做:折回到文件的开始 
  96. scanned = 1;
  97. index = 0;
  98. goto retry;
  99. }
  100. if (!is_range)
  101. mapping->writeback_index = index;
  102. if (bio)
  103. mpage_bio_submit(WRITE, bio);
  104. return ret;
  105. }

如果页上有buffer那么这些buffer将被用来获得硬盘映射。我们仅支持整个已被映射且脏的页,还有特殊的情况:在文件结尾没被映射的页。如果页没有buffers那么页在这儿被映射。如果所有的块被发现是连续的,那么页能直接进入BIO,否则,回到mapping的writepage()函数。 函数mpage_writepage设计的目标是能估计还有多少页需要被写,这样它能智能地分配合适大小的BIO。但现在仅支持分配全部尺寸(16页)的BIO。
  1. static struct bio *mpage_writepage(struct bio *bio, struct page *page,
  2.            get_block_t get_block, sector_t *last_block_in_bio,
  3.             int *ret, struct writeback_control *wbc)
  4. {
  5. struct address_space *mapping = page->mapping;
  6. struct inode *inode = page->mapping->host;
  7. const unsigned blkbits = inode->i_blkbits;
  8. unsigned long end_index;
  9.   //每页的块数
  10. const unsigned blocks_per_page = PAGE_CACHE_SIZE >> blkbits;
  11. sector_t last_block;
  12. sector_t block_in_file;
  13. sector_t blocks[MAX_BUF_PER_PAGE];
  14. unsigned page_block;
  15. unsigned first_unmapped = blocks_per_page;
  16. struct block_device *bdev = NULL;
  17. int boundary = 0;
  18. sector_t boundary_block = 0;
  19. struct block_device *boundary_bdev = NULL;
  20. int length;
  21. struct buffer_head map_bh;
  22. loff_t i_size = i_size_read(inode);
  23. if (page_has_buffers(page)) {//如果页上有buffer
  24. struct buffer_head *head = page_buffers(page);
  25. struct buffer_head *bh = head;
  26. //如果所有的buffer是映射且是脏的 
  27. page_block = 0;
  28. do {
  29. BUG_ON(buffer_locked(bh));
  30. if (!buffer_mapped(bh)) {//buffer是非映射的
  31. //非映射的脏buffer是由函数__set_page_dirty_buffers 映射数据创建 
  32. if (buffer_dirty(bh))//脏buffer
  33. goto confused;
  34. if (first_unmapped == blocks_per_page)
  35. first_unmapped = page_block;
  36. continue;
  37. }
  38. if (first_unmapped != blocks_per_page)
  39. goto confused; /* hole -> non-hole */
  40.       //buffer非脏或非更新的
  41. if (!buffer_dirty(bh) || !buffer_uptodate(bh))
  42. goto confused;
  43. if (page_block) {
  44. if (bh->b_blocknr != blocks[page_block-1] + 1)
  45. goto confused;
  46. }
  47. blocks[page_block++] = bh->b_blocknr;
  48. boundary = buffer_boundary(bh);
  49. if (boundary) {
  50. boundary_block = bh->b_blocknr;
  51. boundary_bdev = bh->b_bdev;
  52. }
  53. bdev = bh->b_bdev;
  54. } while ((bh = bh->b_this_page) != head);
  55. if (first_unmapped)
  56. goto page_is_mapped;
  57. /*页有buffer,但所有的buffer没被映射,这些页被在内存空洞上的pagein或读创建,这些空洞被函数block_read_full_page处理,如果address_space也用mpage_readpages,那么这种情况很少发生。*/
  58. goto confused;
  59. }
  60. //页没有buffers,映射它到硬盘 
  61. BUG_ON(!PageUptodate(page));
  62.   //计算文件中页序号对应的块序号
  63. block_in_file = page->index << (PAGE_CACHE_SHIFT - blkbits);
  64. last_block = (i_size - 1) >> blkbits;
  65. map_bh.b_page = page;
  66. for (page_block = 0; page_block < blocks_per_page; ) {
  67. map_bh.b_state = 0;
  68. if (get_block(inode, block_in_file, &map_bh, 1))
  69. goto confused;
  70. if (buffer_new(&map_bh))//如果buffer是新的
  71.       //找到块号对应的buffer,清除脏标识并等待
  72. unmap_underlying_metadata(map_bh.b_bdev,
  73. map_bh.b_blocknr);
  74. if (buffer_boundary(&map_bh)) {
  75. boundary_block = map_bh.b_blocknr;
  76. boundary_bdev = map_bh.b_bdev;
  77. }
  78. if (page_block) {
  79. if (map_bh.b_blocknr != blocks[page_block-1] + 1)
  80. goto confused;
  81. }
  82. blocks[page_block++] = map_bh.b_blocknr;
  83. boundary = buffer_boundary(&map_bh);
  84. bdev = map_bh.b_bdev;
  85. if (block_in_file == last_block)
  86. break;
  87. block_in_file++;
  88. }
  89. BUG_ON(page_block == 0);
  90. first_unmapped = page_block;
  91. page_is_mapped:
  92. end_index = i_size >> PAGE_CACHE_SHIFT;
  93. if (page->index >= end_index) {
  94. /*如果页跨过i_size,它在每writepage函数上一定会被赋0值,因为这页可能被映射。 一个文件映射多页,如果文件没有多页的大小,则当映射时剩下的空间是0,并且写这块区域时,它不能被写入到文件中去。*/
  95. unsigned offset = i_size & (PAGE_CACHE_SIZE - 1);
  96. char *kaddr;
  97. if (page->index > end_index || !offset)
  98. goto confused;
  99. kaddr = kmap_atomic(page, KM_USER0);
  100. memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);//设置为0
  101. flush_dcache_page(page);
  102. kunmap_atomic(kaddr, KM_USER0);
  103. }
  104. //这页将去BIO,我们先送这页到BIO 
  105. if (bio && *last_block_in_bio != blocks[0] - 1)
  106. bio = mpage_bio_submit(WRITE, bio);//提交BIO
  107. alloc_new:
  108. if (bio == NULL) {//分配新的BIO
  109. bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9),
  110. bio_get_nr_vecs(bdev), GFP_NOFS|__GFP_HIGH);
  111. if (bio == NULL)
  112. goto confused;
  113. }
  114. /*在标识buffer为clean之前必须尝试加页到bio,当混乱坏路径(OOM)发现所有的bh标识干净时(它将不写任何东西),它将非常混乱。*/
  115. length = first_unmapped << blkbits;
  116. if (bio_add_page(bio, page, length, 0) < length) {//加页到bio
  117. bio = mpage_bio_submit(WRITE, bio);//提交bio
  118. goto alloc_new;
  119. }
  120. //我们有自己的BIO,因此我们现在能标识buffer为干净的,
  121.   //确信仅设置我们将写的buffer。
  122. if (page_has_buffers(page)) {//页上有buffer
  123. struct buffer_head *head = page_buffers(page);
  124. struct buffer_head *bh = head;
  125. unsigned buffer_counter = 0;
  126. do {
  127. if (buffer_counter++ == first_unmapped)
  128. break;
  129. clear_buffer_dirty(bh);//清除脏标识
  130. bh = bh->b_this_page;
  131. } while (bh != head);
  132. //如果页不是更新的或同时发生的readpage不能与bh一起串行化,并且在我们达到platter之前它将读取硬盘时,我们不能删除bh
  133. if (buffer_heads_over_limit && PageUptodate(page))
  134. try_to_free_buffers(page);//释放buffer
  135. }
  136. BUG_ON(PageWriteback(page));
  137. set_page_writeback(page);//设置页状态标识PG_writeback
  138. unlock_page(page);
  139. if (boundary || (first_unmapped != blocks_per_page)) {
  140. bio = mpage_bio_submit(WRITE, bio);
  141. if (boundary_block) {//边界块
  142.       //调用ll_rw_block函数写块对应的buffer到设备
  143. write_boundary_block(boundary_bdev,
  144. boundary_block, 1 << blkbits);
  145. }
  146. } else {
  147. *last_block_in_bio = blocks[blocks_per_page - 1];
  148. }
  149. goto out;
  150. confused:
  151. if (bio)
  152. bio = mpage_bio_submit(WRITE, bio);
  153. *ret = page->mapping->a_ops->writepage(page, wbc);
  154. //调用者有一个节点上的引用,这样mapping是稳定的 
  155. if (*ret) {
  156. if (*ret == -ENOSPC)
  157. set_bit(AS_ENOSPC, &mapping->flags);
  158. else
  159. set_bit(AS_EIO, &mapping->flags);
  160. }
  161. out:
  162. return bio;
  163. }

函数mpage_bio_submit赋上回调函数,提交读写操作,函数分析如下(在fs/mpage.c中):
  1. struct bio *mpage_bio_submit(int rw, struct bio *bio)
  2. {
  3. bio->bi_end_io = mpage_end_io_read;//赋上读完后函数,用来进行页标识处理
  4. if (rw == WRITE)
  5. bio->bi_end_io = mpage_end_io_write;//赋上写完后处理函数
  6. submit_bio(rw, bio);//提交操作
  7. return NULL;
  8. }

函数submit_bio 提交块I/O读写操作,函数分析如下(在drives/block/ll_rw_block.c中):
  1. void submit_bio(int rw, struct bio *bio)
  2. {
  3. int count = bio_sectors(bio);
  4. BIO_BUG_ON(!bio->bi_size);
  5. BIO_BUG_ON(!bio->bi_io_vec);
  6. bio->bi_rw = rw;
  7. if (rw & WRITE)
  8. mod_page_state(pgpgout, count);
  9. else
  10. mod_page_state(pgpgin, count);
  11.   /*这个函数是文件系统与块设备驱动程序的接口,它向块设备驱动程序提交块I/O操作,从这里开始进行驱动层,在块设备驱动程序中分析这一函数。*/
  12. generic_make_request(bio);
  13. }

块设备节点映射的数据同步回写函数sync_blockdev
Linux kernel virtual filesystem 04.gif
函数sync_blockdev的调用层次图

函数sync_blockdev通过块设备的设备节点的地址空间,回写并等待所有的与一个块设备相关的脏数据写回到设备上。函数sync_blockdev的调用层次图如上图。

函数sync_blockdev分析如下(在fs/buffer.c中):
  1. int sync_blockdev(struct block_device *bdev)
  2. {
  3. int ret = 0;
  4. if (bdev) {
  5. int err;
  6.     //写回块设备节点映射的脏页数据
  7. ret = filemap_fdatawrite(bdev->bd_inode->i_mapping);
  8. err = filemap_fdatawait(bdev->bd_inode->i_mapping);
  9. if (!ret)
  10. ret = err;
  11. }
  12. return ret;
  13. }
函数filemap_fdatawait遍历所给地址空间回写页的链表并等待在他们上面,其中参数mapping表示等待的地址空间结构。函数filemap_fdatawait分析如下(在/mm/filemap.c中):
  1. int filemap_fdatawait(struct address_space *mapping)
  2. {
  3. loff_t i_size = i_size_read(mapping->host);
  4. if (i_size == 0)
  5. return 0;
  6. return wait_on_page_writeback_range(mapping, 0,
  7. (i_size - 1) >> PAGE_CACHE_SHIFT);
  8. }

  函数wait_on_page_writeback_range等待对从start到end的页完成回写操作。函数分析如下(在/mm/filemap.c中):
  1. static int wait_on_page_writeback_range(struct address_space *mapping,
  2. pgoff_t start, pgoff_t end)
  3. {
  4. struct pagevec pvec;
  5. int nr_pages;
  6. int ret = 0;
  7. pgoff_t index;
  8. if (end < start)
  9. return 0;
  10. pagevec_init(&pvec, 0);
  11. index = start;
  12. while ((index <= end) &&
  13.       //查找有回写标识PAGECACHE_TAG_WRITEBACK的页数
  14. (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index,
  15. PAGECACHE_TAG_WRITEBACK,
  16. min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1)) != 0) {
  17. unsigned i;
  18. for (i = 0; i < nr_pages; i++) {
  19. struct page *page = pvec.pages[i];
  20. /* until radix tree lookup accepts end_index */
  21. if (page->index > end)
  22. continue;
  23.       //等待一页回写完成
  24. wait_on_page_writeback(page);
  25. if (PageError(page))
  26. ret = -EIO;
  27. }
  28. pagevec_release(&pvec);
  29. cond_resched();//调度
  30. }
  31. //检查明显的写错误
  32. if (test_and_clear_bit(AS_ENOSPC, &mapping->flags))
  33. ret = -ENOSPC;
  34. if (test_and_clear_bit(AS_EIO, &mapping->flags))
  35. ret = -EIO;
  36. return ret;
  37. }

函数wait_on_page_writeback等待一页完成回写动作,函数分析如下(在include/linux/pagemap.h中):
  1. static inline void wait_on_page_writeback(struct page *page)
  2. {
  3. if (PageWriteback(page))//页上有回写标识
  4. wait_on_page_bit(page, PG_writeback);
  5. }

函数wait_on_page_bit分析如下(在mm/filemap.c中):

  1. void fastcall wait_on_page_bit(struct page *page, int bit_nr)
  2. {
  3.   //得到在页上等待队列
  4. wait_queue_head_t *waitqueue = page_waitqueue(page);
  5. DEFINE_PAGE_WAIT(wait, page, bit_nr);//得到页等待队列结构wait
  6. do {
  7. //设置当前进程状态,将wait.wait加到页等待队列waitqueue中
  8. prepare_to_wait(waitqueue, &wait.wait, TASK_UNINTERRUPTIBLE);
  9. if (test_bit(bit_nr, &page->flags)) {//测试标识
  10. sync_page(page);
  11. io_schedule(); //在需要调度时调度
  12. }
  13. } while (test_bit(bit_nr, &page->flags));
  14. finish_wait(waitqueue, &wait.wait);
  15. }
  1. static inline int sync_page(struct page *page)
  2. {
  3. struct address_space *mapping;
  4. smp_mb();//
  5. mapping = page_mapping(page);//得到页对应的地址空间
  6.   //调用地址空间结构对应的页同步函数
  7. if (mapping && mapping->a_ops && mapping->a_ops->sync_page)
  8. return mapping->a_ops->sync_page(page);
  9. return 0;

  10. }

http://blog.chinaunix.net/uid-24237502-id-106067.html
-dump time is 1970-01-01_08-51-07 -reboot reg is 80 -reset mode is d -reason is kernel_crash -bootcause_cmdline is Reboot into panic -exception_reboot_reason: kernel_crash Vmcoreinfo: -kimage_voffset = 0xffffffbf88000000 -phys_offset = 0x80000000 -kaslr_offset = 0x80000 -vabits_actual = 39 -exception_kernel_version:Linux version 5.15.74 (lhl@ISS) (OHOS (dev) clang version 15.0.4 (llvm-project 81cdec3cd117b1e6e3a9f1ebc4695d790c978463), LLD 15.0.4) #1 SMP PREEMPT Thu Jun 19 11:17:09 CST 2025 -exception_panic_reason:Oops - BUG: Fatal exception -exception_file_infoi: -exception_task_id:2271 -exception_task_family:[malloc-multi-th, 2271][hdcd, 1651] -exception_pc_symbol:[<ffffffc00848ee10>] __vma_adjust+0xc44/0xc78 -exception_stack_info:[<ffffffc00913ac08>] get_exception_stack_info+0x128/0x2a4 [<ffffffc00913afd4>] prepare_exception_info+0x178/0x1d0 [<ffffffc00913bc68>] sysdump_panic_event+0x730/0x92c [<ffffffc00821e0fc>] atomic_notifier_call_chain+0x88/0x100 [<ffffffc0081deaa0>] panic+0x1a8/0x444 [<ffffffc00814bed8>] die+0x2dc/0x2f4 [<ffffffc00814d5b8>] bug_handler+0x54/0xf8 [<ffffffc008139fc0>] brk_handler+0xa8/0x170 [<ffffffc008173c30>] do_debug_exception+0xc4/0x1a8 [<ffffffc00980e968>] el1_dbg+0x48/0x68 [<ffffffc00980e7a4>] el1h_64_sync_handler+0x48/0xa4 [<ffffffc008091b10>] el1h_64_sync+0x7c/0x80 [<ffffffc00848ee10>] __vma_adjust+0xc44/0xc78 [<ffffffc008491bc0>] __split_vma+0x14c/0x260 [<ffffffc008491e18>] __do_munmap+0xf8/0x724 [<ffffffc008492630>] __vm_munmap.llvm.15742267499309760575+0x94/0x180 [<ffffffc00849276c>] __arm64_sys_munmap+0x50/0x68 [<ffffffc008159204>] invoke_syscall+0x6c/0x15c [<ffffffc008159110>] el0_svc_common.llvm.9499517201915720397+0xd4/0x120 [<ffffffc008158fc4>] do_el0_svc+0x34/0xac [<ffffffc00980edb0>] el0_svc+0x2c/0x94 [<ffffffc00980ed20>] el0t_64_sync_handler+0x8c/0xf0 [<ffffffc008091e48>] el0t_64_sync+0x1b4/0x1b8 -dump time is 1970-01-01_22-30-34 -reboot reg is 80 -reset mode is d -reason is kernel_crash -bootcause_cmdline is Reboot into panic -exception_reboot_reason: kernel_crash Vmcoreinfo: -kimage_voffset = 0xffffffbf88000000 -phys_offset = 0x80000000 -kaslr_offset = 0x80000 -vabits_actual = 39 -exception_kernel_version:Linux version 5.15.74 (lhl@ISS) (OHOS (dev) clang version 15.0.4 (llvm-project 81cdec3cd117b1e6e3a9f1ebc4695d790c978463), LLD 15.0.4) #1 SMP PREEMPT Thu Jun 19 11:17:09 CST 2025 -exception_panic_reason:Oops: Fatal exception -exception_file_infoi:not-bugon -exception_task_id:2335 -exception_task_family:[malloc-multi-th, 2335][hdcd, 1563] -exception_pc_symbol:[<ffffffc0084c492c>] kmem_cache_alloc+0x128/0x3dc -exception_stack_info:[<ffffffc00913ac08>] get_exception_stack_info+0x128/0x2a4 [<ffffffc00913afd4>] prepare_exception_info+0x178/0x1d0 [<ffffffc00913bc68>] sysdump_panic_event+0x730/0x92c [<ffffffc00821e0fc>] atomic_notifier_call_chain+0x88/0x100 [<ffffffc0081deaa0>] panic+0x1a8/0x444 [<ffffffc00814bed8>] die+0x2dc/0x2f4 [<ffffffc0081744dc>] __do_kernel_fault+0x298/0x2f4 [<ffffffc0081740d0>] do_bad_area+0x40/0x100 [<ffffffc00987fe4c>] do_translation_fault+0x5c/0x70 [<ffffffc00817364c>] do_mem_abort+0x74/0x138 [<ffffffc00980e844>] el1_abort+0x44/0x68 [<ffffffc00980e7c0>] el1h_64_sync_handler+0x64/0xa4 [<ffffffc008091b10>] el1h_64_sync+0x7c/0x80 [<ffffffc0084c492c>] kmem_cache_alloc+0x128/0x3dc [<ffffffc0081d7e6c>] vm_area_alloc+0x34/0xac [<ffffffc00848fc8c>] mmap_region+0x268/0x728 [<ffffffc00848f754>] do_mmap+0x3d4/0x540 [<ffffffc00845d308>] vm_mmap_pgoff+0xd0/0x234 [<ffffffc008490208>] ksys_mmap_pgoff+0xbc/0x108 [<ffffffc00814b394>] __arm64_sys_mmap+0x44/0x54 [<ffffffc008159204>] invoke_syscall+0x6c/0x15c [<ffffffc008159110>] el0_svc_common.llvm.9499517201915720397+0xd4/0x120 [<ffffffc008158fc4>] do_el0_svc+0x34/0xac [<ffffffc00980edb0>] el0_svc+0x2c/0x94 [<ffffffc00980ed20>] el0t_64_sync_handler+0x8c/0xf0 [<ffffffc008091e48>] el0t_64_sync+0x1b4/0x1b8 -dump time is 1970-01-01_23-09-04 -reboot reg is 80 -reset mode is d -reason is kernel_crash -bootcause_cmdline is Reboot into panic -exception_reboot_reason: kernel_crash Vmcoreinfo: -kimage_voffset = 0xffffffbf88000000 -phys_offset = 0x80000000 -kaslr_offset = 0x80000 -vabits_actual = 39 -exception_kernel_version:Linux version 5.15.74 (lhl@ISS) (OHOS (dev) clang version 15.0.4 (llvm-project 81cdec3cd117b1e6e3a9f1ebc4695d790c978463), LLD 15.0.4) #1 SMP PREEMPT Thu Jun 19 11:17:09 CST 2025 -exception_panic_reason:Oops - BUG: Fatal exception -exception_file_infoi: -exception_task_id:3311 -exception_task_family:[malloc-multi-th, 3311][hdcd, 1535] -exception_pc_symbol:[<ffffffc00848ee10>] __vma_adjust+0xc44/0xc78 -exception_stack_info:[<ffffffc00913ac08>] get_exception_stack_info+0x128/0x2a4 [<ffffffc00913afd4>] prepare_exception_info+0x178/0x1d0 [<ffffffc00913bc68>] sysdump_panic_event+0x730/0x92c [<ffffffc00821e0fc>] atomic_notifier_call_chain+0x88/0x100 [<ffffffc0081deaa0>] panic+0x1a8/0x444 [<ffffffc00814bed8>] die+0x2dc/0x2f4 [<ffffffc00814d5b8>] bug_handler+0x54/0xf8 [<ffffffc008139fc0>] brk_handler+0xa8/0x170 [<ffffffc008173c30>] do_debug_exception+0xc4/0x1a8 [<ffffffc00980e968>] el1_dbg+0x48/0x68 [<ffffffc00980e7a4>] el1h_64_sync_handler+0x48/0xa4 [<ffffffc008091b10>] el1h_64_sync+0x7c/0x80 [<ffffffc00848ee10>] __vma_adjust+0xc44/0xc78 [<ffffffc008491bc0>] __split_vma+0x14c/0x260 [<ffffffc008491e88>] __do_munmap+0x168/0x724 [<ffffffc008492630>] __vm_munmap.llvm.15742267499309760575+0x94/0x180 [<ffffffc00849276c>] __arm64_sys_munmap+0x50/0x68 [<ffffffc008159204>] invoke_syscall+0x6c/0x15c [<ffffffc008159110>] el0_svc_common.llvm.9499517201915720397+0xd4/0x120 [<ffffffc008158fc4>] do_el0_svc+0x34/0xac [<ffffffc00980edb0>] el0_svc+0x2c/0x94 [<ffffffc00980ed20>] el0t_64_sync_handler+0x8c/0xf0 [<ffffffc008091e48>] el0t_64_sync+0x1b4/0x1b8 -dump time is 1970-01-01_23-52-44 -reboot reg is 80 -reset mode is d -reason is kernel_crash -bootcause_cmdline is Reboot into panic -exception_reboot_reason: kernel_crash Vmcoreinfo: -kimage_voffset = 0xffffffbf88000000 -phys_offset = 0x80000000 -kaslr_offset = 0x80000 -vabits_actual = 39 -exception_kernel_version:Linux version 5.15.74 (lhl@ISS) (OHOS (dev) clang version 15.0.4 (llvm-project 81cdec3cd117b1e6e3a9f1ebc4695d790c978463), LLD 15.0.4) #1 SMP PREEMPT Thu Jun 19 11:17:09 CST 2025 -exception_panic_reason:CFI failure (target: 0x7fb3780000) -exception_file_infoi:not-bugon -exception_task_id:2080 -exception_task_family:[irq/128-dwc3, 2080][kthreadd, 2] -exception_pc_symbol:[<ffffffc00913b728>] sysdump_panic_event+0x1f0/0x92c -exception_stack_info:[<ffffffc00913ac08>] get_exception_stack_info+0x128/0x2a4 [<ffffffc00913afd4>] prepare_exception_info+0x178/0x1d0 [<ffffffc00913bc68>] sysdump_panic_event+0x730/0x92c [<ffffffc00821e0fc>] atomic_notifier_call_chain+0x88/0x100 [<ffffffc0081deaa0>] panic+0x1a8/0x444 [<ffffffc0083fc9a0>] __cfi_slowpath_diag+0x1e4/0x230 [<ffffffc0082b56d0>] rcu_do_batch+0x36c/0x8f4 [<ffffffc0082b4fe8>] rcu_core+0x210/0x58c [<ffffffc0082ade4c>] rcu_core_si+0x20/0x30 [<ffffffc008090460>] __do_softirq+0x170/0x5c8 [<ffffffc0081ea344>] do_softirq+0x78/0xf0 [<ffffffc0081ea280>] __local_bh_enable_ip+0xec/0x138 [<ffffffc00813e280>] local_bh_enable+0x28/0x38 [<ffffffc008d507f4>] dwc3_thread_interrupt+0x12bc/0x15d0 [<ffffffc00828ee04>] irq_thread_fn+0x54/0xe4 [<ffffffc00828ea84>] irq_thread+0x1c8/0x3b8 [<ffffffc00821c220>] kthread+0x170/0x1d4 [<ffffffc008095118>] ret_from_fork+0x10/0x20 -dump time is 1970-01-01_08-26-27 -reboot reg is 80 -reset mode is d -reason is kernel_crash -bootcause_cmdline is Reboot into panic -exception_reboot_reason: kernel_crash Vmcoreinfo: -kimage_voffset = 0xffffffbf88000000 -phys_offset = 0x80000000 -kaslr_offset = 0x80000 -vabits_actual = 39 -exception_kernel_version:Linux version 5.15.74 (lhl@ISS) (OHOS (dev) clang version 15.0.4 (llvm-project 81cdec3cd117b1e6e3a9f1ebc4695d790c978463), LLD 15.0.4) #1 SMP PREEMPT Sat Jun 14 17:38:52 CST 2025 -exception_panic_reason:Oops: Fatal exception in interrupt -exception_file_infoi:not-bugon -exception_task_id:14 -exception_task_family:[ksoftirqd/0, 14][kthreadd, 2] -exception_pc_symbol:[<7f9b140000>] 0x7f9b140000 -exception_stack_info:[<ffffffc009165794>] get_exception_stack_info+0x128/0x2a4 [<ffffffc009165b60>] prepare_exception_info+0x178/0x1d0 [<ffffffc0091667f4>] sysdump_panic_event+0x730/0x92c [<ffffffc00821f770>] atomic_notifier_call_chain+0x88/0x100 [<ffffffc0081e0028>] panic+0x1b4/0x458 [<ffffffc00814c94c>] die+0x2cc/0x2f4 [<ffffffc008175984>] __do_kernel_fault+0x298/0x2f4 [<ffffffc0098ab4a8>] do_page_fault+0x2e8/0x694 [<ffffffc008174af4>] do_mem_abort+0x74/0x138 [<ffffffc0098393dc>] el1_abort+0x44/0x68 [<ffffffc009839358>] el1h_64_sync_handler+0x64/0xa4 [<ffffffc008091b10>] el1h_64_sync+0x7c/0x80 [<7f9b140000>] 0x7f9b140000 [<ffffffc0082b6a88>] rcu_core+0x210/0x58c [<ffffffc0082af8ec>] rcu_core_si+0x20/0x30 [<ffffffc008090460>] __do_softirq+0x170/0x5c8 [<ffffffc0081ed0ec>] run_ksoftirqd+0x50/0xac [<ffffffc008224724>] smpboot_thread_fn+0x2d4/0x3d8 [<ffffffc00821d894>] kthread+0x170/0x1d4 [<ffffffc008095118>] ret_from_fork+0x10/0x20 #include <malloc.h> #include <time.h> #include <sched.h> #include <errno.h> #include <string.h> #include <pthread.h> #include "test.h" #define THREAD_MAX_N 8 #define SIZE_ALIGN (4 * sizeof(size_t)) #define MMAP_THRESHOLD 131052 #define FREE_CYCLE 16 #define THRESHOLD (MMAP_THRESHOLD / 16) #define ITER_TIME 80 #define NANOSEC_PER_SEC 1e9 #define MALLOC_TIME (ITER_TIME * (THRESHOLD / (SIZE_ALIGN + 1))) void free_all(void **ptr) { for (int j = 0; j < FREE_CYCLE; j++) { free(ptr[j]); } } void *func(void *arg) { int *val = (int *)arg; cpu_set_t mask; struct timespec ts[2]; int num = 0; void *ptr[FREE_CYCLE]; CPU_ZERO(&mask); CPU_SET(0, &mask); if (sched_setaffinity(0, sizeof(mask), &mask) < 0) { t_error("Set CPU affinity of thread %d failure, ERROR:%s\n", *val, strerror(errno)); return NULL; } for (int i = 0; i < ITER_TIME; ++i) { for (size_t size = 0; size < THRESHOLD; size += SIZE_ALIGN + 1) { if (num == FREE_CYCLE) { free_all(ptr); num = 0; } ptr[num] = malloc(size); if (!ptr[num]) { t_error("Thread %d malloc failed for size %u\n", *val, size); *val = errno; return NULL; } num++; } } *val = 0; return NULL; } int main(int argc, char *argv[]) { struct timespec ts[2]; pthread_attr_t attr; pthread_t tids[THREAD_MAX_N]; int t_result[THREAD_MAX_N] = {0}; int flag = 0; int ret; int i; ret = pthread_attr_init(&attr); if (ret < 0) { t_error("Init pthread attribute failed: %s\n", strerror(errno)); return -1; } clock_gettime(CLOCK_REALTIME, ts); for (i = 0; i < THREAD_MAX_N; ++i) { t_result[i] = i; ret = pthread_create(&tids[i], &attr, func, &t_result[i]); if (ret < 0) { t_error("Create pthread %u failed: %s\n", i, strerror(errno)); flag = -1; break; } } for (i = 0; i < THREAD_MAX_N; ++i) { ret = pthread_join(tids[i], NULL); if (ret < 0) { t_error("Join thread %u failed: %s\n", i, strerror(errno)); } } clock_gettime(CLOCK_REALTIME, ts + 1); (void)pthread_attr_destroy(&attr); double cost = (ts[1].tv_sec - ts[0].tv_sec) * NANOSEC_PER_SEC + (ts[1].tv_nsec - ts[0].tv_nsec); if (!flag) { t_printf("Malloc and free %d threads %d times cost %lf s\n", THREAD_MAX_N, MALLOC_TIME, cost / NANOSEC_PER_SEC); t_status = 0; } return t_status; } 在鸿蒙开发者手机 4.1版本中 使用的时5.15.74版本linux内核,在跑上述的单核cpu多线程malloc和free时,出现了前面所述的几种内核崩溃类型,在最新linux5.15.y版本中是否有相应的解决提交记录
最新发布
07-20
-exception_panic_reason:Oops - Undefined instruction: Fatal exception -exception_file_infoi:not-bugon -exception_task_id:18051 -exception_task_family:[ptz-bgd-1-IMAGE, 18051][main, 585] -exception_pc_symbol:[<ffffffc0082310f4>] propagate_entity_load_avg+0xf8/0x35c -exception_stack_info:[<ffffffc001806994>] get_exception_stack_info+0x150/0x2d8 [sysdump] [<ffffffc0018067ec>] prepare_exception_info+0x16c/0x1c4 [sysdump] [<ffffffc0018099c4>] sysdump_panic_event+0x76c/0x8ec [sysdump] [<ffffffc0082044fc>] atomic_notifier_call_chain+0x8c/0x138 [<ffffffc0081b4d64>] panic+0x1b8/0x44c [<ffffffc0081137bc>] die+0x5a0/0x67c [<ffffffc008114748>] do_el1_undef+0x68/0x98 [<ffffffc00977cd60>] el1_undef+0x34/0x54 [<ffffffc00977cc04>] el1h_64_sync_handler+0x54/0xb4 [<ffffffc008091310>] el1h_64_sync+0x7c/0x80 [<ffffffc0082310f4>] propagate_entity_load_avg+0xf8/0x35c [<ffffffc008230da4>] update_load_avg+0x1b4/0x40c [<ffffffc008231b74>] propagate_entity_cfs_rq+0x88/0x1f4 [<ffffffc0082341f8>] detach_entity_cfs_rq+0x84/0xb4 [<ffffffc0082331d4>] migrate_task_rq_fair+0x64/0x1c0 [<ffffffc0082182d8>] set_task_cpu+0xd8/0x2ac [<ffffffc0018d6c20>] lb_pull_tasks+0x1b8/0x278 [unisoc_sched] [<ffffffc0018d3028>] android_rvh_sched_newidle_balance+0x298/0x344 [unisoc_sched] [<ffffffc008238b48>] newidle_balance+0x88/0x79c [<ffffffc00823d21c>] pick_next_task_fair+0x5c/0x3dc [<ffffffc0091ef33c>] binder_wait_for_work+0x178/0x48c [<ffffffc0091ec080>] binder_thread_read+0x270/0x2bc4 [<ffffffc0091e7e98>] binder_ioctl_write_read+0x120/0x588 [<ffffffc0091e2bbc>] binder_ioctl+0x1c4/0xfd8 [<ffffffc0085eff20>] __arm64_sys_ioctl+0x184/0x20c [<ffffffc00811fb68>] invoke_syscall+0x60/0x150 [<ffffffc00811fa9c>] el0_svc_common+0x8c/0xf8 [<ffffffc00811f9a0>] do_el0_svc+0x28/0x98 [<ffffffc00977d248>] el0_svc+0x24/0x84 [<ffffffc00977d1c0>] el0t_64_sync_handler+0x88/0xec [<ffffffc00809164c>] el0t_64_sync+0x1b8/0x1bc
06-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值