简体   繁体   中英

How to use a file pointer of a file descriptor with multiple child processes without getting “Bad file descriptor” error in C?

I am implementing a scenario in which the parent process forks a number of child processes, these child processes do a computation and return the result to the parent process via a pipe. Since the child processes have to use an external library's data types (GMP's mpz_t type), they need to use this library's own output stream function. This function allows a file pointer as an input instead of file descriptor. Therefore, I obtain file pointer of the pipe's writing end and use to write some data. Below is given child and parent process portions of the code:

pid_t ppid;
ppid = getpid();
struct sigaction sig;
sigemptyset(&sig.sa_mask);
sig.sa_flags = 0;
sig.sa_handler = sig_usr;

if(sigaction(SIGINT,&sig,NULL) != 0)
    printf("\ncan't catch SIGINT\n");

if(sigaction(SIGUSR1,&sig,NULL) != 0)
    printf("\ncan't catch SIGINT\n");

pid_t childpid;

pid_t childpids[operand1Length*operand2Length];
int childPIDInd = 0;

//Create pipe: (must do before fork() so FDs are inherited by child)
int pipefd[2];  //array to hold pipe FDs
pipe(pipefd);

for(i=operand2Length-1, k=0; i>=0; i--, k++){
    for(j=operand1Length-1, l=0; j>=0; j--, l++){

        childpid = fork();

        switch(childpid){
            case -1:
                //fork error
                perror("fork failed!\n");
                exit(EXIT_FAILURE);

            case 0:
                close(pipefd[0]);
                subOperandLength = subOperands[k].length;

                FILE* fp = NULL;

                fhe_mul(subOperands[k].operand[subOperandLength-1-k-l], num1->operand[j], num2->operand[i], pk);

                while(WritePermit);  // unless parent process sends a signal any child process cannot enter this critical section.

                fp = fdopen(pipefd[1], "w");

                if(fp == NULL)
                    fprintf(stderr, "Child Process #%d file pointer is NULL. Error: %s. Pipe FD: %d\n", getpid(), strerror(errno), pipefd[1]);  
                    //Except the child process which enters the critical section first, 
                    //for all other child processes fp is NULL.

                gmp_fprintf(fp, "%Zd\n", subOperands[k].operand[subOperandLength-1-k-l]);
                gmp_fprintf(fp, "%d\n", k);
                gmp_fprintf(fp, "%d\n", subOperandLength-1-k-l);

                fflush(fp);
                fclose(fp);

                kill(ppid, SIGUSR1);

                exit(EXIT_SUCCESS);

            default:
                childpids[childPIDInd] = childpid;
                childPIDInd++;

                close(pipefd[1]);

                if(i == 0 && j == 0){ // last child was created
                    kill(childpids[0], SIGINT);

                    mpz_t deneme;
                    mpz_init(deneme);

                    FILE* fs = fdopen(pipefd[0], "r");

                    int forIndex, pidIndex;

                    for(forIndex=0, pidIndex=1; forIndex<4; forIndex++, pidIndex++){
                        while(WritePermit2);
                        while((gmp_fscanf(fs, "%Zx\n", &deneme)) > 0){
                            gmp_fprintf(stdout, "Parent Process #%d: %Zd\n", getpid(), deneme);
                        }
                        kill(childpids[pidIndex], SIGINT);
                        WritePermit2=1;
                    }

                    fclose(fs);

                    int status;
                    int i=0;
                    int clean = 1;
                    while (i < operand1Length*operand2Length) {
                        wait(&status);
                        if(!WIFEXITED(status))
                            clean = 0;
                        i++;
                    }

                    if(!clean){
                        printf("I am having some problems with my children! :'(\n");
                        exit(EXIT_FAILURE);
                    }

        }
    }
}

Only for one of the child processes fp works. Then, somehow, it becomes NULL, so gmp_fprintf raises error at other child processes.

Please, do not hesitate to ask if you need more detail about the code. Thank you for your help in advance!

With the code present, the default: case of the switch , inside the double-nested for loops, contains close(pipefd[1]); . Clearly, this only works on the first iteration of the inner loop; thereafter, the pipe is broken. Subsequent children do not get an operational pipe because their parent closed the file descriptor.

The fix is to ensure that the parent does not close the write end of the pipe until all the children are created.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM