创建单独的子函数来计算文件中的每个字母

我正在尝试为每个需要在文件中计数的letter创建单独的child process 。 我在parent process读取文件,但输出全为零。 我不明白我做错了什么。 我需要为每个字母使用子进程,但我不确定如何为每个字母创建单独的进程。 请帮忙! 这是我的代码:

 #include  #include  #include  #include  #include  #include  #include  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; } 

父进程打开文件进行读取时分叉的问题是,尽管所有子进程都inheritance了打开文件描述符的副本,但它们都共享相同的文件描述

男子叉

除以下几点外,子进程与父进程完全相同:

[…]

  • 子进程inheritance父进程打开文件描述符的副本。 子节点中的每个文件描述符引用相同的打开文件描述(请参阅open(2) )作为父节点中的相应文件描述符 。 这意味着两个文件描述符共享打开文件状态标志,文件偏移量和信号驱动的I / O属性(请参阅fcntl(2)F_SETOWNF_SETSIG的说明)。

您可以执行此类程序,但是您必须将子项彼此同步,因为每次子项执行fgetc(file) ,所有子项的文件描述都会提前。 必须编写同步,例如所有孩子等待其他人停止阅读,做rewind然后最后阅读。 在这种情况下,所有这些孩子都没有收获。

有关这方面的更多信息,请参阅此问题的优秀答案 : 有人可以在fork()之后解释有关“文件描述符”的简单描述吗?

您的代码的另一个问题是:

 printf("%c: %i\n", characters[i], counter[i]); 

fork复制进程,它们都在不同的内存空间中运行。 子counter是父counter的副本,但子进程中counter的修改只会影响该进程的counter ,父进程的counter不受此影响。 因此,在这种情况下,您始终打印0,因为父级从未更改counter

此外,即使子counter的修改以某种方式传播到父级,父级也应该等待子进程在访问变量之前进行修改。 同样需要同步。

为了让孩子的工作受益,父母必须与孩子们沟通。 一种方法是为每个孩子创建一个管道。 父级关闭管道的写入端,子级关闭管道的读取端。 当孩子完成它的工作时,它会将结果写在管道的写入端回到父级并退出。 然后,父母必须等待每个孩子,从管道的读取端读取。

这个程序正是这样做的:

 #include  #include  #include  #include  #include  #include  #include  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(稍后进行错误检查)。 然后进入循环并创建一个新子项并关闭第一个孩子的父管道的写入结束。 然后它再次进入循环并检查是否为每个字符创建了一个子进程。 如果是这种情况,则等待该孩子退出并检查其退出状态。 当且仅当子进程正常退出(退出状态为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