Кроссплатформенная двунаправленная IPC - PullRequest
3 голосов
/ 31 октября 2008

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

Вот упрощенное объяснение того, что мне нужно сделать: скажем, у меня есть большое количество простых программ, которые читают из stdin и пишут в stdout. (Это я не могу коснуться). По сути, ввод в stdin - это команда типа «Установить температуру 100» или что-то в этом роде. А на выходе появляется событие «Температура установлена ​​на 100» или «Температура упала ниже заданного значения».

Что я хотел бы сделать, так это написать приложение, которое может запускать несколько этих простых программ, наблюдать за событиями и затем отправлять им команды по мере необходимости. Мой первоначальный план состоял в том, чтобы что-то вроде popen, но мне нужно двунаправленное popen, чтобы получить и каналы чтения и записи. Я вместе взломал что-то, что я называю popen2, где я передаю ему команду для запуска и два FILE *, которые заполняются потоком чтения и записи. Затем все, что мне нужно сделать, это написать простой цикл, который читает из каждого stdouts каждого из процессов, выполняет необходимую ему логику и затем записывает команды обратно в соответствующий процесс.

Вот какой-то псевдокод

FILE *p1read, *p1write;
FILE *p2read, *p2write;
FILE *p3read, *p3write;

//start each command, attach to stdin and stdout
popen2("process1",&p1read,&p1write);
popen2("process2",&p2read,&p2write);
popen2("process3",&p3read,&p3write);

while (1)
{
   //read status from each process
   char status1[1024];
   char status2[1024];
   char status3[1024];
   fread(status1,1024,p1read);
   fread(status2,1024,p2read);
   fread(status3,1024,p3read);

   char command1[1024];
   char command2[1024];
   char command3[1024];
   //do some logic here

   //write command back to each process
   fwrite(command1,p1write);
   fwrite(command2,p2write);
   fwrite(command3,p3write);
}

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

Теперь это прекрасно работает на моем компьютере с UNIX и даже довольно хорошо на компьютере с Windows XP с Cygwin. Однако теперь мне нужно заставить его работать на Win32 изначально.

Сложная часть заключается в том, что мой popen2 использует fork () и execl () для запуска процесса и назначения потоков в stdin и stdout дочерних процессов. Есть ли чистый способ сделать это в Windows? По сути, я хотел бы создать popen2, который работает в Windows так же, как моя версия для Unix. Таким образом, в этой функции был бы единственный специфичный для Windows код, и я мог бы сойти с рук, если бы все остальное работало так же.

Есть идеи?

Спасибо!

Ответы [ 2 ]

5 голосов
/ 31 октября 2008

В Windows вы сначала вызываете CreatePipe (аналогично pipe (2)), затем CreateProcess. Хитрость в том, что CreateProcess имеет параметр, в котором вы можете передать stdin, stdout, stderr недавно созданного процесса.

Обратите внимание, что когда вы используете stdio, вам нужно выполнить fdopen, чтобы впоследствии создать объект файла, который ожидает номера файлов. В Microsoft CRT номера файлов отличаются от дескрипторов файлов ОС. Таким образом, чтобы вернуть другой конец CreatePipe вызывающей стороне, вам сначала нужно _open_osfhandle, чтобы получить номер файла CRT, а затем fdopen на этом.

Если вы хотите увидеть рабочий код, проверьте _PyPopen в

http://svn.python.org/view/python/trunk/Modules/posixmodule.c?view=markup

0 голосов
/ 31 октября 2008

Я думаю, что вы очень хорошо начали свою проблему, используя функцию popen2 () для отвлечения кроссплатформенных проблем. Я ожидал прийти и предложить «розетки», но я уверен, что это не актуально после прочтения вопроса. Вы можете использовать сокеты вместо каналов - это будет скрыто в функции popen2 ().

Я на 99% уверен, что вы можете реализовать необходимые функции в Windows, используя Windows API. Что я не могу сделать, так это надежно указать вам правильные функции. Тем не менее, вы должны знать, что Microsoft имеет большинство доступных API-вызовов POSIX-подобных, но перед именем стоит префикс '_'. Есть также собственные вызовы API, которые достигают эффектов fork и exec.

Ваши комментарии свидетельствуют о том, что вам известны проблемы с доступностью данных и возможными взаимоблокировками. Будьте осторожны.

...