目录
一:fork系统调用
查看fork使用手册 man fork
man fork
使用fork()函数需要引入头文件
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
这个fork没有任何的参数,但是会返回一个pid_t,pid_t称为进程ID号,也就是PID,用Int来接收也可以,但习惯性使用pid_t标准接收
这个fork函数就是用来开辟出一个新的进程,也称为创建子进程(子进程:因为我们在执行程序的时候一定会main开始走,这个程序一旦被运行,这个程序本身立马转变成一个进程,在代码里面再开一个进程,创建这个新的进程就是子进程)
fork机制:一次调用 两次返回
子进程返回0,父进程返回子进程ID,出错为-1【一次调用,两次返回】
以往学习的函数,调用只能得到一个结果
今天学习的这个fork函数的调用,可以返回出3种结果
成功返回出的一个是非负整数,一个为0,失败返回-1
二:多进程特性
fork机制: 一次调用 两次返回
#include<iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t id = 0;
//一次调用 两次返回
id = fork();
if (id > 0)
{
cout << "1111" << endl;
}
else if(id == 0)
{
cout << "2222" << endl;
}
return 0;
}
看起来if逻辑不合理,但是要是理解了这个fork有两次返回,就可以明白这个结果了
可以查看fork两次返回PID值
#include<iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t id = 0;
//一次调用 两次返回
id = fork();
if (id > 0)
{
cout << "1111 pid = " << getpid() << endl;
}
else if(id == 0)
{
cout << "2222 pid = " << getpid() << endl;
}
return 0;
}
说明当前这个程序有两个进程,一个进程47590,一个进程47660,
也就是说一次一个进程47590返回,一次是另外一个进程47660返回
父进程返回id是子进程pid 父进程返回子进程ID
#include<iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t id = 0;
//一次调用 两次返回
id = fork();
if (id > 0)
{
cout << "父进程 pid = " << getpid( ) << "返回的id = " << id << endl;
}
else if(id == 0)
{
cout << "子进程 pid = " << getpid( ) << endl;
}
return 0;
}
父进程返回子进程ID
为什么两个进程可以同时跑呢?
在ubuntu系统中运行程序,多个进程对于一个操作系统来说是可以同时运行的
操作系统允许多个进程同时运行
只要是linux认为是一个进程的东西,根据PCB,那个进程就应该要有一个代码段,父进程有一个代码段,子进程也有一个代码段
一次调用,两次返回:
通过fork这个函数生成一个新的进程,那个新的进程,会将当前的整段所有的代码全部复制一份交给进程来执行,各自执行各自的内容,各自返回各自fork的返回值就不一样了
父进程返回一个结果,子进程返回一个结果,也就是两次返回
不同的环境下,返回的结果值不同
进程特点:
1.一台计算机中同时运行
2.一个程序不止有一个进程(就像main函数只有一个,跑起来会有两个进程)
3.进程之间数据不共享
也就是说,程序运行的时候,代码可以创建 n个进程(理论是n个,实际效果还要根据各自电脑的硬件支持,CPU不好的就跑不了几个)
电脑要是用软件很卡,要么就扩展内存,要么就换个好点的CPU(运算效率更高,执行东西更多)
可以做到一个程序 可以生成有 n个进程
既不在父进程也不在子进程的代码段,打印over测试一下如下
#include<iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t id = 0;
//一次调用 两次返回
id = fork();
if (id > 0)
{
cout << "父进程 pid = " << getpid( ) << "返回的id = " << id << endl;
}
else if(id == 0)
{
cout << "子进程 pid = " << getpid( ) << endl;
}
cout << "over" << endl;
return 0;
}
两个over ,父进程子进程都有属于自己的代码段,复制代码执行两次,有两个over,
可以查看一下id,如下
cout << "over" << getpid( )<< endl;
一个over是49195,一个over是49265
父进程 子进程都有属于自己的代码段
进程同时执行,但是什么时候被调用什么时候去执行是随机的 (父进程子进程随机先后顺序)
有可能走一句走over,也有可能父子全部走完再走over
现实中的例子就比如VM虚拟机,VS2019同时在使用,都是进程都在运行,
CPU自己会主动去切,一会跑VM,一会跑VS,这就是随机调用
也就是CPU的随机时间片轮转(随机轮片)
* 多进程可以同时运行,一个程序运行不代表只有一个进程
做个测试:一个程序,n个进程,这n个进程是否相互影响
在父进程和子进程中分别添加一个死循环测试一下,用到死循环(return处断点执行不到),
重新生成解决方案,在ubuntu中 终端命令运行程序测试
#include<iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t id = 0;
//一次调用 两次返回
id = fork();
if (id > 0)
{
while (1)
{
cout << "父进程 pid = " << getpid() << "返回的id = " << id << endl;
sleep(1);
}
}
else if(id == 0)
{
while (1)
{
cout << "子进程 pid = " << getpid() << endl;
sleep(1);
}
}
cout << "over" << getpid()<< endl;
return 0;
}
观察可知
父进程和子进程都有在跑 ,二者逻辑相互不受影响。
因为它们都是进程,它们同时运行,所以它们相互不受影响
PCB管理中代码段是独立的,一个代码段死循环,对另外一个代码段不会产生什么影响
即便是同一个程序产生的多个进程,代码段依然是独立的,相互之间不受任何影响
多进程之间代码独立 不会互相影响
不同的进程执行不同的业务:在使用VS编写代码过程中也可以同时QQ音乐播放听歌
由此可见,多进程的编程方式就显得比较重要,非常值得学习!
做个测试:父进程,子进程之间数据独立 (PCB中数据段 独立)
用一个全局变量number,简单测试一下
#include<iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int number = 0;
int main()
{
pid_t id = 0;
//一次调用 两次返回
id = fork();
if (id > 0)
{
while (1)
{
number++;
cout << "父进程 pid = " << getpid() << "返回的id = " << id <<"number = "<<number<< endl;
sleep(1);
}
}
else if(id == 0)
{
while (1)
{
number++;
cout << "子进程 pid = " << getpid() << "number = " << number << endl;
sleep(1);
}
}
cout << "over" << getpid()<< endl;
return 0;
}
在ubuntu中测试结果如下
不难看出,父1子1,父2子2,父3子3... :没有叠加(PCB中除了代码段,还有数据段)
多进程之间数据独立,不能共享
有时候父先有时候子先:多进程 随机调用
可以将
int number = 0;
放入main函数中
全局变量 换 局部变量 简单测试一下,发现最终的结果都是一样的
也就是说进程的数据都不会叠加,数据是独立的,数据不共享!
多进程之间数据独立,不能共享
做个测试:对于这个number数据,只占用一个地址
#include<iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int number = 0;
int main()
{
pid_t id = 0;
//一次调用 两次返回
id = fork();
if (id > 0)
{
while (1)
{
number++;
cout << "父进程 pid = " << getpid() << "返回的id = " << id <<"number = "<<&number<< endl;
sleep(1);
}
}
else if(id == 0)
{
while (1)
{
number++;
cout << "子进程 pid = " << getpid() << "number = " << &number << endl;
sleep(1);
}
}
cout << "over" << getpid()<< endl;
return 0;
}
测试结果如下
一个地址,数据各自加各自的【多进程之间数据不共享】
如果进程越来越多,地址越多,内存也越来越大,这么一来,非常不合理
也就是说,地址只有一个,但是数据有其各自的堆栈段
(通过这个堆栈段计算数据,在要使用的时候再赋值给那一个地址就可以了),这样就保证了进程在不断增加的情况下,空间得到一定的节约
一个数据不需要很多地址,否则开一个进程,内存乘2,不符合实际应用场景
代码中就定义了一个变量,也就只分配给这个变量一个内存空间
* 多进程之间数据独立,不能共享,数据地址相同,值不同
小结:
fork机制:一次调用 两次返回
子进程返回0 父进程返回子进程ID
原理:代码执行fork,会生成子进程,子进程将所有代码、数据进行拷贝
代码套路:
if(pid > 0)
{
父进程逻辑
}
else if(pid == 0)
{
子进程逻辑
}
三:fork创建进程
在for循环中执行fork
问如下情况开几个进程?
for(int i =0;i<2;i++)
{
fork();
}
i = 0 ---》 1父 1子 (子再 自己(父) + 孙)
i = 1 ---》 1父 1子 (子再 自己(父) + 孙)
也就是4个(公式法: n的2次方) 2 的 2次方 = 4
针对上述情况,对于子进程,需要break来退出循环,来退出子进程
for(int i =0;i<2;i++)
{
fork();
if(id > 0)//父进程
{
}
else if(id == 0)//子进程
{
break;
}
}
退出子进程还有其他方法:
return 0;//退出子进程
for(int i =0;i<2;i++)
{
fork();
if(id > 0)//父进程
{
}
else if(id == 0)//子进程
{
return 0;
}
}
退出子进程还有其他方法:
exit (0);//退出子进程
for(int i =0;i<2;i++)
{
fork();
if(id > 0)//父进程
{
}
else if(id == 0)//子进程
{
exit(0);
}
}