Search code examples
cdebugginggdbpipevalgrind

pipe write and read error in c: program freeze up


I am running the c code with multiprocessing by fork, and use the pipe to make child processes communicate with the parent process.

But when running the write part, let's say 3 out of 13 processes were succeeded, and then the program was just frozen up, which means, it couldn't go any further, neither segmentation fault, nor stopped anyhow.

I could not use gdb to debug, even with set follow-fork-mode child, or Valgrind, the program was simply frozen.

The codes are as following:

  1. Function:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <unistd.h>


struct IntArrLen {
 int length;
 int index;
 int* arr;
};

struct IntArrLenArr {
 struct IntArrLen *intArrLen;
 int max_index;
 int length;
};

void write_check(int fd, void *buffer, size_t len){
    char *p = buffer;
    while(len > 0){
        size_t wlen = write(fd, p, len);
        if(wlen <= 0){
            printf("Error when writing.\n");
            exit(0);
        }
        p += wlen;
        len -= wlen;
    }
}

void write_intArrLen(int fd, struct IntArrLen *p){
    write_check(fd, &p->index, sizeof(p->index));
    write_check(fd, &p->length, sizeof(p->length));
    write_check(fd, p->arr, p->length * sizeof(*p->arr));
}


void write_intArrLenArr(int fd, struct IntArrLenArr *p){
    write_check(fd, &p->max_index, sizeof(p->max_index));
    write_check(fd, &p->length, sizeof(p->length));
    int i;
    for(i=0; i<p->length; i++)
        write_intArrLen(fd, &p->intArrLen[i]);
}


void read_check(int fd, void *buffer, size_t len){
    char *p = buffer;
    while (len > 0){
        size_t rlen = read(fd, p, len);
        if(rlen <= 0){
            printf("Error when reading.\n");
     
            exit(0);
        }
        p += rlen;
        len -= rlen;
    }
}

void read_intArrLen(int fd, struct IntArrLen *p){
    read_check(fd, &p->index, sizeof(p->index));
    read_check(fd, &p->length, sizeof(p->length));
    p->arr = malloc(p->length * sizeof(*p->arr));
    if(!p->arr){
        printf("ran out of memory.\n");
        exit(0);
    }
    read_check(fd, p->arr, p->length * sizeof(*p->arr));
}

void read_intArrLenArr(int fd, struct IntArrLenArr *p){
    read_check(fd, &p->max_index, sizeof(p->max_index));
    read_check(fd, &p->length, sizeof(p->length));
    p->intArrLen = malloc(p->length * sizeof(*p->intArrLen));
    if(!p->intArrLen){
        printf("ran out of memoty.\n");
        exit(0);
    }
    int i;
    for(i=0; i<p->length; i++)
        read_intArrLen(fd, &p->intArrLen[i]);
}

struct IntArrLenArr getRes(int num1, int num2){
    struct IntArrLenArr ret;
    ret.length = num1;
    ret.max_index = num2;
    ret.intArrLen = malloc(sizeof(struct IntArrLen) * num1);
    int i, j;
    for(i=0; i<num1; i++){
        ret.intArrLen[i].length = num1;
        ret.intArrLen[i].index = num2;
        ret.intArrLen[i].arr = malloc(sizeof(int) * num1);
        for(j=0; j<num2; j++){
            ret.intArrLen[i].arr[j] = j;
        }
    }

    return ret;
}


int main(void){

    struct IntArrLenArr res;
    res.max_index = 0;
    res.length = 0;
    int i;

    pid_t child_pid;
    int *fds = malloc(sizeof(int) * 13 * 2);

    for(i=0; i<13; i++){
        if(pipe(fds + i*2) <0)
            exit(0);
    }

    for(i=0; i<13; i++){
        //fflush(NULL);
        child_pid =fork();
        if(child_pid == 0){
            close(fds[i*2]);
            res = getRes(20, 3000000); // 300,000 works but not with 3000,000
            if(res.length != 0){
                printf("-----------%d\n", i);
                write_intArrLenArr(fds[i*2+1], &res);
                printf("+++++++++++%d\n", i);
            }
            close(fds[i*2+1]);
            exit(0);
        }else if(child_pid == -1){
            printf("fork error\n");
            exit(0);
        }
    }

    for(i=0; i<13; i++){
        close(fds[i*2+1]);
        read_intArrLenArr(fds[i*2], &res);
        printf(".................%d\n", i);
        if (res.length > 0){
            printf("do something\n");
       }
    }

    return 1;
}
                                                                                  

res above like something like this:

res -> length: 20
    -> max_index: 458965845
    -> IntArrLen: -> IntArrLen[0] -> length: 125465
                                  -> index: 45687987
                                  -> int * arr: 123,1565,48987,45879,... // 125465 numbers
                  -> IntArrLen[1] -> length: 5465798956
                                  -> index: 34579999
                                  -> int * arr: 78123,1565,48987,45879,... // 5465798956 numbers
                  -> IntArrLen[2] -> length: 5465798956
                                  -> ....
                  -> ...

Could anybody help me to find out what is wrong here? Or is there any other way that I can somehow debug the code? Thanks a lot!!


Solution

  • I dereived this code from the code in the question (source file pipe53.c compiled to the executable pipe53):

    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    struct IntArrLen
    {
        int length;
        int index;
        int *arr;
    };
    
    struct IntArrLenArr
    {
        struct IntArrLen *intArrLen;
        int max_index;
        int length;
    };
    
    static size_t bytes_allocated = 0;
    
    struct IntArrLenArr getRes(int num1, int num2);
    void read_check(int fd, void *buffer, size_t len);
    void read_intArrLen(int fd, struct IntArrLen *p);
    void read_intArrLenArr(int fd, struct IntArrLenArr *p);
    void write_check(int fd, void *buffer, size_t len);
    void write_intArrLen(int fd, struct IntArrLen *p);
    void write_intArrLenArr(int fd, struct IntArrLenArr *p);
    
    static void fd_close(int fd)
    {
        close(fd);
        //fprintf(stderr, "%d: closing %d\n", (int)getpid(), fd);
    }
    
    static void report_memory_used(void)
    {
        fprintf(stderr, "%d: bytes allocated = %zu\n", (int)getpid(), bytes_allocated);
    }
    
    static void *memory_allocator(size_t nbytes)
    {
        void *vp = malloc(nbytes);
        bytes_allocated += nbytes;
        report_memory_used();       // Dire straights!
        return vp;
    }
    
    void write_check(int fd, void *buffer, size_t len)
    {
        char *p = buffer;
        fprintf(stderr, "%d: writing %zu bytes to fd %d\n",
                (int)getpid(), len, fd);
        while (len > 0)
        {
            ssize_t wlen = write(fd, p, len);
            if (wlen <= 0)
            {
                fprintf(stderr, "%d: Error when writing fd = %d.\n",
                        (int)getpid(), fd);
                exit(0);
            }
            p += wlen;
            len -= wlen;
        }
    }
    
    void write_intArrLen(int fd, struct IntArrLen *p)
    {
        write_check(fd, &p->index, sizeof(p->index));
        write_check(fd, &p->length, sizeof(p->length));
        write_check(fd, p->arr, p->length * sizeof(*p->arr));
    }
    
    void write_intArrLenArr(int fd, struct IntArrLenArr *p)
    {
        write_check(fd, &p->max_index, sizeof(p->max_index));
        write_check(fd, &p->length, sizeof(p->length));
        for (int i = 0; i < p->length; i++)
            write_intArrLen(fd, &p->intArrLen[i]);
    }
    
    void read_check(int fd, void *buffer, size_t len)
    {
        char *p = buffer;
        while (len > 0)
        {
            ssize_t rlen = read(fd, p, len);
            if (rlen < 0)
            {
                fprintf(stderr, "%d: Error %d (%s) when reading fd = %d.\n",
                       (int)getpid(), errno, strerror(errno), fd);
                exit(0);
            }
            if (rlen == 0)
            {
                fprintf(stderr, "%d: Premature EOF when reading fd = %d.\n",
                        (int)getpid(), fd);
                break;
            }
            p += rlen;
            len -= rlen;
        }
    }
    
    void read_intArrLen(int fd, struct IntArrLen *p)
    {
        read_check(fd, &p->index, sizeof(p->index));
        read_check(fd, &p->length, sizeof(p->length));
        p->arr = memory_allocator(p->length * sizeof(*p->arr));
        if (!p->arr)
        {
            printf("ran out of memory.\n");
            fprintf(stderr, "%d: ran out of memory (%zu bytes requested)\n",
                    (int)getpid(), p->length * sizeof(*p->arr));
            exit(EXIT_FAILURE);
        }
        read_check(fd, p->arr, p->length * sizeof(*p->arr));
    }
    
    void read_intArrLenArr(int fd, struct IntArrLenArr *p)
    {
        read_check(fd, &p->max_index, sizeof(p->max_index));
        read_check(fd, &p->length, sizeof(p->length));
        p->intArrLen = memory_allocator(p->length * sizeof(*p->intArrLen));
        if (!p->intArrLen)
        {
            fprintf(stderr, "%d: ran out of memory (%zu bytes requested)\n",
                    (int)getpid(), p->length * sizeof(*p->intArrLen));
            exit(EXIT_FAILURE);
        }
        for (int i = 0; i < p->length; i++)
            read_intArrLen(fd, &p->intArrLen[i]);
    }
    
    struct IntArrLenArr getRes(int num1, int num2)
    {
        struct IntArrLenArr ret;
        ret.length = num1;
        ret.max_index = num2;
        ret.intArrLen = memory_allocator(sizeof(struct IntArrLen) * num1);
        if (ret.intArrLen == NULL)
        {
            fprintf(stderr, "%d: failed to allocate %zu bytes of memory\n",
                    (int)getpid(), sizeof(struct IntArrLen) * num1);
            exit(EXIT_FAILURE);
        }
        for (int i = 0; i < num1; i++)
        {
            ret.intArrLen[i].length = num1;
            ret.intArrLen[i].index = num2;
            ret.intArrLen[i].arr = memory_allocator(sizeof(int) * num1);
            if (ret.intArrLen[i].arr == NULL)
            {
                fprintf(stderr, "%d: failed to allocate %zu bytes of memory\n",
                        (int)getpid(), sizeof(int) * num1);
                exit(EXIT_FAILURE);
            }
            for (int j = 0; j < num2; j++)
            {
                ret.intArrLen[i].arr[j] = j;
            }
        }
    
        return ret;
    }
    
    int main(void)
    {
        struct IntArrLenArr res;
        res.max_index = 0;
        res.length = 0;
    
        atexit(report_memory_used);
    
        printf("Parent process: %d\n", (int)getpid());
    
        int *fds = memory_allocator(sizeof(int) * 13 * 2);
    
        for (int i = 0; i < 13; i++)
        {
            if (pipe(fds + i * 2) < 0)
            {
                fprintf(stderr, "failed to create pipe %d\n", i);
                exit(EXIT_FAILURE);
            }
        }
    
        for (int i = 0; i < 13; i++)
        {
            pid_t child_pid = fork();
            if (child_pid == 0)
            {
                printf("%d: Child process: %d - pipe [%d,%d]\n",
                        (int)getpid(), i, fds[i * 2 + 0], fds[i * 2 + 1]);
                for (int j = 0; j < 13; j++)
                {
                    fd_close(fds[j * 2 + 0]);
                    if (i != j)
                        fd_close(fds[j * 2 + 1]);
                }
                //res = getRes(20, 3000000); // 300,000 works but not with 3000,000
                res = getRes(20, 300000); // 300,000 works but not with 3000,000
                report_memory_used();
                if (res.length != 0)
                {
                    printf("-----------%d\n", i);
                    write_intArrLenArr(fds[i * 2 + 1], &res);
                    printf("+++++++++++%d\n", i);
                }
                fd_close(fds[i * 2 + 1]);
                exit(0);
            }
            else if (child_pid == -1)
            {
                fprintf(stderr, "fork error\n");
                exit(EXIT_FAILURE);
            }
            else
            {
                fprintf(stderr, "%d: launched child %d\n", (int)getpid(), (int)child_pid);
            }
        }
    
        for (int i = 0; i < 13; i++)
        {
            fd_close(fds[i * 2 + 1]);
            read_intArrLenArr(fds[i * 2 + 0], &res);
            printf(".................%d\n", i);
            if (res.length > 0)
            {
                printf("do something\n");
            }
            fd_close(fds[i] * 2 + 0);
        }
    
        free(fds);
        return 0;
    }
    

    An example of the output I get from running it (on a MacBook Pro) is:

    $ ./pipe53 2>&1 | cat           # I actually pipe the output to "so | pbcopy" …
    23380: bytes allocated = 104
    23380: launched child 23383
    23380: launched child 23384
    23383: bytes allocated = 424
    23383: bytes allocated = 504
    23380: launched child 23385
    23384: bytes allocated = 424
    23384: bytes allocated = 504
    23380: launched child 23386
    23385: bytes allocated = 424
    23385: bytes allocated = 504
    23380: launched child 23387
    23386: bytes allocated = 424
    23386: bytes allocated = 504
    23380: launched child 23388
    23387: bytes allocated = 424
    23387: bytes allocated = 504
    23388: bytes allocated = 424
    23388: bytes allocated = 504
    23380: launched child 23389
    23380: launched child 23390
    23389: bytes allocated = 424
    23389: bytes allocated = 504
    23380: launched child 23391
    23390: bytes allocated = 424
    23390: bytes allocated = 504
    23380: launched child 23392
    23391: bytes allocated = 424
    23391: bytes allocated = 504
    23380: launched child 23393
    23392: bytes allocated = 424
    23392: bytes allocated = 504
    23380: launched child 23394
    23393: bytes allocated = 424
    23393: bytes allocated = 504
    23380: launched child 23395
    23394: bytes allocated = 424
    23394: bytes allocated = 504
    23380: Premature EOF when reading fd = 3.
    23380: Premature EOF when reading fd = 3.
    23395: bytes allocated = 424
    23395: bytes allocated = 504
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 5.
    23380: Premature EOF when reading fd = 5.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 7.
    23380: Premature EOF when reading fd = 7.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 9.
    23380: Premature EOF when reading fd = 9.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 11.
    23380: Premature EOF when reading fd = 11.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 13.
    23380: Premature EOF when reading fd = 13.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 15.
    23380: Premature EOF when reading fd = 15.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 17.
    23380: Premature EOF when reading fd = 17.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 19.
    23380: Premature EOF when reading fd = 19.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 21.
    23380: Premature EOF when reading fd = 21.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 23.
    23380: Premature EOF when reading fd = 23.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 25.
    23380: Premature EOF when reading fd = 25.
    23380: bytes allocated = 104
    23380: Premature EOF when reading fd = 27.
    23380: Premature EOF when reading fd = 27.
    23380: bytes allocated = 104
    23380: bytes allocated = 104
    Parent process: 23380
    .................0
    .................1
    .................2
    .................3
    .................4
    .................5
    .................6
    .................7
    .................8
    .................9
    .................10
    .................11
    .................12
    

    AFAICT, the getRes() function (with both size 300,000 and 3,000,000) is not triggering anything being written by the child processes.

    Note the careful use of identifying PIDs in the debugging messages.

    You need to revise getRes() so that it causes data to be written to the parent process.