C 带fork()的二叉进程树
我的OS类的第一个项目是使用C 带fork()的二叉进程树,c,fork,binary-tree,C,Fork,Binary Tree,我的OS类的第一个项目是使用fork()创建一个进程树,该进程树的深度由用户在命令行中指定。每个叶级节点都需要对数据进行排序,并使用命名管道(FIFO)将其传递回其父节点 我可以用fork()创建一个N深度树,每个进程有两个子进程。我不明白的是,如何将FIFO一直传递给树下的每个孩子,然后让这个过程对FIFO中的某些数据执行排序,然后再将其返回到树的顶部 以下是迄今为止我用于构建树的伪代码: void CreateTree(int level) { if level = 0 return
fork()
创建一个进程树,该进程树的深度由用户在命令行中指定。每个叶级节点都需要对数据进行排序,并使用命名管道(FIFO)将其传递回其父节点
我可以用fork()
创建一个N深度树,每个进程有两个子进程。我不明白的是,如何将FIFO一直传递给树下的每个孩子,然后让这个过程对FIFO中的某些数据执行排序,然后再将其返回到树的顶部
以下是迄今为止我用于构建树的伪代码:
void CreateTree(int level)
{
if level = 0 return
int left_child = fork();
if(left_child != 0) //we are the parent
{
int right_child = fork();
if(right_child == 0)
CreateTree(level - 1);
}
else
{
CreateTree(level-1);
}
}
那么,我如何单独抓取每个进程来处理它们呢?您提到了fifo,也称为命名管道,我们来看看。(此处代码假定为*nix): 这个快速示例显示了从父级向子级发送数据,让子级操作数据,然后将数据返回给父级。因此,您不是在“传递”fifo,而是每个进程(或子进程)都有权访问
char*
,这将为他们提供fifo的名称,以便他们可以根据需要打开它进行读写。您可以采用此概念,并将其扩展到您拥有的每个子节点:
int main()
{
int fd, n, ret;
fd_set rfds;
char * myfifo = "/tmp/myfifo";
mkfifo(myfifo, 0666); // Create this buffer
if(fork()) //Kid code
{
char kid_buffer[4] = {0};
char temp;
fd = open(myfifo, O_RDONLY); //Open the fifo for reading
n = read(fd, kid_buffer, 4);
printf("Kid %d read %d bytes, parent gave us %s\n",getpid(), n, kid_buffer);
fflush(stdout);
close(fd);
// "sort" the data the parent gave us
temp = kid_buffer[0];
kid_buffer[0] = kid_buffer[1];
kid_buffer[1] = kid_buffer[2];
kid_buffer[2] = temp;
kid_buffer[3] = '\0';
printf("Kid %d reoriginized the list %s\n",getpid(), kid_buffer);
fflush(stdout);
// send the data back
fd = open(myfifo, O_WRONLY);
write(fd, kid_buffer, strlen(kid_buffer));
close(fd);
return 0;
}
else
{
char arr[] = "abc";
//Open the fifo for writing
fd = open(myfifo, O_WRONLY);
write(fd, arr, strlen(arr)); //Sent my data to kid
printf("Parent process %d, just sent my data %s to the kid\n", getpid(), arr);
fflush(stdout);
close(fd);
//Open the fifo for reading
fd = open(myfifo, O_RDONLY);
n = read(fd, arr, 4);
// show the data we got back
printf("Parent %d read %d bytes, kid gave us back %s\n",getpid(), n, arr);
fflush(stdout);
close(fd);
}
unlink(myfifo);
return 0;
}
因此,从这里的输出中,您可以看到父级创建了自己的数组“abc”,并且它被子级(通过FIFO传递)修改为“bca”,现在它与父级一起返回并格式化
mike@linux-4puc:~> ./a.out
Parent process 4295, just sent my data abc to the kid
Kid 4294 read 3 bytes, parent gave us abc
Kid 4294 reoriginized the list bca
Parent 4295 read 3 bytes, kid gave us back bca
- 为每个孩子分配一个数字(不是PID;在有PID之前,您需要知道数字!)
- 创建一个FIFO(
),其名称为mkfifo()
,其中N是数字FIFO.N
- 每个孩子都知道该写入哪个FIFO
- 每个家长都知道要从哪个FIFO读取。(大概父母只是在运行合并而不是排序。)
初始化所有FIFO后,如何知道哪个进程正在执行以及何时执行?当我在构建树之后回到主程序时,有没有办法根据PID和控制语句来确定哪个进程正在运行 流程可分为“叶”流程和“非叶”流程 叶进程没有任何子进程。它们执行排序分配,并将排序后的数据写入输出FIFO。它们将在FIFO上被阻止,直到它们的父进程打开它进行读取。当它们完成写入时,它们关闭FIFO,父进程获得EOF 每个非叶进程正在合并来自其两个子进程的排序数据。每个非叶进程都需要知道它自己的输出FIFO是什么(根节点可能写入标准输出而不是FIFO),以及它的两个子进程的FIFO是什么。可能非叶进程创建fifo.$$.1和fifo.$$.2(其中,$$是已经运行的非叶进程的PID),而不是让父进程预先创建它们。然后它分叉它的两个子级,用一个变量指示每个子级使用哪个FIFO。然后,非叶进程打开两个FIFO进行读取(以及它自己的输出FIFO进行写入),并合并两个数据流(从两个数据流中读取一行,将较小的写入输出,读取替换行,直到其中一个出现EOF,然后从另一个完成读取)。一旦完成,进程将删除它创建的两个FIFO(基本卫生),并关闭其输出FIFO并退出
在顶层(原始流程),基本机制与任何非叶流程相同;它创建两个FIFO,启动两个子FIFO,打开FIFO进行读取,在两个流上进行合并,写入标准输出,而不需要与另一个FIFO混淆。您没有说明任何数据流要求,例如叶子要排序的数据源。在分工方面,叶节点将进行排序,但分支只需合并。从某种意义上说,您正在创建一个使用进程和FIFO而不是堆栈的混合 如前所述,您可以使用简单但不雅观的方法来分配要排序的值数组,并在主进程中预先创建所有FIFO。基于每个子节点的标识符或索引号,它将从整个阵列和适当的FIFO中选择一系列数据(例如,
FIFO.N
,用于节点N用于向其父节点传输数据的FIFO)。回想一下,例如,使用fork
创建的子进程共享其父进程的地址空间,并且可以在全局范围内查看数组
二叉树很好地组合成一个数组。根据维基百科
二叉树也可以作为数组中的隐式数据结构以广度优先的顺序存储,如果树是一个完整的二叉树,这种方法不会浪费空间。在这种紧凑的排列中,如果节点有索引i,则其子节点位于索引2i+1(对于左子节点)和2i+2(对于右子节点)处,而其父节点(如果有)位于索引处⌊(i-1)/2⌋ (假设根的索引为零)
注意⌊x⌋ 是不大于x的最大整数,也称为。在C语言中,您可以通过将(i-1)/2
的值赋给int
类型的变量来获得下限
要在树的周围穿行节点标识符,可以使用如下代码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
void proc_tree(int i, int current_depth, int max_depth)
{
pid_t kid = fork();
if (kid == -1) {
fprintf(stderr, "[%d]: fork: %s\n", getpid(), strerror(errno));
}
else if (kid == 0) {
/* child */
printf("[%d]: i=%d (depth %d)\n", getpid(), i, current_depth);
if (current_depth < max_depth) {
proc_tree(2*i+1, current_depth+1, max_depth);
proc_tree(2*i+2, current_depth+1, max_depth);
}
exit(EXIT_SUCCESS);
}
else {
/* parent */
pid_t pid;
int status;
pid = waitpid(kid, &status, 0);
if (pid == -1)
fprintf(stderr, "[%z]: waitpid: %s\n", getpid(), strerror(errno));
}
}
#包括
#包括
#包括
无效进程树(int i,int current\u depth,int max\u depth)
{
pid_t kid=fork();
如果(kid==-1){
fprintf(stderr,[%d]:fork:%s\n,getpid(),strerror(errno));
}
else if(kid==0){
/*孩子*/
printf(“[%d]:i=%d(深度%d)\n”,getpid(),i,当前深度);
if(当前深度<最大深度){
过程树(2*i+1,当前深度+1,最大深度);
过程树(2*i+2,当前深度+1,最大深度);
}
退出(退出成功);
}
否则{
/*母公司*/
pid_t pid;
智力状态;
pid=waitpid(孩子和状态,0);
int main(int argc, char *argv[])
{
int depth;
if (argc != 2) {
fprintf(stderr, "Usage: %s depth\n", argv[0]);
return EXIT_FAILURE;
}
depth = atoi(argv[1]);
if (depth < 0) {
fprintf(stderr, "%s: depth must be non-negative\n", argv[0]);
return EXIT_FAILURE;
}
proc_tree(0, 0, depth);
return EXIT_SUCCESS;
}
$ ./tree-sort 3
[28837]: i=0 (depth 0)
[28838]: i=1 (depth 1)
[28839]: i=3 (depth 2)
[28840]: i=7 (depth 3)
[28841]: i=8 (depth 3)
[28842]: i=4 (depth 2)
[28843]: i=9 (depth 3)
[28844]: i=10 (depth 3)
[28845]: i=2 (depth 1)
[28846]: i=5 (depth 2)
[28847]: i=11 (depth 3)
[28848]: i=12 (depth 3)
[28849]: i=6 (depth 2)
[28850]: i=13 (depth 3)
[28851]: i=14 (depth 3)