我正试着写一个程序来读取一堆unix命令,并创建子进程来执行它们.它有一个参数来确定我希望同时运行的子进程的最大数量.我将stdin重定向到一个单独的文件中,该文件中充满了这样的命令:./a.out 3 < batchcmds.当我运行代码时,它会一遍又一遍地重读所有命令,但是当我注释掉switch语句时,它会按预期运行(当然,创建进程和运行命令除外).我已经通过测试确认,所有的子进程在到达while循环的末尾之前就终止了,所以主进程一定是被某种方式搞砸了,导致它重复读取一个文件.下面是我的代码:

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

int makearg(char*, char***);

#define BUFFERSIZE 1024

int main(int argc, char** argv) {
        // reads argument
        int maxpc = atoi(argv[1]);
        int pc = 0;

        // read from the file and assign it to a child
        char command[BUFFERSIZE];
        while(fgets(command, 128, stdin) > 0) {
                char** args;
                makearg(command, &args);

                if(pc >= maxpc) {
                        wait(NULL);
                        pc--;
                }

                switch(fork()) {   // When I comment out from here to the comment below it works as expected
                        case 0:
                                // is child
                                execvp(args[0], args);
                                printf("execv failed to run %s", command);
                                exit(0);
                                printf("child is immortal\n");
                                return -1;
                        case -1:
                                printf("failed to create child\n");
                                break;
                        default:
                                // is parent
                                pc++;
                }  // The comment below
                printf("end while loop: %x:%s", getpid(), command);
        }

        // wait for children to finish
        for(; pc > 0; pc--) wait(NULL);

        return 0;
}

int makearg(char* s, char*** args) {
        // count the tokens in the string
        int tokencount = 1;
        int slength = 1;
        for(char* c = s; *c != '\0'; c++) {
                if(*c == ' ')
                        tokencount++;
                slength++;
        }

        // set up the array of tokens
        char** tokens = (char**)malloc(tokencount*sizeof(char*));
        if(tokens == NULL) {
                return -1;
        }
        for(int i = 0; i < tokencount; i++)
                tokens[i] = (char*)malloc(slength*sizeof(char));
        if(tokens == NULL) {
                for(int i = 0; i < tokencount; i++)
                        free(tokens[i]);
                free(tokens);
                return -1;
        }

        // copy data over to the token array
        int itoken = 0;
        int ichar = 0;
        for(char* c = s; *c != '\0'; c++) {
                if(*c == ' ') {
                        tokens[itoken][ichar] = '\0';
                        itoken++;
                        ichar = 0;
                }
                else if(*c != '\n') {
                        tokens[itoken][ichar] = *c;
                        ichar++;
                }
        }
        tokens[itoken][ichar] = '\0';

        *args = tokens;
        return tokencount;
}

这真的把我难住了,我找不到任何人有类似的问题.任何帮助将不胜感激!

推荐答案

您的程序中至少存在三个问题.

一个是execvp的第二个参数应该是一个以空指针结束的指针列表,但您的makearg routine 不会构造这样的列表.需要两个步骤来解决此问题.第一,malloc(tokencount*sizeof(char*));应该是malloc((tokencount+1)*sizeof(char*));.其次,在 routine 的末尾,用空指针tokens[tokencount] = 0;标记列表的末尾.

另一个原因是Forking 会导致重复输出.这是因为C流通常是缓冲的,这意味着像printf这样的函数不会立即将输出发送到操作系统以写入设备.相反,数据被写入由C流接口管理的缓冲区.当缓冲区已满或新行字符被写入时(如果流是line buffered),它被发送到操作系统.连接到终端的流通常是行缓冲的.连接到管道的流通常是完全缓冲的.

当您的程序派生时,由printf(或其他 routine )放入缓冲区但尚未发送到操作系统的任何数据都将复制到子程序中.最终,父进程和子进程都可能会将其数据副本发送到操作系统,因此您将看到两条消息.这些重复的消息可能会让您相信程序正在多次执行操作,例如重新处理相同的命令.事实并非如此.只有信息被复制,而不是该程序之前的工作.

解决这个问题的一种方法是将标准输出流设置为不缓冲,这可以通过在程序开始时执行setvbuf(stdout, NULL, _IONBF, 0);来实现.然而,这就失go 了缓冲的所有好处(尤其是效率).另一种解决方案是在fork调用之前立即执行fflush(stdout);.这会将所有挂起的输出发送到系统,因此缓冲区中不会有要复制的数据.

第三个问题是fgets(command, 128, stdin) > 0.C标准没有定义将指针(由fgets返回)与空指针常量(0)与>进行比较.你可以把它比作!=.此外,最好使用NULL来清楚地表明您正在与空指针进行比较:fgets(command, 128, stdin) != NULL.

C++相关问答推荐

有什么方法可以检测SunOS上的SparcWorks吗?

strftime函数中%s的历史意义是什么?为什么没有记录?

使用SWI—Prolog的qsave_program生成二进制文件有什么好处?'

返回一个包含数组的 struct

如何创建由符号组成的垂直结果图形?

增加getaddrinfo返回的IP地址数量

CSAPP微型shell 实验室:卡在sigprocmask

我怎么才能用GCC编译一个c库,让它包含另一个库呢?

在创建动态泛型数组时,通过realloc对故障进行分段

我在反转双向链表时遇到问题

C堆栈(使用动态数组)realloc内存泄漏问题

当用C打印过多的';\n';时输出不正确

传递给函数的 struct 中的数组

按字典顺序打印具有给定字符的所有可能字符串

无法理解 fgets 输出

GDB 用内容初始化数组

在 C/C++ 中原子按位与字节的最佳方法?

无法在 C 中打开文本文件,我想从中读取文本作为数据并将其写入数组

我们可以在不违反标准的情况下向标准函数声明添加属性吗?

将十六进制值或十进制值分配给 uint16_t 有什么区别?