Linux - 进程概念(进程状态优先级)

Posted ikun66666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux - 进程概念(进程状态优先级)相关的知识,希望对你有一定的参考价值。

1.进程状态

操作系统中进程有多种状态模型

三态模型

进程状态分为 就绪态,执行态,阻塞态。

就绪(Ready)状态:指进程已处于准备好运行的状态,即进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行。
执行(Running)状态:指进程已获得CPU,其程序正在执行的状态。
阻塞(Block)状态:正在执行的进程由于发生某事件,暂时无法继续执行的状态,亦即进程的执行受到阻塞。

五态模型

五态模型中增加了创建和终止状态

2.Linux下的进程状态

阻塞:进程等待某种资源就绪的过程
进程因为等待某种条件就绪,而导致的一种不推进的状态(进程卡住了),阻塞一定实在等待某种资源。
为什么阻塞?
进程要通过等待的方式,等具体的资源被别人用完之后,在被自己使用。

挂起:其实也是一种阻塞态,挂起状态后,进程的代码和数据放到了磁盘中,等需要被使用时,再从磁盘中取到内存中

看看Linux内核源代码怎么说

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。下面的状态在kernel源代码里定义:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = 
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
;
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列 里。
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep)。
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

R运行状态(running):

进程只要是R状态,就一定是在CPU上运行吗? 不是!

并不直接代表进程再运行,而代表该进程再运行队列中排队。

传统意义上新建态、就绪态在Linux中就是R状态

下面来看一个例子:

代码1:含printf

printf 本质就是向外设打印消息,循环打印的过程中外设不会一直处于运行状态,所处理的代码在等待队列中(CPU执行速度非常快)

代码2:不含printf

没有printf后,CPU就不需要等待外设打印字符,CPU一直处于判断while语句,所以CPU一直处于R状态!

S睡眠状态(sleeping):

可中断休眠,本质就是一种阻塞状态

等待scanf输入,如果一直不输入,就一直阻塞

D磁盘休眠状态(Disk sleep):

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

D状态下,操作系统都无法杀死该模式下的进程

T停止状态(stopped):

可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。

kill -19 进程PID //暂停进程
kill -18 进程PID //继续进程

为什么Ctrl+C后,还在运行?

Ctrl+C发送了一个SIGINT信号给进程,这个信号通常被用来终止进程。但是,进程在S状态下,是处于可中断状态的,也就是说,如果有一个信号到来,它是可以被打断的,但是如果这个进程正在等待的事件没有发生,它会一直等待下去,即使收到SIGINT信号也无法立即退出。这时候,只能等待这个进程等待的事件发生或者等待被取消,才能使它从S状态中退出。

如果需要强制停止一个S状态的进程,可以使用kill命令,向进程发送SIGKILL信号。但是,这种操作可能会对系统造成一些不良影响,因此应该谨慎使用。

kill -9 PID

X死亡状态(dead):

这个状态只是一个返回状态,你不会在任务列表里看到这个状态

3.Z(zombie)-僵尸进程:

僵死状态(Zombies)是一个比较特殊的状态。

当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程

僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

举个例子:

一个人突然死亡,普通人不会对现场进行清理,而是报警等警察和法医对该人进行信息的采集,之后才清理现场。

其中,某人充当的角色是进程、警察和法医充当的角色的父进程或者操作系统。

通过代码来模拟僵尸状态的进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()

    pid_t id = fork();  //创建进程
    int count = 5;
    while (1)
    
        if (id == 0)  //子进程
        
            while (count)  //循环5次
            
                printf("i am process..child---.pid:%d,ppid:%d\\n,count: %d", getpid(), getppi d(), --count);
                sleep(1);
            
            printf("child quit....\\n");
            exit(1);
        
        else if (id > 0)  //父进程
        
            printf("i am process..father---pid:%d,ppid:%d\\n", getpid(), getppid());
            sleep(1);
        
    
    return 0;

用以下来查看进程状态

while :; do ps aux |head -1&&ps aux|grep a.out;echo "#######################";sleep 1;done

僵尸进程的危害

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

4.孤儿进程

在Linux中,进程的关系主要是父子关系。

一对父子进程中的父进程退出了,子进程还在运行,就会形成孤儿进程。

如果没有进程来回收该子进程的信息,那么会变成僵尸状态,会存在内存泄漏的问题。

为了解决这个问题,该子进程会立即被1号操作系统进程领养。

int main()

    pid_t id = fork();
    if (id == 0)
    
        // child
        while (1)
        
            printf("我是子进程:pid: %d, ppid: %d\\n", getpid(), getppid());
            sleep(1);
        
    
    else
    
        // parent
        int cnt = 10;
        while (1)
        
            printf("我是父进程:pid: %d, ppid: %d\\n", getpid(), getppid());
            sleep(1);
            if (cnt-- <= 0)
                break;
        
    
    return 0;

这里1号进程就是操作系统,也就是说这个进程被操作系统领养了

父进程的僵尸状态未被看见,因为其被其父进程即bash回收

爹嘎了之后,又给自己找了一个爹,PPID为1,也就是操作系统

为什么领养?如果不领养,子进程后续再退出,无人回收,游离的进程多了,占据更多的内存空间。

并且我们可以看到,领养之前,子进程S+前台运行,领养之后,自动由前台变为后台运行,如果想杀掉该进程

  • kill -9 PID

  • killall myproc(进程名称)

5.进程优先级

基本概念

CPU中的资源是有限的,不可能多个进程一起在CPU上运行,利用优先级把进程有效的先后排好,改善了系统的性能。

  • cpu资源分配的先后顺序,就是指进程的优先权(priority)

  • 优先权高的有优先执行权。

  • 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

优先级和权限的区别?

优先级是在有权限的前提下,谁先谁后的问题。

查看系统进 进程

在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容:

我们很容易注意到其中的几个重要信息,有下:

UID : 代表执行者的身份 PID : 代表这个进程的代号 PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号 PRI :代表这个进程可被执行的优先级,其值越小越早被执行 NI :代表这个进程的nice

PRI and NI

PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值
PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值nice其取值范围是-20至19,一共40个级别。

PRI vs NI

需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。
可以理解nice值是进程优先级的修正修正数据

用top命令更改已存在进程的nice:

top
进入top后按“r”–>输入进程PID–>输入nice值

nice/renice也可以调整优先级

setpriority

其他概念

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高 效完成任务,更合理竞争相关资源,便具有了优先级 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为 并发

linux进程概念

愿我如星君如月,夜夜流光相皎洁。

文章目录

进程的定义

对于进程的定义,从不同的角度可以有不同的定义,其中较为典型的定义有:
(1) 进程是程序的一次执行。
(2) 进程是一个程序及其数据在处理及上顺序执行时所发生的过程。
(3) 进程是具有独立功能的程序在一个数据集合上运行的过程,它是一个实体的运行过程,它是系统进行资源分配的和调度的一个独立单位。

我们平时写的代码经过编译链接之后会形成一个.exe的可执行程序,这个可执行的程序本质上就是一个文件(linux上一切接文件),这个文件是放在我们常说的什么C盘、D盘…等等上的。当我们用鼠标双击并使它开始执行,此时先从输入设备(磁盘)加载数据到内存,之后CPU在从内存中读入数据才开始真正的处理数据,最后CPU将处理好的数据写回内存,最后从内存中写出到输出设备中。
在上面所说的执行过程其满足的是冯诺依曼体系结构

输入设备:键盘、网卡磁盘、话筒、摄像头
输出设备:显示器、音响、网卡磁盘
为什么要采用这种结构呢?
因为CPU的速度非常快,但是输入输出设备非常慢(相较与内存到CPU),如果输入输出设备直接与CPU进行打交道,此时整体的计算机的效率会因为CPU去等待输入输出设备而被拉慢了,这就是我们常说的木桶原理。
那么为什么内存到CPU就比输入输出设备到CPU快呢?这里不是多一步操作,不应该变的更慢了才对吗?
不能直接从表面去看待问题,虽然多了一个步骤,但不能这么去理解。
由于内存到CPU的速度相较于输入输出设备到CPU的速度较快,并且我们的内存是一个具有装载数据能力的,我们可以先让数据从输入设备预装载到内存中。当我们现在要计算100条数据,此时我们直接把这100条数据预装载到内存中,在让CPU去内存里拿数据,此时整体效率会提升,并且CPU的命中率也会相应的增加。
举个例子:
当你回家去吃饭,你妈妈看到你回来了然后说我先去买菜,买完菜再去洗菜最后在去炒菜,这样时间太久了。最高效的是,你提前和你妈妈说你要回来吃饭了,然后你妈妈提前把菜准备好,回到家就可以直接吃饭了,这样可以大大提高你的效率。
正如我们的CPU此时只需要从内存拿数据计算,计算完成后写回内存即可,而数据的写入和输出由其他设备来完成。
结论:1、正是因为内存具有数据存储能力,并且能预装载数据进而提高效率的秘诀,而数据的预装多少是由OS来决定的。
2、这里的存储器指的是内存,不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)外设(输入或输出设备)
3、要输入或者输出数据,也只能写入内存或者从内存中读取。
4、一句话,所有设备都只能直接和内存打交道。

进程的创建过程

当我们输入ps aux时,我们可以查看到当前系统中所存在的进程。

而这些进程的创建过程是要经过那些步骤呢?
当我们开机的时候启动的第一个程序就是我们的操作系统,而操作系统是一个用来进行管理驱动、文件、内存、进程管理的一个软件。而进行管理我们就应该想到6字真言,先描述,再组织
我们先将进程信息描述好被放在一个叫做进程控制块的数据结构PCB(Process Control Block)中,可以理解为进程属性的集合。描述好后OS将新创建的PCB以双向链表的形式将它连接起来。只要OS拿到该双向链表的头指针即可访问完所有的进程。

task_struct

task_struct是Linux内核的一种数据结构,它会被装载到RAM中并且包含着进程的信息。每个进程都把它的信息放在 task_struct 这个数据结构体,task_struct 包含了这些内容:
(1)标示符 : 描述本进程的唯一标识符,用来区别其他进程。
(2)状态 :任务状态,退出代码,退出信号等。
(3)优先级 :相对于其他进程的优先级。
(4)程序计数器:程序中即将被执行的下一条指令的地址。
(5)内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
(6)上下文数据:进程执行时处理器的寄存器中的数据。
(7) I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
(8) 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

在上面所讲的PCB就是tack_struct的一种,其中上面的标识符为每个进程的pid,而我们可以通过getpid()函数来获取到每个进程的pid。

通过系统调用创建进程

fork()创建子进程

fork()函数创建子进程,并且有两个返回值,父子进程代码共享,数据各自开辟空间,各自私有一份(采用写时拷贝),当子进程修改了变量值,不会影响父进程的变量。
子进程执行从fork函数之后的父进程的代码。
fork函数的返回值
fork函数会返回两个返回值,一个给父进程返回的是子进程的pid,另一个返回0值给子进程。

我们通过fork()函数来创建子进程,并通过if条件判断来进行分流操作。

注意:这里父子进程的调度是由操作系统来调度的,因此不确定谁先被调度。但是有一个能肯定的是,不管谁先被执行完,父进程都会来等待子进程执行完才退出,不然子进程一直是僵尸进程导致资源的浪费。

进程的状态

R—运行状态

如果该进程是R状态并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。

S—浅度休眠状态

意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠)。
我们可以利用如下代码来模拟出一个S状态。

当上面的可执行程序运行后,我们利用 ps aux|head -1&&ps aux |grep myproc.exe|grep -v grep命令来查看当前进程的状态。

并且在浅度睡眠下,该进程是可以被杀死的。

D—深度睡眠状态

一个进程处于深度睡眠状态,表示尽管OS发现此时内存不够用了该进程不会被杀掉,只有当该进程被唤醒才可以被杀死。有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。

T—暂停状态

可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。

Z—僵尸状态

子进程先于父进程退出, 子进程在退出的时候会向父进程发送SIGCHLD信号,而父进程收到SIGCHLD信号之后是忽略处理的, 所以是子进程的退出资源没有进程回收, 从而变成了僵尸进程。
僵尸进程的危害:

  • 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。就像我们平时写的代码中的返回值一样,返回一个结果给你,然后通过这个结果来看看执行的结果正不正确。当父进程如果一直不读取,那子进程就一直处于Z状态。
  • 由于维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB就会一直都要维护退出状态的信息。
  • 当一个父进程创建了很多子进程,子进程都不回收,就会造成内存资源的浪费,因为数据结构对象本身就要占用内存。从而造成内存泄漏。

    X—死亡状态
    这个状态只是一个返回状态,在任务列表里看不到这个状态。

+状态—前台运行
让前台运行改至后台运行在执行可执行程序后加 & 即可。如果想要杀死该进程使用kill -9 命令。

孤儿进程

孤儿进程:父进程如果提前退出,那么子进程后退出,进入Z状态之后,子进程就称之为“孤儿进程” 孤儿进程,此时子进程被1号init进程领养,最后资源由init(1号)进程回收。
利用如下代码来模拟实现孤儿进程。

进程的优先级

基本概念:就是cpu资源分配给进程的先后顺序,就是指进程的优先权。
优先级越高的进程有优先执行的权力。
①查看系统进程指令 ps -l,显示结果如下:

UID : 代表执行者的身份。
PID : 代表这个进程的代号。
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号。
PRI :代表这个进程可被执行的优先级,其值越小越早被执行 。
NI :代表这个进程的nice值。
其中 PRI 和 NI 与进程的优先级有关
进程的NI(nice)值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。
可以理解nice值是进程优先级的修正修正数据,nice最大与最小修改范围为[-20,19]。
而PRI此时显示的就是进程的优先级,PRI值越小就代表该进程的执行的优先级越高。
linux中优先级=优先级(默认的优先级比如上面的80)+nice值
上面那个默认的优先级不会被改变,所以它的优先级最高是60,最低是99
用top命令更改已存在进程的nice: top 进入top后按“r”–>输入进程PID–>输入nice值

查看进程优先级

[ZJ@VM-0-11-centos 0917]$ ps -al

使用top命令修改进程的优先级

修改进程的优先级


当你输入的nice值,超过[-20,19]时,默认按最大的计算。
I、降低进程优先级

II、升高进程优先级


我们发现升高优先级中,我们一开始的优先级被我们降低至了90,之后我们进行提高。如果你输入的nice值超过了范围,则默认用最大数。
此时我们提高优先级发现并不是我们想要的79,因为在上面我们先降低了了它的优先级,此时在加(-20)那么不应该是60吗?
这里的PRI并不是从你改变的当前值开始改变的。而是从默认老的优先级(80)开始改变。
所以我们得出一个公式: PRI(new)=PRI(old)+nice
注意:当你输入的nice值,超过[-20,19]时,默认按最大的计算。

进程的特征

  • 动态性
    进程的实质是进程实体的执行过程,因此,动态性就是进程的最基本的特征。动态性还表现在:“它由创建而产生,由调度而执行,由撤销到消亡。”可见,进程实体有一定的生命期,而程序则只是一组有序指令的集合,并存放于某种介质上,其本身不具有活动的含义,因此程序是静态的,而进程是动态的。
  • 并发性
    是指多个进程实体同存于内存中,且能在一段时间内同时运行。引入进程的目的也正是为了使其进程实体和其他进程实体并发执行。因此,并发性是进程的另一重要特征,同时也成为OS的重要特征。而程序(OS并没有为它创建PCB)是不能参与并发执行的。
  • 独立性
    独立性是指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。

如果小伙伴还没看懂可以在评论区留言,我会在评论区给你解答!
如有错误之处还请各位指出!!!
那本篇文章就到这里啦,下次再见啦!

以上是关于Linux - 进程概念(进程状态优先级)的主要内容,如果未能解决你的问题,请参考以下文章

linux进程概念

linux进程概念

Linux入门进程概念(超详解,建议收藏)

Linux入门进程概念(超详解,建议收藏)

Linux从青铜到王者第六篇:Linux进程概念第二篇

Linux进程概念(精讲)