我正在编写一个程序,其中我 fork 了两次以创建 2 个子进程,这些子进程通过它们的标准输出向父进程发送信息。 children 启动程序使其递归。发生的情况是父进程在读取子进程时卡在读取循环中,但这不是一个永远的循环(有有限的预期输出);它只是不会越过它。
char response1[256];
double complex r1[sizeof(workable)/sizeof(float)];
char* thing = NULL;
int countC1 = 1;
int bytesread;
char* response1Whole = NULL;
while((bytesread = read(pipefdc1b[0],response1, 256)) > 0){
fprintf(stderr,"PID:%d -> Reading Bytes from Child 1 Bytes read: %d\n\n", getpid(), bytesread);
if(bytesread == 256){
response1Whole = (char*) realloc(response1Whole, countC1*256 );
}else{
response1Whole = (char*) realloc(response1Whole, (countC1-1)*256+bytesread);
}
strcat(response1Whole, response1);
countC1++;
fprintf(stderr,"PID:%d -> Current Builts Input %s\n\n", getpid(), response1Whole);
}
fprintf(stderr,"PID:%d -> Child 1 Read\n\n", getpid());
我认为这是卡住的代码片段。它打印出所有“当前内置输入”和“从子 1 读取字节”部分,但从未到达“子 1 读取”部分。值得注意的是,有一个有限且确定的输出,它读取的内容是预期的,程序就停止了。
if(count == 1){
fprintf(stdout,"%s", st);
fflush(stdout);
exit(EXIT_SUCCESS);
}
这是决定输出的代码块。我觉得这里可能存在一些问题,它没有正确终止它的输出流,但它又退出了。
这里是创建管道到“问题”区域的代码:
int pipefdc1a[2]; //p->c1
int pipefdc1b[2]; //c1->p||
int pipefdc2a[2]; //p->c1
int pipefdc2b[2]; //c2->p
if(pipe(pipefdc1a) == -1){
fprintf(stderr, "Pipe Creation Failed\n");
exit(EXIT_FAILURE);
}
if(pipe(pipefdc1b) == -1){
fprintf(stderr, "Pipe Creation Failed\n");
exit(EXIT_FAILURE);
}
if(pipe(pipefdc2a) == -1){
fprintf(stderr, "Pipe Creation Failed\n");
exit(EXIT_FAILURE);
}
if(pipe(pipefdc2b) == -1){
fprintf(stderr, "Pipe Creation Failed\n");
exit(EXIT_FAILURE);
}
//fprintf(stderr,"PID:%d -> Pipes Created\n\n", getpid());
fflush(stdout);
pid_t pid = fork();
pid_t pid2;
switch (pid) {
case -1:
fprintf(stderr, "Cannot fork!\n");
exit(EXIT_FAILURE);
case 0:
//child 1
close(pipefdc1a[1]);
close(pipefdc1b[0]);
close(pipefdc2a[0]);
close(pipefdc2a[1]);
close(pipefdc2b[0]);
close(pipefdc2b[1]);
dup2(pipefdc1a[0],STDIN_FILENO);
close(pipefdc1a[0]);
dup2(pipefdc1b[1], STDOUT_FILENO);
close(pipefdc1b[1]);
execlp("./forkFFT","forkFFT", NULL);
exit(EXIT_FAILURE);
default:
//parent
fflush(stdout);
pid2 = fork();
switch(pid2){
case -1:
fprintf(stderr, "Cannot fork!\n");
exit(EXIT_FAILURE);
case 0:
//child 2
//fprintf(stderr,"PID:%d -> New Child 2 Created\n\n", getpid());
close(pipefdc1a[1]);
close(pipefdc1a[0]);
close(pipefdc1b[0]);
close(pipefdc1b[1]);
close(pipefdc2a[1]);
close(pipefdc2b[0]);
dup2(pipefdc2a[0],STDIN_FILENO);
close(pipefdc2a[0]);
dup2(pipefdc2b[1], STDOUT_FILENO);
close(pipefdc2b[1]);
execlp("./forkFFT","forkFFT", NULL);
exit(EXIT_FAILURE);
default:
//parent
fprintf(stderr, "entered parent switch. PID1: %d, PID2: %d\n\n", pid, pid2);
write(pipefdc1a[1], stp1, evensize);
write(pipefdc2a[1], stp2, oddsize);
close(pipefdc1a[1]);
close(pipefdc2a[1]);
char response1[256];
char response2[256];
double complex r1[sizeof(workable)/sizeof(float)];
double complex r2[sizeof(workable)/sizeof(float)];
char* thing = NULL;
int countC1 = 1;
int bytesread;
char* response1Whole = NULL;
while((bytesread = read(pipefdc1b[0],response1, 256)) > 0){
fprintf(stderr,"PID:%d -> Reading Bytes from Child 1 Bytes read: %d\n\n", getpid(), bytesread);
if(bytesread == 256){
response1Whole = (char*) realloc(response1Whole, countC1*256 );
}else{
response1Whole = (char*) realloc(response1Whole, (countC1-1)*256+bytesread);
}
strcat(response1Whole, response1);
countC1++;
fprintf(stderr,"PID:%d -> Current Builts Input %s\n\n", getpid(), response1Whole);
}
fprintf(stderr,"PID:%d -> Child 1 Read\n\n", getpid());
char* token = strtok(response1Whole, "\n");
while(token != NULL){
double real = (double)strtof(token, &thing);
if(token == thing){
fprintf(stderr, "Real Part is NAN\n");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Real Number Read from child 1: %lf. PID1: %d, PID2: %d\n\n", real, pid, pid2);
double imaginary = 0.0;
if(thing != NULL){
fprintf(stderr,"Thing String: %s\n", thing);
char* check = NULL;
imaginary = (double)strtof(thing, &check);
if(check == thing){
fprintf(stderr, "Imaginary Part is NAN\n");
exit(EXIT_FAILURE);
}
}
fprintf(stderr, "Imaginary part of number 1: %lf. PID1: %d, PID2: %d\n\n",imaginary, pid, pid2);
r1[countC1] = real + imaginary*I;
token = strtok(NULL,"\n");
}
fprintf(stderr, "made it 1!\n\n");
char* thing2 = NULL;
int countC2 = 0;
FILE* pipe2File = fdopen(pipefdc2b[0], "r");
while(fgets(response2, 256, pipe2File) != NULL){
fprintf(stderr, "Reading from child 2. PID1: %d, PID2: %d\n\n", pid, pid2);
double real = (double)strtof(response2, &thing2);
if(response1 == thing2){
fprintf(stderr, "Real Part is NAN\n");
exit(EXIT_FAILURE);
}
fprintf(stderr, "Real Number Read from child 2: %lf. PID1: %d, PID2: %d\n\n", real, pid, pid2);
double imaginary = 0.0;
if(thing2 != NULL && thing2[0] != '\n'){
fprintf(stderr,"Thing2 String: %s\n", thing2);
char* check2 = NULL;
imaginary = (double)strtof(thing2, &check2);
if(check2 == thing2){
fprintf(stderr, "Imaginary Part is NAN\n");
exit(EXIT_FAILURE);
}
}
fprintf(stderr, "Imaginary part of number 2: %lf. PID1: %d, PID2: %d\n\n",imaginary, pid, pid2);
r2[countC2] = real + imaginary*I;
countC2++;
}
fclose(pipe2File);
fprintf(stderr, "made it 2!\n\n");
waitpid(pid, &status, 0);
if(status == 1){
fprintf(stderr, "Child did not terminate normally!\n");
exit(EXIT_FAILURE);
}
waitpid(pid2, &status, 0);
if(status == 1){
fprintf(stderr, "Child did not terminate normally!\n");
exit(EXIT_FAILURE);
}
我为 child 1 和 2 尝试了 2 个不同的循环,因为我在某个时候认为这是问题所在,但事实并非如此。
我对 C 的理解有些局限;如果你能回答并解释那就太好了。
我正在努力遵守“最少要求的代码”规则,但如果您觉得还需要其他东西,请询问,我会添加它们。
最佳答案
正如我在 comment 中猜测的那样,您没有关闭足够多的文件描述符 — 这一次,问题出在父进程中,尽管通常是子进程没有进行足够的关闭。
经验法则:如果您
dup2()
管道的一端连接到标准输入或标准输出,同时关闭
返回的原始文件描述符
pipe()
尽快地。
特别是,您应该在使用任何
exec*()
函数族。
如果您使用以下任一方式复制描述符,则该规则也适用
dup()
或者
fcntl()
使用 F_DUPFD
您通过 4 个 pipe()
调用创建了 8 个文件描述符。在 child 中,您根据经验法则关闭它们。
但是,在尝试从子文件读取到 EOF 之前,您的父文件只关闭了 2 个文件描述符。而且,由于它仍然有管道的写端,它正在从打开的地方读取,所以它永远不会得到 EOF 指示,因为理论上它可以写入管道。
因此,您的父代码如下所示:
write(pipefdc1a[1], stp1, evensize);
write(pipefdc2a[1], stp2, oddsize);
close(pipefdc1a[1]);
close(pipefdc2a[1]);
应该看起来更像:
write(pipefdc1a[1], stp1, evensize);
write(pipefdc2a[1], stp2, oddsize);
close(pipefdc1a[1]);
close(pipefdc2a[1]);
close(pipefdc1a[0]); // Extra
close(pipefdc2a[0]); // Extra
close(pipefdc1b[1]); // Extra
close(pipefdc2b[1]); // Read all about it!
我认为您需要考虑编写函数来完成工作,而不是将所有代码都放在 main()
程序中,但您仍然需要处理调用 close ()
足够频繁了。
关于c - 在 fork 卡在未知位置的读取循环中后从子管道读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53764592/