У меня есть проект, который, как я думал, будет относительно простым, но он оказался более болезненным, чем я надеялся. Во-первых, большая часть кода, с которым я взаимодействую, - это устаревший код, над которым у меня нет контроля, поэтому я не могу вносить большие изменения в парадигму.
Вот упрощенное объяснение того, что мне нужно сделать: скажем, у меня есть большое количество простых программ, которые читают из стандартного ввода и записывают в стандартный вывод. (К ним я не могу прикоснуться). По сути, ввод на стандартный ввод - это команда типа «Установить температуру на 100» или что-то в этом роде. И выходом является событие «Температура установлена на 100» или «Температура упала ниже заданного значения».
Я бы хотел написать приложение, которое может запускать кучу этих простых программ, отслеживать события и затем при необходимости отправлять им команды. Мой первоначальный план состоял в том, чтобы сделать что-то вроде popen, но мне нужен двунаправленный popen, чтобы получить каналы чтения и записи. Я взломал что-то вместе, что я называю popen2, где я передаю ему команду для запуска и два FILE *, которые заполняются потоком чтения и записи. Затем все, что мне нужно сделать, это написать простой цикл, который читает из каждого стандартного вывода каждого из процессов, выполняет необходимую логику и затем записывает команды обратно в соответствующий процесс.
Вот какой-то псевдокод
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, будет в этой функции, и я смогу обойтись без того, чтобы все остальное работало таким же образом.
Есть идеи?
Спасибо!





Я думаю, вы очень хорошо начали свою проблему, используя функцию popen2 (), чтобы абстрагироваться от кроссплатформенных проблем. Я ожидал прийти и предложить «сокеты», но я уверен, что это не актуально после прочтения вопроса. Вы можете использовать сокеты вместо каналов - это будет скрыто в функции popen2 ().
Я на 99% уверен, что вы можете реализовать требуемые функции в Windows, используя Windows API. Чего я не могу сделать, так это надежно указать вам на нужные функции. Однако вы должны знать, что у Microsoft есть большинство доступных вызовов API, подобных POSIX, но имя имеет префикс '_'. Существуют также собственные вызовы API, которые достигают эффектов fork и exec.
Ваши комментарии предполагают, что вы знаете о проблемах с доступностью данных и возможных тупиках - будьте осторожны.
Как и Cygwin. Я знаю, что есть проблемы и трудности - отсюда и ласковые слова «которые достигают эффекта fork и exec».
В 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
В WIN32 НЕТ механизма fork (), хотя продукт Interix явно поддерживает его.