实验三 .进程通信实验
一、学习fork()函数,了解如何编程实现进程创建
参考这篇博客linux中fork()函数详解(原创!!实例讲解)_linux fork()函数-CSDN博客
fork入门知识
一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
让我们来看下面这个例子
#include <sys/types.h>
#include <stdio.h>
//#include <unistd.h>
#include <stdlib.h>
int value=5; //where?
int main(){
int i; //where?
pid_t pid;
for(i=0;i<2;i++){ // How many new processes and printfs?
pid=fork();
if(pid==0){
value += 15;
printf("Child: value = %d\n",value);
}
else if(pid>0){
wait(NULL);
printf("PARNET: value = %d\n",value);
exit(0); //Notice:What will happen with or without this line?
}
}
}
运行结果:
通过本例可以一窥fork函数在循环中的执行顺序及原理,这里建议看看原大佬博客的笔记,写的十分详细且易懂
二、学习管道的编程思想,并实现进程间通信
管道(Pipe) 管道介绍 管道pipe是进程间通信最基本的⼀种机制。两个进程可以通过管道,⼀个在管道⼀端向管道发送其数据(写入 管道),⽽另⼀个进程可以在管道的另⼀端从管道读取数据。 管道以半双⼯的⽅式⼯作,即它的数据流是单向的。因此使⽤管道时的规则⼀般是读管道数据的进程关闭管道 的写入端,⽽写管道进程关闭其读端⼝
管道pipe系统语法说明 pipe系统调⽤的语法为
#include <unistd.h>
int pipe(int pipe_id[2]);
如果pipe系统调⽤执⾏成功,返回0,pipe_id[0]和pipe_id[1]中将放入管道两端的描述符。 出错返回-1。
下面是⼀段由并发的⽗⼦进程合作将整数X的值从1加到10的⽰意程序。其中的⽗⼦进程开展协作时,两者之间通过管道进⾏通信。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int pid;
int pipe1[2];
int pipe2[2];
int x;
if (pipe(pipe1) < 0) {
perror("failed to create pipe1");
exit(EXIT_FAILURE);
}
if (pipe(pipe2) < 0) {
perror("failed to create pipe2");
exit(EXIT_FAILURE);}
pid = fork();
if (pid < 0) {
perror("failed to create new process");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// ⼦进程=>⽗进程:⼦进程通过pipe2[1]进⾏写
// ⼦进程<=⽗进程:⼦进程通过pipe1[0]读
// 因此,在⼦进程中将pipe1[1]和pipe2[0]关闭
close(pipe1[1]);
close(pipe2[0]);
do {
read(pipe1[0], &x, sizeof(int));
printf("child %d read: %d\n", getpid(), x++);
write(pipe2[1], &x, sizeof(int));
} while (x <= 9);
close(pipe1[0]);
close(pipe2[1]);
} else {
// ⽗进程<=⼦进程:⽗进程从pipe2[0]读取⼦进程传过来的数
// ⽗进程=>⼦进程:⽗进程将更新的值通过pipe1[1]写入,传给⼦进程
// 因此,⽗进程会先关闭pipe1[0]和pipe2[1]端⼝
close(pipe1[0]);
close(pipe2[1]);
x = 1;
do {
write(pipe1[1], &x, sizeof(int));
read(pipe2[0], &x, sizeof(int));
printf("parent %d read: %d\n", getpid(), x++);
} while (x <= 9);
close(pipe1[1]);
close(pipe2[0]);
}
return EXIT_SUCCESS;
}
运行结果:
三、进程通信实例
小作业:
设有⼆元函数 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)
笔者解法:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
// 计算 f(x)
int f_x(int x) {
if (x > 1)
return f_x(x - 1) * x;
else
return 1;
}
// 计算 f(y)
int f_y(int y) {
if (y > 2)
return f_y(y - 1) + f_y(y - 2);
else
return 1;
}
int main() {
int x = 5, y = 5; // 你可以根据需要改变 x 和 y 的值
int pipe_fd1[2], pipe_fd2[2];
// 创建两个无名管道
if (pipe(pipe_fd1) == -1 || pipe(pipe_fd2) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t pid1, pid2;
// 创建第一个子进程来计算 f(x)
if ((pid1 = fork()) == 0) {
close(pipe_fd1[0]); // 关闭读端
int result_x = f_x(x);
write(pipe_fd1[1], &result_x, sizeof(result_x));
close(pipe_fd1[1]); // 关闭写端
exit(EXIT_SUCCESS);
} else if (pid1 < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
// 创建第二个子进程来计算 f(y)
if ((pid2 = fork()) == 0) {
close(pipe_fd2[0]); // 关闭读端
int result_y = f_y(y);
write(pipe_fd2[1], &result_y, sizeof(result_y));
close(pipe_fd2[1]); // 关闭写端
exit(EXIT_SUCCESS);
} else if (pid2 < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
// 父进程读取子进程的结果并计算 f(x, y)
close(pipe_fd1[1]); // 关闭写端
close(pipe_fd2[1]); // 关闭写端
int result_x, result_y;
read(pipe_fd1[0], &result_x, sizeof(result_x));
read(pipe_fd2[0], &result_y, sizeof(result_y));
close(pipe_fd1[0]); // 关闭读端
close(pipe_fd2[0]); // 关闭读端
int result = result_x + result_y;
printf("f(x,y) = f(x) + f(y) = %d + %d = %d\n", result_x, result_y, result);
return 0;
}
运行结果: