не может получить блокировку файла

FileLocker_wo.h

#include <string>

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

bool unlock_file(int& aFileDescriptor);

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

FileLocker_wo.cpp

namespace Utils
{
namespace FileLocker
{
bool lock_file(std::string aFileName, int& 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(int& 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  && (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 часов.

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

2

Решение

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

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

Что это значит, в этом кусочке вашего кода

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

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

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

3

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

Других решений пока нет …

По вопросам рекламы [email protected]