我正在尝试为需要在文件中计数的每个child process
创建单独的letter
。我在parent process
中读取了文件,但输出全为零。我不明白我做错了什么。我需要为每个字母使用子进程,但我不确定如何为每个字母创建单独的进程。请帮忙!这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/wait.h>
int main(int argc, char **argv){
char characters[26] = { "abcdefghijklmnopqrstuvwxyz" };
int counter[26] = { 0 };
int n = 26;
int c;
char ch;
if (argc < 2)
return 1;
pid_t pids[26];
for (int i = 0; i < n; i++){
pids[i] = fork();
if (pids[i] < 0) {
printf("Error");
exit(1);
} else if (pids[i] == 0) {
while (c = fgetc(file) != EOF){
if (c == characters[i])
counter[i]++;
}
exit(0);
} else {
FILE *file = fopen(argv[1], "r");
if(file == NULL)
printf("File not found\n");
while (c = fgetc(file) != EOF);
fclose(file);
wait(NULL);
}
}
for (int i = 0; i < n; ++i){
printf("%c: %i\n", characters[i], counter[i]);
}
return 0;
}
答案 0 :(得分:1)
父母打开文件进行阅读时分叉的问题就是这样 虽然所有孩子都继承了开放文件描述符的副本,但他们都是 分享相同的文件说明。
除了以下几点外,子进程与父进程完全相同:
[...]
您可以执行此类程序,但您必须与子项同步
彼此,因为每次孩子做fgetc(file)
时,文件描述
所有孩子的进步。必须编写同步,例如
所有的孩子都等待其他人停止阅读,做一个rewind
然后最后
读。在这种情况下,所有这些孩子都没有收获。
有关详细信息,请参阅此excellent answer 问题:Can anyone explain a simple description regarding 'file descriptor' after fork()?
您的代码的另一个问题是:
printf("%c: %i\n", characters[i], counter[i]);
fork
复制进程,它们都在不同的内存空间中运行。
孩子counter
是父母counter
的副本,但是修改了
子进程中的counter
只会影响该进程的counter
,
父母counter
不受此影响。所以在这种情况下,你永远都是
打印0,因为父级从未更改counter
。
此外,即使儿童counter
的修改会以某种方式传播到
父母,父母应等待让子进程进行
在访问变量之前进行修改。同步将是
需要的。
为了让孩子的工作受益,父母必须与之沟通 这些孩子。一种方法是为每个孩子创建一个管道。该 父母关闭了管道的书写端,孩子关闭了阅读端 管道。当孩子完成其工作时,它会将结果写在书写的结尾 管道回到父母并退出。然后父母必须等待每个孩子, 从管道的读取端读取。
这个程序就是这样做的:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
int main(int argc, char **argv)
{
char characters[26] = "abcdefghijklmnopqrstuvwxyz";
if(argc != 2)
{
fprintf(stderr, "usage: %s file\n", argv[0]);
return 1;
}
size_t i;
int pipes[26][2];
// creating the pipes for all children
for(i = 0; i < 26; ++i)
{
if(pipe(pipes[i]) < 0)
{
perror("unable to create a pipe");
return 1;
}
}
pid_t pids[26];
memset(pids, -1, sizeof pids);
for(i = 0; i < 26; ++i)
{
pids[i] = fork();
if(pids[i] < 0)
{
fprintf(stderr, "Unable to fork for child %lu: %s\n", i, strerror(errno));
continue;
}
if(pids[i] == 0)
{
// CHILD process
// closing reading end of pipe
close(pipes[i][0]);
FILE *fp = fopen(argv[1], "r");
if(fp == NULL)
{
close(pipes[i][1]);
exit(1);
}
int n = 0, c;
while((c = getc(fp)) != EOF)
{
if(c == characters[i])
n++;
}
// sending answer back to parent through the pipe
write(pipes[i][1], &n, sizeof n);
fclose(fp);
close(pipes[i][1]);
exit(0);
}
// PARENT process
// closing writing end of pipe
close(pipes[i][1]);
}
printf("Frequency of characters for %s\n", argv[1]);
for(i = 0; i < 26; ++i)
{
if(pids[i] < 0)
{
fprintf(stderr, "%c: could not create child worker\n", (char) i + 'a');
close(pipes[i][0]);
continue;
}
int status;
waitpid(pids[i], &status, 0);
if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
{
// child ended normally and wrote result
int cnt;
read(pipes[i][0], &cnt, sizeof cnt);
printf("%c: %d\n", (char) i + 'a', cnt);
} else {
printf("%c: no answer from child\n", (char) i + 'a');
}
close(pipes[i][0]);
}
return 0;
}
父母创建了26个管道,每个管道用于一个孩子。它创建了一个数组
pids并将它们初始化为-1(稍后进行错误检查)。然后进入
循环并创建一个新子项并关闭父管道的写入结束
对于i
个孩子。然后它再次进入循环并检查是否有孩子
为每个角色创建了一个进程。如果是这样,那就等了
孩子退出并检查它的退出状态。当且仅当孩子退出
通常(退出状态为0),它从管道的读取端读取
并打印结果,否则会输出错误消息。然后关闭了
读取管道末端并退出。
与此同时,每个孩子都会关闭管道的读取端并打开文件 读。通过这样做,孩子们不会共享文件描述并且可以 彼此独立地读取文件的内容并计算出来 分配给孩子的信件的频率。如果出现问题的话 打开文件,孩子关闭管道的书写端并退出 返回状态为1,向父母发出信号,说明出错了 它不会通过管道发送任何结果。如果一切顺利,孩子 将结果写入管道的写入端并以退出状态退出 0。
该程序的输出及其来源是:
$ ./counter counter.c
Frequency of characters for counter.c
a: 44
b: 5
c: 56
d: 39
e: 90
f: 40
g: 17
h: 26
i: 113
j: 1
k: 5
l: 35
m: 6
n: 68
o: 45
p: 59
q: 2
r: 78
s: 71
t: 65
u: 25
v: 5
w: 10
x: 3
y: 6
z: 5