当前位置: 首页 > news >正文

Linux——进程操作之创建

创建进程是编程的常见操作。本节我们将对创建进程进行学习。

目录

一.fork()使用

(一).返回值

(二).进程独立

(三).子进程退出

二.写时拷贝

三.EIP寄存器(PC指针)


一.fork()使用

#include<unistd.h>

pid_t fork(NULL);

(一).返回值

fork创建进程失败会返回-1。

fork成功的返回值有两个。对于父进程而言是创建的子进程的pid(大于0的值);对于子进程而言返回值是0。

讲的通俗一些,fork之后的代码我们可以看成有两份。父进程、子进程分别执行这两份,各自都会拿到fork相应的返回值。即fork对于父子进程分别返回了不同值。

小编喜欢画图来使问题清晰易懂:

(二).进程独立

看过上图应该也就清楚了,如果想让子进程分别执行不同代码,只需要对id值进行判断即可。

if(id == 0)
{
    //子进程代码
}
else if(id > 0)
{
    //父进程代码
}
else
{
    //进程创建失败
}

(三).子进程退出

当子进程执行完毕后,会由当前父进程回收。

1.如果父进程一直不回收,子进程会变成僵尸进程(Z)

2.如果父进程先退出,子进程会变成孤儿进程,由Bash“收养”,改为后台运行。 

二.写时拷贝

可先参考这篇文章稍作了解:Linux——进程地址空间

我们知道,子进程与父进程会有一份该程序代码。这本质上就是当父进程创建子进程时,CPU把父进程的PCB结构体(稍作改变)、进程地址空间拷贝一份放入运行队列,形成子进程。因此,子进程代码段与父进程一样。

但是,进程地址空间通过页表后映射的物理内存与父进程相同。假如我们在fork之前创建了一个变量,只要子进程不改变变量值,那么父子就会一直共用这一个物理地址,即实际上只有一个变量真实存在。

如果子进程改变了变量值,那么内存管理模块会为子进程分配一块属于它自己的空间,页表改变映射,指向这块新分配的物理地址。这样的行为就叫做写时拷贝

对于内存而言,写时拷贝具有节约空间,提高效率的意义,有利于内存的合理分配。

这也就解释了为什么fork成功返回值会有两个:

pid_t id = fork();
if(id == 0) {...}
else if(id > 0) {...}
else {...};

因为在fork内部创建子进程后,子进程改变了id值,因此内存为子进程专门开辟了空间存放id。

但是这里要注意,如果我们打印id地址的话会发现,父子进程id地址相同。

#include<string.h>
  #include<unistd.h>
  #include<stdio.h>
  int main()
  {
    pid_t id = fork();
    if(id == 0)
    {
      printf("I am child process, id_address: %p\n", &id);
      exit(0);
    }
    else{
      printf("I am father process, id_address: %p\n", &id);                                                                                        
    }
      return 0;
  }

 这是因为虽然物理地址分开了,但是别忘了子进程的mm_struct拷贝自父进程,因此id虚拟地址与父进程一致

当然,小编还是利用图片进一步解释:

 

 

三.EIP寄存器(PC指针)

我们可能会疑惑,为什么子进程在创建之后会从fork之后代码开始执行呢。

这是因为有EIP寄存器存在。

EIP寄存器只有一个(在CPU中),用于记录每一个进程的下一条指令的地址

对于父进程而言,EIP寄存器内数据属于自己的上下文数据,会在PCB结构体中有所记录,task_struct结构体中成员cpu_context用于记录CPU内寄存器相关数据,包括EIP寄存器。

因此当子进程进行创建时,会复制PCB结构体,因而其EIP值与父进程相同,父进程下一条指令自然是fork之后的,所以子进程也是从fork后开始执行。

If you can't write it down in English, you can't code it. —— Peter Halpern


如有错误,敬请斧正

相关文章:

  • 葡聚糖-聚乙烯亚胺Dextran-PEI,聚乙烯亚胺修饰纤维二糖/香菇多糖/辣根过氧化氢酶/溶菌酶
  • 浙大MBA网上报名关键信息点提醒,选错一个,回头重来
  • 本地git操作-之分支合并与回滚
  • Three 之 three.js (webgl)物体描边效果(outline)三种实现方式的简单整理(后期渲染/MeshBasicMaterial/法线扩展)
  • nginx平滑升级、nginx支持的kill信号
  • Golang中进行Url编码,再也不怕奇奇怪怪的参数格式了
  • Linux机器时钟同步
  • Go语言基础 指针 运算符
  • Elasticsearch部署和问题汇总
  • 如何搭建边缘计算盒子IVP02E环境部署?
  • 【Linux私房菜】第八期——面试
  • jest 使用 jest-allure 测试报告
  • navicat连接远程服务器报错代码:10038
  • 笔试题/面试题——WEB前端性能优化,提高页面加载速度
  • CNN-运动鞋品牌识别
  • JavaScript 如何正确处理 Unicode 编码问题!
  • Babel配置的不完全指南
  • django开发-定时任务的使用
  • JavaScript DOM 10 - 滚动
  • Markdown 语法简单说明
  • nginx 负载服务器优化
  • PhantomJS 安装
  • PHP的类修饰符与访问修饰符
  • Tornado学习笔记(1)
  • Vim Clutch | 面向脚踏板编程……
  • 阿里云购买磁盘后挂载
  • 使用Gradle第一次构建Java程序
  • 世界上最简单的无等待算法(getAndIncrement)
  • 手写双向链表LinkedList的几个常用功能
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • 我们雇佣了一只大猴子...
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • ​虚拟化系列介绍(十)
  • #{}和${}的区别是什么 -- java面试
  • #define,static,const,三种常量的区别
  • #控制台大学课堂点名问题_课堂随机点名
  • $.each()与$(selector).each()
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (笔试题)分解质因式
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (利用IDEA+Maven)定制属于自己的jar包
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (转)jQuery 基础
  • (转)mysql使用Navicat 导出和导入数据库
  • (转)关于pipe()的详细解析
  • (转载)Google Chrome调试JS
  • ***通过什么方式***网吧
  • *2 echo、printf、mkdir命令的应用
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .cfg\.dat\.mak(持续补充)
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET Core IdentityServer4实战-开篇介绍与规划