Ситуация с ext4 / fsync не ясна в Android (Java) - PullRequest
13 голосов
/ 16 января 2011

Статья Тима Брея "Безопасное сохранение данных" оставила мне открытые вопросы. Сегодня ему более месяца, и я не видел никаких последующих действий, поэтому решил обратиться к этой теме здесь.

Одним из пунктов статьи является то, что FileDescriptor.sync () должен вызываться для обеспечения безопасности при использовании FileOutputStream. Сначала я был очень раздражен, потому что я никогда не видел ни одного кода Java, выполняющего синхронизацию в течение 12 лет, которые я выполняю на Java. Тем более, что справиться с файлами довольно просто. Кроме того, стандартный JavaDoc FileOutputStream никогда не намекал на синхронизацию (Java 1.0 - 6). После некоторых исследований я понял, что ext4 может быть первой основной файловой системой, требующей синхронизации. (Существуют ли другие файловые системы, где рекомендуется явная синхронизация?)

Я ценю некоторые общие мысли по этому вопросу, но у меня также есть некоторые конкретные вопросы:

  1. Когда Android выполнит синхронизацию с файловой системой? Это может быть периодическим и дополнительно основанным на событиях жизненного цикла (например, процесс приложения переходит в фоновый режим).
  2. FileDescriptor.sync () заботится о синхронизации метаданных? Это синхронизация каталога измененного файла. Сравните с FileChannel.force ().
  3. Обычно никто не пишет напрямую в FileOutputStream. Вот мое решение (вы согласны?): FileOutputStream fileOut = ctx.openFileOutput(file, Context.MODE_PRIVATE); BufferedOutputStream out = new BufferedOutputStream(fileOut); try { out.write(something); out.flush(); fileOut.getFD().sync(); } finally { out.close(); }

Ответы [ 2 ]

10 голосов
/ 17 января 2011

Android будет выполнять синхронизацию, когда это необходимо - например, когда экран выключается, выключается устройство и т. Д. Если вы просто смотрите на «нормальную» работу, явная синхронизация приложениями никогда не требуется.

Проблема возникает, когда пользователь вытаскивает батарею из своего устройства (или выполняет полную перезагрузку ядра), и вы хотите убедиться, что вы не потеряете данные.

Итак, первое, что нужно понять: проблема в том, что внезапно отключается питание, поэтому не может произойти полное отключение, и вопрос о том, что произойдет в постоянном хранилище в этой точке.

Если вы просто пишете один независимый новый файл, это не имеет значения, что вы делаете. Пользователь мог вытащить батарею, когда вы находились в процессе написания, прямо перед тем, как начать писать и т. Д. Если вы не синхронизируете, это просто означает, что с момента, когда вы закончили писать, прошло больше времени, во время которого вы извлекали батарею потеряет данные.

Большая проблема здесь, когда вы хотите обновить файл. В этом случае при следующем чтении файла вы хотите иметь либо предыдущее содержимое, или новое содержимое. Вы не хотите писать что-то наполовину или терять данные.

Это часто делается путем записи данных в новый файл, а затем переключения на них из старого файла. До ext4 вы знали, что после того, как вы закончили запись файла, дальнейшие операции с другими файлами не будут выполняться на диске до тех, которые находятся над этим файлом, так что вы можете безопасно удалить предыдущий файл или выполнить операции, которые зависят от вашего нового файла. будучи полностью написанным.

Однако теперь, если вы пишете новый файл, затем удаляете старый, и батарея извлекается, при следующей загрузке вы можете увидеть, что старый файл удален и создан новый файл, но содержимое нового файла не полный. Выполняя синхронизацию, вы гарантируете, что новый файл будет полностью записан в этот момент, чтобы можно было вносить дальнейшие изменения (например, удаление старого файла), которые зависят от этого состояния.

1 голос
/ 19 ноября 2011

fileOut.getFD().sync(); должно быть в пункте finally до close().

sync() намного важнее, чем close(), учитывая долговечность.

Итак, каждый раз, когда вы хотите «закончить» работу с файлом, вы должны sync() его перед close() его использованием.

posix не гарантирует, что ожидающие записи будут записаны на диск при запуске close().

...