题目描述
设有二元函数 f(x,y) = f(x) + f(y),
其中,f(x)=f(x-1)*x (x>1), f(x)=1 (x=1)
f(y) = f(y-1) + f(y-2) (y> 2), f(y)=1 (y=1,2)
请编程建立 3个并发协作进程或线程,它们分别完成 f(x,y)、f(x)、f(y)。
实验思路
三个函数,对应创建三个进程:两个子进程f(x)和f(y),一个父进程f(x,y)。要实现父子进程间的通信,子进程将当前值传给父进程,父进程进行计算。这些进程应该同步并发向前走,否则父进程计算的值将会错误。由于管道默认是同步读写,即如果管道读端无数据则读命令阻塞直到数据到达,反之如果管道写端有数据则写命令阻塞直到数据被读走。所以同步并发的问题可以通过管道实现。建立4个管道,分别为pipe11[2],pipe12[2],pipe21[2],pipe22[2]。
f(x)的子进程通过pipe11[2],pipe12[2]与父进程通信,f(y)的子进程通过pipe21[2],pipe22[2]与父进程通信,父进程根据这两个值实现f(x,y)功能。
实验涉及的知识点
1、管道通信机制:
管道pipe 是进程间通信最基本的一种机制。在内存中建立的管道称为无名管道,在磁盘上建立的管道称为有名管道。无名管道随着进程的撤消而消失,有名管道则可以长久保存,shell 命令符| 建立的就是无名管道,而shell 命令mkfifo 建立的是有名管道。两个进程可以通过管道一个在管道一端向管道发送其输出,给另一进程可以在管道的另一端从管道得到其输入.管道以半双工方式工作,即它的数据流是单方向的.因此使用一个管道一般的规则是读管道数据的进程关闭管道写入端,而写管道进程关闭其读出端。管道既可以采用同步方式工作也可以采用异步方式工作。
pipe 系统调用的语法为:
#include <unistd.h>
int pipe(int pipe_id[2]);
pipe 建立一个无名管道,pipe_id[0]中和pipe_id[1]将放入管道两端的描述符如果pipe 执行成功返回0,出错返回-1.
管道读写的系统调用语法为:
#include <unistd.h>
ssize_t read(int pipe_id,const void *buf,size_t count);
ssize_t write(int pipe_id,const void *buf,size_t count);
read 和write 分别在管道的两端进行读和写。
pipe_id 是pipe 系统调用返回的管道描述符。
Buf是数据缓冲区首地址, count说明数据缓冲区以size_t 为单位的长度。
read 和write 的返回值为它们实际读写的数据单位。
注意管道的读写默认的通信方式为同步读写方式,即如果管道读端无数据则读者阻塞直到数据到达,反之如果管道写端有数据则写者阻塞直到数据被读走。
源代码
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#define READ 0
#define WRITE 1
int fx(int x);
int fy(int y);
int main()
{
pid_t pid1, pid2; //child 1,2
int pipe11[2], pipe12[2], pipe21[2], pipe22[2]; //4 pipes
int x, y; //input variable
if (pipe(pipe11) < 0)
{
perror("pipe create failed\n");
exit(EXIT_FAILURE);
}
if (pipe(pipe12) < 0)
{
perror("pipe create failed\n");
exit(EXIT_FAILURE);
}
if (pipe(pipe21) < 0)
{
perror("pipe create failed\n");
exit(EXIT_FAILURE);
}
if (pipe(pipe22) < 0)
{
perror("pipe create failed\n");
exit(EXIT_FAILURE);
}
pid1 = fork();
if (pid1 < 0)
{
fprintf(stderr, "child1 fork failed\n");
exit(EXIT_FAILURE);
}
else if (pid1 == 0)
{
int x, ansX;
//printf("This is child 1\n");
close(pipe11[WRITE]);
close(pipe12[READ]);
//wait parent write to child1
read(pipe11[READ], &x, sizeof(int));
ansX = fx(x);
//write to parent
write(pipe12[WRITE], &ansX, sizeof(int));
close(pipe11[READ]);
close(pipe12[WRITE]);
exit(EXIT_SUCCESS);
}
else
{
//printf("This is parent to fork 2\n");
pid2 = fork();
if (pid2 < 0)
{
fprintf(stderr, "child2 fork failed\n");
exit(EXIT_FAILURE);
}
else if (pid2 == 0)
{
int y, ansY;
//printf("This is child 2\n");
close(pipe21[WRITE]);
close(pipe22[READ]);
//wait parent write to child2
read(pipe21[READ], &y, sizeof(int));
ansY = fy(y);
//write to parent
write(pipe22[WRITE], &ansY, sizeof(int));
close(pipe21[READ]);
close(pipe22[WRITE]);
exit(EXIT_SUCCESS);
}
else
{
int x, y, ansX, ansY, ans;
//printf("This is parent \n");
//input
//printf("x = \n");
// scanf("%d", &x);
x = 3;
//printf("y = \n");
// scanf("%d", &y);
y = 4;
close(pipe11[READ]);
close(pipe12[WRITE]);
close(pipe21[READ]);
close(pipe22[WRITE]);
//write to child1 x
write(pipe11[WRITE], &x, sizeof(int));
//write to child2 y
write(pipe21[WRITE], &y, sizeof(int));
//read answer X of child1
read(pipe12[READ], &ansX, sizeof(int));
//read answerY of child2
read(pipe22[READ], &ansY, sizeof(int));
//calculate
ans = ansX + ansY;
printf("f(x) = %d\n", ansX);
printf("f(y) = %d\n", ansY);
printf("f(x,y) = %d\n", ans);
}
}
exit(EXIT_SUCCESS);
}
int fx(int x)
{
if (x <= 1)
{
return 1;
}
else
{
return fx(x - 1) * x;
}
}
int fy(int y)
{
if (y <= 2)
{
return 1;
}
else
{
return fy(y - 1) + fy(y - 2);
}
}