【问题标题】:C - creating process tree using pipes, select, fork, and execlC - 使用管道、select、fork 和 execl 创建进程树
【发布时间】:2013-10-11 20:51:33
【问题描述】:

我正在尝试编写以下两部分程序。在一个文件(“root.c”)中,我读入了 1 和 0 的随机字符串。然后我将生成的字符串分成两半,并通过 fork() 将每一半发送到自己的进程。每个子进程使用 execl() 来运行第二个程序(“bit_count.c”)。

在 bit_count.c 中,它: a) 检查(半)字符串的长度是否为 2 或更少。如果是,则返回
的数量 1 和 0 到它的父进程。 b) 如果不是,它开始递归地将字符串分成两半,并将每一半发送到它自己的新进程(复制 root.c 中的过程)。这将创建一个二叉进程树,直到字符串的所有片段长度为 2 个字符或更少。 c) 左右子节点的计数结果由父节点聚合,返回给父节点,直到返回根进程,根进程聚合最高的两个子节点,输出给用户。

我对这个项目的问题是将 2 个字符的计数返回给父级。我现在的想法是使用 dup2() 将父级的左右读取管道引导到标准输入,并从子级使用 fprintf 打印到标准输出。父级的 select() 函数应该捕获返回的输出,对吗?

我的第二个问题是输出的格式。如果计数是整数,那么在这种情况下使用 select() 返回它的最佳方法是什么?我在下面附上了我的代码,只是被警告它可能是一团糟 - 我对 C 代码生疏了,这是我第一次接触 select() 和 execl()。

root.c:

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char* argv[]) {
if (argc != 2) {
    perror("input file name");
    printf("%d", argc);
    exit(1);
}

FILE* fp;
if((fp = fopen(argv[1], "r")) == NULL) {
    perror("open file");
}
fseek(fp, 0, SEEK_END);
long fsize = ftell(fp);
fseek(fp, 0, SEEK_SET); 
char *bits = malloc(fsize+1);
fread(bits, fsize, 1, fp);
fclose(fp);


char *left_half = malloc( fsize/2 + 1 );
char *right_half;
if (fsize%2) right_half = malloc( fsize/2 + 2 );
else right_half = malloc( fsize/2 + 1 );

if (!left_half || !right_half) perror("array split");
memcpy(left_half, bits, fsize/2);
if (fsize%2) memcpy(right_half, bits + fsize/2, fsize/2 + 1);
else memcpy(right_half, bits + fsize/2, fsize/2);


int fd_left[2], fd_right[2];
int zero, one;
int *left_res, *right_res;
pid_t left, right;
struct timeval tv;
fd_set readfds;

tv.tv_sec = 2;
tv.tv_usec = 500000;

if ((pipe(fd_left) == -1) || (pipe(fd_right) == -1)){ 
        perror("Create pipe error"); 
        exit(1); 
}

FD_ZERO(&readfds);
FD_SET(fd_left[0], &readfds);
FD_SET(fd_right[0], &readfds);

if ((left=fork()) == 0) {
        close(fd_left[0]);
        execl("./bit_count", "bit_count", left_half, NULL);
        perror("initiating recursion"); 
        exit(1);
}
else if(left > 0) {
    if ((right = fork())==0) {
        close(fd_right[0]);
        execl("./bit_count", "bit_count", right_half, NULL);
        perror("initiating recursion"); 
        exit(1);
    }
    else if (right > 0) { 
        close(fd_right[1]);
        close(fd_left[1]);
        char *left;
        char *right;
        dup2(fd_left[0], 0);
        dup2(fd_right[0], 0);

        int ret = select(2, &readfds, NULL, NULL, &tv);
        read(fd_left[0], &left_res, 1);
        read(fd_right[0], &right_res, 1);           
        printf("Back in root process!\n");

    }   
}

zero = (*right_res + *left_res);
one = (*(left_res+sizeof(int)) + *(right_res+sizeof(int)));

printf("%s had %d zeroes and %d ones\n", argv[1], zero, one);
return 0;
}

bit_count.c(仅相关部分):

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
if (argc != 2) {
    perror("sent bit string");
    printf("%d", argc);
    exit(1);
}
char *bit_string = argv[1];
int size = strlen(bit_string);
int counts[2];
counts[0] = 0;
counts[1] = 0;
if (!(size > 2)) {

    int i=0;
    for(; i < size; i++) {
        if (bit_string[i]=='1') ++counts[1];
        else ++counts[0];
    } 

    fprintf(stdout, "%p", &counts);
    fflush(stdout);
    return 0;
}
  } 

【问题讨论】:

    标签: c select exec fork pipe


    【解决方案1】:
    1. 我现在的想法是使用 dup2() 将父级的左右读取管道引导至标准输入,并使用 fprintf 从子级将其打印到标准输出。父级的 select() 函数应该捕获返回的输出,对吧?

    没有。在调用 execl() 之前,您需要在子进程中 dup2(fd[1], STDOUT_FILENO)。 bit_count 还应该如何了解管道?然后在父级中,您可以从 fd[0] 中读取。为了使事情更容易,您可以将 bit_count 设为一个函数,然后直接在子进程中调用它,而无需使用 execl()。然后,您可以从子级写入 fd[1](如果您将其设为全局,或将值传递给 bit_count 函数)。

    1. 我的第二个问题是输出的格式。如果计数是整数,那么在这种情况下使用 select() 返回的最佳方法是什么?

    您可以使用write(STDOUT_FILENO, &amp;counts, 2*sizeof(int)) 将整数直接写入管道,而不是将它们格式化为字符串。这样,父级不需要将它们转换回整数。

    【讨论】:

    • 感谢您的回复!我使用 dup2 将这条线添加到每个分叉的子节点,并在 bit_count 末尾调用 write()。虽然现在的输出至少说我收到了 8 个字节,但我仍然收到了不可能的计数(例如,对于 01 和 01,我从每个孩子那里得到 0 和 5 的计数,而不是 1 和 1。我知道 bit_count 计数正确,接收它仍然有问题。我应该从父级的读取管道中删除 dup2(它们在我的原始代码中)吗?另外 - 不幸的是需要使用 execl。感谢您的帮助!
    • 你还在这样阅读:read(fd_left[0], &amp;left_res, 1);?几个问题:left_res 被声明为指向 int 的指针,因此当您执行 &left_res 时,您会得到指向 int 的指针。你最好声明int left_res;,或者可能是int left_res[2];。此外,您告诉read 只读取 1 个字节。如果每个孩子都在写两个整数,您还需要在父母中读取该数量。
    猜你喜欢
    • 1970-01-01
    • 2014-07-27
    • 2021-12-15
    • 2013-02-27
    • 2014-05-12
    • 2021-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多