Как использовать файл как мьютекс в Linux и C? - PullRequest
4 голосов
/ 06 сентября 2011

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

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

Вот что я попробовал:

flock(lock_file, LOCK_EX)

// critic section

flock(lock_file, LOCK_UN)

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

Ответы [ 3 ]

4 голосов
/ 06 сентября 2011

Я бы определенно рекомендовал использовать фактический мьютекс (как было предложено в комментариях); например, библиотека pthread обеспечивает реализацию . Но если вы хотите сделать это самостоятельно, используя файл в образовательных целях, я бы посоветовал взглянуть на этот ответ , который я недавно опубликовал, в котором описывается метод для этого в Python. В переводе на C это должно выглядеть примерно так (Внимание: непроверенный код, используйте на свой страх и риск; мой C ржавый):

// each instance of the process should have a different filename here
char* process_lockfile = "/path/to/hostname.pid.lock";
// all processes should have the same filename here
char* global_lockfile = "/path/to/lockfile";
// create the file if necessary (only once, at the beginning of each process)
FILE* f = fopen(process_lockfile, "w");
fprintf(f, "\n"); // or maybe write the hostname and pid
fclose(f);

// now, each time you have to lock the file:
int lock_acquired = 0;
while (!lock_acquired) {
    int r = link(process_lockfile, global_lockfile);
    if (r == 0) {
        lock_acquired = 1;
    }
    else {
        struct stat buf;
        stat(process_lockfile, &buf);
        lock_acquired = (buf.st_nlink == 2);
    }
}
// do your writing
unlink(global_lockfile);
lock_acquired = 0;
4 голосов
/ 06 сентября 2011

Стандартный метод блокировки файла использует такие параметры, как O_EXCL при вызове open(), чтобы попытаться создать файл. Вы сохраняете PID процесса, используя блокировку, чтобы вы могли определить, существует ли процесс еще (используя kill() для тестирования). Вам нужно беспокоиться о параллелизме - очень много.

Шаги:

  • Определить имя файла блокировки на основе имени FIFO
  • Открыть файл блокировки, если он существует
  • Проверить, существует ли процесс, использующий его
    • Если существует другой процесс, он имеет управление (выход с ошибкой или ожидание выхода)
    • Если другой процесс отсутствует, удалить файл блокировки
  • На данный момент файл блокировки не существовал при последней проверке.
  • Попробуйте создать его с помощью open() и O_EXCL среди других опций.
  • Если это работает, ваш процесс создал файл - у вас есть разрешение на это.
  • Запишите свой PID в файл; закройте его.
  • Откройте FIFO - используйте его.
  • Когда закончите (atexit()?), Удалите файл блокировки.

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

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

Внимательно посмотрите на справочную страницу open() в вашей системе, чтобы узнать, есть ли другие варианты, которые могут вам помочь. Иногда процессы используют каталоги (mkdir()) вместо файлов, потому что даже root не может создать второй экземпляр с заданным именем каталога, но у вас возникают проблемы с тем, как узнать PID процесса с открытым ресурсом и т. Д.

3 голосов
/ 06 сентября 2011

Ваш пример так же хорош, как вы собираетесь использовать flock (2) (что, в конце концов, всего лишь "консультативный" замок (то есть, на самом деле вовсе не замок ) ). В справочной странице для моей системы Mac OS X есть несколько важных условий:

Блокировки для файлов, а не файловые дескрипторы. То есть файловые дескрипторы, дублирующиеся с помощью dup (2) или fork (2), не приводят к нескольким экземплярам блокировка, а точнее несколько ссылок на одну блокировку. Если процесс, удерживающий блокировку файла, разветвляется, а потомок явно открывает файл, родитель потеряет свой замок

и

Процессы, заблокированные в ожидании блокировки, могут быть разбужены сигналами.

и то, и другое указывает на возможные сбои.


// был бы комментарием, но я хотел бы процитировать man-страницу как можно более длинную

...