Платформо-независимая блокировка файлов? - PullRequest
6 голосов
/ 21 марта 2009

Я занимаюсь очень сложной вычислительной работой, которая время от времени выдает результаты. Задача состоит в том, чтобы просто симулировать одну и ту же вещь целую кучу раз, поэтому она делится между несколькими компьютерами, которые используют разные ОС. Я хотел бы направить вывод всех этих экземпляров в один и тот же файл, поскольку все компьютеры могут видеть одну и ту же файловую систему через NFS / Samba. Вот ограничения:

  1. Необходимо разрешить безопасное одновременное добавление. Должен блокироваться, если какой-то другой экземпляр на другом компьютере в данный момент добавляет файл.
  2. Производительность не считается. Ввод / вывод для каждого экземпляра составляет всего несколько байтов в минуту.
  3. Простота имеет значение. Весь смысл этого (помимо чистого любопытства) в том, что я могу перестать каждый раз записывать в отдельный файл и вручную объединять эти файлы.
  4. Не должен зависеть от деталей файловой системы. Должен работать с неизвестной файловой системой в монтировании NFS или Samba.

Я использую язык D, если это имеет значение. Я посмотрел, в стандартной библиотеке нет ничего, что могло бы сделать это. Как D-специфичные, так и общие, не зависящие от языка ответы полностью приемлемы и оценены.

Ответы [ 5 ]

7 голосов
/ 21 марта 2009

В NFS вы сталкиваетесь с некоторыми проблемами с кэшированием на стороне клиента и устаревшими данными. Я написал независимый модуль блокировки ОС для работы над NFS раньше. Простая идея создания файла [datafile] .lock не подходит для NFS. Основная идея, чтобы обойти это, состоит в создании файла блокировки [datafile] .lock, который, если присутствует, означает, что файл НЕ заблокирован, и процесс, который хочет получить блокировку, переименовывает файл в другое имя, например [datafile] .lock. [ имя хоста]. [PID]. Переименование - это достаточно атомарная операция, которая работает достаточно хорошо по NFS, чтобы гарантировать эксклюзивность блокировки. Остальное, в основном, представляет собой связку отказоустойчивости, циклов, проверки ошибок и извлечения блокировки в случае, если процесс умирает до снятия блокировки и переименования файла блокировки обратно в [datafile] .lock

2 голосов
/ 21 марта 2009

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

Тогда, если вы когда-нибудь захотите изменить формат данных, вам нужно будет изменить только сервер, а не все клиенты.

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

2 голосов
/ 21 марта 2009

Файл блокировки с поворотом

Как и в других ответах, самый простой способ - создать файл блокировки в том же каталоге, что и файл данных.

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

Таким образом, последовательность записи в файл данных будет:

  1. Проверить наличие файла блокировки

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

  3. Если файл блокировки отсутствует, создайте его с моим идентификатором и повторите весь цикл, чтобы избежать состояния гонки (еще раз проверьте, что файл блокировки действительно мой).

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

Другое решение

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

Проблемы с NFS

Хорошо, я добавляю несколько вещей, потому что Джири Клоуда правильно указал, что NFS использует кэширование на стороне клиента , что приведет к тому, что фактический файл блокировки будет находиться в неопределенном состоянии.

Несколько способов решить эту проблему:

  • смонтировать каталог NFS с опциями noac или sync. Это просто, но не полностью гарантирует согласованность данных между клиентом и сервером, хотя, тем не менее, могут возникнуть проблемы, хотя в вашем случае все может быть в порядке.

  • Откройте файл блокировки или файл данных, используя атрибуты O_DIRECT, O_SYNC или O_DSYNC. Предполагается, что это вообще отключит кеширование.
    Это снизит производительность, но обеспечит согласованность.

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

  • Не используйте NFS и просто используйте Samba: есть хорошая статья на эту тему и почему NFS, вероятно, не лучший ответ на ваш сценарий использования.
    В этой статье вы также найдете различные способы блокировки файлов.

  • Решение Джири тоже хорошее.

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

Что-то другое

Используйте небольшой сервер базы данных, чтобы сохранить свои данные и вообще обойти проблемы с блокировкой NFS / SMB, или сохранить свою текущую систему с несколькими файлами данных и просто написать небольшую утилиту для объединения результатов.
Это может быть самым безопасным и простым решением вашей проблемы.

2 голосов
/ 21 марта 2009

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

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

Это уже много лет используется такими приложениями, как CVS, на многих платформах. Единственная проблема возникает в тех редких случаях, когда происходит сбой приложения во время записи и перед снятием блокировки.

1 голос
/ 21 марта 2009

Я не знаю D, но я думаю, что использование файла мьютекса для работы может работать. Вот некоторый псевдокод, который может оказаться полезным:

do {
  // Try to create a new file to use as mutex.
  // If it's already created, it will throw some kind of error.
  mutex = create_file_for_writing('lock_file');
} while (mutex == null);

// Open your log file and write results
log_file = open_file_for_reading('the_log_file');
write(log_file, data);
close_file(log_file);

close_file(mutex);
// Free mutex and allow other processes to create the same file.
delete_file(mutex); 

Итак, все процессы попытаются создать файл мьютекса, но продолжить сможет только тот, кто победит. Как только вы напишите свой вывод, закройте и удалите мьютекс, чтобы другие процессы могли сделать то же самое.

...