【问题标题】:How can I create a temporary buffer in C?如何在 C 中创建临时缓冲区?
【发布时间】:2018-05-09 05:28:16
【问题描述】:

我正在我的程序中执行一些物理计算,其中需要将输出存储到临时缓冲区并通过管道传递。

缓冲区需要共享不同的数据类型:首先,我需要存储我正在学习的主题的名称;其次是我的计算结果(所有float 数字)。

代码如下所示:

initialdata.dat

Aston Martin Vantage V12|07.7|090
Ferrari LaFerrari       |09.6|111
Lamborghini Aventador   |09.6|097
Porsche 911 Turbo S     |09.6|092
Tesla Model S P100D     |10.0|069
Hennessey Venom GT      |10.3|120
Bugatti Chiron          |11.2|114
Koenigsegg Agera        |10.3|121

Main.c:

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

#define READ  0
#define WRITE 1
#define M 2 // Number of subjects.

int main(){
    int pipeToChild[2];
    if (pipe(pipeToChild) < 0) {
        fprintf(stderr,"Error in pipe!");
        return -1;
    }

    pid_t pid[M];
    srand (time(NULL));

  // Declares the file pointer with the information.
    FILE * pFile;
    char buffer[34]; // Buffer where subject info is sent to childs.
    char tempBuffer[50]; // The buffer that causes problems.
    pFile = fopen ("initialdata.dat","r");

    for(int i = 0; i < M; i++){
        pid[i] = fork();
        if (pid[i] < 0){
            printf("Fork error!\n");
            return -1;
        }
        else if(pid[i]==0){ 
            // Creates the pipes (one per child) to pass the temporary buffer to the results process (still not created).
            int pipeToResults[2];
            if (pipe(pipeToResults) < 0) {
                fprintf(stderr,"Error in pipe!");
                return -1;
            }

            // Receives the car information from the file through a pipe.
            char receivedValue[34];
            receivedValue[33] = '\0';
            close(pipeToChild[WRITE]);
            read(pipeToChild[READ], receivedValue, sizeof(receivedValue));

        // Processes the information and tokenizes it.
            char name[25];
            char CHARacceleration[6];
            char CHARmaxSpeed[4];
            strcpy(name, strtok(receivedValue, "|"));
            strcpy(CHARacceleration, strtok(NULL, "|"));
            strcpy(CHARmaxSpeed, strtok(NULL, "|"));    
            float acceleration = atof(CHARacceleration);
            float maxSpeed = atoi(CHARmaxSpeed);

            // Adds 0.0X to acceleration.
            float randomNum = rand() % 5;
            acceleration = acceleration + randomNum/100;

            float distance = 0;
            float TA = 0; // Time using Uniformly Accelerated Linear Motion.
            float TB = 0; // Time using Linear Motion.
            float TE = 0.5; // Time increment.
            float currentVelocity = 0; // In m/s.

            // Applies different physical calculations depending on the case.
            while (distance <= 1000){
                TA += TE;
                if (currentVelocity < maxSpeed){ // Acceleration > 0
                    distance = (acceleration*pow((TA),2))/2;
                    currentVelocity = acceleration*TA;
                    distance = 2*distance;
                }
                else{ // Acceleration = 0
                    TB += TE;
                    currentVelocity = maxSpeed;
                    distance += maxSpeed*TB;
                }
            }

            // Debug purposes, just for ensuring everything gets processed the right way.
            printf("Name: %s\n", name);
            printf("Distance: %.2f m\n", distance);
            printf("Time: %.2f s\n", TA+TB);
            printf("Max speed reached: %.2f km/h\n", currentVelocity*3.6);
            printf("Acceleration: %.2f m/s^2\n", acceleration);
            printf("\n");

        // Comment this if you want to switch between the situations I talked about.
        sprintf(tempBuffer, "%s %.2f %.2f %.2f %.2f", name, distance, TA+TB, currentVelocity, acceleration);
            printf("Buffer: %s\n\n", tempBuffer);
            exit(0);
        }
        else if(pid[i]>0){
            // Generates a random subject from the list. Buggy section, fixed it the best way I could.
            int randCar = rand() % 15 + 1;
            if (randCar % 2 == 0)
                randCar--;
            for (int i = 1; i <= randCar; i++){
                if (pFile != NULL)
                    fgets (buffer, sizeof(buffer), pFile);
                else
                    perror ("ERROR reading file!");
            }
            char toSend[34]; //This will be passed through the `pipeToChild` pipe.
            strcpy(toSend, buffer);

            // Loads pipe.
            close(pipeToChild[READ]); 
            write(pipeToChild[WRITE], toSend, strlen(toSend));
            close(pipeToChild[WRITE]);
        }
    }
    for (int i=0;i<M;i++){
        waitpid(pid[i], NULL, 0);
    }
    fclose(pFile);
    return 0;
}

不过,输出会有所不同,具体取决于我是否使用sprintf。例如,对于 M=2,输出应该是:

案例一:否sprintf:

I'm the child process 1 with PID 12304
Name: Bugatti Chiron          
Distance: 1012.61 m
Time: 9.50 s
Max speed reached: 383.72 km/h
Aceleration: 11.22 m/s^2

I'm the child process 2 with PID 12305
Name: Bugatti Chiron          
Distance: 1012.61 m
Time: 9.50 s
Max speed reached: 383.72 km/h
Aceleration: 11.22 m/s^2

案例 2:sprintf:

I'm the child process 2 with PID 12307
I'm the child process 1 with PID 12306
Name: Bugatti Chiron          
Distance: 1012.61 m
Time: 9.50 s
Max speed reached: 383.72 km/h
Aceleration: 11.22 m/s^2
Buffer: Bugatti Chiron    1012.61 9.50 383.82 11.22

“冲刺”有什么问题?为什么这条线搞砸了整个程序?

编辑:该程序是一个简单的飙车模拟器,其中 M 辆汽车在 1000 米直线上竞争。 master 进程创建 M 辆随机汽车(尽管此功能未正确实现),并通过单个管道将存储在 .dat 文件中的一些数据传递给 M 个子进程。

每辆车都是一个子进程,在其中进行计算。一旦我们得到这些值,每个子进程都会通过自己的管道将存储在临时缓冲区中的数据传递给一个results进程,该进程将它们存储在输出文件中。请注意,此功能仍未实现,因为首先我需要设法创建缓冲区。我的问题只是关于缓冲区问题。

【问题讨论】:

  • 如果没有Minimal, Complete, and Verifiable Example,就很难说出任何具体的内容,我们所能做的就是猜测(并且可能猜错了)。请read about how to ask good questions.
  • 一个猜测:这是由于您无法控制调度程序的进程,额外的sprintf 调用可能会导致调度程序意外切换进程?这与输出缓冲一起可能会导致多进程程序中的输出问题。
  • 带有注释// printf... 很可能是个问题。如果您调用printf 并且不刷新输出缓冲区,然后调用fork,您将获得重复的输出。尝试在循环末尾添加flush
  • 已更新完整代码。

标签: c linux operating-system printf system-calls


【解决方案1】:

当您fork 时,您创建了一个新进程。如果没有任何类型的同步,创建的两个子进程会同时运行。因此,每个子进程的输出可能会相互交错,具体取决于操作系统如何决定调度每个进程。 sprintf 的存在并没有改变这一点。

您可能需要通过管道传递消息以使进程相互同步以控制每个进程何时打印。

【讨论】:

    【解决方案2】:

    char tempBuffer[50]; 被初始化为 50 而不是 34,这是我发送的数据的确切大小。

    【讨论】:

    • 但请注意,如果每个浮点数由 7 个字符表示,并且每个数字之间有 1 个空格,则 31chars + 25 字符表示标题或 6 字符表示缓冲区溢出。您刚刚推出了 Undefined Behavior 的红地毯。不要跳过缓冲区大小,256 应该在这里留出足够的空间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-04
    • 1970-01-01
    相关资源
    最近更新 更多