Нужна помощь с именованными трубами и popen - PullRequest
0 голосов
/ 27 июля 2010

У меня есть маленький C-сервер, который должен принять соединение и разветвить дочерний процесс.Мне нужно stderr дочернего процесса, чтобы перейти к уже существующему именованному каналу, stdout дочернего процесса, чтобы перейти к stdout родителя, и stdin дочернего tp происходят из того жепоместите в качестве stdin родителя.

Мои первоначальные попытки включали popen(), но я никогда не мог получить именно то, что хотел.

Наконец, это конкретное решение должно работать тольков солярисе.Спасибо.

РЕДАКТИРОВАТЬ: Обновил вопрос в надежде на более точное отображение того, что я пытаюсь сделать.Спасибо за терпение ко мне.

EDIT2: Мне также нужно, чтобы родительский элемент получил возвращаемое значение дочернего процесса и затем что-то сделал с ним, если это что-то изменит.

1 Ответ

5 голосов
/ 27 июля 2010

Возможно, вы используете неправильную функцию - popen() используется, когда вы хотите, чтобы вызывающая программа либо записывала на стандартный ввод разветвленного процесса, либо читала с его стандартного выхода.Кажется, вы не хотите ни того, ни другого.Он также принимает два аргумента.

Ваши требования также несколько противоречивы:

  • Я хочу, чтобы он (в идеале) наследовал stdin и stdout от родителя

  • любые входные данные для родителя отправляются дочернему элементу, а любые исходные данные для дочернего узла возвращаются родителю

  • но, как минимум, я бы хотел, чтобынаследовать stdin и записывать stdout в именованный канал

Первый вариант прост - он не требует специального кодирования.Любые данные, передаваемые в stdin родительского элемента, также будут доступны в stdin дочернего элемента (но только один из двух процессов сможет его прочитать).Стандартный вывод ребенка обычно идет в то же место, что и стандартный вывод родителя.Если вы хотите, чтобы родитель прочитал стандартный вывод ребенка, тогда вам нужен канал - и тогда popen() уместно, но «минимум» запутывает.

Итак, давайте определим, что вы действительно хотите?

Опция 1

  1. Стандартная ошибка дочернего элемента должна идти в именованный канал.
  2. Стандартный вывод дочернего элемента должен быть прочитан вызывающим процессом.
  3. Стандартный ввод дочернего элемента должен исходить из того же места, что и стандартный ввод родительского элемента.
  4. Именованный канал уже существует.

Следовательно:

FILE *fp = popen("/run/my/command -with arguments 2>/my/other/pipe", "r");

Обратите внимание, что дочерний элемент будет зависать, пока процесс не откроет / my / other / pipe для чтения;это, в свою очередь, означает, что если родительский процесс читает из fp, он также будет зависать, пока какой-то другой процесс не откроет «/ my / other / pipe» для чтения.

Option 2

  1. Стандартная ошибка потомка должна идти в именованный канал.
  2. Стандартный вывод потомка должен идти в стандартный вывод родителя.
  3. Стандартный ввод потомкадолжен исходить из того же места, что и стандартный ввод родительского элемента.
  4. Именованный канал уже существует.

Теперь popen() не подходит, и мы попадаем в голый `fork& exec 'код.Далее следует больше псевдокода, чем действующего C.

if ((pid = fork() < 0)
    error
else if (pid > 0)
{
    /* Parent - might wait for child to complete */
}
else
{
    int fd = open("/my/other/pipe", O_WRONLY|O_NONBLOCK);
    if (fd < 0)
        error
    dup2(fd, 2);  /* There is a symbolic name for stderr too */
    close(fd);    /* Do not want this open any more */
    char *cmd[4] = { "/bin/sh", "-c", "/run/my/command -with arguments", 0 };
    execv(cmd[0], cmd);
    error - if execv returns, it failed!
}

Если вы абсолютно уверены, что никто не выполнял какие-либо трюки на вас, например закрытие stdout, вы можете избежать использования dup2(), закрыв stderr (fd = 2) перед вызовом open().Однако, если вы сделаете это, вы не сможете больше сообщать об ошибках - потому что вы закрыли stderr.Итак, я бы сделал это, как показано.

Если у вас другое требование, укажите, чего вы хотите достичь.


Как отмечает p2vb, если вы хотите, чтобы родитель ждалчтобы ребенок закончил, достаточно просто использовать system().Если родительский элемент должен продолжать работу во время работы дочернего элемента, вы можете попробовать system(), где строка команды заканчивается амперсандом (&), чтобы поместить дочерний элемент в фоновый режим, или вы можете использовать код, описанный в варианте 2 выше.

Используя system(), у родителя будет мало шансов прочитать / my / other / pipe, который получает стандартную ошибку от потомка.Вы можете легко заблокировать блокировку, если ребенок производит много.

Кроме того, будьте осторожны с вашим флагом FD_CLOEXEC - установите его для файлов, которые вы не хотите, чтобы дочерний элемент изменял.В Linux вы можете использовать флаг O_CLOEXEC при вызове open();в Solaris его необходимо установить с помощью fcntl() - осторожно.

...