c - 读取写入管道的信息后无法退出while循环
问题描述
TLDR:您必须关闭所有子项中所有管道的写入端。只有当没有进程的写入端仍然打开时,读取才会检测到 EOF。归功于@Bodo
作为操作系统课程作业的一部分,我正在尝试从格式为的文件中读取行x operand y
并将这些行分发到不同的子进程,以便每个人都可以将这些行作为输入并进行计算和编写它到一个输出文件。
通过获得正确的结果,我觉得我快到了,但是在将所有写入的行读取到管道的读取端之后,我的代码似乎导致了一个无休止的 while 循环。
这是相关的代码片段
int child_work(int pipes[][2], int proc, int procid, FILE * out)
{
int i;
pid_t mypid;
Expression exp;
float result;
int procidx = procid;
char expression[MAIN_BUF_LEN];
int r_val;
printf("entered while loop for child process %d\n", mypid);
while(1)
{
if ( (r_val = read(pipes[procid][0], expression, MAIN_BUF_LEN)) > 0)
{
printf("return values of read: %d\n", r_val);
exp_readln(&exp, expression);
result = exp_cal(&exp);
printf("[#%d]: %d %0.3f\n", procidx, mypid, result);
fprintf(out, "#%d: %d %0.3f\n", procidx, mypid, result);
fflush(out);
procidx += proc;
}
else
{
break;
}
}
printf("exited while loop and reached end of child process %d\n", mypid);
return 0;
int main(int argc, char **argv)
{
if (argc != 4)
{
printf("not enough arguments");
return 0;
}
const char *infile; // Name of infile
const char *outfile; // Name of outfile
int proc; // Number of child process to fork
// Save arguments to variables
infile = argv[1];
outfile = argv[2];
sscanf(argv[3], "%u", &proc);
int pipes[proc][2]; // Pipes to be created
pid_t child_pids[proc]; // store all the pids of children created
int i; // Loop counter
char buf[MAIN_BUF_LEN];
Expression exp;
FILE * in_ptr, *out_ptr;
// Open infile with read-only, outfile with write and append.
if ((in_ptr = fopen(infile, "r")) == NULL)
{
printf("Error in opening file. Ending program. \n");
return 1;
}
out_ptr = fopen(outfile, "a+");
// Get parent pid and print to outfile
int ppid = getpid();
fprintf(out_ptr, "%d\n", ppid);
fflush(out_ptr);
// $proc pipes should be created and saved to pipes[proc][2]
for (i = 0; i < proc; ++i)
{
// TODO
if (pipe(pipes[i]) == -1 )
{
printf("Pipe failed for pipe %d\n", i);
return 1;
}
}
// $proc child processes should be created.
// Call child_work() immediately for each child.
for (i = 0; i < proc; ++i)
{
int pid;
// create child only if in parent process
if (getpid() == ppid)
{
pid = fork();
if (pid != 0)
printf("created child with child pid %d\n", pid);
child_pids[i] = pid;
}
if (pid == 0) // in child process
{
child_work(pipes, proc, i, out_ptr);
break;
}
else if (pid < 0) // error in forking
{
printf("Fork failed.\n");
}
}
// Close reading end of pipes for parent
for (i = 0; i < proc; ++i)
{
// TODO
if (getpid() == ppid)
close(pipes[i][0]);
}
// Read lines and distribute the calculations to children in round-robin
// style.
// Stop when a empty line is read.
char* line = NULL;
size_t len = 0;
ssize_t read = 0;
int j = 0;
while ((read = getline(&line, &len, in_ptr)) != -1) {
//printf("Retrieved line of length %zu:\n", read);
//printf("%s", line);
j = j % proc;
write(pipes[j++][1], line, strlen(line)+1);
}
// Close all the pipes when the task ends
for (i = 0; i < proc; ++i)
{
// close(pipes[i][READ]);
close(pipes[i][WRITE]);
}
printf("Task 6 complete!");
for (i = 0; i < proc; ++i)
{
waitpid(child_pids[i], NULL, 0);
}
fprintf(out_ptr, "\n");
fflush(out_ptr);
return 0;
}
这是我得到的输出,由于进程不会终止,它似乎陷入了无限循环。return values of read:
此外,根据我正在使用的特定输入文件,值应该是 22 或 23,但我不知道为什么它会为特定的后续子进程递增。似乎没有一个子进程能够退出 while 循环,因为这printf("exited while loop and reached end of child process %d\n", mypid);
似乎没有被执行。我的理解是,如果已经读取了一个管道,则返回值将是读取的行的字节大小,如果达到EOF或错误,则返回值分别为0或-1。
entered while loop for child process 16016
entered while loop for child process 16017
entered while loop for child process 16018
entered while loop for child process 16020
return values of read: 22
entered while loop for child process 16019
[#0]: 16016 1.783
return values of read: 22
return values of read: 22
[#2]: 16018 0.061
[#1]: 16017 0.195
return values of read: 22
return values of read: 22
[#5]: 16016 0.269
return values of read: 46
[#10]: 16016 1.231
return values of read: 22
return values of read: 22
[#6]: 16017 0.333
return values of read: 22
return values of read: 46
[#11]: 16017 1.684
[#7]: 16018 -0.734
return values of read: 46
[#12]: 16018 0.134
[#3]: 16019 0.778
return values of read: 68
[#4]: 16020 -0.362
return values of read: 68
[#9]: 16020 0.506
[#8]: 16019 -0.450
对于我可能犯的愚蠢错误,我将不胜感激。谢谢!
解决方案
代码中有几个问题。
不幸的是,我无法编译它并修复错误,因为它不完整。
您不能像这样定义大小不恒定的数组。
int pipes[proc][2]; // Pipes to be created
我希望编译器在这一行显示警告。
您应该使用动态分配 (malloc
) 或静态分配具有最大大小的数组,并检查proc
不大于最大值。您必须关闭所有子节点中所有管道的写入端。
read
只有当没有进程的写端仍然打开时,才会检测 EOF 。代替
while(1) { if ( (r_val = read(pipes[procid][0], expression, MAIN_BUF_LEN)) > 0) { /*...*/ } else { break; } }
我建议
while((r_val = read(pipes[procid][0], expression, MAIN_BUF_LEN)) > 0) { /*...*/ }
代替
pid = fork(); if (pid != 0) printf("created child with child pid %d\n", pid);
它应该是
pid = fork(); if (pid > 0) printf("created child with child pid %d\n", pid);
因为
pid < 0
是一个错误。代替
if (pid == 0) // in child process { child_work(pipes, proc, i, out_ptr); break; }
采用
if (pid == 0) // in child process { child_work(pipes, proc, i, out_ptr); return 0; }
break;
孩子将在循环之后继续执行代码,该代码将读取文件并在返回for
时写入管道。child_work
It is not guaranteed that every child will get its turn to
read
from the pipe before the parentwrite
s the next data, so it may get two or more messages in a singleread
. In real applications you should also be prepared to handle incompleteread
orwrite
calls and to continue writing/reading the remaining data with additionalread
orwrite
calls.I think the easiest way to handle partial
read
orwrite
would be to use buffered IO. You can usefdopen
with the wrtite file descriptor or the read file descriptor of the pipe and write/read the data as a line of text terminated with a newline using e.g.fprintf
orfgets
respectively.
推荐阅读
- css - Element UI - VueJS - 在 IOS 上选择输入双击错误
- graph - 查找所有拓扑顺序
- .net-core - Task.IsFaulted 没有在我的 .Net 核心中捕获异常
- parsing - 如何在 CLion 中启用 CPP 文件的解析上下文切换器?
- c++ - 如何使用 open62541 一次使用 OPC-UA 编写多个节点?
- algorithm - 如何在时间 O(log(n)) 处拆分给定节点的 AVL 树?
- javascript - 无限获取循环
- javascript - 尝试将信息发布到 heroku 时出现 404(未找到)
- javascript - 渐进式 Web 应用程序和后台 Web 套接字连接
- kubernetes - GCP - 如何使用 Ingress 在负载均衡器上为多个应用程序提供服务?