Подтвердить что ты не робот

Оптимальный метод блокировки файлов

У Windows есть возможность открыть файл с эксклюзивными правами доступа. Unix этого не делает.

Чтобы обеспечить эксклюзивный доступ к некоторому файлу или устройству, в Unix обычно используется файл блокировки, обычно хранящийся в каталоге /var/lock.

Инструкция C open( "/var/lock/myLock.lock", O_RDWR | O_CREAT | O_EXCL, 0666 ) возвращает -1, если файл блокировки уже существует, иначе он его создает. Функция является атомарной и обеспечивает отсутствие состояния гонки.

Когда ресурс освобождается, файл блокировки удаляется следующей инструкцией remove( "/var/lock/myLock.lock" ).

Есть две проблемы с этим методом.

  • Программа может завершиться без удаления блокировки. Например, потому что он убит, сбой или что-то еще. Файл блокировки остается на месте и будет препятствовать любому доступу к ресурсу, даже если он больше не используется.

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

Для записи я использую файл блокировки для обеспечения эксклюзивного доступа к устройству, подключенному к последовательному порту (фактически/dev/ttyUSBx). Консультативный метод, требующий сотрудничества, в порядке. Но эксклюзивный доступ должен быть обеспечен между разными пользователями.

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

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

4b9b3361

Ответ 1

Вероятно, вы должны использовать flock(), как в

fd = open(filename, O_RDWR | O_CREAT, 0666); // open or create lockfile
//check open success...
rc = flock(fd, LOCK_EX | LOCK_NB); // grab exclusive lock, fail if can't obtain.
if (rc)
{
    // fail
}

Ответ 2

Взгляните на просветительскую презентацию Фиксирующие трюки и ловушки:

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

Изменить: Чтобы более точно решить ваши вопросы:

Есть ли лучший способ синхронизации, чем файл блокировки?

Как уже упоминал @Hasturkun, и как сказано выше, системный вызов, который вам нужно использовать, flock(2). Если ресурс, который вы хотите разделить с несколькими пользователями, уже основан на файлах (в вашем случае это /dev/ttyUSBx), вы можете flock сам файл устройства.

Как определить, работает ли процесс, создавший файл блокировки?

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

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

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

Ответ 3

Ответ Хастуркуна - это тот, который поставил меня на путь.

Вот код, который я использую

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>

/*! Try to get lock. Return its file descriptor or -1 if failed.
 *
 *  @param lockName Name of file used as lock (i.e. '/var/lock/myLock').
 *  @return File descriptor of lock file, or -1 if failed.
 */
int tryGetLock( char const *lockName )
{
    mode_t m = umask( 0 );
    int fd = open( lockName, O_RDWR|O_CREAT, 0666 );
    umask( m );
    if( fd >= 0 && flock( fd, LOCK_EX | LOCK_NB ) < 0 )
    {
        close( fd );
        fd = -1;
    }
    return fd;
}

/*! Release the lock obtained with tryGetLock( lockName ).
 *
 *  @param fd File descriptor of lock returned by tryGetLock( lockName ).
 *  @param lockName Name of file used as lock (i.e. '/var/lock/myLock').
 */
void releaseLock( int fd, char const *lockName )
{
    if( fd < 0 )
        return;
    remove( lockName );
    close( fd );
}

Ответ 4

Я использовал код, отправленный chmike, и заметил одно небольшое несовершенство. У меня была проблема с гонкой во время открытия файла блокировки. Иногда несколько потоков открывают файл блокировки одновременно.

Поэтому я удалил "remove (lockName)"; line из функции "releaseLock()". Я не понимаю, почему, но каким-то образом это действие помогло ситуации.

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

void testlock(void) {
  # pragma omp parallel num_threads(160)
  {    
    int fd = -1; char ln[] = "testlock.lock";
    while (fd == -1) fd = tryGetLock(ln);

    cout << omp_get_thread_num() << ": got the lock!";
    cout << omp_get_thread_num() << ": removing the lock";

    releaseLock(fd,ln);
  }
}

Ответ 5

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

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

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

1: open the lock file creating it if it doesn't exist
2: ask for an exclusive lock an agreed byte range in the lock file
3: when the lock is granted then
    4: <do my processing here>
    5: release my lock
    6: close the lock file
end

На шаге: вы можете либо заблокировать ожидание блокировки, либо предоставить ее немедленно. Блоки, которые вы блокируете, фактически не должны существовать в файле. Если вы можете получить копию Advanced Unix Programming от Marc J. Rochkind, он разработает полную библиотеку C, которая использует этот метод, чтобы обеспечить способ синхронизации программ, которые убираются ОС, однако программы завершаются.