не удается установить блокировку для файла

#c #linux #filelock

#c #linux #блокировка файла

Вопрос:

FileLocker_wo.h

 #include <string>

namespace Utils
{
        namespace FileLocker
        {
                bool lock_file(std::string aFileName, intamp; aFileDescriptor);

                bool unlock_file(intamp; aFileDescriptor);

                bool is_file_locked(std::string aFileName);
        };
}
  

FileLocker_wo.cpp

 namespace Utils
{
        namespace FileLocker
        {
                bool lock_file(std::string aFileName, intamp; aFileDescriptor)
                {
                        aFileDescriptor = open(aFileName.c_str(), O_RDWR);

                        if (aFileDescriptor != -1)
                        {
                                if (lockf(aFileDescriptor, F_TLOCK, 0) == 0)
                                {
                                        return true;
                                }

                                std::cout << strerror(errno) << std::endl;
                        }

                        return false;
                }

                bool unlock_file(intamp; aFileDescriptor)
                {
                        if (lockf(aFileDescriptor, F_ULOCK, 0) == 0)
                        {
                                std::cout << "unloced file" <<  std::endl;
                                close(aFileDescriptor);
                                return true;
                        }
                        close(aFileDescriptor);
                        return false;
                }

                bool is_file_locked(std::string aFileName)
                {
                        int file_descriptor = open(aFileName.c_str(), O_RDWR);

                        if (file_descriptor != -1)
                        {
                                int ret = lockf(file_descriptor, F_TEST, 0);

                                if (ret == -1  amp;amp; (errno == EACCES || errno == EAGAIN))
                                {
                                        std::cout << "locked by another process" << std::endl;
                                        close(file_descriptor);
                                        return true;
                                }

                                if (ret != 0)
                                {
                                    std::cout << "return value is " << ret << " " << strerror(errno) << std::endl;
                                }

                        }
                        close(file_descriptor);
                        return false;
                }
        }
}
  

p1.cpp

 #include <iostream>
#include <fstream>

#include "FileLocker_wo.h"


int main()
{

        int fd = -1;
        if (Utils::FileLocker::lock_file("hello.txt", fd))
        {
                std::ofstream out("hello.txt");
                out << "hello ding dong" << std::endl;
                out.close();

                std::cout << "locked" << std::endl;
                sleep(5);
                if (Utils::FileLocker::unlock_file(fd))
                {
                        std::cout << "unlocked" << std::endl;
                }
        }

        return 0;
}
  

p2.cpp

 #include "FileLocker_wo.h"
#include <iostream>
#include <fstream>

int main()
{
        int max_trys = 2;
        int trys = 0;
        bool is_locked = false;

        do
        {
                is_locked = Utils::FileLocker::is_file_locked("hello.txt");

                if (!is_locked)
                {
                        std::cout << "not locked" << std::endl;
                        break;
                }

                std::cout << "locked" << std::endl;

                sleep(1);
                  trys;
        }
        while(trys < max_trys);

        if (!is_locked)
        {
                std::string s;
                std::ifstream in("hello.txt");
                while(getline(in,s))
                {
                        std::cout << "s is " << s << std::endl;
                }
        }

        return 0;
}
  

Я пытаюсь получить блокировку файла в одном процессе и проверяю, есть ли какая-либо блокировка для этого файла в другом процессе, используя lockf (p1.cpp , p2.cpp ).

В p1.cpp Я блокирую файл hello.txt и ожидание в течение 5 секунд. Тем временем я начинаю p2.cpp и проверка наличия какой-либо блокировки другим процессом, но всегда получается, что блокировки нет> Я застрял с этим в течение последних 2 часов.

Кто-нибудь может сказать, что в этом не так?

Комментарии:

1. Измените свои программы так, чтобы они печатали strerror(errno) в каждом месте, где lockf только что было возвращено ненулевое значение. Запустите их снова. Расскажите нам, что они печатают.

2. он не возвращает ненулевое значение. У меня есть журналы на месте, но они не печатаются, потому что он всегда возвращает 0. редактирование этих изменений журнала теперь в code.

3. Не следует ли вам создать какой-нибудь объект, чтобы вам не приходилось постоянно открывать файл? В is_file_locked() вы открываете, но не закрываете файловый дескриптор; у вас закончатся файловые дескрипторы. Вы не закрываете дескриптор файла в lock_file() или в unlock_file() , но это возлагает ответственность за закрытие файла на блокировщик (тем самым разблокируя файл). Я полагаю, деструктор и RAII упростили бы жизнь.

4. Добавлены близкие изменения, но все еще не работает… должны ли мы закрыть файл в lock_file без разблокировки? Я закрываюсь только в unlock_file и is_file_locked. Обновлен код.

5. В качестве информации у меня также есть классовая версия File locker, но она также не работает.

Ответ №1:

Вы столкнулись с одной из самых неприятных ошибок проектирования в блокировках файлов POSIX. Вы, вероятно, не знали об этом, потому что вы читаете только lockf справочную страницу, а не fcntl manpage, так что вот важная часть fcntl manpage:

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

Это означает, что в этом фрагменте вашего кода

     if (Utils::FileLocker::lock_file("hello.txt", fd))
    {
            std::ofstream out("hello.txt");
            out << "hello ding dong" << std::endl;
            out.close();
  

вы теряете блокировку для файла при вызове out.close() , <em>даже если</em> «описание открытого файла» на уровне ОС отличается от того, что вы использовали в ! out lock_file

Для безопасного использования блокировок POSIX вы должны убедиться, что вы вызываете open() файл, подлежащий блокировке, <em>один и только один</em> раз для каждого процесса, вы никогда не должны дублировать дескриптор файла, и вы должны закрывать его снова только тогда, когда будете готовы снять блокировку. Поскольку может отсутствовать какой-либо способ (даже с использованием непереносимых расширений) создать объект iostreams из файлового дескриптора или извлечь файловый дескриптор из объекта iostreams, путь наименьшего сопротивления заключается в использовании <em>только</em> примитивов ввода-вывода уровня операционной системы (, , , open , close ,,,,,,,,,,,,,,,,,,,) с файлами, к которым вам нужно применить блокировки POSIX. read write fcntl lseek ftruncate

Комментарии:

1. У меня есть другой вопрос в этом контексте, будет ли close (fd) закрывать все дескрипторы, используемые ofstream (если он используется для открытия того же файла, и я не вызывал ofstream.close())?

2. Нет, у ofstream есть свое собственное «описание открытого файла», и закрытие (fd) на него не повлияет. Однако блокировка будет снята при первом из ofstream.close() и close(fd) , поскольку блокировка связана с процессом , а не с открытым файлом.

3. хорошо, я хотел спросить, нужно ли мне по-прежнему вызывать ofstream.close(), если я вызываю close(fd)?

4. Да, это то, что я сказал. Пожалуйста, обратите внимание, что я также сказал, что вам, вероятно, нужно вообще прекратить использование iostreams.