不断地读写管道以一种方式工作,而另一种方式只工作一次

Constantly reading and writing pipe works one way and just once in the other

我正在 Raspberry Pi 4 上用 C++ 编写程序来控制一些电机并通过 GPIO 引脚取回速度传感器的信息。

为此,有一个带有“fork”的主控制程序。 父进程进行所有计算,子进程通过 SPI 通信不断发送和接收数据(发送和接收数据是同时完成的,库不允许做任何一个)。

在每个循环中,子级读取父级发送的数据(这部分有效)并将输入读取的数据(工作一次)发送回父级。

我实际上已经用电缆连接了“读取”数据,因此我可以控制输入并确定它们的状态。

在下面的示例中,我将请求的信息提供给程序,让父级写入子级,这有效(选择电机 0/15,速度为 0,方向 0=停止和不停止程序)

第一次尝试时,它告诉我读取的数据是 10,这是正确的。

然后我将我的跳线移动到另一个值(在本例中为 15),但第二次尝试时它仍然给我 10

我检查了我的程序,子进程的write收到了好的数据,确实是管道有问题,我想不通。管道似乎关闭了...

这是终端视图

问题出在父线之间

if (read(fdCP[0], In0, sizeof(short int)*2) == -1) { std::cout << "Something went wrong in Parent read :'( " <<'\n'; }

和子线

 //If here I print the content of "In0", it is correct and fitting what the program read
 if(write(fdCP[1], In0, sizeof(short int)*2) == -1) { std::cout << "Something went wrong in child write :'( " <<'\n'; }; //sending data through the pipe

这里是为了便于阅读而删除了大部分内容的程序。

#include <bcm2835.h> //SPI & GPIO communication library
#include <stdio.h>
#include <unistd.h>     //got fork() in it
#include <sys/wait.h>
#include <vector>
#include <iostream> //for Cout to display stuff
#include <thread>   //for multithreading
#include <string.h>
#include <time.h>

using std::vector;
int SRDInit();
char* SendReceiveData(char* SpiOut, char* SpiIn); //Defines the existance of this function and will check in the compiled stuff if it exists (spoiler : it does)
int SRDClose();
int main(int MotNb, int Spd1, int Dir1)
{
    //Fork and multiprocessing stuff
    bool EndThis = false;   //Variable for ending the continous look of sending and receiving data from shift registers
    //SPI Communication buffers and data storages
    char SpiOut[2]={0,0}; //Defining the char to send by SPI and set it to 0
    short int In0[2] = {0}; //Char array for receiving data
    int Sp1 = 0;        // iteration variable for speed
    short int SpdDir[4] = {0, 0, 0, 0}; //creates a table with 4 ints inside 0 for motor number, 1 for Speed, 2 for Dirrection and 3 for killing the child

//###################################################################################   
//Actual beginning of the program
//###################################################################################
        int fdPC[2];            //File descriptor for the piping Parent to child
        int fdCP[2];            //File descriptor for the piping Child to Parent
        std::cout << "Init FD" << '\n';
        if (pipe(fdPC) == -1){          //creating the pipe for communication with the second program sending and receiving data
            //fd[0] = Read fd[1]=write
            std::cout << "an error occured while creating the pipe Parent to Child" << std::endl;
            return 1;
        }
        if (pipe(fdCP) == -1){          //creating the pipe for communication with the second program sending and receiving data
            //fd[0] = Read fd[1]=write
            std::cout << "an error occured while creating the pipe Child to Parent" << std::endl;
            return 1;
        }
        std::cout << "Forking" << '\n';
        int id = fork();        //forking the program to create a second one playing on another processor
        std::cout << "Forked ID = " << id << '\n';
        if (id < 0){
            std::cout << "An error occured while forking :'(" << std::endl;
        }
        else if (id > 0){           //if in the main process
            std::cout << "Enter Main process" << '\n';
//###################################################################################   
//Main process.
//###################################################################################
            close (fdPC[0]);        //closing the read side of the Parent to child pipe
            close (fdCP[1]);        //closing the write side of the child to parent pipe
            fcntl(fdPC[1], F_SETFL,O_NONBLOCK);
            fcntl(fdCP[0], F_SETFL,O_NONBLOCK);
            int MotNb = 0;          //variable for the number of the motor to be modified
            short int OldIn0[2] = {0, 0};
            short int NewIn0[2] = {0, 0};
            
            while(EndThis == false){
                std::cout << "Gi'me Motor 0-15" <<'\n';
                std::cin >> MotNb;
                std::cout << "Gi'me speed" <<'\n';
                std::cin >> Spd1;
                std::cout << "Gi'me dirrection 0=3= stop 1=fwd 2=bwd" <<'\n';
                std::cin >> Dir1;
                std::cout << "Stop that? Y/N" <<'\n';       //check if we want to close the program
                std::cin >> EndProg;
                
                SpdDir[0] = MotNb;          //storing the Motor number
                SpdDir[1] = Spd1;           //storing the speed
                SpdDir[2] = Dir1;           // storing the dirrection

                if(write(fdPC[1], SpdDir, sizeof(short int)*4) == -1) { std::cout << "Something went wrong in Parent write :'( " <<'\n'; }      //Writing through the pipe to send data to the Child
                if (read(fdCP[0], In0, sizeof(short int)*2) == -1) { std::cout << "Something went wrong in Parent read :'( " <<'\n'; }

                NewIn0[0] = In0[0];
                NewIn0[1] = In0[1];
                if (NewIn0[0] != OldIn0[0] || NewIn0[1] != OldIn0[1]) {
                    std::cout << "SOMETHING CHANGED !!! Old In0 = " << +OldIn0[0]<< +OldIn0[1] << " and NewIn0 = " << +NewIn0[0]<< +NewIn0[1] << '\n';  
                    OldIn0[0] = NewIn0[0];
                    OldIn0[1] = NewIn0[1];
                }
                
                if (SpdDir[3] > 0){
                    EndThis = true;
                    std::cout << "Stopping main" <<'\n';        //check if we want to close the program
                }
            }   //End of PARENT while
            write(fdPC[1], SpdDir, sizeof(int)*3);      //Writing through the pipe to send data to the Child for ending the program
            //End of main program, closing everything and freeing memory because we're tidy people
            close (fdPC[1]);        //closing the write side of the Parent to child pipe
            close (fdCP[0]);        //closing the read side of the child to parent pipe
            if (wait(NULL) == -1){              //wait for the child to finish working == -1
                std::cout << "No children to wait for" << std::endl;
            }else {
                std::cout << "waiting for children to finish" << '\n';
            }
        }
        else{                   // if in the secondary process
            std::cout << "Enter child process" << '\n';
//###################################################################################   
//Child process. Send Speed and dirrection requested and receive data (because no choice for receiving)
//###################################################################################
            close (fdPC[1]);        //closing the write side of the Parent to child pipe
            close (fdCP[0]);        //closing the read side of the child to parent pipe
            fcntl(fdPC[0], F_SETFL,O_NONBLOCK);
            fcntl(fdCP[1], F_SETFL,O_NONBLOCK);
            
            int MotNb = 0;          //variable for the number of the motor to be modified
            //Actual beginning of child program
            char* SpiIn=(char*)malloc(sizeof(char)*17);     //creation de la data qui sera échangée malloc est fermé apres l'appel a la fonction
            if(SpiIn==NULL){
                perror("Char allocation of SendReceiveData FAILED !!");
                return 1;
            }
            while(EndThis == false){
                read(fdPC[0], SpdDir, sizeof(short int)*4);//read data from the pipe put them in SpdDir varaible
                MotNb = SpdDir[0];          //storing the motor nb
                Spd1 = SpdDir[1];           //storing the speed
                Dir1 = SpdDir[2];           // storing the dirrection
                
        //Some amazing coding has been deleted here ;)

                    In0Raw = SendReceiveData(SpiOut, SpiIn);        //Send data in "SpiOut" and puts the data received in In0Raw
                    In0[0] = In0Raw[1];     //Data is no read in the logical direction TBC
                    In0[1] = In0Raw[0];     //Data is no read in the logical direction TBC

                //If here I print the content of "In0", it is correct and fitting what the program read
                if(write(fdCP[1], In0, sizeof(short int)*2) == -1) { std::cout << "Something went wrong in child write :'( " <<'\n'; }; //sending data through the pipe
                sleep(1);
                if (SpdDir[3] > 0){
                    EndThis = true;
                std::cout << "Ending child" << '\n';
                }
            }   //End of CHILD while
            //End of child program, closing everything and freeing memory because we're tidy people
            close (fdPC[0]);        //closing the read side of the Parent to child pipe
            close (fdCP[1]);        //closing the write side of the child to parent pipe
            free(In0Raw);           //free memory allocated to In0Raw for "SendReceiveData"
            std::cout << "Child ended" << '\n';
            exit(EXIT_SUCCESS);
        }
    
//###################################################################################
    if (id > 0){
        std::cout << "End of program enter a key and press ENTER to finish" <<'\n';
        std::cin >> Spd1;
        SRDClose();
        free(In0Raw);       //release memory of the pointer just in case
    }
    return 0;
}

如果您想知道“SendReceiveData”是什么,它只是一个通过 SPI 连接发送和接收数据的非常小的函数。它运行良好,非常基础 ;)

我知道问题出在哪里,但我完全不知道问题出在哪里。看起来管道在从 Child 写到 Parent 后关闭了,但它在另一个方向上工作得很好,从 Parent 发送指令到 Child

(如果需要,我可以复制整个代码而不是这个摘录)

预先感谢您的帮助! :)

我已经阅读了文档(由朋友提供)write() manual,它没有帮助,但对某些人可能有用。

我尝试使用 errno 来理解错误,为此我检查了文档 errno manual 并且很有用。

write() 发回了与“EAGAIN”相对应的错误“资源暂时不可用”,但最重要的是,它说“可能与 EWOULDBLOCK 的值相同” 对应于“操作会阻塞”

我搜索并找到了一个答案是“缓冲区已满”的讨论

所以,我问是否可以覆盖 write() 文件描述符中的数据,答案是“不”:'( Is it possible to overwrite a write() data?

您必须在再次写入之前读取文件描述符中的数据。

显然,线程更好,我必须调查一下。

希望这对某人有所帮助。