我正在尝试编写一个程序来读取一堆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;
}
这真的把我难住了,我找不到有类似问题的人。任何帮助将不胜感激!
问题在于标准输入/标准输出的共享...请记住,fork 几乎复制了所有内容,并且共享输入/输出确实是一团糟。 最好是确保根本没有缓冲,以便对输入/输出 C 函数的任何调用都将直接从操作系统层获取/放置。 添加:
setvbuf(stdin,NULL,_IONBF,0);
setvbuf(stdout,NULL,_IONBF,0);
循环之前。