Как связать стандартный вывод в одном дочернем процессе со стандартным входом другого дочернего процесса в C? - PullRequest
4 голосов
/ 02 сентября 2011

Я бездельничал в C, пытаясь понять, как это сделать.Допустим, у меня есть моя основная программа, родительский процесс.Родитель создает три дочерних процесса, каждый из которых в конечном итоге будет запускать программы (но это сейчас не важно).То, что я хотел бы сделать, это сделать так, чтобы стандартный вывод первого ребенка был получен стандартным выводом второго ребенка.Stdout второго ребенка будет затем получен stdin третьего ребенка.Stdin / stdout родительского процесса совсем не запутались.

Пока что у меня есть

pipe(procpipe);

parentPid = getpid();

for(i = 0; i < 3; i++)
{
    if(getpid() == parentPid)
    {
        child[i] = fork();
        if(child[i] == 0)
        {
            mynumber = i+1;
        }
    }
}

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

Изменить: Забыл упомянуть: предположим, что все мои переменные правильно инициализированы.Int 'mynumber' - это то, что дочерний процесс знает при создании, какое это число, поэтому я могу дать ему инструкции через

if(mynumber == whatever)

1 Ответ

14 голосов
/ 02 сентября 2011

Итак, у вас есть цикл, который создает несколько дочерних процессов.Каждый из этих дочерних процессов будет использовать два канала: читать из предыдущего и записывать в следующий.Чтобы настроить канал для конца чтения, вам нужно закрыть конец записи канала и dup2 конец чтения в стандартный ввод.Аналогично для трубы, в которую будет записываться процесс.

void set_read(int* lpipe)
{
    dup2(lpipe[0], STDIN_FILENO);
    close(lpipe[0]); // we have a copy already, so close it
    close(lpipe[1]); // not using this end
}
  
void set_write(int* rpipe)
{
    dup2(rpipe[1], STDOUT_FILENO);
    close(rpipe[0]); // not using this end
    close(rpipe[1]); // we have a copy already, so close it
}

Когда вы разветвляете каждого потомка, вам нужно прикрепить к нему трубы.

void fork_and_chain(int* lpipe, int* rpipe)
{
    if(!fork())
    {
        if(lpipe) // there's a pipe from the previous process
            set_read(lpipe);
        // else you may want to redirect input from somewhere else for the start
        if(rpipe) // there's a pipe to the next process
            set_write(rpipe);
        // else you may want to redirect out to somewhere else for the end

        // blah do your stuff
        // and make sure the child process terminates in here
        // so it won't continue running the chaining code
    }
}

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

// This assumes there are at least two processes to be chained :)

// two pipes: one from the previous in the chain, one to the next in the chain
int lpipe[2], rpipe[2];

// create the first output pipe
pipe(rpipe);

// first child takes input from somewhere else
fork_and_chain(NULL, rpipe);

// output pipe becomes input for the next process.
lpipe[0] = rpipe[0];
lpipe[1] = rpipe[1];

// chain all but the first and last children
for(i = 1; i < N - 1; i++)
{
    pipe(rpipe); // make the next output pipe
    fork_and_chain(lpipe, rpipe);
    close(lpipe[0]); // both ends are attached, close them on parent
    close(lpipe[1]);
    lpipe[0] = rpipe[0]; // output pipe becomes input pipe
    lpipe[1] = rpipe[1];
}

// fork the last one, its output goes somewhere else      
fork_and_chain(lpipe, NULL);
close(lpipe[0]);
close(lpipe[1]);

Закрывающие биты очень важны!Когда вы работаете с открытым каналом, будет четыре дескриптора открытого файла: два в родительском процессе и два других в дочернем процессе.Вы должны закрыть все те, которые вы не будете использовать.Вот почему приведенный выше код всегда закрывает нерелевантные концы каналов в дочерних процессах, и оба конца на родительских процессах.

Также обратите внимание, что я обращаю особое внимание на первый и последний процессы, потому что я надеваюне знаю, откуда поступит вход для цепочки, и куда пойдет выход.

...