Как использовать канал между родительским и дочерним процессами после вызова popen? - PullRequest
0 голосов
/ 03 февраля 2019

Я хочу связаться с дочерним процессом следующим образом:

int main(int argc, char *argv[])
{
    int bak, temp;
    int fd[2];
    if (pipe(fd) < 0)
    {
        // pipe error
        exit(1);
    }
    close(fd[0]);
    dup2(STDOUT_FILENO, fd[1]);
    fflush(stdout);
    bak = dup(1);
    temp = open("/dev/null", O_WRONLY);
    dup2(temp, 1);
    close(temp  );
    Mat frame;
    std::vector<uchar> buf;
    namedWindow( "Camera", WINDOW_AUTOSIZE );
    VideoCapture cam(0 + CAP_V4L);
    sleep(1);
    if (!cam.isOpened())
    {
        cout << "\nCould not open reference " << 0 << endl;
        return -1;
    }
    for (int i=0; i<30; i++)
    {
        cam>>frame;
    }
    //cout<<"\nCamera initialized\n";
    /*Set the normal STDOUT back*/
    fflush(stdout);
    dup2(bak, 1);
    close(bak);

    imencode(".png",frame, buf);
    cout<<buf.size()<<endl;
    ssize_t written= 0;
    size_t s = 128;
    while (written<buf.size())
    {
     written += write(fd[1], buf.size()+written, s);

    }
    cout<<'\0';
    return 0;

}

Процесс, соответствующий компиляции приведенного выше исходного кода, вызывается из родительского процесса с popen.

* 1006.* Обратите внимание, что я пишу в стандартный вывод, который был дублирован с pipe.

Родитель будет читать данные и отправлять их в сокет UDP.

Если я сделаю что-то вродеэто:

#define BUFLEN 128
FILE *fp;
char buf[BUFLEN];
if ((fp = popen("path/to/exec", "r")) != NULL)
{
    while((fgets(buf, BUFLEN, fp)!=NULL))
    {
        sendto(sockfd, buf, strlen(buf),0, addr, alen);
    }
}

программа работает, т.е. получатель sendto получит данные.

Я попытался использовать pipe, как это было сделано в дочернем процессе:

    int fd[2];
    if (pipe(fd) < 0)
    {
        // pipe error
        exit(1);
    }
    close(fd[1]);
    dup2(STDIN_FILENO, fd[0]);
    if ((fp = popen("path/to/exec", "r")) != NULL)
    {
        while((read(fd[0], buf, BUFLEN) > 0)
        {
            sendto(sockfd, buf, strlen(buf),0, addr, alen);
        }
    }

но с этим не отправляются.

Так как же использовать pipe в этом случае для достижения того же поведения в первом случае?Должен ли я делать dup2(STDIN_FILENO, fd[0]); или dup2(STDOUT_FILENO, fd[0]);?

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

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

В родительском:

if (pipe(fd) < 0)
{
    // pipe error
    exit(1);
}
close(fd[0]);

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

Затем в дочернем элементе:

if (pipe(fd) < 0)
{
    // pipe error
    exit(1);
}
close(fd[1]);

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

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

0 голосов
/ 04 февраля 2019

Обычный способ вручную настроить канал, из которого родительский процесс может читать стандартный вывод дочернего процесса, имеет следующие общие шаги:

  • родитель создает канал, вызывая pipe()
  • parent fork() s
  • parent закрывает (уточнение: его копия ) конец записи канала
  • child дублирует конец записи канала наего стандартный вывод через dup2()
  • дочерний элемент закрывает исходный дескриптор файла для конца записи канала
  • (необязательно) дочерний элемент закрывается (уточнение: его копия )конец чтения канала
  • дочерний выполняет желаемую команду или выполняет требуемую работу напрямую

Затем родительский объект может прочитать выходные данные дочернего элемента из конца чтения канала.

Функция popen() делает все это за вас, плюс заключает конец родительского канала в FILE.Конечно, он может и будет устанавливать канал, идущий в противоположном направлении, если это то, что запрашивает вызывающий.

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

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

В целом, однако, есть более широкая картина, которую вы, похоже, не оценили.Даже если вы выполнили перенаправление, которое, как вам кажется, нужно в родительском объекте, чтобы оно могло быть унаследовано дочерним объектом, popen() выполняет свое собственное перенаправление в канал свое собственное создание .Возвращаемое FILE * - это средство, с помощью которого вы можете прочитать вывод ребенка.Никакое предыдущее перенаправление вывода, которое вы, возможно, выполнили, не уместно (уточнение: стандартного вывода ребенка).

В принципе, подход, подобный вашему, мог бы использоваться для создания второго перенаправления, идущего другим путем, но при этомТочка удобства фактор popen() полностью потерян.Было бы лучше пойти по прямому маршруту pipe / fork / dup2 / exec до конца, если вы хотите перенаправить ввод дочернего элемента и .


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

...