Creating seperate child function for counting each letter in a file

扶醉桌前 提交于 2019-12-13 10:25:14

问题


I am trying to create separate child process for each letter that needs to be counted in a file. I have the file being read in a parent process, but the output is all zeros. I don't understand what I am doing wrong. I need to use child processes for each of the letters, but I am not exactly sure how to create separate processes for each letter. Please help! Here is my code:

#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:


The problem with forking when the parent has open a file for reading, is that although all children inherit copies of open file descriptors, they all share the same file description.

man fork

The child process is an exact duplicate of the parent process except for the following points:

[...]

  • The child inherits copies of the parent's set of open file descriptors. Each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent. This means that the two file descriptors share open file status flags, file offset, and signal-driven I/O attributes (see the description of F_SETOWN and F_SETSIG in fcntl(2)).

You can do such a program, but you would have to synchronize the children with each other, because every time a child does fgetc(file), the file description advances for all children. The synchronization would have to be written such as all children wait for the others to stop reading, do a rewind and then finally read. In that case having all these children is no gain at all.

For more information about that, see this excellent answer from this question: Can anyone explain a simple description regarding 'file descriptor' after fork()?

Another problem with your code is this:

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

fork duplicates the process and they both run in separate memory spaces. The children's counter is a copy of the parent's counter, but a modification of counter in a child process will only affect the counter for that process, the parent's counter is not affected by that. So in this case you are always printing 0, because the parent never changed counter.

Also, even if the modification of a child's counter would somehow propagate to the parent, the parent should wait for the child process to make the modification before accessing the variable. Again synchronization would be needed for that.

For the parent to benefit of the work of the children, it must communicate with the children. One way to do it is by creating a pipe for each of the children. The parent closes the writing end of the pipes, the children close the reading end of the pipe. When the child does its work, it writes the results on the writing end of it's pipe back to the parent and exits. The parent must then wait for every children, read from the reading end of the pipe.

This program does exactly that:

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

The parent creates 26 pipes, each one for a child. The it creates an array for the pids and initializes them to -1 (later for error checking). Then enters in the loop and creates a new child and closes the writing end of the parent's pipe for the i-th child. Then it goes again into a loop and checks if a child process was created for every character. If that's the case, it waits for that child to exit and checks it's exit status. If and only if the child exits normally (with an exit status of 0), it reads from the reading end of the pipe and prints the result, otherwise it prints an error message. Then it closes the reading end of the pipe and exits.

Meanwhile every child closes its reading end of the pipe and open a file for reading. By doing this, the children don't share the file description and can independently from each other read the contents of the file and calculate the frequency of the letter assigned to the child. If something goes wrong when opening the file, the child closes the writing end of the pipe and exits with a return status of 1, signalling to the parent, that something went wrong and that it won't send any result through the pipe. If everything goes well, the child writes the result in the writing end of the pipe and exits with an exit status of 0.

The output of this program with the its source is:

$ ./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


来源:https://stackoverflow.com/questions/48547996/creating-seperate-child-function-for-counting-each-letter-in-a-file

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!