创建单独的子函数以计算文件中的每个字母 [英] Creating seperate child function for counting each letter in a file

查看:74
本文介绍了创建单独的子函数以计算文件中的每个字母的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

推荐答案

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

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:

[...]

  • 子级继承父级打开文件描述符集的副本.子级中的每个文件描述符都引用相同的 文件说明(请参见 open(2) )作为父级中相应的文件描述符.这意味着两个文件描述符共享 打开文件状态标志,文件偏移和信号驱动的I/O属性(请参阅F_SETOWN和F_SETSIG的描述" rel ="nofollow noreferrer"> fcntl(2) ).
  • 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)).

您可以执行这样的程序,但是必须将子进程与 彼此,因为每次孩子做fgetc(file)时,文件描述 对所有孩子的进步.同步必须这样写: 所有的孩子都在等其他孩子停止阅读,先做rewind然后最后 读.在那种情况下,拥有所有这些孩子根本毫无收获.

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.

有关此的更多信息,请参见此优秀答案 问题:有人可以解释有关文件描述符"放在fork()之后?

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复制该过程,并且它们都在单独的内存空间中运行. 子女的counter是父母子女的counter的副本,但经过修改 子进程中counter的值只会影响该进程的counter, 父母的counter不受此影响.所以在这种情况下,你总是 打印0,因为父级从未更改counter.

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.

此外,即使孩子的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.

该程序正是这样做的:

#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个管道,每个子管道用于一个孩子.它为创建一个数组 pid并将其初始化为-1(以后用于错误检查).然后进入 循环并创建一个新的孩子,并关闭父管道的书写端 为第i个孩子.然后它再次进入循环并检查是否有孩子 为每个角色创建了进程.如果是这样,它会等待 子退出并检查其退出状态.当且仅当孩子退出时 通常(退出状态为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.

与此同时,每个孩子都关闭管道的读取端并打开一个文件 读.这样,孩子们就不会共享文件描述,并且可以 彼此独立地读取文件的内容并计算 分配给孩子的字母的频率.如果出问题了,什么时候 打开文件后,孩子关闭管道的书写端,并以 返回状态1,向父级发出信号,说明出了点问题,并且 它不会通过管道发送任何结果.如果一切顺利,孩子 将结果写入管道的写入端,并以退出状态退出 为0.

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

这篇关于创建单独的子函数以计算文件中的每个字母的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆