буферизованное чтение / запись данных изображения между родителем и ребенком не завершается - PullRequest
0 голосов
/ 07 марта 2019

Введение и общая цель

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

Теперь я пытаюсь отправить изображение из родительского процесса в дочерний процесс (созданный путем вызова popen из родительского процесса), который должен показать iamge.

Изображение представляет собой изображение в градациях серого png. Он открывается библиотекой OpenCV и кодируется с использованием функции imencode той же библиотеки. Таким образом, результирующие закодированные данные сохраняются в структуре std::vector типа uchar, а именно в img в коде ниже.

Нет ошибок при отправке некоторых данных

Размер изображения жестко запрограммирован в child в переменной img_size для простоты). Это используется для выделения памяти, в которой хранятся полученные данные. Распределение следующее:

    u_char *buf = (u_char*)malloc(img_size*sizeof(u_char));

Отправка данных изображения

Родитель записывает закодированные данные (т.е. данные, содержащиеся в векторе img) в поток FILE*, возвращаемый popen, используя fwrite, в то время как дочерний объект читает данные с fread, используя как FILE* результат преобразования файлового дескриптора STDIN_FILENO:

FILE * fp = fdopen(STDIN_FILENO, "r");

Запись данных выполняется в блоках 4096 байтов внутри цикла while, в то время как чтение данных читает в двух вызовах fread (я знаю, что это ужасно, но это изменится).

Цикл записи следующий:

while (written<img.size())
{
    //bytes to send: 4096 or the remaining ones if less than 4096
    toWrite = BUFLEN < (img.size()-written) ? BUFLEN : (img.size()-written);
    //total bytes that have been sent until now
    written += toWrite*fwrite ( img.data()+written, toWrite, 1, f );
    printf("written: %ld\n", written);
 }

img.data() возвращает указатель на первый элемент в массиве, который внутренне используется векторной структурой. written хранит количество байтов, которые были записаны до сих пор, и используется как индекс. fwrite возвращает количество блоков (размером toWrite байт каждый), которые фактически были записаны, и это используется для обновления written.

Считывание данных очень похоже и выполняется следующей строкой (bytes2Copy инициализируется в 4096):

//read all possible bytes in blocks of 4096 until less than 4096 bytes remain
elRead = fread ( buf, bytes2Copy, img_size/bytes2Copy, fp);
total_bytes_read += elRead*bytes2Copy;
bytes2Copy = img_size-total_bytes_read; //now less than 4096 bytes remain
printf("child received %ld\n", total_bytes_read);
//read remaining bytes
elRead = fread ( buf+total_bytes_read, bytes2Copy, 1, fp);

buf - массив, в котором хранятся полученные данные. bytes2Copy - это либо BUFLEN (то есть 4096), либо для последнего блока данных оставшиеся данные (например, если общее количество байтов равно 5000, то после 1 блока 4096 байтов другой блок 5000-4096 ожидается).

код

Рассмотрим этот пример. Ниже приведен процесс запуска дочернего процесса с popen

#include <stdlib.h>
#include <unistd.h>//read
#include "opencv2/opencv.hpp"
#include <iostream>
#include <sys/wait.h>
#include "opencv2/opencv.hpp"
#define BUFLEN 4096

using namespace cv;

int main(int argc, char *argv[])
{
    int status;
    Mat frame;
    std::vector<uchar> img;
    //read image as grayscale
    frame = imread("/home/path/img/test.png",0);
    //encode image and put data into the vector buf
    imencode(".png",frame, img);
    //send the total size of vector to parent

    size_t toWrite = 0;
    size_t written= 0;
    FILE * f = popen("/home/path/childProcess", "w");
    printf("forked\n");
    while (written<img.size())
    {
        //send the current block of data
        toWrite = BUFLEN < (img.size()-written) ? BUFLEN : (img.size()-written);
        //written += write(STDOUT_FILENO, buf.data()+written, toWrite);
        written += toWrite*fwrite ( img.data()+written, toWrite, 1, f );
        printf("written: %ld\n", written);
    }
    wait(&status);
    return 0;

}

и процесс, открываемый выше, соответствует следующему:

#include <stdlib.h>
#include <unistd.h>
#include "opencv2/opencv.hpp"
#include <iostream>
#include <time.h>
#define BUFLEN 4096

int main(int argc, char *argv[])
{
    cv::Mat frame;
    size_t img_size = 115715;
    u_char *buf = (u_char*)malloc(img_size*sizeof(u_char));
    size_t total_bytes_read = 0;
    size_t elRead =0;
    size_t bytes2Copy = BUFLEN;
    FILE * fp = fdopen(STDIN_FILENO, "r");


    elRead = fread ( buf, bytes2Copy, img_size/bytes2Copy, fp);
    total_bytes_read += elRead*bytes2Copy;
    bytes2Copy = img_size-total_bytes_read;
    printf("child received %ld\n", total_bytes_read);
    elRead = fread ( buf+total_bytes_read, bytes2Copy, 1, fp); //sostituire 1
    total_bytes_read += elRead*bytes2Copy;//bytes_read_tihs_loop;
    printf("child received %ld\n", total_bytes_read);

    cv::namedWindow( "win", cv::WINDOW_AUTOSIZE );
    frame  = cv::imdecode(cv::Mat(1,total_bytes_read,0, buf), 0);
    cv::imshow("win", frame);
    cv::waitKey(0);
    return 0;

}

Ошибка

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

Ребенок читает данные в блоках 4096. Однако во втором вызове fread(), где отсутствует менее 4096 байтов, он пытается прочитать только отсутствующие байты: в моем случае второй вызов frad должен прочитать 1027 байтов (115715%4096). В этом втором вызове он ничего не читает, хотя родитель отправляет (или, по крайней мере, кажется, отправляет) все данные.

Почему fread() не читает все пропущенные байты?

Я работаю над этим изображением: enter image description here

Могут также быть ошибки в том, как я пытаюсь декодировать обратно изображение, поэтому любая помощь там будет признательна.


Рабочий код для старого вопроса

Обратите внимание, что после ответа на исходный вопрос мне удалось правильно отправить данные, но от ребенка к родителю (в другом направлении). рабочий код для этого следующий:

Родитель:

#include <stdlib.h>
#include <unistd.h>//read
#include "opencv2/opencv.hpp"
#include <iostream>
#include <time.h>
#define BUFLEN 1024

int main(int argc, char *argv[])
{
    //file descriptor to the child process
    FILE *fp;
    cv::Mat frame;
    char temp[10] ={0};
    size_t bytes_read_tihs_loop = 0;
    size_t total_bytes_read = 0;
    unsigned short int elRead =0;
    size_t bytes2Copy = BUFLEN;
    //launch the child process with popen
    clock_t start, end;
    double cpu_time_used;

    start = clock();
    if ((fp = popen("/home/path/childProcess", "r")) == NULL)
    {
        //error
        return 1;
    }

    //read the number of btyes of encoded image data
    fgets(temp, 10, fp);
    //convert the string to int
    size_t bytesToRead = atoi((char*)temp);
    //some prints
    std::cout<<bytesToRead<<std::endl;

    //allocate memory where to store encoded iamge data that will be received
    u_char *buf = (u_char*)malloc(bytesToRead*sizeof(u_char));

    //initialize the number of bytes read to 0
    printf ("bytesToRead: %ld\n",bytesToRead);
    elRead = fread ( buf+total_bytes_read, bytes2Copy, bytesToRead/bytes2Copy, fp);
    total_bytes_read += elRead*bytes2Copy;
    bytes2Copy = bytesToRead-total_bytes_read;
    elRead = fread ( buf+total_bytes_read, bytes2Copy, bytesToRead/bytes2Copy, fp);
    total_bytes_read += elRead*bytes2Copy;
    printf("%lu bytes received over %lu expected\n", total_bytes_read, bytesToRead);
    printf("%lu final bytes read\n", total_bytes_read);
    pclose(fp);
    cv::namedWindow( "win", cv::WINDOW_AUTOSIZE );
    frame  = cv::imdecode(cv::Mat(1,total_bytes_read,0, buf), 0);
    cv::imshow("win", frame);
    end = clock();
    cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
    printf("time with %d buffer length: %f\n", BUFLEN, cpu_time_used);
    cv::waitKey(0);
    return 0;

}

Ребенок:

#include <unistd.h> //STDOUT_FILENO
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace std;
using namespace cv;

#define BUFLEN 4096

int main(int argc, char *argv[])
{
    Mat frame;
    std::vector<uchar> buf;
    //read image as grayscale
    frame = imread("/home/path/test.png",0);
    //encode image and put data into the vector buf
    imencode(".png",frame, buf);
    //send the total size of vector to parent
    cout<<buf.size()<<endl;
    unsigned int written= 0;

    int i = 0;
    size_t toWrite = 0;
    //send until all bytes have been sent
    FILE * f = fdopen(STDOUT_FILENO, "w");
    while (written<buf.size())
    {
        //send the current block of data
        toWrite = BUFLEN < (buf.size()-written) ? BUFLEN : (buf.size()-written);
        //written += write(STDOUT_FILENO, buf.data()+written, toWrite);
        written += toWrite*fwrite ( buf.data()+written, toWrite, 1, f );
        i++;
    }
    return 0;

}
...