进程的理解

目录

什么叫做进程?

如何管理进程?

操作系统与进程

 所以为什么要有PCB呢?

PCB主要内容?

查看进程信息:

 如何理解一个进程的动态运行呢?

 进程的启动:

 查看进程:

ps axj |  grep   想要查看的进程 

显示头部信息,又查到我们需要的进程:

进程pid 

kill -9 

创建进程的方式 ppid

 fork

所以fork ()之后的的代码是父子进程共享的! 

为什么要创建子进程?

 fork 的返回值

 证明父子进程执行了不同的代码:

查看运行结果:


什么叫做进程?

担当分配系统资源(cpu 时间、内存)的实体,当然不止如此。

如何管理进程?

在操作系统的内部进程可以同时存在多个!因为同时存在多个进程,所以操作系统需要对进程进行管理!那操作系统如何管理进程?

 

首先,一个可执行程序在未被运行之前,所在的位置是磁盘中,而磁盘是外设!

而可执行程序要运行起来,则需要先把可执行程序加载到内存中,所以内存中就需要有可执行程序的代码和数据。

而磁盘中不止这一个可执行程序,而且,这个可执行程序什么时候开始执行,什么时候执行它,这么多可执行程序,如何准确执行这个可执行程序?

所以,对于操作系统而言,把可执行程序加载到内存中这一步骤不算做进程,这是进程对应的代码和数据而已。

而进程也需要被管理的,因为有多个进程会同时存在,所以进行先描述后组织,则:

操作系统中需要有一个结构体:struct PCB 这里面包含了进程的所有属性!且必须要有一个内存指针,这个内存指针是指向该进程/可执行程序的代码和数据

 

一个进程就是一个PCB ! 所以进程就是PCB 结构体 + 该进程的可执行程序的 代码和 数据,这只是针对此现象一个模糊的概念。

PCB是进程的一部分,但是进程不等于PCB!

所以那些进程应该被调度,应该被暂停,应该被使用变成了对PCB结构体的管理。

操作系统与进程

操作系统的本质就是一个软件,所以在开机前,它也是在磁盘中的一个可执行文件,一个二进制文件。

所以在开机时,操作系统就是第一个加载到内存中的进程/软件!

而在之后的进程控制化,也就是PCB结构体是在操作系统内部的!所以在开机加载时,就是在等待操作系加载,也在等待操作系统进行开辟其他进程PCB的空间。

因为PCB内部的内存指针对应着内存中存储的可执行程序的代码和数据,所以对应PCB的管理就是对进程的管理,于是将每一个PCB串联起来,将PCB放入链表中,所以对PCB的增删查改管理就变成了,对这个PCB链表的增删查改管理

所以,操作系统对可执行程序进行管理,就演化为了对这个链表的管理!

 所以为什么要有PCB呢?

 因为操作系统(OS)要对进程进行管理!PCB是方便操作系统进行管理!

以Linux为例:

在linux中的PCB叫做:struct task_struct 进程控制化

PCB是所有进程控制块的统称,而struct task_strutc 是Linux中的进程控制块的具体称呼。

cpu进行调度的时候可以直接调度进程队列,来管理进程! 

然后需要运行的代码放在了队列头部,当这个进程运行完成后,在进行新的进程控制块调度

所以进程在进程队列中的排队等待,并不是进程的数据和代码在排队而是进程的进程控制块也就是struct task_struct在进行排队

PCB主要内容?

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  •  I/O 状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表
  • 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息 

查看进程信息:

可以使用/proc 指令进行系统文件夹查看进程

只有进程在运行时,才能够查看到这个进程的系统文件目录

一个进程在磁盘上的数据被删除后,还能跑的原因是进程在运行前,它的数据和代码就已经被加载到了内存中了,所以在磁盘上删除了进程的可执行程序,但是进程仍然能运行的原因就是内存中还有进程的代码和数据

同时进程的PCB会就它对于的可执行程序的所在路径,也会记录这个可执行程序当前工作路径

 

 如何理解一个进程的动态运行呢?

只要我们的进程,struct task_struct 这个进程控制块处在不同的进程队列中,进程就可以访问不同的资源 

 进程的启动:

 

./运行程序,加载可执行程序到内存中,这一步也叫做启动/运行进程,所有系统提供的命令本质都是一个表示x86 - 64位下的可执行程序,所以可执行指令 =  可执行程序  = 进程  所以使用指令就是使用可执行文件,就是启动/运行进程!

 查看进程:

 ps axj  

  • ps 是查看当前系统当中可执行的进程   xj 是查看进程的详细信息  
  • axj 的顺序无所谓

ps axj |  grep   想要查看的进程 

如下图所示,想要查看的进程是myprocess 就跳出了myprocess这个进程的信息 

不过为啥会有两个,也就是第二个行呢?因为grep 也是一个可执行的进程,且grep是一个行过滤,包含进程名字的行都会被提取出来,而寻找启动这个进程的grep在提取时也会带上我们需要查找这个进程的名字

显示头部信息,又查到我们需要的进程:

ps ajx | head -1 && ps ajx | grep 需要查询的进程名字 

进程pid 

同时我们可以看到每一个进程都有 pid 这是进程的唯一标识符!

如果一个进程想要知道自己的pid是多少怎么办?不适用ps进行查找!这该怎么办?

因为在每一个进程的task_struct内部都有一个pid_t :pid  属性!

进程想要获取pid 就像等于用户想要这个进程的pid 所以用户是不能直接访问操作系统内部的内核数据结构的进程pid的

所以必须要求操作系统给与一个系统调用!让进程获取自己的pid,也就是使用getpid 

 

直接调用getpid 就能获取自己的pid!

例如:

当代码编译运行后:

使用ps 命令进行查看:

kill -9 

可以使用 kill -9   需要终止的进程的pid   进行终止进程!

如下图所示:

 

创建进程的方式 ppid

一个task _struct 的内部还有一个属性,那就是 pid_t: ppid ,ppid 是进程的父进程的id ,所以使用getppid 就是获取当前进程的父进程的pid 。

于是,就可以得出,一个进程通常是由他的父进程创建的!

进程每一次启动的pid都不一样,但是这个进程的父进程pid都是一样的! 

 fork

创建一个进程,就是操作系统多了一个进程,多了一套进程代码和数据,以及一套PCB结构

同时用户是没有权力在操作系统内新增一个PCB 也就是task_struct 所以还是需要一个系统调用进行进程的创建!

系统调用 fork 函数 在当前的进程下创建该进程的子进程!

 在fork()创建完毕后,有两个执行流,之前的代码有一个进程在跑,也就是第一行printf是一个进程再跑,而在fork后,fork后面的代码有两个进程再跑!

所以fork ()之后的的代码是父子进程共享的! 

 

创建一个进程的本质是系统中多一个进程,根据进程的概念,就是多了一个内核控制块(task_struct)多了一块代码和数据,父进程的代码和数据是磁盘加载来的!那么子进程的代码和数据是哪里来的?在默认的情况下,子进程的代码和数据是继承父进程的代码和数据!

为什么要创建子进程?

主要原因是 想要让子进程执行和 父进程不一样的代码!

想要子进程执行一部分,父进程执行一部分,两个进程并发执行,提高效率!

例如:

 fork 的返回值

因为fork的返回值是分为三种 分别是大于0 等于 0 小于0 

小于0表示创建子进程失败,等于0表示当前的进程是子进程,大于0表示当前的进程是父进程

所以可以通过返回值 来判断 子进程是否创建成功或者当前的进程是父进程还是子进程

又如上图所示,我们通过id 在使用if语句进行判断和分流,让父子进程分别执行不同的代码。

 证明父子进程执行了不同的代码:

首先,在fork创建子进程之后的代码,本质上还是父子进程共享的,但是之后使用了if语句进行了分支处理,并且使用了id的判断,导致了父进程、子进程执行了不同的部分

且,else if 和 else 是同时进程的,和之前C++语言当中的else if  else 语句中的不一样,因为这里的else if  else是多进程,之前的是单进程进行的,所以这里并不是语法的错误,而是进程的原因

其次,id 怎么会即等于0又大于0?这个和虚拟地址空间以及父子写实拷贝有关!

最后为什么fork为什么会有两个返回值?能够进入if判断两次?

因为fork是一个由系统提供的系统调用函数

fork在本质上它的内部是一大串代码,这串代码的最终是由一个return id 进行返回的,但是创建父子进程的关键就在于这个return id 

因为在id之前代码都是一样的,在id这个地方执行流就会开始出现分歧,就会创建出子进程,所以在id这里就会有两个不同的流向,分别就是父进程和子进程,所以fork会有两个返回值的原因就是这个,也是因为这个才会有fork之后进程会变成两个!

查看运行结果:

可以看到上面两个 id是0的是子进程 id是8248的是父进程!

同时,因为父子进程是并发的,并列的,所以杀掉这两个的其中一个都不会对另一个造成影响!

如图所示,杀掉了父进程,子进程仍然在运行!这是因为父进程的PCB和子进程的PCB是并列的,只不过在逻辑上有父子关系,子进程的代码和数据是拷贝父进程的,但是因为父子进程需要各自的独立,所以原则上数据是分开的,但是二者能看到同一个数据的原因也和虚拟地址有关!

不过杀掉父进程后,子进程会跑到后台运行

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值