图控大叔
构图传递思想
阅读从未如此简单!!!
01
前言
本次要分享的内容是如何在Linux环境下进行子进程的创建,以及子进程创建中所需要的fork与vfork函数的区别,话不多说,赶紧上车!
02
什么是进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
注:该内容来源于百度百科。
1,进程:
(1)正在运行的一个程序
(2)它代表一种资源的载体(独立的应用的应用程序)
(3)资源的最小单位
(4)每一个进程独立包括虚拟内存,文件描述符资源,信号资源等,不与其他进程共享资源
应用场景:
1,调度第三方程序
2,调度其他程序的时候,我们需要传输一定的资源或者是指令过去给另外一个程序时,我们需要应用进程间通信
3,启用服务
上面提到了进程,那么很多人很快就想知道另一个答案:什么的线程?
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
注:该内容来源于百度百科。
2,线程:
(1)调度的最小单位(CPU在轮询指令运行的最小单位)
(2)进程下面的一个子级单位(所有的线程都是在进程的基础上运行的),一个进程当中,可以运行多个线程
(3)所有的线程共享进程的所有资源
(4)每一个线程独立一片栈空间(栈空间默认大小为8M)
应用场景:
基本上所有的多任务的开发,优先采用多线程
03
子进程的创建函数
#include
int main(int argc,const char **argv){
printf("hello world\n");
return 0;
}
在编程开发中,只要我们随便运行一个程序,它就以一个进程的身份在运行了,哪怕这个程序的函数如上面那么简单。
而我们今天所要说的,就是在一个进程的基础上,创建子进程,这个过程就如同老母猪生仔,哈哈。比如,主进程A开始运行的时候,系统会给主进程A分配相应的内容空间,而在主进程A的生命周期里面,创建了子进程B。此时,为了方便描述,我们把主进程A称为父进程A。父进程A与其子进程B在内存空间上的关系将与具体的创建函数有关。具体的创建函数也就是我们今天要提到的函数fork() 和 函数vfork()
04
fork与vfork
fork()需包含头文件:
#include
函数原型:
pid_t fork(void);
函数功能:
创建出一条子进程
返回值:
1、成功创建子进程的情况下会将0返回给子进程
2、子进程的PID返回父进程,
3、如果失败返回-1,子进程不会被创建
错误代码:
EAGAIN 内存不足.
ENOMEM 内存不足, 无法配置核心所需的数据结构空间.
vfork()
fork()与vfork()相同点需包含头文件:
#include
#include
函数原型:
pid_t vfork(void);
函数功能:
创建出一条子进程
返回值:
1、成功创建子进程的情况下会将0返回给子进程
2、子进程的PID返回父进程,
3、如果失败返回-1,子进程不会被创建
错误代码:
EAGAIN 内存不足.
ENOMEM 内存不足, 无法配置核心所需的数据结构空间.其他说明
一般配合exec函数族使用
相同点
<1> 函数在执行的过程当中,会将父进程的资源复制一份,放到子进程里面去运行,
其中下面是会被子进程继承的资源
1,父进程的运行的用户的ID跟组ID
2,环境变量(库路径,命令路径,命令路径等等)
3,进程组ID跟会话ID
4,打开的文件描述符
5,信号响应函数
6,虚拟内存(堆,栈,程序段落等等)
以下属性就是独立,没有继承的:
1,进程ID
2,记录锁(文件锁)
3、挂起的信号
<2> 子进程会从后的下一条逻辑语句开始运行。
这样就避免了不断调用fork()或者vfork()而产生无限子孙的悖论。
fork()与vfork()不同点
不同点:
1、关于内存空间
fork函数在创建子进程的时候,
系统会将父进程的内存资源复制一份给子进程,
此时,父、子进程之间的内存相互独立。
vfork函数在创建子进程的时候,
子进程会与父进程共用同一片内存,
引用的内存是父进程的内存。
说明:
vfork这样的操作是为了节约fork函数创建子进程而进行的内存拷贝这一环节的时间
(vfork子进程是不会拷贝父进程的虚拟内存的内容),
2、关于运行
fork函数在创建子进程之后,
父、子进程之间谁先运行由系统调度决定。
vfork函数在创建子进程之后,
父进程会加入睡眠,
除非子进程调用exec系列函数去加载第三方程序,
或者子进程结束,
父进程才会被唤醒。
3、关于退出
fork函数创建的子进程可以使用return或者exit函数退出
但是vfork函数创建的子进程不可以用return退出,负责程序将返回至创建子进程的位置重新开始,
vfork函数只能使用exit函数退出
05
fork与vfork:内存空间
fork函数:父、子进程的内存空间互相独立实验
#include
#include
#include
/*
fork函数:父、子进程的内存空间互相独立实验
*/
int main(int argc,const **argv){
pid_t cpid[2];
int i = 0;
cpid[0] = fork();
if(cpid[0] < 0)
{
perror("fork cipd[0] 子进程创建失败");
return -1;
}
if(cpid[0] == 0)
{
i = i + 10;
printf("cpid[0] id is %d i is %d\n",getpid(),i);
sleep(1);
exit(0);
}
cpid[1] = fork();
if(cpid[1] < 0)
{
perror("fork cipd[1] 子进程创建失败");
return -1;
}
if(cpid[1] == 0)
{
i = i + 10;
printf("cpid[1] id is %d i is %d\n",getpid(),i);
//sleep(1);
exit(0);
}
while(1)
{
printf("father id is %d i is %d\n",getpid(),i);
break;
}
return 0;
}
fork函数:父、子进程的内存空间互相独立实验
输出结果:
father id is 9226 i is 0
gec@ubuntu:/mnt/hgfs/my/system_io/fork_整理$ cpid[1] id is 9228 i is 10
cpid[0] id is 9227 i is 10
分析:
1、因为 i的初始值为0,并且fork函数所创建的父、子进程的内存相互独立
所以父进程输出的i值为0
2、因为子进程继承了父进程的文件操作符
所以在父进程结束之后,子进程依旧在该终端打印结果
3、fork创建子进程之后,
父、子进程的运行顺序由系统调度决定
vfork函数:子进程引用父进程的内存空间的实验
#include if(cpid[
vfork函数:子进程引用父进程内存空间实验结果分析
/*
输出结果:
cpid[0] id is 9235 i is 10
cpid[1] id is 9236 i is 20
father id is 9234 i is 20
分析:
1、因为 i的初始值为0,而vfork函数所创建的子进程引用父进程的内存空间
所以父进程输出的i值为20
2、在vfork函数中,因为父进程创建子进程后开始进入睡眠
等待子进程结束或者调用第三方程序后才唤醒,
所以父进程最后一个结束
*/
06
fork与vfork:退出
fork 与 vfork的不同点
3、关于退出
fork函数创建的子进程可以使用return或者exit函数退出
但是vfork函数创建的子进程不可以用return退出,负责程序将返回至创建子进程的位置重新开始,
vfork函数只能使用exit函数退出
参考代码
#include
#include
#include
int main(int argc,const **argv){
pid_t cpid[2];
cpid[0] = vfork();
if(cpid[0] < 0)
{
perror("vfork cipd[0] 子进程创建失败");
return -1;
}
if(cpid[0] == 0)
{
printf("cpid[0] id is %d \n",getpid());
exit(0);
//return 0;
}
cpid[1] = vfork();
if(cpid[1] < 0)
{
perror("vfork cipd[1] 子进程创建失败");
return -1;
}
if(cpid[1] == 0)
{
printf("cpid[1] id is %d i is %d\n",getpid(),i);
exit(0);
//return 0;
}
while(1)
{
printf("father id is %d i is %d\n",getpid(),i);
//sleep(1);
break;
}
return 0;
}
说明:
两个点可以更改,然后进行测试
1、return 与 exit
2、fork与vfork喜欢搞事情的童鞋,自己去测试测试
07
思考
vfork函数如果创建子进程后,父进程就进入睡眠,那么如果需要创建两个或者更多个子进程,代码该怎么实现呢?
08
结尾
关于Linux环境下子进程的创建所用的函数及部分细节就分享到这里,如果存在纰漏,还希望读者不吝赐教,谢谢!