Межпроцессное взаимодействие и Unix Domain Socket
Недавно по работе пришлось решать достаточно интересную задачу.
Нужно было написать приложение — демон, которое записывает мультикаст поток в файл.
При этом необходимо было писать несколько мультикаст потоков параллельно…
Более того… нужно централизованно управлять этими демонами!
Немного теории
По сути само приложение по себе — достаточно простое… Но вот встает вопрос управления… Когда демон уже запущен — ему надо передавать команды… например сменить файл, в который пишется поток… или вообще прекратить запись. Встал вопрос — как управлять такими демонами…
Перелапатив несколько страниц гугла и начитавшись заумных статей было решено остановиться на варианте с Unix Domain Socket…
Архитектура проста… Приложению при запуске, помимо параметров «чего куда писать» передается его уникальный id, по которому генерируется имя управляющего сокета ( банально sock_[id] ). Все сокеты создаются в заранее оговоренной директории на сервере… пусть это будет /tmp/my_socks/.
Таким образом приложение-менеджер при запуске делает листинг этой директории и смотрит — какие процессы у него запущены… затем проводит «ping» этих процессов (через эти же сокеты) и проверяет — какие из них действительно живы… Мертвые сокеты (у которых процесс по каким-то причинам отсутствует ) — можно сразу же удалить…
Ну а после того как сокет открыт можно обмениваться любыми командами с конкретным процессом… получается вот такое двухстороннее межпроцессное взаимодействие.
Но это теория! Ниже немного практики…
Реализация
Приложение, записывающее поток было реализовано на gcc
Нужные нам инклуды и структуры
Инициализируем сокет и начинаем его слушать:
void init_ctrl_socket() // Создаем сокет.. Семейство AF_UNIX указывает что это будет локальный unix-socket
if ( ( ctrlsock = socket(AF_UNIX, SOCK_STREAM, 0 ) ) < 0 ) savelog( "ERROR: control socket creating error" );
exit( 1 );
>// Делаем сокет неблокирующим.. чтобы при чтении не ждать..
int flags = fcntl(ctrlsock, F_GETFL, 0 );
if ( fcntl(ctrlsock, F_SETFL, flags | O_NONBLOCK) < 0 ) savelog( "ERROR: don't set socket on nonblocket mode" );
exit( 1 );
>// Вот тут самое интересное..
// биндим сокет. Передаем ему — путь до сокета..saun.sun_family = AF_UNIX;
strcpy(saun.sun_path, «/tmp/my_socks/sock_01» ); // вот тут путь до сокета
int len = sizeof (saun.sun_family) + strlen(saun.sun_path);if (bind(ctrlsock, ( struct sockaddr *)&saun, len) < 0 ) savelog( "ERROR: control socket binding error" );
exit( 1 );
>if (listen(ctrlsock, 5 ) < 0 ) savelog( "ERROR: control socket listening error" );
exit( 1 );
>
>
void check_ext_command() int fromlen = sizeof ( struct sockaddr );
if ( (ns = accept(ctrlsock, ( struct sockaddr *) &fsaun, &fromlen) ) > 0 ) char lbuf[ 255 ];
int ilen;
ilen = recv( ns, &lbuf, 255 , 0 );
lbuf[ilen]= ‘\0’ ;
do_remote_command( lbuf ); // Тут вызывается функция обработки команды..
close(ns);
>
>
Запись в сокет производиться как и обычно:
Менеджер для процессов
Менеджер для процессов был написан на php. Для того чтобы было удобно управлять ими через браузер…
Вот пример опроса существующих процессов
class reccontrol
var $socket_path = ‘/tmp/my_socks/’ ; // Путь где лежат сокеты приложений
var $socket_prefix = ‘sock_’ ; // Префикс сокеты (для эстетической красоты :))
var $list = array ();function getList() $this ->writers_info = array ();
$arr = scandir( $this ->socket_path);
unset ( $arr [ 0 ]); // .
unset ( $arr [ 1 ]); // ..
foreach ( $arr as $k => $s_name ) if ( ! $this ->ping( $s_name ) ) <
// Если не ответил на пинг — значит процесс умер или повис
@unlink( $this ->socket_path . $s_name );
unset ( $arr [ $k ] );
>
>
$this -> list = $arr ;>
// Реализация «опроса процессов»
function ping( $s_name ) $socket_name = $this ->socket_path. $s_name ; // полный путь
$sock = $this ->getSock( $socket_name ); // Вынес в отдельную функцию для удобстваif ( ! $sock ) return false ; // не отвечает — значит пинг не прошел
$buf = «ping» ;
if ( @socket_send( $sock , $buf , strlen( $buf ), 0 ) == false ) return false ; // не записать — значит пинг не прошел
>$buf = » ;
@socket_recv( $sock , $buf , 1024 , 0 );
if ( $buf ) <
// в $buf — то, что вернул нам процесс.. какую-то полезную инфу о себе
addlog( «Процесс » . $s_name . » ответил: » . $buf );
>socket_close( $sock );
return true ; // Сокет ответил — всё ок!
>// Непосредственно создание unix сокета.. даже проще, чем в Си 🙂
function getSock( $socket_name ) // Указываем, что семейство AF_UNIX
$socket = @socket_create(AF_UNIX, SOCK_STREAM, 0 );
// в $socket_name у нас лежит что-то типа /tmp/my_socks/sock_1 — т.е. путь до сокета
if ( @socket_connect( $socket , $socket_name ) == false ) < $err = socket_last_error();
if ( $err ) return false ; // не удалось открыть сокет 🙁
>
> else return $socket ; // всё ок — возвращаем дескриптов сокета
>
>>
Вот такое вот получается взаимодействие…
Здесь опубликованы только небольшие части кода — выполняющие основной функционал… Целиком просто всё это выглядит весьма громоздко и конкретная программа бесполезна обычному обывателю… Весь смысле в идеи управления…
Можно зайдя на web страничку получить информацию с нескольких процессов (она позвращается при пинге) и дать определенным процессам определенные команды…