【问题标题】:Forked process isn't calling function in C分叉的进程没有在 C 中调用函数
【发布时间】:2016-05-20 07:59:29
【问题描述】:

这里是 C 初学者。函数send_chars_to_reducers 似乎没有在fork_mappers 函数中创建的分叉进程中被调用。

C 代码

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>

#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26

const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;

const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 1000;

int mapper_pipes[4][2];
int reducer_pipes[26][2];

void pipe_wrapper(int pipefd[]) {
    int ret = pipe(pipefd);
    if (ret == -1) {
        perror("Error. Failed when trying to create pipes.");
        exit(EXIT_FAILURE);
    }
}

void create_mapper_pipes(void) {
    int i;
    for (i = 0; i < NUM_OF_MAPPERS; i++) {
        pipe_wrapper(mapper_pipes[i]);
    }
}

void create_reducer_pipes(void) {
    int i; 
    for (i=0; i < NUM_OF_REDUCERS; i++) {
        pipe_wrapper(reducer_pipes[i]);
    }
}

// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
    if (syscall_val < 0) {
        perror(syscall_name);
        exit(errno);
    } else {
        //No syscall error we can return
        return syscall_val;
    }
}

void send_chars_to_reducers(void) {
    printf("hello from send_chars_to_reducers\n");
}

void fork_mappers(void) {


    /* Constants useful to all children */
    char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
    int rlen = 0;

    int i;
    for (i=0; i<NUM_OF_MAPPERS; i++) {
        pid_t mapper_pid = print_if_err(fork(), "fork");
        close(mapper_pipes[i][PIPE_WRITE_END]);
        if (mapper_pid == 0) {
            rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
            while(rlen > 0) {    
                send_chars_to_reducers();
                printf("read line from forked_mappers, p%d: %s\n", i, ibuf);
                rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
            }
            _exit(0);
        }
    }
}

void fork_reducers(void) {
    printf("hello from fork_reducers\n"); 
    int i;
    for (i = 0; i < NUM_OF_REDUCERS; i++) {
        pid_t reducer_pid = print_if_err(fork(), "fork");
        if (reducer_pid == 0) {
            while (1 == 1) {

            }       
        }
    }
}

void send_lines_to_mappers(void) {
    int wlen = 0;
    char obuf[PIPE_BUFFER_SIZE];
    int ob_size;
    int count = 0;

    char buff[BUFFER_SIZE]; // a buffer for each line of the file
    FILE *input_file = fopen("input.txt", "r");
    // read the input file line by line
    while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
        printf("read line from send_lin_to_mappers: %s\n", buff);
        ob_size = sizeof buff;
        switch(count) {
            case 0 :
                write(mapper_pipes[0][PIPE_WRITE_END], buff, ob_size);
                close(mapper_pipes[0][PIPE_WRITE_END]);
                break;
            case 1 : 
                write(mapper_pipes[1][PIPE_WRITE_END], buff, ob_size);
                close(mapper_pipes[1][PIPE_WRITE_END]);
                break;
            case 2 :
                write(mapper_pipes[2][PIPE_WRITE_END], buff, ob_size);
                close(mapper_pipes[2][PIPE_WRITE_END]);
                break;
            case 3 : 
                write(mapper_pipes[3][PIPE_WRITE_END], buff, ob_size);
                close(mapper_pipes[3][PIPE_WRITE_END]);
                break;
            default :
                printf("you did something wrong in send_lines_to_mappers loop");
        }
        count++;
    }
    fclose(input_file);
}

int main(void) {
    // Setup the mapper pipes
    create_mapper_pipes();
    create_reducer_pipes();
    fork_reducers();
    fork_mappers();
    send_lines_to_mappers();

    return 0;
}

输出

hello from fork_reducers
read line from send_lin_to_mappers: I like coding in C.

read line from send_lin_to_mappers: I like manually allocating memory, and opening the registers window in Visual Studio to see the values of the eax register and blitting graphics to the screen and all the stuff that Dr. Dobbs wrote about in the 90s.

read line from send_lin_to_mappers: My programming friends seem to believe that understanding this level of programming is good in a hand-wavy, theoretical sense, but when you consider all the web development, Java frameworks, and existing libraries most programmers today rely on, it's hard to really pin down a solid answer to the question "Why learn C?"

read line from send_lin_to_mappers: This is my attempt to answer that question, and I believe it comes down to the basic programming concept of abstraction.

【问题讨论】:

  • close(mapper_pipes[i][PIPE_WRITE_END]); 那是在错误的位置。您的意思是为子进程关闭它,但同时为父进程和子进程关闭它。
  • 嘿再次@kaylum,问题是如果我在if (mapper_pid == 0) 之后立即关闭,我会得到hello from send_chars_to_reducers 来打印,但进程不会退出。
  • 好吧,两个错误不代表正确:) 你现在拥有的肯定是错误的。在那里留下明显的错误代码,因为它掩盖了另一个问题,这并不是真正的制胜策略。所以修复它并解释随后发生的错误。对于初学者,如何确定它没有退出?
  • @kaylum 我知道它没有退出,因为光标没有重置,还因为如果我运行 ps 我会看到四个分叉的子进程。

标签: c unix fork


【解决方案1】:

问题在于共享句柄的进程数。 每个减速器都有管道打开 每个映射器都打开了管道,因此管道永远不会正确关闭(reducer 处于繁忙的循环中。

    if (mapper_pid == 0) {
      int j;
      for( j = 0; j < NUM_OF_MAPPERS; j++ ){
        close( mapper_pipes[j][PIPE_WRITE_END]);
        if( j != i ){
           close(mapper_pipes[j][PIPE_READ_END]);
        }
    }

我注释掉了reducer fork,然后修复了mapper fork以关闭子进程中的所有管道句柄,除了我们要读取的那个。

这开始使程序工作。 从pipe documentation,您应该关闭管道中未使用的位。但是您已经预先打开了所有管道(可能更好地本地化它们),因此必须关闭子进程中所有未使用的句柄才能让操作系统正确整理资源

【讨论】:

    猜你喜欢
    • 2012-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    • 2017-06-04
    • 2012-05-05
    • 1970-01-01
    相关资源
    最近更新 更多