首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >描述进程和进程状态

描述进程和进程状态

作者头像
绝活蛋炒饭
发布2024-12-16 16:22:16
发布2024-12-16 16:22:16
2480
举报
文章被收录于专栏:绝活编程学习绝活编程学习

1.描述进程的类---PCB

1.1 PCB进程管理模块(是什么?)

我们可以启动多个应用程序--->也就是可以把多个exe文件加载到内存里面。 那么操作系统要不要把内存里exe文件管理起来? 要的 那么如何描述呢?先描述,再组织

我们可以启动多个应用程序--->也就

为什么每一个程序加载到内存里都要生成一次PCB对象呢? 因为操作系统要进行管理。

在操作系统这门学科中 PCB是一个统称; 每一个具体的操作系统都有他们各自的PCB。 如Linux的PCB就叫task_struct


1.2什么是进程

未来:所以对于进程的控制与操作,都是对于PCB模块的处理,与可执行程序无关。

1.3 PCB内部,属性都有哪些?(以 task_struct 为例)

task_struct---PCB的一种

在Linux中描述进程的结构体叫做task_struct。 task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息

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

1.3.1 关于程序计数器的粗略介绍

程序计数器本质就是存储着下一条指令的地址,让CPU可以舒适的待在 取指令和执行指令的循环中


2.Linux程序关于进程的操作

2.1在Linux环境下如何去查看进程 : ps axj

根据指令也是进程这个现象,我们得出结论:所有我们用的独立的指令,都是程序,在运行的时候都会变成进程


我们发现就是当程序运行起来的时候,进程才会出现。当程序结束的时候进程也就消失了。所以,我们得出结论 : 进程也是有生命的

2.2那么在代码内部我们如何去获取自己的进程号呢?

我们先明白我们要获取的是pcb内部的属性pid。 内核数据结构 + 可执行程序 =进程 所以,我们要获取的内容是属于操作系统内核的。 所以,我们需要用到操作系统内核提供的系统调用接口--------getpid


2.3一般来说,在操作系统内部,普通进程都有父进程

我们发现,每次我重新启动程序,虽然我的进程每次都是不一样的。但是,我的父进程每次都是一样的。

我们查看了一下这个进程,发现23616是一个叫bash 的进程。 我们得出结论:操作系统内部所有在命令行启动的进程都是这个bash的子进程 。 bash : 命令行解释器。


2.4查看进程的另一种方式

其实上,在Linux下,他还把进程以文件的形势储存在 /proc目录下,进程文件以进程自己的pid命名。

我们稍微看一部分,前面就是一些文件属性,后面的蓝色字体就是文件名,也就是进程pid

2.5查看一下进程文件内部信息

我们就简单看一 cwd :当前工作目录 exe:可执行文件所处位置


3.创建进程:系统调用接口fork()

从前面来看我们创建进程都是用命令解释器来运行可执行文件,从而创建了进程。 那么,我们可不可以写出一段代码,让可执行文件创建出,除自己外的进程呢?

3.1 fork(),创建进程

我们发现就是,使用了fork函数之后,产生了一个新的进程,并且,新进程是旧进程的子进程。

同样的,我们发现fork()之后,两个进程进行的操作是一样的, 所以我们得出第一个结论:fork()之后,代码共享。

3.2 fork函数的返回值

直接使用 man 指令 如果成功创建进程,子进程的pid被传给父进程,0被传给子进程 如果失败,返回给父进程-1,没有子进程被创建,设置errno。


3.3进程分流

一般来讲,我们希望子进程和父进程能够实现不同的操作。 所以根据返回值不同,我们可以用if语句进行判断,让父子进程做不同的操作。


3.4.fork()函数实现创建进程的基本流程

首先,我们知道 进程 = 内部数据结构 + 可执行程序和数据

生成的子进程中,内部数据结构的数据(task_struct) 就是以父进程的数据为模版写的, 当然pid和ppid肯定是用自己的。 而其中的可执行程序和数据则是二者共享。

3.5那么子进程自己要修改数据怎么办呢?

就是将要改变的数据,在申请一块空间,用于存储修改后的值

3.6关于fork()函数的返回值的三问

3.6.1 为什么给父进程返回pid,而给子进程返回0.

其实,是因为每个进程能有很多子进程,只能有一个父进程。父进程:子进程=1:n 所以,需要给父进程返回子进程的pid作为标识,而子进程的父进程是唯一的,所以返回0就行了。

3.6.2 fork()函数为什么能返回两次

return函数一般是函数的最后一步,前面的步骤一般都已经完成了,也就函数的核心逻辑已经完成了。那么就是,子进程也已经创建出来了。返回的本质其实上也就是写入。 所以,也就是在两个不同的进程中,返回语句都被执行了,所以就是了。

3.6.3 id作为一个变量为什么会同时有两个值

首先,我们要先明白一个点,就是一个进程的崩溃并不会影响到另一个进程。 进程任意之间都是相互独立的,并不会相互影响。

所以OS为了保证这一点呢,就设计一个变量可以代表两个地址 。

id这个变量在父进程标识一个地址,而在子进程又标识另一个地址。


4.进程状态

4.1.关于进程排队的问题

进程 = task_struct + 可执行程序和数据

4.2.那么进程在排队的时候是谁在排队呢?又是怎么样排队的呢?

之前,我们就提到了,进程在排队的时候就是task_struct在排队 。(只要是排队,一般都是task_struct在排队)

4.2.1task_struct在排队的时候一般都不会只在一个数据结构里面,task_struct可以在多个数据结构内部进行排队。

利用内部类,同时在不同的数据结构中排队。 每一个内部类都可以代表一个PCB在数据结构中排队。

4.2.2在排队的队列中只有内部类的信息,那么又是怎么找到PCB的地址的

PCB的地址是根据内部类自己本身的地址,与内部类自身与task_structd的偏移量计算得出。

4.3进程的三个状态 :运行,阻塞,挂起

我们要明白一点就是,进程不是一直在运行的, 即使他已经被加载到了CPU上(时间片:当一个进程占据了CPU大概一毫秒时,就会被踢出)。

像上面这个程序,就有可能是已经被加载到了CPU上了 ,开始运行了,但是他在等待你的输入,所以,他此时就停下来了。


进程的状态体现到代码上也可能就是task_struct内部的一个整型变量

进程状态这个属性又决定了什么事情呢?决定了你PCB下一步干什么事情。


4.3.1运行状态

运行状态:其实就是该进程随时准备好了被调度。此时的PCB会被链接到CPU的运行队列上。 此时,PCB是被链接在两个数据结构内部的。



4.2.2阻塞状态

就是当我们的进程在等待软硬件资源的时候,但是软硬件资源却没有准备好的时候,此时这个进程就不在适合在CPU的运行队列里排队。 PCB就会进行如下两步: 1.将自己的状态设置为阻塞; 2.排队不在排在运行队列,将自己排在等待的软件件资源的PCB的后面。


以硬件为例

我们知道,硬件也是被OS管理的,也是符合先描述后组织。 所以,操作系统,也会管理着描述硬件的类。

进程一开始会在CPU的运行队列里进行排队。

然后,在等待软硬件资源的时候,就会把自己设置成阻塞状态,然后,链接到硬件之后。

就好比是,前面的scanf函数,那个可执行程序的进程就会等待键盘资源,然后 在继续执行。

进程状态的变迁,就会引起OS将PCB变迁到不同的队列当中。


4.2.3挂起状态

挂起状态,一般只会发生计算机内存资源比较吃紧的情况,此时,操作系统将一些还不是马上就要被使用的可执行文件,先移除内存,放到磁盘上,空出部分资源以供使用,防止操作系统直接挂掉。

用于唤入和唤出的磁盘空间,就称为swap分区。 关于swap分区的设置,一般就设置为和内存差不多大就行了。 如果设置得太大了,内存太依赖这个空间,频繁的进行唤入和唤出,会导致效率降低 如果设置的太小了,不够用。

5.Linux下具体的进程状态

进程的状态:具体到代码的上,其实就是一个变量。

R 运行状态( running ) : 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。 S 睡眠状态( sleeping): 意味着进程在等待事件完成 (这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep ))。 D 磁盘休眠状态( Disk sleep )有时候也叫不可中断睡眠状态( uninterruptible sleep ),在这个状态的进程通常会等待IO 的结束。 T 停止状态( stopped ): 可以通过发送 SIGSTOP 信号给进程来停止( T )进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。 Z僵死状态( Zombies )是一个比较特殊的状态。当进程退出并且父进程(使用 wait() 系统调用 , 后面讲) 没有读取到子进程退出的返回代码时就会产生僵死( 尸 ) 进程 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入 Z 状态 X 死亡状态( dead ):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

5.1.S睡眠状态和D磁盘休眠状态

S睡眠状态(sleeping): 意味着进程在等待事件完成,这种情况就相当于我们之前提到的阻塞状态。

D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。

如果,在这种等待情况下,将PCB设置为S状态,当这个PCB在等待的过程中占据了太多资源,这个进程很有可能被OS杀死。 被杀死后,磁盘写完的时候返回信息,找不到人返回了 ,或者写一半找不到PCB了,那么就可能导致文件丢失。

所以就有了 D磁盘休眠状态 ,这个状态也是阻塞状态的一种,但是,却不会被OS杀死。


5.2 T停止状态(stopped)

这里的停止状态有两种:T和t

这里的T就相当于进程直接被停止了,如该进程被 kill -9 直接杀死了。

而t就是一个被追踪的停止状态,就相当于该可执行程序被gdb被调试的时候。


5.3 Z僵死状态(Zombies)

僵死状态( Zombies )是一个比较特殊的状态。当进程退出并且父进程(使用 wait() 系统调用 , 后面讲) 没有读取到子进程退出的返回代码时就会产生僵死 ( 尸 ) 进程 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入 Z 状态

僵尸进程危害

进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z 状态?是的! 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在 task_struct(PCB) 中,换句话说,Z 状态一直不退出, PCB 一直都要维护?是的! 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的! 因为数据结构对象本身就要占用内存,想想C 中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间! 内存泄漏 ? 是的

5.4 死亡状态X

死亡状态X:意味着进程已经终止,且资源已经被回收,它只是一个返回状态,不再存在于进程列表中。


6.孤儿进程

父进程如果提前退出,那么子进程后退出,进入 Z 之后,那该如何处理呢? 父进程先退出,子进程就称之为 “ 孤儿进程 ” 孤儿进程被 1 号 init 进程领养,当然要有 init 进程回收喽。

我们这里看到其实上1号进程就是操作系统。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.描述进程的类---PCB
    • 1.1 PCB进程管理模块(是什么?)
    • 1.2什么是进程
    • 1.3 PCB内部,属性都有哪些?(以 task_struct 为例)
      • 1.3.1 关于程序计数器的粗略介绍
  • 2.Linux程序关于进程的操作
    • 2.1在Linux环境下如何去查看进程 : ps axj
    • 2.2那么在代码内部我们如何去获取自己的进程号呢?
    • 2.3一般来说,在操作系统内部,普通进程都有父进程
    • 2.4查看进程的另一种方式
    • 2.5查看一下进程文件内部信息
  • 3.创建进程:系统调用接口fork()
    • 3.1 fork(),创建进程
    • 3.2 fork函数的返回值
    • 3.3进程分流
    • 3.4.fork()函数实现创建进程的基本流程
    • 3.5那么子进程自己要修改数据怎么办呢?
    • 3.6关于fork()函数的返回值的三问
      • 3.6.1 为什么给父进程返回pid,而给子进程返回0.
      • 3.6.2 fork()函数为什么能返回两次
      • 3.6.3 id作为一个变量为什么会同时有两个值
  • 4.进程状态
    • 4.1.关于进程排队的问题
    • 4.2.那么进程在排队的时候是谁在排队呢?又是怎么样排队的呢?
      • 4.2.1task_struct在排队的时候一般都不会只在一个数据结构里面,task_struct可以在多个数据结构内部进行排队。
      • 4.2.2在排队的队列中只有内部类的信息,那么又是怎么找到PCB的地址的
    • 4.3进程的三个状态 :运行,阻塞,挂起
      • 4.3.1运行状态
      • 4.2.2阻塞状态
      • 4.2.3挂起状态
  • 5.Linux下具体的进程状态
    • 5.1.S睡眠状态和D磁盘休眠状态
    • 5.2 T停止状态(stopped)
    • 5.3 Z僵死状态(Zombies)
    • 5.4 死亡状态X
  • 6.孤儿进程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档