进程的基本概念 && 进程的创建(重要)

本文详细解析了进程的基本概念,进程控制块的作用,以及子进程的创建过程,重点介绍了fork函数的两个返回值和其背后的原理。通过理解fork,我们得知进程共享代码但数据独立,父进程和子进程的创建在fork返回前完成,确保了代码的执行顺序。
摘要由CSDN通过智能技术生成

目录

进程的基本概念

进程控制块-PCB

学前补充

预备知识

​编辑

创建(子)进程

理解fork函数的两个返回值


进程的基本概念

基本概念:正在执行中的一个程序

内核层面:担当分配系统资源(CPU时间、内存)的实体

操作系统如何管理进程:

先描述:

  1. 操作系统将一个磁盘中的可执行程序加载到内存中时,此时内存中只有该可执行程序的代码和数据,假如将这些内容看作一个进程,那么该进程是什么时候开始被调度的?已经被调度了多少时间了?等问题就会出现,很明显这些信息(什么时候?多长时间?)是“代码 + 数据”部分不包含的,所以加载到内存的可执行程序的代码和数据不是完整的进程,它们只是进程的代码段和数据段,是进程的一部分
  2. 操作系统为了管理进程,会将代码段和数据段中的属性以及进程所需的其它属性(当前状态,加载时间...)都进行描述,并将所有描述放入一个叫PCB的结构体中(PCB中还有一个用于访问内存中该进程数据段和代码段的内存指针,以及存放下一个PCB地址的next指针)
  3. 一个进程对应一个PCB,每当加载一个进程时系统就会生成一个对应的PCB结构体用于表示该进程的各种信息(进程 = PCB + 自己的代码段和数据段)

后组织:

  1. 将所有PCB连接成一个链表
  2. 此时,操作系统对进程的管理就变成了对链表的增删改查

进程控制块-PCB

基本概念:是进程所有属性的集合,是一个内核中的数据结构,在不同OS中PCB会有不同的名称(Linux中叫“task_struct”、RTOS中叫"TCB"、Windows中叫"EPROCESS")

功能:便于OS对进程的管理

PCB的生成和使用过程: 

  1. 磁盘中的可执行程序的代码段和数据段被加载到内存中
  2. 操作系统会生成该可执行程序对应进程的属性集合-进程控制块
  3. 所有进程控制块会连接成一个数据结构(队列等)
  4. CPU对进程进程调度时,会去队列中选取进程调度

结论:所谓的进程在操作系统排队不是代码和数据排队,实际上是进程控制块在排队

问题:进程是如何动态运行的?

答:进程控制块通常会被放置在不同的队列中,操作系统可以根据进程的状态和优先级动态调度这些队列中的进程,从而实现了进程的动态运行和资源管理(同一个进程控制块可以被放置在多个不同的队列中)

学前补充

  1. 使用./可以执行可执行文件的本质是让系统创建进程并运行
  2. 在本质上,我们自己所写的代码形成的可执行文件 == 系统命令 
  3. linux中运行的大多数指令,本质都是运行进程
  4. ps指令用于查看所有的当前进程

预备知识

task_struct中的属性:

  1. 标识符:每个进程对应一个特殊的标识符
  2. 状态:任务状态,退出代码,退出信号等
  3. 优先级:相对于其它进程的优先级
  4. 程序计数器:程序中即将被执行的下一条指令的地址
  5. 内存指针:包括程序代码和进程相关数据的指针,还有和其它进程共享的内存空的指针
  6. 上下文数据:进程执行时处理器的寄存器中的数据
  7. I / O状态信息:包括显示的I/O请求,分配给进程I/O设备和被进程使用的文件列表
  8. 记账信息:可能包括处理器时间综合,使用的时钟数总和,时间限制,记账号等
  9. 其它信息

1、显示当前所有进程的详细信息指令:ps axj

2、显示指定进程的详细信息指令:ps axj | grep 指定进程名的关键字

有两行的原因是,grep也是一个进程,它用来过滤出叫myprocess的内容

3、查看获取的首行信息:ps axj | head -1

4、同时执行多个指令:指令1 && 指令2 && ...(&&的妙用)

5、查看获取的首行信息与指定的进程信息指令:ps axj | head -1 && ps axj | grep 指定关键字

6、 每一个进程都有属于自己的独一无二的标识符PID(PCB中的unsigned int类型的变量)

7、因为用户不能直接访问OS的内核,所以不能访问位于OS的内核数据结构PCB中的PID变量

8、在不使用ps指令的前提下,用户想要获取自己所写的可执行程序的PID,需要使用操作系统提供的系统调用接口getpid():

  1. 接口原型:pid_t getpid(void)(pid_t类型是对unsigned int 类型的封装)
  2. 包含头文件:<unistd.h> 和 <sys/types.h>(后者包含了对pid_t等自定义类型的封装定义)
  3. 功能:返回当前进程的PID

9、get ppid指令是用于获取当前进程的父进程的PID

10、每次启动子进程时,子进程的pid都不一样,但是父进程的ppid都一样

11、父进程就是命令行解释器bash(当前进程不是fork创建的子进程时)

12、ctrl + c 和 kill -9 进程PID都可以杀死进程,但二者在本质上还是有区别的

创建(子)进程

基本概念:新建进程就是在OS的内核数据结构中新增一个进程控制块,而用户不能直接对操作系统的内核数据结构进行增删改查,需要使用OS提供的系统调用接口fork()

为什么说内核数据结构而不是内核:可以明确表示PCB是存储于OS的某个数据结构中的

接口原型:pid_t fork(void)

包含头文件:<unistd.h> 和 <sys/types.h>

功能:创建子进程

监控脚本指令:先打印一行,等待三秒后打印两行hello world,等待五秒后程序结束

while :; do ps axj | head -1 && ps axj | grep myprocess |  grep -v grep; sleep 1; done

结论:fork后,父子进程的代码共享

问题:创建一个新的进程就意味着在OS的内核数据结构中多了一个新的PCB,而进程 = PCB + 数据和代码,这意味着内存中应该也多了属于该进程的数据段和代码段,父进程的数据段和代码段可能是从磁盘中获取的,但这个新的子进程的代码段和数据段是从哪里来的?

解释:默认情况下子进程会继承父进程的代码段和数据段(这就是为什么fork之后父子进程的代码才是共享的)但是因为进程具有独立性,所以从原则上讲父子进程的数据段是要分开的,而代码段由于具有只读性质,故可以共享(数据段的独立是通过写时拷贝技术实现的)

fork函数的返回值:

  • 返回值 == -1:创建子进程失败
  • 返回值 == 0:运行子进程代码
  • 返回值 > 0:运行父进程代码

注意事项:

  1. fork函数会有两次返回 
  2. 两次的返回值分别是用于标识是子进程的0和告诉父进程其子进程的pid

理解fork函数的两个返回值

问题:不考虑特殊情况,当一个函数执行到return时,函数的核心工作是否已经完成?

答:是,因此在调用fork()函数后,父进程和子进程的创建是在fork()函数的运行中完成的而不是返回后完成的,即fork函数中的部分代码会被父子共享包括return id,因此返回两次

~over~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值