C: Как перенаправить именованный канал в стандартный вывод / дочерний процесс - PullRequest
3 голосов
/ 27 декабря 2011

В основном я хочу сделать в C (и без буферизации) то же самое, что и этот bash-скрипт:

#!/bin/sh
cat ./fifo_in | myprogram > ./fifo_out

Другими словами, я хочу выполнить «myprogram» и перенаправить его stdin и stdout на дваканалы, которые были созданы ранее.

Другая программа передает данные в fifo_in и считывает данные из fifo_out.

Конечно, было бы легко просто прочитать из ./fifo_in, буферизировать его вparent и запись в stdin myprogram (и наоборот для stdout и ./fifo_out), но я думаю, что, вероятно, есть способ разрешить myprogram читать / писать непосредственно из / в fifo без буферизации в родительском процессе.

Edit:

Кажется, что ответ Евгения правильный, но я не могу заставить его работать.

Я использую эту функцию на стороне C, что мне кажется правильным:

pid_t execpipes(const char *wd, const char *command, const char *pipename)
{
char pipename_in[FALK_NAMESIZE];
char pipename_out[FALK_NAMESIZE];
strcpy(pipename_in, FALKPATH);
strcat(pipename_in, "/");
strcat(pipename_in, FALK_FIFO_PATH);
strcat(pipename_in, "/");
strncat(pipename_in, pipename, FALK_NAMESIZE-2);
strcpy(pipename_out, pipename_in);
strcat(pipename_out, "R");

pid_t pid;
pid = fork();
if (pid < 0)
{   //Error occured
    perror("fork");
    exit(1);
}
if (pid == 0)
{
    chdir(wd);
    d("execpipes: pipename_in=\"%s\"\n", pipename_in);
    d("          pipename_out=\"%s\"\n", pipename_out);
    freopen(pipename_in,"r",stdin);
    freopen(pipename_out,"w",stdout);

    d("execpipes: command=\"%s\"\n", command);

    execl("/bin/sh", "sh", "-c", command, (char *)NULL); // using execv is probably faster
    // Should never get here
    perror("execl");
    exit(1);
}
return pid;
}

Я читаю и пишу каналы из PHP-скрипта (размещена только соответствующая часть):

$pipe_in = fopen($fp.$pipename, "w");
$DEBUG .= "Write to pipe_in\n";
$ret = fwrite($pipe_in, $in);

$pipe_out = fopen($fp.$pipename.'R', "r");
$DEBUG .= "Read from pipe_out\n";
$atext = fread($pipe_out, 200000);  // Program hangs here

Программа запускается правильно, получает входные данные через $pipe_in правильно, обрабатываетданные правильнои (поскольку он работал в течение многих месяцев), я предполагаю, что он корректно выводит данные на стандартный вывод, но когда я пытаюсь прочитать из $pipe_out, он зависает.Я знаю, что сами каналы настроены правильно, потому что если я не открою $pipe_out, программа не получит никакого ввода - это имеет смысл, потому что нет считывателя для $pipe_out и, следовательно, конвейер не завершен.Так что я могу открыть $pipe_out, но я не могу ничего с него прочитать, что довольно странно.

Edit2:

Программа работает, спасибо, ребята - По какой-то причине первый канал долженбыть закрытым, прежде чем вы сможете прочитать из второго канала:

$pipe_in = fopen($fp.$pipename, "w");
$pipe_out = fopen($fp.$pipename.'R', "r");
$DEBUG .= "Write to pipe_in\n";
$ret = fwrite($pipe_in, $in);
fclose($pipe_in);

$DEBUG .= "Read from pipe_out\n";
$atext = fread($pipe_out, 200000);
fclose($pipe_out);

unlink($fp.$pipename);
unlink($fp.$pipename.'R');

Ответы [ 4 ]

2 голосов
/ 27 декабря 2011

Я бы написал небольшую оболочку для моей программы, которая делает

freopen("./fifo_in","r",stdin)
freopen("./fifo_out","w",stdout)

(Конечно, не с постоянными путями!), Затем выполните myprogram

0 голосов
/ 28 декабря 2011

Возможно, вы ищете именованную трубу ?Например:

   mkfifo fifo_in

В качестве тестовой заглушки для my_program.c, чтобы прочитать fifo_in через буферизованный stdin:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
   char buf[80];
   if (!freopen("./fifo_in", "r", stdin)) {
      perror("freopen");
      exit(EXIT_FAILURE);
   }
   while (!ferror(stdin)) {
      while (fgets(buf, sizeof buf, stdin))
         fputs(buf, stdout);
      sleep(1);
   }
   return 0;
}

Затем в качестве теста для писателя,используя оболочку bash:

for x in {1..10}; do 
   echo $x 
   echo $x >> fifo_in 
   sleep 1 
done

Примечания:

  • Я бы предпочел использовать небуферизованный ввод / вывод.
  • Автор, по крайней мере, намоя машина блокируется до тех пор, пока не появится считыватель.
  • В данном примере считыватель не может определить время окончания записи.
0 голосов
/ 27 декабря 2011

Как насчет

myprogram < ./fifo_in > ./fifo_out

?

Что касается избавления от буферизации: так как ваша программа непосредственно читает / записывает каналы, буферизация не должна причинять вам вред.

Важным моментом является то, что процесс, который пишет fifo_in, должен корректно сбрасываться, поэтому вам не придется ждать.То же самое относится и к вашему выводу: как только «рабочий блок» будет завершен, очистите ваш stdout, который сделает данные доступными для любого, кто читает выходной канал.

Но вы ничего не можете сделать вmyprogram чтобы заставить писателя fifo_in очистить свои буферы.

[EDIT] Чтобы сделать это из C (без помощи оболочки), используйте такой код:

- Put the names of the two pipes into local variables on the stack
- Call `fork()`. If that returns '0', then open the two fifos with `freopen()` [like Eugen suggested][1]
- Call `execve` to launch the real exec.

Это (в двух словах), что делает оболочка, когда она запускает команды.Убедитесь, что родительский процесс (тот, где fork() возвращает PID! = 0) обрабатывает сигнал SIGCHLD

0 голосов
/ 27 декабря 2011

Оболочка Korn поддерживает сопроцессы, которые, я думаю, эффективно выполняют то, что вы просите: читать из канала и записывать в канал (который может быть stdout и stdin процесса C)

http://www.dartmouth.edu/~rc/classes/ksh/coprocesses.html
...