pipe() 和 fork() 示例:basic_string::M_construct null 无效

pipe() and fork() example: basic_string::M_construct null not valid

另一个让并行进程工作的尝试。请原谅代码量,但每次尝试缩短它都会使错误消失。

到目前为止我测试了什么:

但是当我 运行 第二次运行相同的功能时,它总是失败。

这是创建子进程的函数:

//parent sends binary data from list to child which sends back modified data
bool processParallel6(std::vector<std::pair<int, std::string>> & data)
{
    //define pipe
    int parent2Child[2];
    int child2Parent[2];

    //create pipe
    pipe(parent2Child);
    pipe(child2Parent);

    //fork
    pid_t child = fork();

    if(child == 0) //child process
    {
        //close not needed end of pipe
        close(parent2Child[1]);
        close(child2Parent[0]);

        for(;;)
        {
            struct pollfd pfd;
            pfd.fd = parent2Child[0];
            pfd.events = POLLIN;

            //wait until data is available at the pipe
            cout << "c: poll ..." << endl;
            if(poll(&pfd, 1, -1) < 0)
            {
                cout << "c: poll: " << strerror(errno) << endl;
                exit(-1);
            }
            cout << "c: poll says there are data" << endl;

            if((pfd.revents&POLLIN) == POLLIN)
            {
                int data;
                std::string text;
                if(!readData3(parent2Child[0], data, text))
                    exit(-2);
                cout << "c: data received: " << data << " " << text <<  endl;

                if(data == -1)
                    break;

                if(!writeData3(child2Parent[1], data * 2, text + text))
                    exit(-3);
                cout << "c: sent data to parent: " << 2 * data << " " << text + text << endl;
            }
        }

        close(parent2Child[0]);
        close(child2Parent[1]);
        exit(0);
    }
    else //parent process
    {
        //close not needed end of pipe
        close(parent2Child[0]);
        close(child2Parent[1]);

        //send data to child
        if(!writeData3(parent2Child[1], data.back().first, data.back().second))
            return false;
        cout << "p: wrote data: " << data.back().first << " " << data.back().second << endl;
        data.pop_back();

        //read result from child
        for(;;)
        {
            struct pollfd pfd;
            pfd.fd = child2Parent[0];
            pfd.events = POLLIN;

            //wait until data is available at the pipe
            cout << "p: poll ..." << endl;
            if(poll(&pfd, 1, -1) < 0)
            {
                cout << "p poll: " << strerror(errno) << endl;
                return false;
            }
            cout << "p: poll says there are data" << endl;

            if((pfd.revents&POLLIN) == POLLIN)
            {
                int data;
                std::string text;
                if(!readData3(child2Parent[0], data, text))
                    return false;
                cout << "p: data received: " << data << " " << text << endl;
            }

            if(data.empty())
                break;

            if(!writeData3(parent2Child[1], data.back().first, data.back().second))
                return false;
            cout << "p: wrote data: " << data.back().first << " " << data.back().second << endl;
            data.pop_back();
        }

        //send stop data
        if(!writeData3(parent2Child[1], -1, "notext"))
            return false;
        cout << "p: sent stop data " << endl;

        //wait for child to end
        wait(nullptr);

        //close all pipes
        close(parent2Child[1]);
        close(child2Parent[0]);
    }

    return true;
}

为了读取和写入数据,我使用了这两个函数:

bool readData3(int fd, int & number, std::string & text)
{
    char numberBuf[sizeof(int)];
    int bytesRead = read(fd, numberBuf, sizeof(int));
    if(bytesRead > 0)
    {
        number = *(int *)numberBuf;
    }
    else if(bytesRead < 0)
    {
        cout << "readData3: " << strerror(errno) << endl;
        return false;
    }

    char sizeBuf[sizeof(int)];
    int size = -1;
    bytesRead = read(fd, sizeBuf, sizeof(int));
    if(bytesRead > 0)
    {
        size = *(int *)sizeBuf;
    }
    else if(bytesRead < 0)
    {
        cout << "readData3: " << strerror(errno) << endl;
        return false;
    }

    char textBuf[size];
    bytesRead = read(fd, textBuf, size);
    if(bytesRead > 0)
    {
        text = std::string(textBuf);
    }
    else if(bytesRead < 0)
    {
        cout << "readData3: " << strerror(errno) << endl;
        return false;
    }
    return true;
}

bool writeData3(int fd, const int number, const std::string text)
{
    int bytesWritten = write(fd, &number, sizeof(int));
    if(bytesWritten < 0)
    {
        cout << "writeData3: " << strerror(errno) << endl;
        return false;
    }

    int size = text.size() + 1;
    bytesWritten = write(fd, &size, sizeof(int));
    if(bytesWritten < 0)
    {
        cout << "writeData3: " << strerror(errno) << endl;
        return false;
    }

    bytesWritten = write(fd, text.c_str(), size);
    if(bytesWritten < 0)
    {
        cout << "writeData3: " << strerror(errno) << endl;
        return false;
    }

    return true;
}

最后我运行像这样:

#include <iostream>
#include <vector>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <bitset>
#include <memory>
#include <poll.h>
#include <cstring>

using namespace std;

int main(int /*argc*/, char* /*argv*/[])
{
    std::vector<std::pair<int, std::string>> data;
    data.push_back(std::make_pair(1, "one"));
    data.push_back(std::make_pair(2, "two"));
    cout << "6a ########################################################" << endl << flush;
    processParallel6(data);
    cout << "6b ########################################################" << endl << flush;
    processParallel6(data);

    return 0;
}

这是输出:

6a ###############################################
p: wrote data: 2 two
p: poll ...
c: poll ...
c: poll says there are data
c: data received: 2 two
p: poll says there are data
p: data received: 4 twotwo
p: wrote data: 1 one
p: poll ...
c: sent data to parent: 4 twotwo
c: poll ...
c: poll says there are data
c: data received: 1 one
p: poll says there are data
p: data received: 2 oneone
p: sent stop data 
c: sent data to parent: 2 oneone
c: poll ...
c: poll says there are data
c: data received: -1 notext
6b ###################################################
c: poll ...
terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_M_construct null not valid
c: poll says there are data
c: poll ...
c: poll says there are data
c: poll ...

最后 4 行重复了数千次。大多数时候都会出现此输出,但有时我会看到 std::bad_alloc 错误。当我尝试 strace 时它也崩溃了,但是当它 运行s 我在 processParallel6() 的第二个 运行 之后直接看到了带有 mmap、ENOEM 和 'Cannot allocate memory'

的一行

这里发生了什么?为什么第一次可以,第二次不行?

您试图复制无效的 std::string 引用。


std::terminatestd::string 的构造函数中被调用。在调用 writeData3:

时在 processParallel6 中隐式调用构造函数
bool writeData3(int fd, const int number, const std::string text)

...

//send data to child
if(!writeData3(parent2Child[1], data.back().first, data.back().second))
    return false;

您期望 data.back().second 是一个有效的 string 引用,但代码中没有任何内容可以确保是这种情况。

您构造 data 并在其中放置两个条目:

data.push_back(std::make_pair(1, "one"));
data.push_back(std::make_pair(2, "two"));

在第一次调用 processParallel6 时,您 运行 以下代码块 两次 :

if(!writeData3(parent2Child[1], data.back().first, data.back().second))
    return false;
cout << "p: wrote data: " << data.back().first << " " << data.back().second << endl;
data.pop_back();

此时data为空。您不能再次调用 processParallel6,因为它期望 data 至少包含一个元素。