简体   繁体   English

在 C 中的父子之间使用 Pipes IPC 进行多次测试

[英]Multiple tests with Pipes IPC between parent and child in C

I am writing a program of Parented Process Communication with pipes, the point is that I need to send from an endline different amounts of data (large ones) but I don't know how to ordered 'split' the message into smaller ones (4096 Bytes each one) and send them in order to the read endline.我正在编写一个带有管道的父进程通信程序,关键是我需要从端线发送不同数量的数据(大数据),但我不知道如何将消息“拆分”成更小的数据(4096每个字节)并将它们按顺序发送到读取端。 I've tried with a cycle that makes (message size / 4096) iterations but it didn't work so I let that idea away.我尝试了一个循环,使(消息大小/4096)迭代,但它没有工作,所以我放弃了这个想法。

Here is my code:这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>

#define SIZES 6
#define KB 1024

//1KB, 10KB, 100KB, 1MB, 10MB, 100MB
int PACK_SIZES[SIZES] = {1*KB, 10*KB, 100*KB, 1*KB*KB, 10*KB*KB, 100*KB*KB};
//int PACK_TESTS[SIZES] = {1000,1000,100,100,10,5};
int PACK_TESTS[SIZES] = {6,5,4,3,2,1};

void fill(char * bufptr, size_t size);

int main() {
    int tests_amount;
    size_t size;
    
    //pipefd[0] -> lectura
    //pipefd[1] -> escritura
    int pipefd[2];

    int r;  //Variable de retorno.
    pid_t pid;

    r = pipe(pipefd);
    if (r < 0){
        perror("Error al crear la tubería");
        exit(-1);
    }

    printf("Medidas de envío de paquetes con comunicación mediante Tuberías.\n\n");

    pid = fork();
    if (pid == (pid_t) -1){
        perror("Error en fork");
        exit(-1);
    }

    for (int i = 0; i < SIZES; i++){
        int packages_amount;
        tests_amount = PACK_TESTS[i];
        size = PACK_SIZES[i];

        float time0, time1;
        float total_time = 0;

        char *write_buffer[size];
        char *read_buffer[size];
        fill(write_buffer, size);

        for (int j = 1; j <= tests_amount; j++){
            time0 = clock();

            if (pid == 0){
                //Consumidor

                close(pipefd[1]);
                read(pipefd[0], read_buffer, size);
                close(pipefd[0]);
                //printf("\n Mensaje recibido de tamaño %d: %d \n", size, strlen(read_buffer));

            }else {
                //Productor
                close(pipefd[0]);
                write(pipefd[1], write_buffer, size);
                close(pipefd[1]);
            }
            time1 = clock();
            double tiempo = (double)(time1) / CLOCKS_PER_SEC-(double)(time0) / CLOCKS_PER_SEC;
            total_time = total_time + tiempo;
        }

        printf("El promedio con %d Bytes y %d pruebas es: %f seg. \n\n",size,tests_amount,total_time/tests_amount);
        total_time = 0;

    }
    return 0;
}

void fill(char * bufptr, size_t size){
    static char ch = 'A';
    int filled_count;

    //printf("size is %d\n", size);
    if (size != 0){
        memset(bufptr, ch, size);
    } else {
        memset(bufptr, 'q', size);
    }
    
    bufptr[size-1] = '\0';
    
    if (ch > 90)
        ch = 65;
    
    filled_count = strlen(bufptr);

    //printf("Bytes escritos: %d\n\n", filled_count);
    //printf("buffer filled is:%s\n", bufptr);
    ch++;
}

I get something like the following error (I know it's race conditioned but I don't know how to fix it :c):我收到类似以下错误的信息(我知道这是种族限制的,但我不知道如何解决它:c):

Medidas de envío de paquetes con comunicación mediante Tuberías.

El promedio con 1024 Bytes y 6 pruebas es: 0.000002 seg. 


 Mensaje recibido de tamaño 1024: 1023 

 Mensaje recibido de tamaño 1024: 1023 

 Mensaje recibido de tamaño 1024: 1023 

 Mensaje recibido de tamaño 1024: 1023 

 Mensaje recibido de tamaño 1024: 1023 

 Mensaje recibido de tamaño 1024: 1023 
El promedio con 10240 Bytes y 5 pruebas es: 0.000001 seg. 

El promedio con 1024 Bytes y 6 pruebas es: 0.000012 seg. 


 Mensaje recibido de tamaño 10240: 0 

 Mensaje recibido de tamaño 10240: 0 

 Mensaje recibido de tamaño 10240: 0 

 Mensaje recibido de tamaño 10240: 0 

 Mensaje recibido de tamaño 10240: 0 
El promedio con 10240 Bytes y 5 pruebas es: 0.000005 seg. 

El promedio con 102400 Bytes y 4 pruebas es: 0.000001 seg. 


 Mensaje recibido de tamaño 102400: 0 

 Mensaje recibido de tamaño 102400: 0 

 Mensaje recibido de tamaño 102400: 0 

 Mensaje recibido de tamaño 102400: 0 
El promedio con 102400 Bytes y 4 pruebas es: 0.000008 seg. 

Segmentation fault (core dumped)

There are two three issues with两个三个问题

char *write_buffer[size];
char *read_buffer[size];

Firstly, these are variable-length arrays allocated with automatic storage duration .首先,这些是分配有 自动存储持续时间可变长度数组 This is also referred to as being "allocated on the stack" .这也称为“在堆栈上分配” Most operating systems have different limits placed on how large a program's stack can be.大多数操作系统对程序堆栈的大小都有不同的限制。 In a modern Linux, this is usually defaults to 8MB ( ulimit -s to check).在现代 Linux 中,这通常默认为 8MB( ulimit -s检查)。

With the given sizes, you are easily going to overflow the stack on most machines.使用给定的大小,您很容易在大多数机器上溢出堆栈

Secondly, these are arrays of pointer-to-char , meaning the space required is multiplied by sizeof (char *) , and these are not the correct types to store null-terminated strings.其次,这些是指向 char的数组,这意味着所需的空间乘以sizeof (char *) ,这些不是存储以空字符结尾的字符串的正确类型。

Turning up your compiler's warning levels should have alerted you to the second problem, with warnings such as调高编译器的警告级别应该已经提醒您注意第二个问题,警告如下

main.c:55:14: warning: passing argument 1 of ‘fill’ from incompatible pointer type [-Wincompatible-pointer-types]
   55 |         fill(write_buffer, size);
      |              ^~~~~~~~~~~~
      |              |
      |              char **
main.c:16:17: note: expected ‘char *’ but argument is of type ‘char **’
   16 | void fill(char *bufptr, size_t size);
      |           ~~~~~~^~~~~~

and similarly for strlen .同样对于strlen

Thirdly, both buffers exist in both processes, wherein each process only uses one.第三,两个缓冲区都存在于两个进程中,其中每个进程只使用一个。

Although this is a toy program, the use of such large buffers is dubious.虽然这是一个玩具程序,但使用如此大的缓冲区是值得怀疑的。 You are dealing with very simple fixed-length messages.您正在处理非常简单的固定长度消息。 The writer is writing the same byte size - 1 times, followed by the zero byte.写入器正在写入相同的字节size - 1次,然后是零字节。 The reader can just consume size bytes.阅读器可以只消耗size字节。


The next issue is that you repeatedly close both ends of the pipe in each iteration of the inner loop, in each process.下一个问题是,在每个过程中,在内循环的每次迭代中重复关闭管道的两端。

The reading process should close the write end of the pipe, just once, before the outer loop.读取过程应该在外循环之前关闭管道的写入端一次。 The writing process should close the read end of the pipe, just once, before the outer loop.写入过程应该在外循环之前关闭管道的读取端一次。

After the loop, both processes should close their side of the pipe.在循环之后,两个进程都应该关闭管道的一侧。


You do not check the return values of read or write to confirm an error did not occur, or that the amount of data expected to be processed was done so fully.您不检查readwrite的返回值以确认没有发生错误,或者预期要处理的数据量已完全完成。


fill is flawed, although not seen to be so with the way it is used. fill是有缺陷的,尽管它的使用方式并不如此。

if (size != 0){
    memset(bufptr, ch, size);
} else {
    memset(bufptr, 'q', size);
}

bufptr[size-1] = '\0';

If size is zero:如果size为零:

  • memset(bufptr, 'q', size); is effectively a NOP , having no effect.实际上是NOP ,没有效果。

  • bufptr[size-1] = '\0'; will cause unsigned integer overflow, and will index the offset in the array of SIZE_MAX .将导致无符号整数溢出,并将索引SIZE_MAX数组中的偏移量。

Note that if (ch > 90) is only checked after ch has been used.注意if (ch > 90)仅在使用ch检查。 With ASCII, the 27th use of this function will yield a string containing [ .对于 ASCII,第 27 次使用此函数将产生一个包含[的字符串。

filled_count is rather pointless. filled_count是毫无意义的。 The string length will be size - 1 .字符串长度将为size - 1


Here is a cursory example program to play around with.这是一个粗略的示例程序。

If you are interested in profiling the difference between processing a single byte at a time, and processing larger buffers, or want to actually keep the data you process, and want to reintroduce string buffers, know that char foo[size];如果您对分析一次处理单个字节和处理更大缓冲区之间的区别感兴趣,或者想要实际保留您处理的数据,并且想要重新引入字符串缓冲区,请知道char foo[size]; will really only work for relatively small values of size .实际上只适用于相对较小的size值。 You can try using dynamic memory instead.您可以尝试改用动态内存

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#define SIZES 6
#define KB 1024

/* 1KB, 10KB, 100KB, 1MB, 10MB, 100MB */
size_t PACK_SIZES[SIZES] = {1*KB, 10*KB, 100*KB, 1*KB*KB, 10*KB*KB, 100*KB*KB};
int PACK_TESTS[SIZES] = {6,5,4,3,2,1};

static void pdie(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

static char get_next_byte(void) {
    static char byte = 'A';

    if (byte > 'Z')
        byte = 'A';

    return byte++;
}

static int read_exactly(int fd, size_t size) {
    unsigned char b;

    while (size > 0 && 1 == read(fd, &b, 1))
        size--;

    return 0 == size;
}

static int write_exact_string(int fd, size_t size, char b) {
    unsigned char zero = 0;

    while (size > 1 && 1 == write(fd, &b, 1))
        size--;

    return (1 == size) && (1 == write(fd, &zero, 1));
}

int main(void) {
    pid_t pid;
    int pipefd[2];

    if (-1 == pipe(pipefd))
        pdie("pipe");

    if (-1 == (pid = fork()))
        pdie("fork");

    if (0 == pid)
        close(pipefd[1]);
    else
        close(pipefd[0]);

    for (int i = 1; i <= SIZES; i++) {
        size_t expected_size = PACK_SIZES[i];
        int tests_amount = PACK_TESTS[i];

        double total_time = 0;

        for (int j = 1; j <= tests_amount; j++) {
            clock_t start = clock();

            if (pid == 0) {
                if (!read_exactly(pipefd[0], expected_size)) {
                    fprintf(stderr, "Failed to read %zu bytes.\n", expected_size);
                    exit(EXIT_FAILURE);
                }
            } else {
                char byte = get_next_byte();

                if (!write_exact_string(pipefd[1], expected_size, byte)) {
                    fprintf(stderr, "Failed to write an exact string of [%zu*'%c'+'\\0'].\n", expected_size - 1, byte);
                    exit(EXIT_FAILURE);
                }
            }

            double duration = ((double) (clock() - start)) / CLOCKS_PER_SEC;

            printf("Test<%d-%d>: %s %zu bytes in %f.\n", i, j, (pid == 0) ? "Read" : "Wrote", expected_size, duration);
            total_time += duration;
        }

        printf("Time taken in <%s> to process %d * %zu bytes: %f (%f avg.)\n",
                (pid == 0) ? "CHILD" : "PARENT",
                tests_amount,
                expected_size,
                total_time,
                total_time / tests_amount);
    }

    if (0 == pid) {
        close(pipefd[0]);
    } else {
        close(pipefd[1]);

        if (-1 == waitpid(pid, NULL, WUNTRACED))
            pdie("waitpid");
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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