fork 头文件_Linux中子进程的创建:fork()和vfork()

1b57e1fbebebdd30f15b2460d6e549b0.png

图控大叔

构图传递思想

阅读从未如此简单!!!

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

2fdeafc3a686607b815707196fcdd6e2.giffork()
需包含头文件:
#include 

函数原型:
pid_t fork(void);

函数功能:
创建出一条子进程

返回值:
  1、成功创建子进程的情况下会将0返回给子进程
  2、子进程的PID返回父进程,
  3、如果失败返回-1,子进程不会被创建

错误代码:

EAGAIN 内存不足. 

ENOMEM 内存不足, 无法配置核心所需的数据结构空间.

2fdeafc3a686607b815707196fcdd6e2.gifvfork()
需包含头文件:
#include 
#include 

函数原型:
pid_t vfork(void);

函数功能:
创建出一条子进程

返回值:
  1、成功创建子进程的情况下会将0返回给子进程
  2、子进程的PID返回父进程,
  3、如果失败返回-1,子进程不会被创建

错误代码:
EAGAIN 内存不足.
ENOMEM 内存不足, 无法配置核心所需的数据结构空间.
其他说明一般配合exec函数族使用
2fdeafc3a686607b815707196fcdd6e2.giffork()与vfork()相同点
相同点
<1> 函数在执行的过程当中,会将父进程的资源复制一份,放到子进程里面去运行,
    其中下面是会被子进程继承的资源
        1,父进程的运行的用户的ID跟组ID
        2,环境变量(库路径,命令路径,命令路径等等)
        3,进程组ID跟会话ID
        4,打开的文件描述符
        5,信号响应函数
        6,虚拟内存(堆,栈,程序段落等等)

    以下属性就是独立,没有继承的:
        1,进程ID
        2,记录锁(文件锁)
        3、挂起的信号
<2> 子进程会从后的下一条逻辑语句开始运行。
    这样就避免了不断调用fork()或者vfork()而产生无限子孙的悖论。
2fdeafc3a686607b815707196fcdd6e2.giffork()与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函数退出
2fdeafc3a686607b815707196fcdd6e2.gif参考代码
#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函数如果创建子进程后,父进程就进入睡眠,那么如果需要创建两个或者更多个子进程,代码该怎么实现呢?4e02f77557c7f3ff451cddf9b89f85a5.png

08

结尾

       关于Linux环境下子进程的创建所用的函数及部分细节就分享到这里,如果存在纰漏,还希望读者不吝赐教,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值