LINUX.ORG.RU

как залочить файл?


0

0

Такая проблема - нужно залочить файл, чтобы он был доступен только одному потоку на все время его(файла) существования. Примерно получилось следующее: fp = fopen( fname, "w" ); err = ftrylockfile (fp); err = 0; if (err == 0) { //file locked } else { //file is owned by other thread } fclose(fp); unlink (fname); И вот этот кусок кода не работает, т. е. ftrylockfile возвращает 1. Я не большой специалист, к сожалению, так что может кто подскажет что не так или идейку какую подкинет?

anonymous

Ответ на: комментарий от anonymous

> просто flock не канает? А чем flock отличается от ftrylock? В мане:

(Note: this locking has nothing to do with the file locking done by functions like flock(2) and lockf)

Проблема моя в том, что Нужна ссылка на FILE, а flock возвращает int дескриптор.

> open с опцией O_EXCL Та же фигня. Прога не моя и использует fprinf на каждом углу. Не подскажете, как можно работать с файлом с помощью fprinf и иже с ними, при этом открывая файл функцией open.

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

anonymous
()

anonymous (*) (2003-02-07 15:59:51.969):
Судя по тому, что ты привел, ты, скорее всего, невнимательно нарисовал алгоритм.
Это - чушь:
fp = fopen( fname, "w" );
err = ftrylockfile (fp);
err = 0;
if (err == 0){
//file locked
} else {
//file is owned by other thread
}
Она не может работать так, как ты описАл.

Далее, прежде, чем вызывать ftrylockfile, проверь результат fopen().
Может, ты это в read-only директории гоняешь?

> ...как можно работать с файлом с помощью fprinf и иже с ними, при этом
> открывая файл функцией open.
man fdopen

Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

>Судя по тому, что ты привел, ты, скорее всего, невнимательно нарисовал >алгоритм. >Это - чушь: fp = fopen( fname, "w" ); err = ftrylockfile (fp); err = 0; if (err == 0){ //file locked } else { //file is owned by other thread }

>Она не может работать так, как ты описАл.

Если не трудно, тыкни где я не прав

>> ...как можно работать с файлом с помощью fprinf и иже с ними, при >этом >> открывая файл функцией open. >man fdopen Отлично, спасибо. но все же ftrylockfile похоже именно то, что в этом случае нужно. Хотелось бы заставить работать прогу так, как имелось ввиду вначале. Где я туплю?

anonymous
()

anonymous (*) (2003-02-10 15:32:34.471):
> Если не трудно, тыкни где я не прав
Последовательность операторов:
err = ftrylockfile (fp);
err = 0;
if (err == 0){...}else{...}
бессмыссленна: вне зависимости от результатов ftrylockfile всегда
выполнится 1 ветка if'а (а не вторая, как ты описАл).

Далее, если по какой-то причине fopen тебе вернул NULL, то ftrylockfile,
очевидно, всегда вернет !=0. Например, fopen() не прошел из-за того,
что ты его делаешь в закрытой на запись директории.

Еще замечание:
Тебя устроят только flockfile() и ftrylockfile(), никакие flock'и и fcntl'ы
тут не годятся, ибо они - про другое.

В принципе, можешь сам нарисовть нечто вроде ftrylockfile, используя семафорную
технику - для этого тебе придется переписывать почти всю stdio.

Die-Hard ★★★★★
()

Да, еще вдогонку:
А ты отдаешь себе отчет в том, что только ОДИН тред успешно залочит файл?
Если у тебя 20 тредов, и все пытаются залочить один и тот же файл,
то только одному это удастся сделать? И что глупо открывать файл в треде и 
тут же лочить его? Открывать файл можно только в главном треде, или хотя бы 
проверять его на открытость, и получать белиберду с MEMORY-LEAK'ами и 
потерянными файлами типа:

if(  (fp==NULL)&&( (fp=fopen(...))!=NULL )  ){
  if( ftrylockfile(fp)==0 ){//ok, повезло
     операции чтения/записи
     fclose и т.д.
  }else{
     кто-то вклинился между проверками (fp==NULL) и ( (fp=fopen(...))!=NULL )
     ссылка на предыдущее содержание потока fp потеряна
  }
}

Работать будет, но по дороге будешь терять дескрипторы и память.
Оно тебе надо?

Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

> Если не трудно, тыкни где я не прав Последовательность операторов: err = ftrylockfile (fp); err = 0; if (err == 0){...}else{...} бессмыссленна: вне зависимости от результатов ftrylockfile всегда выполнится 1 ветка if'а (а не вторая, как ты описАл).

Мои глубочайшие извинения, err = 0 - это типа закомментировано должно было быть... так, для тестов использовал. В любом случае, проверка значения, возвращаемого fopen ничего не дала - ftrylockfile возвращает 1. Сильно хотелось бы понять почему.

if ((fp = fopen( fname, "w" ) )== NULL) { printf ("\nfopen failed\n"); return 1; } err = ftrylockfile (fp); if ( err == 0) { fprintf (fp, "%s", fname); } else { printf ("\n err = %d\n", err); } fclose(fp); И еще, интересно ваше мнение - следующий код будет решеним моей проблемы?

if ((filedesc = open ( fname, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1) { printf ("\n ---open failed\n"); }

if ((fp = fdopen( filedesc, "w+" ) )== NULL) { printf ("\nfopen failed\n"); return 1; }

anonymous
()

Сорри за форматирование

Мои глубочайшие извинения, err = 0 - это типа закомментировано должно было быть... так, для тестов использовал. В любом случае, проверка значения, возвращаемого fopen ничего не дала - ftrylockfile возвращает 1. Сильно хотелось бы понять почему.

if ((fp = fopen( fname, "w" ) )== NULL) { printf ("\nfopen failed\n"); return 1; }

err = ftrylockfile (fp); if ( err == 0) { fprintf (fp, "%s", fname); } else { printf ("\n err = %d\n", err); } fclose(fp); И еще, интересно ваше мнение - следующий код будет решеним моей проблемы?

if ((filedesc = open ( fname, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1) { printf ("\n ---open failed\n"); }

if ((fp = fdopen( filedesc, "w+" ) )== NULL) { printf ("\nfopen failed\n"); return 1; }

anonymous
()
Ответ на: комментарий от Die-Hard

>Да, еще вдогонку: >А ты отдаешь себе отчет в том, что только ОДИН тред успешно залочит >файл? >Если у тебя 20 тредов, и все пытаются залочить один и тот же файл, >то только одному это удастся сделать? И что глупо открывать файл в >треде и тут же лочить его?

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

> if( (fp==NULL)&&( (fp=fopen(...))!=NULL ) ){

зачем нужна проверка (fp==NULL)?

> кто-то вклинился между проверками (fp==NULL) и ( (fp=fopen(...))!=NULL ) > ссылка на предыдущее содержание потока fp потеряна

А кто может вклинится, если я все делаю в тестовой проге, в которой тредов и близко нет.

anonymous
()
Ответ на: комментарий от anonymous

> я все делаю в тестовой проге, в которой тредов и близко нет.
ВОТ!

Без тредов ftrylockfile всегда возвращает 1 - это такая заглушка в libc.

Просто компилируй с -lpthread, и все сработает.

Но, вообще, я не совсем понимаю логику алгоритма.




Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

>> я все делаю в тестовой проге, в которой тредов и близко нет. >ВОТ!

Действительно, вот! Заработало, спасибо огромное.

>Но, вообще, я не совсем понимаю логику алгоритма.

А логика, хоть и мутная, но есть. Дело в том что каждый тред работает с базой данных отдельно от других. Файлы используются для бекапа и имя файла является хешем имени записи в БД. Так что, если какой-то тред захочет работать с уже используемой записью, то он начнет писать в файл, с которым уже работают, что есть нехорошо. + достаточно простой способ удостоверится, что никто не будет менять одну запись в 2х тредах одновременно. Согласен, что некрасиво немного, может какие идеи есть или уже делали что-то подобное?

anonymous
()
Ответ на: комментарий от anonymous

anonymous (*) (2003-02-10 18:36:49.243):
> ... имя файла является ...
Все, не проканает.

flockfile блокирует не инод, а поток, т.е. FILE *fp
Если fp локальна для треда, то flockfile не в силах ничего заблокировать.

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

if( !pthread_mutex_lock(mymutex) ){
   проверяем, есть ли имя в хэше.
   Если нет, то:
     заносим его туда
   иначе 
     занято 
   pthread_mutex_unlock(mymutex);
}

Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

>flockfile блокирует не инод, а поток, т.е. FILE *fp >Если fp локальна для треда, то flockfile не в силах ничего >заблокировать.

Если я правильно понял, то этим как раз flock занимается. Почему бы им не воспользоваться? Или опять какие-то грабли? Дело в том, что я немного дорабатываю прогу достаточно немелкую и, понятное дело, написанную не мной. Влезать в работу тредов и наставлять семафоров очень не хочется так как поламаю ведь все.

anonymous
()
Ответ на: комментарий от Die-Hard

Как насчет

filedesc = open ( fname, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);

err = flock (filedesc, LOCK_EX | LOCK_NB);

if (err ==0)

{

} else {

}

fp = fdopen( filedesc, "w+" );

anonymous
()

> ...этим как раз flock занимается. Почему бы им не воспользоваться?
Можно. Но потом могут быть грабли с залоченными инодами (если прога сдохнет). Насколько я
понял логику алгоритма, блокировки файлов и не нужны, а нужно только запоминать их имена на
время работы программы. flock хорош, когда надо управляться с парой-тройкой файлов,
а когда их десятки - периодически глюки возникают.

Хотя, решение с flock - самое простое.

Кстати, лучше fcntl (man 2 fcntl), поскольку flock не пашет через NFS.
В нарисованной тобой схеме вместо flock - соответств. вызовы fcntl.

Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

>Кстати, лучше fcntl (man 2 fcntl), поскольку flock не пашет через NFS.<BR> >В нарисованной тобой схеме вместо flock - соответств. вызовы fcntl. <BR>

Отлично, огромное спасибо за помощь.

anonymous
()

Вдогонку - ни flock(), ни fcntl(), строго говоря, НЕ thread safe.

Насколько мне известно, в fcntl() имеются cancellation points - правда,
кажется, только в блокировках. Неплохо б уточнить этот вопрос.

Я бы рискнул юзать fcntl() только в под мутексом.

Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

> Насколько мне известно, в fcntl() имеются cancellation points - правда, кажется, только в блокировках.

>Неплохо б уточнить этот вопрос.

А откуда хоть копать начинать? А то у меня слегка каша в голове.

anonymous
()

> А откуда хоть копать начинать?
В Гугле поискать, на форумах поспрашивать.

Вообще, наверное, можно на это и забить - вряд ли проблема действительно серьезная.
Для успокоения совести можно обкладывать вызовы fcntl мутексами:

До создания тредов:
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;

И обкладываешь вызовы fcntl:
pthread_mutex_lock(&mut);
...fcntl(...)...
pthread_mutex_unlock(&mut);

Слегка подтормаживать будет, зато надежно.

Die-Hard ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.