С МПК ждет ребенка - PullRequest
       17

С МПК ждет ребенка

1 голос
/ 30 апреля 2010

Итак, у меня есть программа, которая создает дочерний процесс и выполняет команду (например, ls).Затем родитель будет использовать каналы для отправки и получения от ребенка.Это прекрасно работает, когда я сам вводю команды из командной строки.

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

Если не использовать sleep (), есть ли лучший способ убедиться, что ребенок побежал, прежде чем пытаться читать из него?

Большое спасибо!

Ответы [ 3 ]

2 голосов
/ 30 апреля 2010

Если вы не установили дескриптор файла канала как неблокирующий, тогда проблем быть не должно. Любое чтение по каналу будет блокироваться до тех пор, пока дочерний процесс не выдаст результат; если вам нужно реагировать на несколько файловых дескрипторов (например, стандартный ввод от пользователя и канал от дочернего процесса), используйте select() или poll().

Я предполагаю, что fgets() возвращает NULL. Это указывает либо на конец файла (что означает, что дочерний элемент закрыл свой конец канала), либо на ошибку. Вы можете проверить, что из этого верно, используя feof() или ferror(). Используйте perror() в случае ошибки, чтобы увидеть, что это за ошибка.

1 голос
/ 30 апреля 2010

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

Во-первых, вы можете использовать условную переменную. Объявите переменную, к которой могут обращаться оба потока, и установите для нее значение 0. Ваш дочерний поток установит его на 1, когда он будет готов для чтения родительским элементом. Родитель будет ждать, пока переменная не изменится на 1 (используя что-то вроде while(!condvar) sleep(1);), затем он прочитает из канала и сбросит переменную до 0, чтобы дочерний элемент знал, что родительский элемент закончен.

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

Редактировать: Поскольку вы порождаете дочерний процесс с fork вместо потоков, вы не можете использовать здесь условную переменную (родительский и дочерний будут иметь отдельные копии).

Вместо этого вы можете использовать функции signal() и kill() для передачи сигналов между процессами. Перед разветвлением используйте getpid, чтобы сохранить копию pid родителя (для дочернего процесса). Также сохраните возвращаемое значение fork, так как оно будет содержать pid ребенка.

Чтобы отправить сигнал другому процессу, используйте что-то вроде:

kill(parent_pid, SIGUSR1);

Процесс приема должен настроить обработчик сигнала. Например:

int signal_received = 0;
void signal_handler(int signal_num) {
    if (signal_num == SIGUSR1)
        signal_received = 1;
}

signal(SIGUSR1, signal_handler);

Функция signal_handler теперь будет вызываться автоматически всякий раз, когда процесс получает номер сигнала SIGUSR1. Ваш поток будет ждать в цикле, наблюдая за изменением этой переменной, используя что-то вроде:

while (1) { // Signal processing loop
    // Wait here for a signal to come in
    while (!signal_received) { sleep(1); }

    // Wake up and do something
    read_from_pipe();
    ...
    signal_received = 0;
}
1 голос
/ 30 апреля 2010

Вопрос в том, насколько вы контролируете ребенка? Если вы пишете ребенку самостоятельно, вы можете заставить ребенка установить флаг в совместно используемой памяти, который он запустил (или коснуться файла или чего-либо другого, что вам нравится), и только после того, как вы узнаете, что ребенок в сети, запустите его.

Если у вас нет контроля над дочерним элементом, все еще нет возможности реализовать описанное выше решение, обернув дочерний элемент собственным сценарием, который сначала запускает дочерний элемент, а затем устанавливает общую память. Это может быть в форме скрипт оболочки следующим образом

#!/bin/sh

$CHILD
my_app_that_set_the_flag

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

...