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

时间:2018-01-31 17:23:38

标签: c fork

我正在尝试为需要在文件中计数的每个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;
}

1 个答案:

答案 0 :(得分:1)

父母打开文件进行阅读时分叉的问题就是这样 虽然所有孩子都继承了开放文件描述符的副本,但他们都是 分享相同的文件说明

  

man fork

     

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

     

[...]

     
      
  • 子项继承父组的打开文件描述符的副本。子中的每个文件描述符都指向相同的打开   文件描述(参见open(2))作为父中的相应文件描述符。这意味着两个文件描述符共享   打开文件状态标志,文件偏移量和信号驱动的I / O属性(请参阅fcntl(2)F_SETOWNF_SETSIG的说明。)
  •   

您可以执行此类程序,但您必须与子项同步 彼此,因为每次孩子做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