【问题标题】:linux pipes and buffers, not sure how to get the loop to worklinux管道和缓冲区,不知道如何让循环工作
【发布时间】:2018-10-05 18:41:29
【问题描述】:

我正在为我正在学习的 Unix 课程用 C 语言编写一个 DB 程序。虽然我已经设法让所有管道、缓冲区和命令正常工作,但 while 循环却让我陷入了循环。该程序将完全执行第一个命令,接受第二个命令,但随后立即退出。我已经用错误检查加载了它,没有抛出任何东西,所以机制很好,但循环中的某个地方存在我无法识别的逻辑问题。

这里是存储库:https://gitlab.com/IrateSwami/c_pipes/tree/dev

示例运行:

1234567 102 08/11/18 4.00
1234567 101 08/14/18 14.00
3456787 9873 08/30/18 100.00
1234567 100 08/16/18 35.00
3456787 9874 09/30/18 4.00
12345 1010 09/01/18 34.00
1001001 905 08/14/18 9.00
1001001 903 08/30/18 11.00
12345 1001 09/14/18 16.00
12345 1111 08/24/18 2.00
12345 1112 08/31/18 44.00
1001001 902 09/25/18 19.00

Enter a command: add,1234567,999,01/01/01,99.99

Enter a command: list

虽然从 slave.c 文件中给我带来了麻烦:

// start the do-while loop for command stuff

// read in from the pipe
error_check = read(read_pipe, buffer, 1000);
if(error_check<0){
    perror("child process error, reading in from pipe");
    exit(-3);
}

// null terminate the end of buffer 
buffer[error_check] = '\0';

// here's where the command stuff starts 
char *first_command;
while(strcmp(first_command, "exit\n") != 0){
    // grab the first thing from the buffer, it'll be the command
    char *command = strtok(buffer, ",");
    first_command = command;
    printf("first command: %s\n", first_command);


    // now for the parameters
    int parameter_count = 0;
    char *parameters[4];

    while(command != NULL){
        command = strtok(NULL, ",");
        parameters[parameter_count] = command;
        parameter_count++;
    }

    // exit appropriately
    if(strcmp(first_command, "exit\n") == 0)
        return 9;

    // add a record
    else if(strcmp(first_command, "add") == 0){
        Record temp_record;
        temp_record.account_number = atoi(parameters[0]);
        temp_record.check_number = atoi(parameters[1]);
        temp_record.amount = atof(parameters[3]);
        strcmp(temp_record.transaction_date, parameters[2]);

        records[record_count] = temp_record;
        record_count++;

        error_check = write(write_pipe, "add completed", strlen(buffer));
        if(error_check<0){
            perror("error writing in add function");
            exit(-6);
        }
    }

    // delete a record
    else if(strcmp(first_command, "delete") == 0){
        for (int i = 0; i < record_count; i++){
            if(
            atoi(parameters[0]) == records[i].account_number && 
            atoi(parameters[1]) == records[i].check_number){

                records[i].account_number = 0;
                records[i].check_number = 0;
                records[i].amount = 0.0;
                strcpy(records[i].transaction_date, "\0");

            }
        }
    }

    // list all the records contained
    else if(strcmp(first_command, "list\n") == 0){

        // write all the records to the buffer 
        position = 0;
        for(int i = 0; i < record_count; i++){
            position += sprintf(buffer+position, "%d %d %s %.2f\n", 
            records[i].account_number,
            records[i].check_number,
            records[i].transaction_date,
            records[i].amount);
        }

        printf("%s\n", buffer);

        // write the buffer to the pipe
        error_check = write(write_pipe, buffer, strlen(buffer));

        // check for errors
        if(error_check<0){
            perror("child process write error");
            exit(-4);
        }

        // make sure the length of the buffer was proper
        if(error_check!=strlen(buffer)){
            printf("child process error, buffer was a weird size\n");
            exit(-5);
        }
    }

    else{
        printf("you didn't input a correct command\n");
    }


    // empty out everything for reuse
    strcpy(buffer, "\0");
    command = "\0";
    first_command = "\0";
    for(int i = 0; i < 4; i++)
        parameters[i] = "\0";

    // grab the new command 
    error_check = read(read_pipe, buffer, 1000);
    if(error_check<0){
        perror("child process error, reading in from pipe");
        exit(-5);
    }

    // null terminate the end of buffer 
    buffer[error_check] = '\0';

    printf("end of child do while buffer: %s\n", buffer);

}

【问题讨论】:

  • 在此处发布您的代码,而不仅仅是在远程站点。我们不想要完整的代码,请将其缩减为 minimal reproducible example,以证明您遇到的问题。
  • 在进入while 循环之前,您不会设置first_command
  • 您应该将\n 放在strtok() 的分隔符列表中,这样您就不需要将它包含在您的strcmp() 调用中。
  • first_command 不需要在while 条件下测试,只需使用while(1)。当用户输入 exit 时,您是 return,因此在这种情况下您将永远无法返回测试。
  • 这不是minimal reproducible example。一方面,它不完整;甚至没有main

标签: c linux pipe system-calls


【解决方案1】:

首先,我们可以注意到,在第一个命令(add ...)发送给它之后,子(从)应该输出的内容并没有出现在屏幕上。

在第二个命令提示符下,如果您停止程序并执行ps,您会注意到孩子已经死了:

$ ps
  PID TTY          TIME CMD
27069 pts/29   00:00:00 master
27070 pts/29   00:00:00 slave <defunct>

slave 的一个主要问题是您将未初始化的first_command 与“退出”进行比较。该比较要求访问地址first_command 处的字节。如果该地址为 NULL,或者更普遍地在您的程序空间之外,您会得到一个分段错误,该错误会杀死孩子。

父进程如何忽视这一点?因为当你从管道中read 时,你只检查错误,而不是 EOF (0),这意味着管道是空的并且是关闭的(IOW,孩子已经死了)。

修复:初始化变量

char *first_command = "";

(或者更好,使用do...while 循环)

您的代码中还有其他一些小问题,有些已经在 cmets 中报告给您,有些在使用 -Wall 编译时清晰显示(始终使用该标志并修复警告),还有一些等待您的睿智;- )

【讨论】:

    猜你喜欢
    • 2018-12-29
    • 1970-01-01
    • 1970-01-01
    • 2015-06-30
    • 2019-09-16
    • 2014-03-16
    • 1970-01-01
    • 2014-10-28
    • 2023-03-29
    相关资源
    最近更新 更多