LINUX.ORG.RU

предотвращение запуска второго экземпляра приложения


0

0

Как можно осуществить сабж?

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

В Windows это решалось поиском окна приложения или созданием com объекта.

В Linux как я понял можно создать bonobo объект, но это лишняя зависимость приложения.

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

В общем если есть предложения постите, желательно чуть-чуть кода :)

ЗЫ Желательно переносимое решения, т.е. POSIX.

т.к. приложение должно работать еще и на FreeBSD и Солярке.

anonymous

проще всего создавать в /tmp файл и загонять в него pid.

при запуске проверять наличие файла, если есть, считать pid и проверить наличие процесса с таким pid

ananas ★★★★★
()

Проще захватить какой нить X selection(SetSelectionOwner), а при запуске
проверять захвачен ли этот селекшон(GetSelectionOwner)

вобщем:
  1) atom = InternAtom("XXX_PRIVATE_ATOM_XXX");
  2) если (None == GetSelectionOwner(atom)) то выходим
  3) SetSelectionOwner(atom, my_window)

в большинстве случаев этого достаточно, хотя гипотетически могут 
возникнуть траблы например между 2) и 3) ктото(другой экземпляр проги) 
захватит этот атом

lg ★★
()
Ответ на: комментарий от ananas

>проще всего создавать в /tmp файл и загонять в него pid. > >при запуске проверять наличие файла, если есть, считать pid и проверить >наличие процесса с таким pid

1)не факт что есть /tmp каталог(не уверен, но по-моему POSIX не гарантирует этого). 2)имя файла должно быть строго задано, т.е. если злоумышлиник создат линк с этим именем например на /etc/passwd, а какой-нибудь идиот запустит программу под rootом то гуд бай детка

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

>в большинстве случаев этого достаточно, хотя гипотетически могут >возникнуть траблы например между 2) и 3) ктото(другой экземпляр проги) >захватит этот атом

гипотетичски? :)

а кстати может есть в Linux мьютексы общепроцессорные, как в винде?

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

> проще всего создавать в /tmp файл и загонять в него pid.
>
> при запуске проверять наличие файла, если есть, считать pid и
> проверить наличие процесса с таким pid
>

Брысь в школу!

Во-первых - не в /tmp, а в /var/run.
Имя файла часто формируют как progname.pid.

Алгоритм работы такой:

1. Открываем файл /var/run/progname.pid для записи с флагом O_CREAT.
2. Пробуем взять блокировку fcntl(F_SETLK) на весь файл.
3. Если не получилось - значит блокировка у другого процесса,
то есть выходим.
4. Если получилось - значит мы первые! Теперь делаем ftruncate(2)
до нулевой длины и пишем туда свой PID.
5. Файл _не_ закрываем!

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

Если же нужна единственная копия процесса _на_пользователя_, а не на всю машину, тогда файл можно писать в $HOME/tmp или где-то рядом.

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

мил человек, в школу я пойду только после того, как вы мне сообщите об успешном выполении команды

touch /var/run/something

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

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

Да было бы неплохо если б существовал именованный mutex, но по-моему такого нет.

Хотя может я ошибаюсь.

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

> мил человек, в школу я пойду только после того, как вы мне сообщите
> об успешном выполении команды
>
> touch /var/run/something
> из-под обычного пользователя.

Естественно в таком случае - в $HOME/tmp/ или $HOME/.progname/
этого самого пользователя.

> а также объясните, на кой хрен мне
> нужен лишний открытый файловый дескриптор

Для тех, кто в танке. Если закрыть дескриптор, блокировка
файла сбросится, и повторный запуск процесса не обнаружит
первую копию потому, что сможет эту блокировку получить.
Понятно? Признак того, что копия процесса уже работает -
- это наличие блокировки на файле, а не просто наличие самого
файла. Не надо читать файл и искать процесс с эти PID, а потом
гадать - тот это процесс или просто PID совпал. Я доступно
излагаю, детям понятно? А когда процесс завершится (_любым_
способом) - блокировка снимется автоматически и новая копия
отдично запустится.
Запись PID в файл носит чисто информационный характер, можно
вообще ничего не писать, а держать блокировку на файле нулевой
длины.

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

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

>Если же нужна единственная копия процесса _на_пользователя_, а не на >всю машину, тогда файл можно писать в $HOME/tmp или где-то рядом.

способ не плохой, конечно со своими недостаками(например у пользователя нет своего домашнего каталога или его дом. каталог "/"), а нельзя проще? типа CreateMutex("MYPROGRAM_MUTEX"); и все?

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

а зачем что-то искать? stat на /proc/PID очень сложно сделать?

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

> способ не плохой конечно со своими недостаками(например у
> пользователя нет своего домашнего каталога или его дом. каталог "/")
> а нельзя проще? типа CreateMutex("MYPROGRAM_MUTEX"); и все?

Это самый переносимый способ и _самый_ постой.

Блокировка файлов - AFAIK единственный вид синхронизации,
который сбрасывается при завершении процесса автоматически.

Да, ты можешь сделать SysV IPC семафор - будет работать,
пока твоя программа не завершится аварийно (или ее не
прибьют SIGKILL'ом) и тебе придется вручную эту блокировку
снимать.
POSIX семафоры тоже подойдут но с таким же успехом.

anonymous
()

а "ps x | grep myprg | grep -v grep" тебе не покатит?

nobody ★★
()
Ответ на: комментарий от lg

а SetSelectionOwner ничего возвращать не должно?
(или еще как-то сообщать при неудачной попытке выполнения)

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

ничего не должен возвращать, но может сгенерить X error если на вход дал хреновенький атом или окно

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

я к тому что наверное проще проверить результат выполнения Set..., чем сравнивать потом с GetSelectionOwner

А потом, что будет если один пользователь с на разных Х-серверах приложение попытается запустить? с файлами оно вроде надежней.

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

anonymous (*) (11.11.2004 18:13:25):

> Это самый переносимый способ и _самый_ постой.

Согласен.

> Блокировка файлов - AFAIK единственный вид синхронизации, который сбрасывается при завершении процесса автоматически.

НЕ согласен.

> Да, ты можешь сделать SysV IPC семафор - будет работать, пока твоя программа не завершится аварийно (или ее не прибьют SIGKILL'ом) и тебе придется вручную эту блокировку снимать.

А можешь sem_flg = SEM_UNDO; сделать ;)

> POSIX семафоры тоже подойдут но с таким же успехом.

Полностью неверное утверждение, ибо:

1) POSIX семафоры могут быть именные, их легко можно разделять между процессами, но их нельзя блокировать и на них нельзя повесить undo.

2) Линуховая реализация их не поддерживает. Более того, линуховые POSIX семафоры вообще не работают между процессами, даже если их разместить в шареной памяти.

На самом деле, SysV IPC семафоры можно использовать для блокировки, (с UNDO флагом), но это -- не POSIX.

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

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

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

Вдогонку:

С fcntl(F_SETLK) могут быть проблемы, поскольку не все файловые системы ее поддерживают; в частности, половина NFS ее не поддерживает (в зависимости от версий и опций монтирования). Кстати, недавно я лицезрел сервер со смонтированной по NFS /tmp.

Так что самым переносимым является, все же, просто файл с PID (только если для определения pid'а не пользовать /proc, конечно)

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

> только если для определения pid'а не пользовать /proc, конечно

а почему? на сколько я помню, и на солярке и на фре /proc имеется.

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

>Так что самым переносимым является, все же, просто файл с PID (только >если для определения pid'а не пользовать /proc, конечно)

вы так считаете?

а если 1)процесс "А" открыл файл, проверил что там ничего нет и 2)управление передалось процессу "Б", который тоже открыл файл и увидел что там ничего нет 3)Управление передалось процессу "А", он записал свой pid. 4)управление передалось процессу "Б", он переписал содержимое файла, запсав свой PID.

каким образом эта схема обеспечивает блокировку вызова второго экземпляра приложения?

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

обычно делают /tmp - в памяти, /tmp_keep на NFS

еще кстати немного извратный но достаточно портируемый метод .. это прибиндиться к какому нибудь левому порту(4519) .. и проверять если bind() с reuseаddr failиться, то апликуха уже запущена :)

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

да /proc во всех юниксах есть. другое дело, что форматы несколько разные, но stat на конкретный pid делать можно, это должно везде сработать.

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

ananas (12.11.2004 16:10:30):

> stat на конкретный pid делать можно

Честно говоря, не понимаю, зачем.

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

2 Die-Hard:

> > Да, ты можешь сделать SysV IPC семафор - будет работать, пока твоя
> > программа не завершится аварийно (или ее не прибьют SIGKILL'ом) и
> > тебе придется вручную эту блокировку снимать.

> А можешь sem_flg = SEM_UNDO; сделать ;)

Согласен... не думал, что Линух SEM_UNDO умеет, ошибку признаю :-)

> > POSIX семафоры тоже подойдут но с таким же успехом.

> Полностью неверное утверждение, ибо:

> 1) POSIX семафоры могут быть именные, их легко можно разделять между
> процессами, но их нельзя блокировать и на них нельзя повесить undo.

Конечно. Поэтому они и подходят с таким же успехом - то есть с никаким.

> 2) Линуховая реализация их не поддерживает. Более того, линуховые
> POSIX семафоры вообще не работают между процессами, даже если их
> разместить в шареной памяти.

Шо, даже в 2.6.x до сих пор не умеет? И типа mutexes, rwlocks и
condition variables тоже не умеют PTHREAD_PROCESS_SHARED.
Мама дорогая :-((( AFAIR , это еще в Solaris 2.6 работало,
плюс еще POSIX mq.

Все-таки самые хороший вариант - именно fcntl() locking.
Естественно должна быть возможность указывать в rubn-time
конфигурации, где размещать это файл (+ разумное default
значение, возможно зависящее от текущей платформы и UID).

BTW, есть еще один метод: делать bind() на какой-либо TCP
порт и не закрывать сокет. Даже проще, чем с fcntl().
Если программа все равно использует TCP, то то же вполне
себе вариант.

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

> да /proc во всех юниксах есть. другое дело, что форматы несколько
> разные, но stat на конкретный pid делать можно, это должно везде
> сработать.

И дохера ты Unix'ов видел, мальчик?

"Никогда не говори 'никогда'" (C)

$ uname -a
AIX ws 3 4 0041CC8A4C00
$ ls /proc
ls: 0653-341 The file /proc does not exist.
$

И где на моей машине /proc ?

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

anonymous (*) (12.11.2004 15:44:34):

> управление передалось процессу "Б", который тоже открыл файл и увидел что там ничего нет

Значит, он должен недленно умереть, поскольку раз файл СУЩЕСТВУЕТ, да еще и ПУСТОЙ, то кто-то его УЖЕ открыл, но написать туда еще не успел.

Существует, конечно, вероятность того, что кто-то открыл файл и умер, не успев туда написать. Что ж, для такого случая можно сделать на него stat, посмотреть, насколько он стар. Все равно рейси, конечно.

Существует "классический" подход, работающий всегда и на всех системах, поддерживающих хардлинки: открываем файл с O_CREAT, затем делаем на него link (man 2 link) и с ним и работаем, man 2 open в районе O_EXCL (O_EXCL, к сожалению, тоже не работает на NFS).

Короче, можно много извращаться в этом направлении, но, как мне кажется, изначальная задача не предполагала уж очень крутую race защищенность.

Да, файлы за собой подтирать надо.

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

2ananas :

Дошло, зачем тебе /proc

Портабильнее сделать kill(pid,0);

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

Опираться на значение PID в файле очень не надежно. Возможен сценарий, когда приложение A запустилось, записало в файл свой PID, а затем умерло "трагической смертью", в результате чего PID в файле остался. Спустя некоторое время вы запускаете приложение A еще раз и оно считывает значение PID из файла и пытается найти процесс с таким PID. И легко может найти!!! С чего вы взяли, что не будет совершенно "левого" процесса с таким PID?

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