Программа записала данные в файл,
write() вернул успешный результат. Кажется, всё сохранилось.Но если в этот момент отключить питание, часть данных может исчезнуть. Причина в том, что успешный
write() ещё не означает запись на физический носитель.▪️Куда попадают данные после write()
Обычно Linux сначала помещает изменения в page cache - область оперативной памяти, используемую для кеширования файлов.
write()
↓
page cache
↓
writeback
↓
накопитель
Ядро самостоятельно решает, когда отправлять изменённые страницы на диск.
Это ускоряет работу, но создаёт промежуток между успешной записью и её сохранением на носителе.
▪️Что делает fsync()
Вызов:
fsync(fd);
запрашивает запись изменённых данных файла и связанных с ним метаданных, необходимых для синхронизации.
Например, программа обновила файл конфигурации. Чтобы не полагаться на фоновый writeback, она может вызвать
fsync() после записи.write(fd, data, size);
if (fsync(fd) == -1) {
perror("fsync");
}
Если вызов завершился успешно, ядро выполнило требуемую синхронизацию файла с устройством хранения.
▪️Чем отличается fdatasync()
fdatasync(fd);
Этот вызов тоже синхронизирует данные, но не обязан записывать все изменения метаданных.
Например, изменение времени модификации файла само по себе не требует синхронизации через fdatasync().
Но если файл увеличился, новый размер необходимо сохранить: без него записанные данные могут оказаться недоступными после сбоя.
Поэтому fdatasync() не означает «записать только содержимое и забыть обо всём остальном».
▪️Что быстрее?
fdatasync() потенциально быстрее, потому что может выполнять меньше операций с метаданными.
Но разница зависит от файловой системы, накопителя и характера записи. Иногда оба вызова приводят практически к одинаковой работе.
▪️Важный нюанс с rename()
Допустим, приложение безопасно обновляет конфигурацию:
1. Создаёт config.tmp
2. Записывает новые данные
3. Вызывает fsync(config.tmp)
4. Переименовывает в config.json
Этого недостаточно, чтобы гарантировать сохранность самого переименования после сбоя.
Для этого обычно требуется дополнительно вызвать
fsync() на файловом дескрипторе родительского каталога.И ещё: даже успешный
fsync() зависит от того, насколько корректно накопитель выполняет команды сброса кеша.▪️Почему это важно
write() подтверждает, что данные приняты системой, а не обязательно сохранены на носителе.fsync() синхронизирует данные и связанные метаданные файла, а fdatasync() может пропустить метаданные, не нужные для последующего чтения данных.Разница особенно важна для баз данных, журналов транзакций и приложений, которым нельзя терять подтверждённые записи.
BashTex 📱 #bash #linux