linux应用程序文件丢失,编写程序来处理导致在Linux上丢失写入的I / O错误

本文探讨了在Linux系统中使用fsync()时遇到-EIO错误的情况,并深入研究了内核层面如何处理这些错误。文章揭示了当fsync()返回-EIO时,文件状态可能处于不确定状态,且错误状态会被清除,导致后续fsync()调用可能报告成功。这对于数据库管理系统和其他关键应用来说可能引发严重的问题。

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

如果内核丢失写入, fsync()将返回-EIO

(注:早期的部分引用了旧的内核;下面更新以反映现代的内核)

它看起来像在end_buffer_async_write(...)失败中的asynchronous缓冲区写出在失败的脏缓冲区页面上为该文件设置了一个-EIO标志 :

set_bit(AS_EIO, &page->mapping->flags); set_buffer_write_io_error(bh); clear_buffer_uptodate(bh); SetPageError(page);

然后通过由wait_on_page_writeback_range(...)调用的由do_sync_mapping_range(...)调用的由sys_sync_file_range(...)调用的sys_sync_file_range2(...)来检测,以实现C库调用fsync() 。

但只有一次!

这个对sys_sync_file_range评论

168 * SYNC_FILE_RANGE_WAIT_BEFORE and SYNC_FILE_RANGE_WAIT_AFTER will detect any 169 * I/O errors or ENOSPC conditions and will return those to the caller, after 170 * clearing the EIO and ENOSPC flags in the address_space.

build议当fsync()返回-EIO或(在联机帮助页中没有logging) -ENOSPC ,它将清除错误状态,所以即使页面从未写入,后续的fsync()也会报告成功。

当然, wait_on_page_writeback_range(...) 在testing时会清除错误位 :

301 /* Check for outstanding write errors */ 302 if (test_and_clear_bit(AS_ENOSPC, &mapping->flags)) 303 ret = -ENOSPC; 304 if (test_and_clear_bit(AS_EIO, &mapping->flags)) 305 ret = -EIO;

因此,如果应用程序希望它能够重新尝试fsync()直到它成功并相信数据在磁盘上,那么这是非常错误的。

我很确定这是我在DBMS中发现的数据损坏的来源。 它重试fsync()并认为一切顺利。

这是允许的吗?

fsync()上的POSIX / SuS文档并没有真正指定这一点:

如果fsync()函数失败,则不能保证未完成的I / O操作已经完成。

Linux的fsync()的手册页没有提到失败时会发生什么。

所以看起来fsync()错误的意思是“不知道你的写作发生了什么,可能已经工作或没有,最好再试一次,以确保”。

较新的内核

在4.9上, -EIO在页面上设置-EIO ,只需通过mapping_set_error 。

buffer_io_error(bh, ", lost async page write"); mapping_set_error(page->mapping, -EIO); set_buffer_write_io_error(bh); clear_buffer_uptodate(bh); SetPageError(page);

在同步方面,我认为它是相似的,虽然结构现在非常复杂。 mm/filemap.c filemap_check_errors现在可以:

if (test_bit(AS_EIO, &mapping->flags) && test_and_clear_bit(AS_EIO, &mapping->flags)) ret = -EIO;

这有很多相同的效果。 错误检查似乎全部通过filemap_check_errors进行testing和清除:

if (test_bit(AS_EIO, &mapping->flags) && test_and_clear_bit(AS_EIO, &mapping->flags)) ret = -EIO; return ret;

我在我的笔记本电脑上使用了btrfs ,但是当我在/mnt/tmp上创build一个ext4环回testing并在其上build立一个perf探测器时:

sudo dd if=/dev/zero of=/tmp/ext bs=1M count=100 sudo mke2fs -j -T ext4 /tmp/ext sudo mount -o loop /tmp/ext /mnt/tmp sudo perf probe filemap_check_errors sudo perf record -g -e probe:end_buffer_async_write -e probe:filemap_check_errors dd if=/dev/zero of=/mnt/tmp/test bs=4k count=1 conv=fsync

我在perf report -Tfind以下调用堆栈:

---__GI___libc_fsync entry_SYSCALL_64_fastpath sys_fsync do_fsync vfs_fsync_range ext4_sync_file filemap_write_and_wait_range filemap_check_errors

通读表明,现代内核的行为是一样的。

这似乎意味着如果fsync() (或者大概write()或者close() )返回-EIO ,那么当你上次成功执行fsync() d或close()时,文件处于某种未定义的状态。 write()十状态。

testing

我已经实现了一个testing用例来演示这种行为 。

启示

DBMS可以通过进入崩溃恢复来解决这个问题。 一个普通的用户应用程序到底应该如何处理? fsync()手册页不会给出警告,意思是“fsync-if-you-feel-like-it”,我期望很多应用程序不能很好地处理这种行为。

错误报告

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值