2016-10-20 8 views
2

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 часов.

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

+3

Измените свои программы на печать 'strerror (errno)' in * every place * где 'lockf' только что вернул ненулевое значение. Запустите их снова. Расскажите нам, что они печатают. – zwol

+1

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

+1

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

ответ

3

Вы столкнулись с одной из самых неприятных ошибок дизайна в файлах POSIX. Вы, наверное, не знал об этом, потому что вы только читали lockf manpage, а не fcntl manpage, так вот важный бит 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 не может быть никакого способа (даже с использованием непортативных расширений), путь наименьшего сопротивления должен использовать только OS/O (open, close, read, write, fcntl, lseek, ftruncate) с файлами, которые необходимо применять для блокировки POSIX.

+0

отлично, что решает проблему, я хотел бы повысить, но я не могу ... – naren

+0

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

+0

Нет, у thestream есть свое «открытое описание файла» и close (fd) не повлияет на него. Однако блокировка будет удалена с помощью * first * of ofstream.close() 'и' close (fd) ', поскольку блокировка связана с * процессом *, а не с открытым файлом. – zwol