Надёжное IPC/синхронизация.

bleyman

Есть обработчик какой-то херни. Ему периодически в Unix msg queue кладут запросы, он их достаёт, обрабатывает (сравнительно долго) и постит ответ. Раньше постил в ту же самую queue, но тут вдруг обнаружилось, что у неё есть размер, который вообще говоря ограничен, поэтому когда вдруг в очереди оказывается штук пять запросов за короткое время, ему придётся дропнуть уже готовый к отправке ответ, потом практически наверняка и следующий, короче говоря всё неправильно и нужно переписать, как минимум сделав две очереди, чем я сейчас и занимаюсь. Но, блин, если уж делать, то хочется сделать хорошо, а я вдруг обнаружил одну проблему, которую встречал в совершенно другом контексте когда-то давно (и даже постил сюда но решения так и не знаю. Проблема, собственно, такая: если запрашивающий процесс в какой-то момент решит, что таймаут истёк и ему больше неинтересно, или просто сдохнет по какой-то причине, то ответ ему так и останется болтаться в очереди ответов. И через некоторое время она забьётся! Более того, поскольку запрашивающие процессы фильтруют очередь по своим пидам, через некоторое время этот ответ получит какой-то другой запрашивающий, что плохо, а предназначенный ему ответ останется на его месте, что тоже плохо!
Можно, конечно, приделывать разнообразные костыли, например чтобы каждый запрашивающий проверял, нет ли мессаг с его пидом до отправления запроса, а сервис обнаружив переполнение очереди ответов тупо дропал оттуда первую мессагу (а, типа, что ещё поделать-то?). И это будет как бы good enough. Но может быть всё-таки есть какая-то идеальная схема, необязательно именно с очередями даже?
Алсо, WTF, почему семафоры после создания оказываются undefined и нужно звать semctl? Это ж мне нужно как-то узнать, нуждается ли оно в иницализации (т.е. являюсь ли я первым процессом, который всё создаёт)?

valodyr

А насколько вообще принципиально общаться очередями? UNIX socket или FIFO не лучше ли будет для каждого клиента? И слушай SIGPIPE.

Marinavo_0507

Правильно, сокеты для клиент-серверного случая лучше подходят. А для SysV IPC лучше, когда известно, какой из процессов главный, он и запустится первый, и будет обрабатывать исключительные ситуации, а остальные вы№бываться не должны.

katrin2201

Более того, поскольку запрашивающие процессы фильтруют очередь по своим пидам, через некоторое время этот ответ получит какой-то другой запрашивающий, что плохо, а предназначенный ему ответ останется на его месте, что тоже плохо!
1. Убрать пиды из критерия фильтра, вместо них воткнуть уникальные идентификаторы. Прикрутить механизм убивающий невостребованные ответы. Понятие "невостребованный" может варироваться.
2. Использовать IPC типа push а не pull.

vall

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

bleyman

Ну, хотелось бы не переделывать совсем уж всё, с другой стороны если можно это сделать достаточно немудрящим образом...
Во-первых, если создавать по паре локальных сокетов на каждую мессагу, это не жирновато будет? Для скоростей менее десятка мессаг в секунду, предположительно.
Во-вторых, если я хочу датаграмные сокеты (чтобы не париться с выделением мессаг из потока, которого потребуют обычные - и пайпы могу ли я узнать, живой ли второй конец, не посылая мессагу? Чтобы не обрабатывать запрос, от которого второй клиент уже отказался. write с нулевой длиной не сработает же, наверное?

Marinavo_0507

Во-первых, если создавать по паре локальных сокетов на каждую мессагу, это не жирновато будет? Для скоростей менее десятка мессаг в секунду, предположительно.
Во-вторых, если я хочу датаграмные сокеты (чтобы не париться с выделением мессаг из потока, которого потребуют обычные - и пайпы могу ли я узнать, живой ли второй конец, не посылая мессагу?
syslog работает так, не особо вроде напрягается
только там ушли с датаграмных сокетов на поточные (под предлогом лучшей устойчивости к DoS)
что там париться, если протокол текстовый, то ищёшь \n, если бинарный, то какой-нибудь TLV разобрать нетрудно

bleyman

Бля! IPC не перестаёт удивлять!
from `man semget`: The data structure associated with each semaphore in the set isn't initialized by the system call. In order to initialize those data structures, one has to execute a subsequent call to semctl(2) to perform a SETVAL or a SETALL command on the semaphore set.
Это как бля понимать? Типа даже если я объявлю, что один из процессов является мастером и пытается создать семафор с IPC_EXCL, а слейвы коннектятся не указывая IPC_CREAT, всё равно есть момент между созданием и инициализацией, в течение которого слейв может наткнуться на неинициализированные данные? Что за фигня вообще?
Но ладно, основная проблема, как выяснилось, такова: как бы невозможно передать другому процессу открытый файловый дескриптор (полученный из socketpair, например). То есть интернеты рассказывают про всякую платформозависимую, черезжёппную и очень чёрную магию, конечно, но она мне дико не нравится. Что же делать?

vall

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

bleyman

в общем забудь про sysv ipc — используй posix ipc

wat
Гугл мне говорит, что это оно и есть, что ты имел в виду?
Я вообще понял уже, что на самом деле UNIX way == "a lot of similar ways to do basically the same, each doing more than you want and doing it badly", и откуда у RMS Michael Stallman'а и его последователей зародилась идея о том, что если упорно называть говно шоколадкой (рабство свободой в его случае то название магически повлияет на реальность. Извините, наболело.
Так вот, если ты имел в виду сокеты, привязанные к ФС, типа сервер слушает один, а клиенты создают временные и через них посылают реквесты, то это тоже пиздец тот ещё. То есть если уж выбирать между теоретической возможностью коллижена в результатах ftok и теоретической возможностью 1) юзера, делающего rm -rf /tmp/, 2) oom или тупо ошибки ведущей к коредампу, убивая запрашивающий процесс до того как тот удалит свой сокет, что ведёт к пункту 1, записанному в крон, 3) и, в конце концов, абсолютно убийственной фичей всей этой поеботы, состоящей в том что я НЕ МОГУ создать временный сокет так, чтобы вызов tmpname (или tmpfile + unlink) и создание, собственно, сокета, произошли атомарно; так вот, я бы первое выбрал, если честно.
Простая же задача: есть сервис, есть клиенты, клиенты посылают сервису запросы и ждут ответа, если ответ не пришёл — таймаутятся. Как это сделать, чтобы вся фигня могла работать 24/7 не течя ничем? Интернетовские, блин, сокеты использовать?

amkharchenko

Ну вот конкретно семафоры SysV и Posix отличаются тем, что последние возможно создавать с заранее заданным значением (в отличие от давно устаревших SysV):
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);

Если я правильно понимаю, это решает вторую проблему исходного поста?

Marinavo_0507

зачем тебе временные файлы-то?

bleyman

А! Оказывается существует ещё один способ! (впрочем, то ли я опечатался, то ли на том древнем CentOS сервере, на котором я это тестил, mq_open не было, например). Для этих штук есть аналог `man 7 IPC`, который их бы перечислил хотя бы?
Ммм, ок, тогда у меня есть два вопроса...
1) Окуда имя семафора и очереди-то брать? Сейчас прога привязывается к своему конфигу (который довольно хитроумно ищется) и это всё очень мило, конечно, но ftok же прямо нужную иноду берёт из того места где я нашёл конфиг, а если я теперь хочу то же самое, но через имя (которое типа должно быть уникальным неужели мне нужно звать realpath? Он меня пугает как-то.
2) Вообще и Гадфазеру в частности: может, я дико туплю, но: клиент должен передать серверу некий токен, который тот мог бы использовать чтобы послать клиенту ответ. Что это может быть за токен? Если бы я использовал socketpair, то это был бы дескриптор одного из концов, но фиг его передашь, процессы вообще никак не связаны (хотя по идее эта несвязанность ничем не обоснована, сливать две кучи кода в одну мне не хочется) и, типа, да, проблема курицы и яйца. Если бы я использовал AF_LOCAL сокеты, то токен мог бы быть именем временного файла, созданного клиентом. Тогда есть две проблемы: во-первых, как обеспечить неутекание имён временных файлов в случае преждевременной кончины клиентов, во-вторых, я не увидел у функции socket параметра "создать бля временный файл прям сразу", она хочет имя, которое я могу получить либо вызвав tmpname (у которой в BUGS использующие её программисты обозваны "наивными" либо вызвав tmpfile и unlink (потому что socket ругается если файл линкнут на момент вызова что в общем-то то же самое. Я упускаю из виду какой-то ещё способ?
EDIT: вообще я уже не знаю, смеяться или плакать. Вот например поискал в гугле по аналогии с этим всем sock_open. Нашёл. http://linux.die.net/man/3/sock_open. Всё совсем как настоящее, только на самом деле это описание воображаемой функции, что заметно по безумному упоминанию carry flag и по отсутствию этого в мане последней убунты, например. Это часто так бывает, что энтузиасты пишут фанфики по ману, а? >.<

vall

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

Marinavo_0507

я не увидел у функции socket параметра "создать бля временный файл прям сразу", она хочет имя
где ты нашёл функцию socket , которая принимает имя?

Marinavo_0507

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

vall

да, но иногда файловые дескрипторы начинают кончаться. но это явно не тот случай.

Marinavo_0507

Переносимые мутексы, решающию проблему с неатомарной инициализацией общих структур, можно сделать хоть через flock. Собственно, этот же файл можно использовать и для генерации ключа для sysvipc, если хочется использовать его.

vall

гуглоиды кстати решая похожие проблемы в андройде вкрячили новый ipc в linux — binder, похоже что-то типа rpc с биндингом на строковые ключи и передачей аргументов через ioctl на чарактер-девайсе. спёрли то-ли из be-os то-ли из palm-os.
Оставить комментарий
Имя или ник:
Комментарий: