#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.