簡體   English   中英

在 C 中的父子之間使用 Pipes IPC 進行多次測試

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

我正在編寫一個帶有管道的父進程通信程序,關鍵是我需要從端線發送不同數量的數據(大數據),但我不知道如何將消息“拆分”成更小的數據(4096每個字節)並將它們按順序發送到讀取端。 我嘗試了一個循環,使(消息大小/4096)迭代,但它沒有工作,所以我放棄了這個想法。

這是我的代碼:

#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++;
}

我收到類似以下錯誤的信息(我知道這是種族限制的,但我不知道如何解決它: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)

兩個三個問題

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

首先,這些是分配有 自動存儲持續時間可變長度數組 這也稱為“在堆棧上分配” 大多數操作系統對程序堆棧的大小都有不同的限制。 在現代 Linux 中,這通常默認為 8MB( ulimit -s檢查)。

使用給定的大小,您很容易在大多數機器上溢出堆棧

其次,這些是指向 char的數組,這意味着所需的空間乘以sizeof (char *) ,這些不是存儲以空字符結尾的字符串的正確類型。

調高編譯器的警告級別應該已經提醒您注意第二個問題,警告如下

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);
      |           ~~~~~~^~~~~~

同樣對於strlen

第三,兩個緩沖區都存在於兩個進程中,其中每個進程只使用一個。

雖然這是一個玩具程序,但使用如此大的緩沖區是值得懷疑的。 您正在處理非常簡單的固定長度消息。 寫入器正在寫入相同的字節size - 1次,然后是零字節。 閱讀器可以只消耗size字節。


下一個問題是,在每個過程中,在內循環的每次迭代中重復關閉管道的兩端。

讀取過程應該在外循環之前關閉管道的寫入端一次。 寫入過程應該在外循環之前關閉管道的讀取端一次。

在循環之后,兩個進程都應該關閉管道的一側。


您不檢查readwrite的返回值以確認沒有發生錯誤,或者預期要處理的數據量已完全完成。


fill是有缺陷的,盡管它的使用方式並不如此。

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

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

如果size為零:

  • memset(bufptr, 'q', size); 實際上是NOP ,沒有效果。

  • bufptr[size-1] = '\0'; 將導致無符號整數溢出,並將索引SIZE_MAX數組中的偏移量。

注意if (ch > 90)僅在使用ch檢查。 對於 ASCII,第 27 次使用此函數將產生一個包含[的字符串。

filled_count是毫無意義的。 字符串長度將為size - 1


這是一個粗略的示例程序。

如果您對分析一次處理單個字節和處理更大緩沖區之間的區別感興趣,或者想要實際保留您處理的數據,並且想要重新引入字符串緩沖區,請知道char foo[size]; 實際上只適用於相對較小的size值。 您可以嘗試改用動態內存

#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