c中的管道写入和读取错误:程序冻结

pipe write and read error in c: program freeze up

我是运行fork的多进程c代码,使用管道让子进程与父进程通信

但是当运行 write部分时,假设13个过程中有3个成功,然后程序就被冻结了,也就是说,它不能再继续了,既没有segmentation fault,也没有停止。

我无法使用 gdb 进行调试,即使使用 set follow-fork-mode childValgrind,程序也被冻结了。

代码如下:

  1. 函数:
#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 像这样:

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
                                  -> ....
                  -> ...

谁能帮我找出这里出了什么问题?或者有没有其他方法可以以某种方式调试代码?非常感谢!!

我从问题中的代码(源文件 pipe53.c 编译为可执行文件 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;
}

我从 运行 它(在 MacBook Pro 上)得到的输出示例是:

$ ./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,getRes() 函数(大小为 300,000 和 3,000,000)未触发子进程写入的任何内容。

请注意在调试消息中谨慎使用标识 PID。

您需要修改getRes(),以便将数据写入父进程。