Linux

关注公众号 jb51net

关闭
首页 > 网站技巧 > 服务器 > Linux > Linux进程状态和优先级

Linux中的进程状态和优先级

作者:格式化、、

这篇文章主要介绍了Linux中的进程状态和优先级方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

本篇文章进行操作系统中进程状态的学习!!!

1、进程状态

1.1、概念

在学习OS中的进程状态时,书上的描述为了在Linux、Windows、Android中都能说的通,往往加深了理解的难度

我们可以先学习具体的Linux进程状态,然后再介绍然后再介绍OS学科的状态如何理解

操作系统是计算机中的一门“哲学”,要考虑很多情况

贴几张不同的进程状态图

1.2、具体的进程状态

1. 具体的进程状态可以分为四种:

运行态:进程只要被加载到运行队列中

如何理解进程被加载到运行队列中?

运行队列也是一个对象,也可以通过描述再组织进行管理

运行队列的属性其中有PCB指针,可以通过队列的性质或复杂的数据结构进行管理

调度器的主要作用是在就绪队列中选择优先级最高的任务运行,如果优先级最高的任务不止一个,则选择队头的任务运行

当运行队列中的进程被调度时,CPU会执行该进程的代码

在这里插入图片描述

2. 终止态:进程已经终止,但还存在,只是永远不运行了,随时等待被释放

进程都已经终止了,为什么不立刻释放资源,而要维护一个终止态呢?

因为释放进程的资源需要时间

CPU不可能一直盯着这个进程,可能还会在做其他事情,所以要维护一个终止态

例子:比如你去吃饭,吃完后,叫老板结账,老板可能没回应你,他可能在给别人点餐

3. 阻塞态:进程在等待某种资源时(非CPU资源),资源没有就绪的时候,该进程会到对应的资源等待队列中进行排队,该进程的代码并没有运行,就叫做"阻塞态"

一个进程在被CPU执行的时候,用的不仅仅是CPU的资源

进程可能申请更多的资源,如:磁盘、网卡、显卡、显示器资源和声卡资源等等…

申请对应的资源无法满足时,是需要排队的,比如:CPU资源在运行队列中排队

申请其他慢设备的资源在对应的队列中进行排队

4. 挂起态:当内存不足时,OS会将短期内不会被调度的进程的数据和代码挪动到磁盘中的swap分区,当内存足够时,会重新加载到内存中

1.3、Linux进程状态

Linux内核源代码中的状态

/*
* 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 */
};

1.3.1、R运行状态(运行态)

R (running)运行状态:进程是在运行中或在运行队列中

-

R状态一般是看不出来的,因为CPU的运算速度非常快,当你运行程序时,CPU就已经执行结束

比如在死循环中打印hello world,进程状态一般为"S",因为CPU太快了

CPU一般在等待显示器资源就绪,所以状态一直为S

[lyh_sky@localhost lesson11]$ ls
makefile  test  test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp 
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
    while (1)
    {
        cout << "hello world!!!" << endl;
        sleep(3);
    }
    return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile 
test:test.cpp
	g++ test.cpp -o test
.PHONY:clean
clean:
	rm -rf test
[lyh_sky@localhost lesson11]$ ./test 

通过不断刷新ps指令来查看进程状态的变化:while :; do ps ajx | head -1 && ps ajx | grep ‘test’ | grep -v grep; sleep 2; echo “#############################################”; done

可以通过不断的死循环来让CPU一直不断的工作,因为死循环没有访问外设资源

[lyh_sky@localhost lesson11]$ ls
makefile  test  test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp 
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
    while (1)
    {
    }
    return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile 
test:test.cpp
	g++ test.cpp -o test
.PHONY:clean
clean:
	rm -rf test
[lyh_sky@localhost lesson11]$ ./test 

1.3.2、S/D睡眠状态(阻塞态)

S (sleeping)睡眠状态:意味着进程在等待事件完成

这里的睡眠也叫做:可中断睡眠(浅睡眠),因为它可以通过指令进行中断

可以使用 kill 【-9】 【进程PID】进行中断

睡眠状态是:该进程申请的资源已经被其他进程使用,该进程在对应的“资源等待队列中进行等待,进程中的代码没有执行

[lyh_sky@localhost lesson11]$ ls
makefile  test  test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp 
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
    while (1)
    {
        cout << "hello world!!!" << endl;
        sleep(3);
    }
    return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile 
test:test.cpp
	g++ test.cpp -o test
.PHONY:clean
clean:
	rm -rf test
[lyh_sky@localhost lesson11]$ ./test 

D磁盘休眠状态(Disk sleep)

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

D状态不能通过指令进行中断,必须等它由D状态变成S状态时才能杀死

经过上图可以发现,如果堆磁盘进行读写时,发生中断,会导致严重的问题

所以才有了D状态,D状态就算是OS也不能对其进行释放

D状态不好模拟,但可以使用"dd指令"进行模拟

1.3.3、T/t停止状态

T停止状态(stopped):

可以通过发送 SIGSTOP 信号或快捷键【ctrl+z】给进程来停止(T)进程

这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行

[lyh_sky@localhost lesson11]$ ls
makefile  test  test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp 
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
    while (1)
    {
        cout << getpid() << endl;
        sleep(2);
    }
    return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile 
test:test.cpp
	g++ test.cpp -o test
.PHONY:clean
clean:
	rm -rf test
[lyh_sky@localhost lesson11]$ ./test 

可以通过kill 【-19】 【进程PID】将指定进程发生停止运行信号

kill 【-18】【进程PID】可以恢复重新运行

还有一种t (tracing stop)状态:针对gdb调试的状态

[lyh_sky@localhost lesson11]$ ls
makefile  test  test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp 
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
    while (1)
    {
        cout << getpid() << endl;
        sleep(2);
    }
    return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile 
test:test.cpp
	g++ test.cpp -o test -g
.PHONY:clean
clean:
	rm -rf test
[lyh_sky@localhost lesson11]$ make
g++ test.cpp -o test -g
[lyh_sky@localhost lesson11]$ gdb test

1.3.3、X死亡状态

X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态

1.3.4、Z(zombie)僵尸进程

概念:

注意:僵尸进程是不能被杀死的,因为已经是僵尸了,不能再死第二次!!!

当一个进程退出的时候,一般不会直接进入X状态(死亡,资源可以立马被会回收),而是进入Z状态

僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程

僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码

只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

进程被创建出来是因为用户有任务要交给它执行,当进程退出的时候,我们不知道这个进程完成的怎么样了,一般需要将进程的执行结果告知给父进程和OS

模拟Z状态:创建子进程,子进程退出,父进程不退出(还在运行),子进程退出之后的状态就是Z

[lyh_sky@localhost lesson11]$ cat test.cpp 
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
    pid_t id = fork();
    int cnt = 3;
    if (id == 0)
    {
        while (cnt)
        {
            cout << "我是子进程,我还有" << cnt-- << "秒时间结束" << endl;
            sleep(1);
        }
        cout << "进入僵尸状态" << endl;
        // 结束子进程,并且返回0
        exit(0);
    }
    else
    {
        while (1)
        {}
    }
    return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile 
test:test.cpp
	g++ test.cpp -o test -g
.PHONY:clean
clean:
	rm -rf test
[lyh_sky@localhost lesson11]$ ./test 

僵尸进程危害:

如果没有人回收子进程的僵尸,该状态会一直被维!该进程的相关资源(task_struct)不会被释放

那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量,是要在内存的某个位置进行开辟空间!

这样就会造成内存泄漏!!!

2、孤儿进程

init进程:它是内核启动的第一个用户级进程

父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?

父进程先退出,子进程就称之为“孤儿进程”

孤儿进程被1号init进程领养,当然要有init进程回收喽

[lyh_sky@localhost lesson11]$ cat test.cpp 
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
    pid_t id = fork();
    int cnt = 3;
    if (id != 0)
    {
        while (cnt)
        {
            cout << "我是父进程,我还有" << cnt-- << "秒时间结束" << endl;
            sleep(1);
        }
        cout << "父进程退出" << endl;
        exit(0);
    }
    else
    {
        while (1)
        {}
    }
    return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile 
test:test.cpp
	g++ test.cpp -o test -g
.PHONY:clean
clean:
	rm -rf test
[lyh_sky@localhost lesson11]$ ./test 

3、Linux进程优先级 

3.1、优先级概念

进程优先级和权限的区别?

进程优先级是:进程获取外设资源的先后顺序问题

权限是:拥有者、所属组和other是否可以进行读写执行操作

它们的区别是:在学校中,我们去吃饭,食堂分教师和学生,我们不能去教师食堂打饭,这就是权限。我们打饭时,需要排队,排在前面可以先打到饭,后面的也一样可以打到饭,这就是优先级

概念:

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

优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能

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

为什么要存在进程优先级呢???

排队的本质就是确认进程的优先级

内存里面永远都是进程占大多数,而资源是少数(外设:磁盘、网卡、显卡…)

进程竞争资源是常态,OS要确认进程的先后循序,不然就乱套了

3.2、Linux下进程优先级的操作

使用【ps -al】指令查看进程更多的属性(包含进程优先级):

top指令

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

[lyh_sky@localhost lesson11]$ ls
makefile  test  test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp 
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
    while (1)
    {
        cout << "hello world!!!" << endl;
        sleep(2);
    }
    return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile 
test:test.cpp
	g++ test.cpp -o test -g
.PHONY:clean
clean:
	rm -rf test
[lyh_sky@localhost lesson11]$ ./test 

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

3.3、PRI 和 NI

PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高

NI就是nice值,其表示进程可被执行的优先级的修正数值

PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为: PRI(new) = PRI(old) + nice

当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行

调整进程优先级,在Linux下,就是调整进程nice值

nice其取值范围是-20至19,一共40个级别,调整NI时,会重新变成初始值80,然后+NI值

例如PRI:70 NI:-10,把NI更改成10时,PRI = 80 + 10 = 90

在root用户下,使用top指令修改进程优先级:

[lyh_sky@localhost lesson11]$ ls
makefile  test  test.cpp
[lyh_sky@localhost lesson11]$ cat test.cpp 
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
int main()
{
    while (1)
    {
        cout << "hello world!!!" << endl;
        cout << "我的pid是: " << getpid() << endl;
        sleep(2);
    }
    return 0;
}
[lyh_sky@localhost lesson11]$ cat makefile 
test:test.cpp
	g++ test.cpp -o test -g
.PHONY:clean
clean:
	rm -rf test
[lyh_sky@localhost lesson11]$ ./test 

进入top指令后输入r,然后回车,然后输入需要修改进程优先级的pid

输入pid后回车,接着输入NI值,就能修改进程优先级了

3.4、PRI vs NI

需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化

可以理解nice值是进程优先级的修正修正数据

4、其他概念

1. 竞争性:系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级

2. 独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰

比如:我们在下载东西时,同时打开浏览器进行浏览,当浏览器挂掉了,是不会影响下载的,反之下载挂掉了,也不会影响浏览器

进程具有独立性,不会因为一个进程挂断或者出现异常,而导致其他进程出现问题

3. 并行:多个进程在多个CPU下,分别同时进行运行,这称之为并行

4. 并发:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

操作系统,就是简单的根据队列来进行先后调度的吗?有没有可能突然来了一个优先级更高的进程?

操作系统中有二种内核,分为“抢占式内核”和“非抢占式内核”

当正在运行的低优先级进程,如果来了一个优先级更高的进程,调度器会直接把正在执行的进程从CPU上剥离,放上优先级更高的进程,这就是“进程抢占”(抢占式内核)

5. 进程上下文:进程在运行中产生的各种寄存器数据,就叫做进程的硬件上下文数据

当进程被剥离:需要保存上下文数据

当进程恢复的时候:需要将曾经保存的 上下文数据恢复到寄存器中

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文