LINUX.ORG.RU

Помогите с семафорами

 


0

1

Извиняюсь за чайниковский вопрос, линуксу начал обучаться совсем недавно. Есть некий кусок кода, вызываемые из различных процессов, запущенных одновременно (доступ к устройству) Надо сделать так, чтобы каждый процесс перед выполнением данного куска кода ждал, чтобы его выполнение завершилось в других процессах, дабы не щемиться одновременно в одну дверь. Прочитал в умных манах про семафоры, решил, что это именно то, что мне надо и накорябал такой алгоритм: создаю бинарный семафор следующим образом:

...
while (sem_open(SEMAPHORE_NAME,O_RDWR)!=SEM_FAILED);  // ждем пока семафор существует - где то выполняется секция
sem_open(SEMAPHORE_NAME, O_CREAT, 0777, 0); //процесс освободился - создаем свой семафор
... // Выполняемая секция кода
sem_unlink(SEMAPHORE_NAME); //освобождаем процесс - убиваем  свой семафор
...
Интуитивно понимаю что фигня, да и работает неправильно. А как надо?


Давно не сталкивался, но насколько я помню, это совсем не так работает.
Ты должен сделать:
1. sem_open - просто открыть семафор. Ты просто получишь дескриптор семафора, ничего больше. Флаг O_RDWR, ЕМНИП, не нужен и бесполезен, нужен O_CREATE.
2. Затем нужно сделать sem_wait, захватить семафор. Если семафор уже занят - процесс будет ждать, пока семафор освободится.
3. Затем - sem_post - отпустить семафор.
4. Удалять (sem_unlink) я бы не стал в принципе, наверное.

unikoid ★★★
()
Последнее исправление: unikoid (всего исправлений: 1)

Если процессы совсем разные (не треды, которые шарят файловые дескрипторы), то можно flock задействовать, на какой-нибудь файл '/tmp/my-app-name.lock'. Запустились — залочили файл, закончились — разблочили. Можно еще в этот файл писать pid текущего процесса, для дебага полезно. Пишут, что могут быть какие-то грабли, но в моей практике все было OK.

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

На C не писал, так что код советовать не буду.

Копипаста с интернетов для питона:

import os
import fcntl

class Lock:
    
    def __init__(self, filename):
        self.filename = filename
        # This will create it if it does not exist already
        self.handle = open(filename, 'w')
    
    # Bitwise OR fcntl.LOCK_NB if you need a non-blocking lock 
    def acquire(self):
        fcntl.flock(self.handle, fcntl.LOCK_EX)
        
    def release(self):
        fcntl.flock(self.handle, fcntl.LOCK_UN)
        
    def __del__(self):
        self.handle.close()

# Usage
try:
    lock = Lock("/tmp/lock_name.tmp")
    lock.acquire()
    # Do important stuff that needs to be synchronized

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

Спасибо, как то о таком простом способе и не подумал, буду пробовать!

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

Никогда так не делай. Если 2 процесса одновременно полезут писать файл, то будет всё что угодно. Т.е. залочить может один процесс, а получить доступ совершенно другой, а то и оба.

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

O_CREAT
This flag is used to create a semaphore if it does not already >exist.

Все равно не совсем понятно, а, наоборот, если семафор уже на тот момент создан, что тогда будет?

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

Хосспаде, ну набери ты уже man 3p sem_open и увидишь буквально в следующем предложении:

If O_CREAT is set and the semaphore already exists, then O_CREAT has no effect

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

Никогда так не делай. Если 2 процесса одновременно полезут писать файл, то будет всё что угодно.

Гм, но нам, вроде, писать и не надо, надо проверить блокировку готового файла, заблокировать и разблокировать в конце

MBK
() автор топика
Ответ на: комментарий от unikoid

Кстати, оттуда же насчет твоего O_RDWR:

If flags other than O_CREAT and O_EXCL are specified in the oflag parameter, the effect is unspecified.

что есть ОЧЕНЬ нехорошо.

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

Хосспаде, ну набери ты уже man 3p sem_open и увидишь буквально в следующем предложении:

Ok, спасибо, дошло, попробую и так тоже.

MBK
() автор топика
Ответ на: комментарий от peregrine

Нет, 2 процесса одновременно захватывают flock.

Тот, у кого получилось, идет дальше, и первым делом пишет в этот же файл свой PID. Второй курит бамбук, и, когда первый скинет лок, пойдет той же дорогой.

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

anonymous
()

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

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

1. sem_open - просто открыть семафор. Ты просто получишь дескриптор >семафора, ничего больше. Флаг O_RDWR, ЕМНИП, не нужен и бесполезен, >нужен O_CREATE.
2. Затем нужно сделать sem_wait, захватить семафор. Если семафор уже >занят - процесс будет ждать, пока семафор освободится.

Ok, сделал так:

...
sem = sem_open(SEMAPHORE_NAME, O_CREAT, 0777, 0);
sem_wait(sem);
...

Процесс висит и ждет. Чего он ждет? Я думал, sem_wait ждет освобождения семафора, но я запускаю самый первый процесс, семафор же вроде свободен? Где я туплю?

MBK
() автор топика
Ответ на: комментарий от unikoid

На пальцах. Когда ты захватываешь семафор (sem_wait) - ты пытаешься уменьшить его значение на единицу. Если значение уже равно нулю - процесс ждет, пока оно не увеличится, чтобы он смог уменьшить его.
Когда ты освобождаешь семафор (sem_post) - ты увеличиваешь его значение на 1.
Из этого можно сделать простой вывод, что начальное значение семфора должно быть равно количеству процессов, которые могут работать с ресурсом одновременно.

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

Возможно, я и вправду тугой, но все равно не понимаю. Я создаю первым процессом единичный семафор и блокирую его

...
sem = sem_open(SEMAPHORE_NAME, O_CREAT, 0777, 1);
sem_wait(sem);
...
Второй экземпляр этого процесса, по идее, должен игнорировать операцию sem = sem_open(SEMAPHORE_NAME, O_CREAT, 0777, 1); как выше было процитировано из мана по нему. И тормозиться на sem_wait пока какой то процесс не сделает sem_post. На деле же, только что проверил, первая строка следующего процесса благополучно освобождает семафор, никто никого не ждет. Как все таки делать правильно? :-(

MBK
() автор топика
Ответ на: комментарий от kvap

Все получилось, спасибо всем и извиняюсь за тупизм!

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