描述进程和进程状态

目录

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.孤儿进程 


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号进程就是操作系统。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值