当前位置:   article > 正文

【linux操作系统】进程的概念_操作系统tast struct pc

操作系统tast struct pc

目录

冯诺依曼体系结构

操作系统

概念

设计OS的目的

如何理解“管理”

系统调用和库函数的概念

进程

进程的概念

描述进程的控制块-PCB

 PCB的内部构成

查看进程

通过系统调用创建进程-fork

 进程状态

 孤儿进程

进程优先级

PRI 和 NI

查看进程优先级的命令

其他概念


冯诺依曼体系结构

计算机都是由硬件组成的

  • 输入设备:键盘,磁盘(外设),网卡,显卡,话筒,摄像头等

        读取文件就是从磁盘读取文件

  • 输出设备:显示器,磁盘,网卡,显卡,音响等(ms/s级别)

        输入输出并不是独立的两套设备,比如磁盘,从磁盘读设备/向磁盘写数据

        s秒级别的比如说网卡,别人给你发送数据,要等几秒。

  • 存储器(内存)

        离cpu越近,越贵越小越快

  • 运算器和控制器(中央处理器CPU)ns级别

 

是否可以不存在内存?直接将数据写入cpu中?

        cpu的成本太高,虽然他很快但是越快的设备它是越贵的,并且他的存储体量也很小。

所以数会从输入设备存到存储器(内存)中,当cpu空闲的时候将数据交给cpu处理,处理好后再将数据传给存储器,再由存储器从输出设备传输。这样就避免了cpu直接接触外设

所以一个计算机的好坏取决于存储器(内存),因为计算机要把所有数据都交给内存。

内存是体系结构的核心设备

【总结】:

  •  这里的存储器指的是内存
  • 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
  • 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
  • 所有设备都只能直接和内存打交道

当两个不同地方的人是如何发消息的呢?

结论:任何外设,在数据层面,基本优先和内存打交道,CPU在数据层面上也直接和内存打交道

操作系统

概念

        任何计算机都包括一个基本的程序集合,称为操作系统(OS),而操作系统在启动下才有意义,因为要将数据与代码加载到内存中,那么OS是什么?OS就是一款专门针对软硬件资源进行管理工作的软件。再整个计算机硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件。

概括的理解,操作系统包括:

  • 内核(进程管理,内存管理,文件管理,驱动管理)
  • 其他程序(如库函数,shell程序等)

设计OS的目的

  • 对下,与硬件交互,管理所有软硬件资源,
  • 对上,为用户提供一个稳定的,高效的,安全的运行环境

如何理解“管理”

管理分为管理者和被管理者,而管理包括作决策的和执行者

举例子校长就是典型的管理者,辅导员是执行者,学生就是被管理者

  1.  管理者和被管理者并不直接打交道,而是通过执行者来获取被管理者的数据。
  2. 如何管理被管理者呢?对被管理者做出各种决策,决策是要有依据的

        那么操作系统就是管理者,它并不直接管理硬件或软件,而是通过执行者来对用户和硬件进行管理,用户操作接口和驱动程序就是执行者。

 那么从校长的角度:

1.如何聚合同一个学生的数据?

        通过数据结构,其中的结构成员可能包括学生的姓名,年龄,性别,学分等等

2.如何将多个学生的聚合数据产生关联

        将学生的数据放到数据结构中来管理,比如队列,二叉树这种。本来对学生的管理工作,变成了,对数据结构的增删查改。

  • 对以一个被管理对象,先描述他的结构,再组织他的特性,将被管理对象使用特定的数据结构组织起来。所以管理的理念,先描述再组织,再组织可以转化对目标的管理,转化成为对数据的管理。

        那么如何管理进程呢?先描述,再组织。描述进程是通过描述进程的结构体,这个进程控制结构体叫做进程控制块(PCB)。

        操作系统:对下管理好软硬件资源,对上为用户提供良好的运行环境-普通用户-程序员-为程序员提供各种基本功能。

        然而操作系统并不相信任何用户,但是它还要给用户提供服务,这就相当于银行,银行本身是不信任你的,但是他依旧会给你提供服务,但是不会直接提供,而是通过银行柜台来间接提供服务。所以操作系统并不会直接向用户提供服务,而是通过系统调用接口来给用户提供服务,叫做OS提供的接口。

系统调用和库函数的概念

  • 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
  • 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

进程

进程的概念

  • 书本概念:加载到内存的程序,叫做进程。程序的一个执行实例,正在执行的程序等
  • 内核概念:担当分配系统资源(CPU时间,内存)的实体

【问题】:系统中中可不可能存在大量的进程?可能

                是操作系统在管理进程吗?  是的

                如何管理呢?先描述,再组织

                用什么描述呢?此时描述作为一个结构体,我们称之为PCB,进程控制块。

                为什么要有PCB?任何进程再形成之时,操作系统要为该进程创建PCB

描述进程的控制块-PCB

        进程信息被放在一个叫做进程控制块的数据结构当中,可以理解为进程属性的集合。操作系统层面上,PCB就是一个结构体类型。在Linux系统中,PCB的形式为struct task_struct{}在这个结构体中,包含了进程所有的属性。PCB是属性的统称,那么struct task_struct就是具体表述这种属性的结构体。

        当运行这个程序,用命令行查看发现运行程序的时候,myproc程序存在pid,而停止运行程序,myporc.c程序的pid也不存在了。

  1. [wjy@VM-24-9-centos test_3_21] cat myproc.c
  2. #include <stdio.h>
  3. #include<unistd.h>
  4. int main()
  5. {
  6. while(1)
  7. {
  8. printf("hello world!\n");
  9. sleep(1);
  10. }
  11. return 0;
  12. }

对于加载到内存的可执行文件和描述进程的结构体task_struct我们统称为进程,而tast_struct由操作系统自动创建,总的来讲进程=程序+操作系统维护进程的相关数据结构

 代码和数据加载到内存后会形成一个一个的task_struct,操作系统并不会对代码和数据进行管理,而是对代码和数据所对应的控制块进行管理。

 PCB的内部构成

1.标识符:描述本进程的唯一标识符,用来区别其他进程。pid

那我们来验证一下pid,获取pid要包含头文件,这个是系统头文件

  1. [wjy@VM-24-9-centos test_3_21]$ cat myproc.c
  2. #include <stdio.h>
  3. #include<unistd.h>
  4. #include <sys/types.h>
  5. int main()
  6. {
  7. while(1)
  8. {
  9. printf("hello world!: pid: %d\n",getpid());
  10. sleep(1);
  11. }
  12. return 0;
  13. }

当一个进程执行,发现他的标识符pid是14793,用ps axj命令也验证了这一点。

除了ctrl+c可以结束进程,还有一个命令 kill -9 "pid"

 除了子进程pid还有父进程ppid,用getppid获取

printf("hello world!: pid: %d, ppid: %d\n",getpid(),getppid());

 这个ppid就是bash

在程序运行退出的时候总有一个return 0,那么这个就是退出码,通过echo $?查看退出码为100(因为代码程序已经将退出码改为100),但是第二次使用echo $?命令发现退出码变成0,这是因为echo $?命令会显示最近一条命令程序的退出码,第二次echo $?显示的是上一次用echo $?的退出码,为0.

  1. [wjy@VM-24-9-centos test_3_21]$ cat myproc.c
  2. #include <stdio.h>
  3. #include<unistd.h>
  4. #include <sys/types.h>
  5. int main()
  6. {
  7. while(1)
  8. {
  9. printf("hello world!: pid: %d, ppid: %d\n",getpid(),getppid());
  10. sleep(1);
  11. }
  12. return 100;
  13. }

 2.状态:任务状态,退出码,推出信号等

上面提到了退出码,运行程序时,会将程序和数据记录到pcb当中,同样退出码也会记录到pcb,然后让其他进程来获取这个状态。

所以task_struct中已经包括了

  1. struct tack_struct
  2. {
  3. int pid;//进程标识符
  4. int code,exit_code;//退出码
  5. int status;//状态
  6. }

 3.优先级:优先级和权限的区别

4.程序计数器:程序中即将被执行的下一条指令的地址,当操作系统执行完指一条指令,指针会自动跳转到下一个要执行的语句。

5.内存指针:包括程序代码和进程相关的数据的指针,还有和其他进程共享的内存块的指针。系统通过内存指针指向的这个信息来找到对应的数据信息,也就是说,系统通过内存指针指向的pcb找到对应的代码和数据。

6.上下文数据:进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]

首先我们要知道,寄存器是存放当前正在运行的进程,平时我们用电脑的时候好像很多进程都同时运行,查看系统的任务管理器会看到,很多程序在运行,但是他们并非真正的同时运行,而是将数据和代码上传到进程控块中(task_struct),这些task_sturct组成一个队列,进行等待。

当一个进程需要5s来运行,进程的代码可能不是很短时间就能运行完毕的,cpu并不能真的分配5s,而是通过时间片,规定每个进程单次运行的时间片,比如5ms,进程1的task_struct运行完这5ms后就在队列后面排队,进程2开始运行,过了这5ms就在进程队列后面排队,如果这个进程的总消耗时间小于5ms,那么这个进程不再排队。

所以在单CPU情况下,用户感受到的多进程同时在运行,本质是通过CPU的快速切换完成的。并且进程是以时间片来运行的。进程可能存在大量的临时数据

当进程1走完这个时间片的进程,需要在队列后面排队,但是走的时候需要将已经运行的进程信息保存下来,以便下次继续运行时候衔接上次运行的结果继续运行,这就叫保护上下文。虽然寄存器只有一个,但是寄存器里的数据是你这个进程的。 当这个队列又轮到进程1来运行,进程将task_stuct中保存的上下文继续交给寄存器来运行,直到5ms结束后,再次将上下文保护起来。将上下文交给寄存器运行的这个过程,叫做恢复上下文

所以保护和恢复上下文是为了让你去做其他的事情,但不耽误当前的事情,并且当你想回来继续学习的时候,可以接着之前你的学习内容继续学习

所以上下文就是:进程执行时所形成的处理器寄存器当中与进程强相关的临时数据

通过上下文,我们能感受到进程是被切换的。

7.I/O状态信息:包括显示显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表,将进程写入写出的操作

8.记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等

查看进程

进程的信息可以通过/proc系统文件夹查看

蓝色部分字体是进程id,也就是说进程启动之后,会在proc目录下形成一个以自身pid编号为目录文件名的文件夹

 当执行一个文件时,查看他的进程,就是查看在proc目录下的pid目录文件内容

 通过ls /proc/pid -al命令可以查看到,exe对应的就是执行该文件的路径,cwd是当前工作目录

 当在myproc.c中写了这样一个程序,发现在该程序的当前路径下出现了要写入的log.txt文件,最初是没有log.txt文件的。但是在写入这个程序后,该路径又出新了log.txt,我们并没有指明该文件的路径,该文件是如何生成在此路径下呢?

进程通过cwd找到当前路径,然后在这个路径下创建文件。

  1. [wjy@VM-24-9-centos test_3_21]$ cat myproc.c
  2. #include <stdio.h>
  3. #include<unistd.h>
  4. #include <sys/types.h>
  5. int main()
  6. {
  7. FILE* fp = fopen("log.txt","w");
  8. fclose(fp);
  9. while(1)
  10. {
  11. printf("hello world!: pid: %d, ppid: %d\n",getpid(),getppid());
  12. sleep(1);
  13. }
  14. return 0;;
  15. }
  16. [wjy@VM-24-9-centos test_3_21]$ ./myproc
  17. hello world!: pid: 9042, ppid: 6957
  18. hello world!: pid: 9042, ppid: 6957
  19. hello world!: pid: 9042, ppid: 6957
  20. ^Z
  21. [1]+ Stopped ./myproc
  22. [wjy@VM-24-9-centos test_3_21]$ ll
  23. total 20
  24. -rw-rw-r-- 1 wjy wjy 0 Mar 29 22:05 log.txt
  25. -rw-rw-r-- 1 wjy wjy 64 Mar 21 11:20 Makefile
  26. -rwxrwxr-x 1 wjy wjy 8616 Mar 29 22:05 myproc
  27. -rw-rw-r-- 1 wjy wjy 240 Mar 29 22:04 myproc.c

通过系统调用创建进程-fork

fork有一下特点:

  • fork有两个返回值
  • 父子进程代码共享,数据各自开辟空间(采用写实拷贝)

通过下面三个代码发现,第一个hello world显示了两次,第二段代码中,以前我们写if else语句,二者只能执行一个,但是绝对不能执行两个,但是这次两个都执行了。而第三个在执行fork之后,执行的printf死循环语句中两个进程居然不一样。

通过这些奇怪的现象,本质是fork之后,有两个执行流。

 

ps:在vim模式下怎么查看手册?

普通模式下,直接输入man + fork命令就能查看手册,在vim模式下,要在esc模式下,“:” + man +fork 查看命令

同样,如果写了makefile文件,想要在vim下直接编译,就是在esc下“:”再加上该有的命令


当写这样一个进程代码,fork下会执行两次cout语句,第二条语句的父进程就是第一条语句的紫禁城,那么第一条语句的父进程就是bash,7758进程就是7759进程的爹

那么如何理解fork创建子进程?

1.通常我们创建进程就是./cmd和执行命令,那么fork又是一种创建进程的方式。但在操作系统角度,这几种创建进程方式没有差别。

2.当fork创建了进程,系统里就多了一个进程,那么就多了一个与进程相关的内核数据结构+进程的代码和数据的PCB块,内核数据结构就是task_struct,进程的代码和数据是谁呢?在第一个进程执行完有了task_struct和代码+数据,那么子进程的代码和数据从哪来?

 fork创建子进程后,默认情况下,会“继承”父进程的代码和数据,内核数据结构task_struct也会以父进程为模板,初始化子进程的task_struct。也就是说fork之后子进程的描述的数据结构(PCB)是跟父进程一样的,里面的代码和数据也是以父进程为模板或者继承父进程。

fork之后,子进程和父进程代码是共享的。那么子进程的代码是不可修改的,父子进程代码只有一份。那么默认情况下,数据也是共享的。在我们的操作系统中,有时候会同时打开很多软件,当一个软件关闭后,不会影响另一个软件,说明进程具有独立性。但是数据也要考虑修改的情况,当父子进程只有读操作时候,他们的数据是共享的,如果要修改一个进程的数据,通过“写时拷贝”来完成数据的独立性。所以写时拷贝是为了维护进程的独立性,让各个进程不被干扰。

但是如果没有写入操作,也就是修改操作的时候,我们是不需要进程写时拷贝,共享一份数据即可。如果只读不写,写时拷贝会造成空间的浪费


 那么我们创建子进程就是为了跟父进程干一样的事情吗?一般是没有意义的,通常父子进程要做不一样的事情。

父子进程一般是靠fork的返回值来完成的!

这样进程中有两种返回值,因为一个id里面有两个结果。但是从前没有出现有两种返回值的情况,但是在fork之后又两种结果返回值。

如何理解有两个返回值?

两个返回值代表两个数据,如果一个函数已经开始执行return,说明函数的核心功能已经执行完了,在上面图中的代码中,fork()执行后,会返回一个id,这也是返回值,先执行父进程的返回值,因为父进程先改变,所以先进行了写时拷贝,也就是说,返回值也是数据,return的时候会进行写入,发生了写时拷贝。这也就证明了return语句也是语句!

如何理解两个返回值的设置

父进程与子进程是1:n,一对多的关系

所以父子进程可以通过if,else分流来做不同的事情,通过下面这段代码,发现子进程的父亲就是父进程的进程

  1. int main()
  2. {
  3. pid_t id=fork();
  4. if(id==0)//子进程
  5. {
  6. while(true)
  7. {
  8. std::cout<<"I am child:pid:"<<getpid()<<",ppid:"<<getppid()<<std::endl;
  9. sleep(1);
  10. }
  11. }
  12. else if(id>0)//父进程
  13. {
  14. while(1)
  15. {
  16. std::cout<<"I am parent:pid:"<<getpid()<<",ppid:"<<getppid()<<std::endl;
  17. sleep(1);
  18. }
  19. }
  20. else
  21. {
  22. }
  23. sleep(1);
  24. return 0;
  25. }

通过ps axj | grep myproc发现这两个进程在运行

 那么fork之后,父子谁先运行?这个是并不确定的,父子的运行是通过调度器来控制谁先后运行

 进程状态

在操作系统中,进程状态的意义是为了方便操作系统快速判断进程,完成特定的功能,比如调度,本质是一种分类。进程状态的信息一般放在task_struct(PCB)中。

为了弄明白正在运行的进程是什么意思,我们需要直到进程的不同状态,一个进程可以有多种状态,在Linux内核里面,进程有时候也叫做任务。

具体状态:

  • R:运行状态。

运行状态的资源不一定占用CPU,有时候会在一个等待队列中,呈现运行状态,等待被调度。这个队列也就是task_struct,描述进程的控制块组成的队列,里面放的都是每一个要执行进程的代码和数据。这时候对进程的调度也就变成了CPU对task_struct的增删查改。

  • S:休眠状态--可中断睡眠--一种等待状态--可中断睡眠
  • D:深度睡眠状态--不可中断睡眠

当我们像完成某种任务的时候,任务条件不具备,比如先从磁盘读入数据,但是磁盘已经满了;想从网络读取数据,网络断了等等这种任务条件不具备,需要进程进行某种等待,需要S或D。

和等待运行队列一样,等待进程会形成一个等待队列,在缺乏条件的时候,这种不可被直接调度的进程,进入等待队列,但是等待队列放的不是代码和数据,而是进程控制块,R状态的队列直接放代码和数据,而阻塞状态队列放PCB,再通过PCB来寻找代码和数据。

 需要注意的是,进程不止会去等待CPU资源。等待CPU资源的是R状态,等待其他设备的时候是S/D状态。当S/D状态下的某个队列中的进程条件就绪,等到了磁盘这种设备,不会立马被CPU调度,而是从S/D状态变为R状态进入R中的队列,等待CPU被调度。

所以进程在运行的时候,有可能因为运行需要,可能会在不同的队列里。在不同的队列里所处的状态也是不一样的。

我们把,从运行状态(run_queue),放到等待队列中,就叫做挂起等待(阻塞);从等待队列放到运行队列,被CPU调度就叫做唤醒进程。系统中一般存在大量阻塞队列,少量运行队列


那么什么是D不可中断睡眠呢?

当一个进程要往磁盘中写入数据,但是此时磁盘并没有时间来做这个进程,磁盘让进程在内存中等待。此时磁盘对进程说,你等我一会,等我把现在的事情忙完,我就执行你的进程。此时进程在内存中等待,磁盘去做别的事情了。此时操作系统会对进程进行管理,看到有进程什么事情都没有做,系统中的资源本来就不够,进程还在占着茅坑不拉屎,这时OS将等待的进程释放。但是等磁盘昨晚当前进城后,回头找刚才等待的进程,发现刚才等待的进程没有了。因为磁盘运行完上个进程无论成功还是失败,都需要对下一个进程返回一个状态,但是此时进程却没有了。

根据以上描述,为了让等待的进程不被操作系统释放,有了不可中断睡眠D。

所以有两种状态睡眠,可以被杀掉的睡眠S,和不可被杀掉的睡眠D。

  • T 暂停状态  --  使数据彻底不更新
  • t  追踪状态  --  当进入调试阶段如gdb模式下,进程处于追踪状态
  • X 死亡状态

一旦进程进入死亡状态,操作系统要回收它的资源,回收进程资源=进程相关的内核数据结构 + 它的代码和数据,当我们创建进程,会创建描述进程的进程控制块以及它的代码和数据,那么释放的也是释放这两种。

死亡状态很难被查到,就在一瞬间,PCB已经被释放。

  • Z  僵尸状态

辨别退出死亡的原因!给出进程退出的信息,也是数据。

那么当进程退出的时候,它的资源并不是立即被释放,而是先进入僵尸状态,将进程退出的所有原因写进PCB的task_struct描述数据结构中,让系统和父进程进行读取,此时的task_struct被称为僵尸状态。所以正常的死亡状态是Z->X->被释放。

一个进程如果不会收,会让进程一直处于僵尸状态,僵尸状态的进程不释放会占用资源,形成内存泄漏。

如果父进程没有检测或回收进程,该进程就会进入僵尸状态Z。为什么会有子进程都停止了,而父进程不回收它的情况?

下面这段代码当中,先创建子进程,两个进程可同时运行,我们知道进程结果为0的是子进程,结果不为零的是父进程,所以我们可以设置父进程等待50秒运行一次,子进程2秒运行一次,父进程在这50秒的时间内会是等待状态,什么都不干。如果把子进程杀掉,父进程处于等待状态,使得被杀掉的子进程没有被回收,变成了僵尸状态。

如果父进程被回收后,子进程如果想被回收,操作系统会想方设法去回收子进程,但是这样的父进程在等待状态,也没有被回收的情况下,父进程就不会管子进程,操作系统也没有办法回收子进程,这样子进程变成僵尸状态。

  1. int main()
  2. {
  3. pid_t id=fork();
  4. if(id==0)
  5. {
  6. //child
  7. while(true)
  8. {
  9. cout<<"I am child,running!" <<endl;
  10. sleep(2);
  11. }
  12. }
  13. else{
  14. //parent
  15. cout<<"father do nothing!"<<endl;
  16. sleep(50);
  17. }
  18. return 0;
  19. }

但如果子进程还在运行,父进程已经挂掉。这样的话子进程就没有人回收了,这时候子进程就变成孤儿进程,它会被1号进程领养,也就是它的ppid变为1,这个1号进程就是操作系统。所以当孤儿进程被杀掉要被回收的时候,1号操作系统进程就来回收这个孤儿进程。


进程是一直在RS状态之间切换,因为外设访问CPU实在太慢,对于CPU而言,我们外设访问的没有CPU运行快,cpu很大程度上都在等待

如果把一个进程暂停,那么是不是也有暂停状态呢?

那怎们暂停呢?

使用kill -l命令查看,发现18和19两个分别是继续进程和暂停进程

 当运行进程,输入kill -19 +进程号,就可以暂停一个进程,通过ps axj | grep myproc查看进程的pid,输入命令kill -19 进程号,输入后发现进程变为T状态。

再让它继续运行,那么输入kill -18 +进程号,将暂停的进程继续运行,当我们运行再次查看进程号发现,进程号的状态后面没有+了,而且按ctrl+z这样的退出信号也不会退出,这是因为,只要运行了暂停命令,这个进程就在后台运行,那么该如何杀掉进程?

杀掉进程:kill -9 进程号

当我们直接运行一个进程,这样的进程是在前台运行的,这样的进程用ps axj | grep mygrep(mygrep是我写的一个文件)命令后,查看到的进程状态后面是带一个“+”的,典型特征是按什么键都不好使,但是ctrl+C/Z后,进程停止。

或者输入fg让后台进程提到前台运行,之后再ctrl+C杀掉进程

 如果在进程运行命令后面加一个取地址,这个叫做后台命令,虽然也在屏幕当中刷新。它的典型表现是可以执行你的命令,但是ctrl+C/Z命令不好使。

[wjy@VM-24-9-centos 330test]$ ./myproc &

 孤儿进程

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

进程优先级

当我们写一个程序并将它运行,用ps -l命令查看,就会查看当前运行程序的信息。里面的PRI就是优先级,我们可以看到,这个进程的优先级是80

  1. [wjy@VM-24-9-centos 330test]$ cat myproc1.c
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. int main()
  5. {
  6. while(1)
  7. {
  8. printf("I am a process,pid:%d,ppid:%d\n",getpid(),getppid());
  9. sleep(1);
  10. }
  11. }

这其中有几个和重要的数据

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

PRI 和 NI

  • PRI就是进程优先级,也就是程序被CPU执行的先后顺序,PRI值越小,优先级越高。
  • NI--nice值:优先级的修正值,新的优先级=老的优先级+修正值,PRI(new)=PRI(old)+nice
    ,进程是对修正值的修改来改变进程优先级。进程不是直接对PRI修改来调整优先级,而是对NI的修正来让进程自己去调整优先级。
  • nice的取值范围一般是-20--19,一共4个级别
  • 但为什么不直接该PRI呢,第一linux系统没有选择这种方式,第二修改NI值更直观。
  •  为什么会有优先级?因为资源太少,分配资源需要排队。
  • 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行

查看进程优先级的命令

调整优先级就是调整nice值,更改方法:

  • 输入top命令,直接回车
  • 进入top后按“r”->进入进程PID,输入nice值

但是这个优先级不是所有的数字都能写的,如果超过-20 - 19 这个范围,大于19,都视为19,小于-20都视为-20.

但是第二次改优先级的时候发现,本来new_priority=old_priority+nice,第二次改变优先级本来值应该在90的基础上加5为95,但是第二次改变值是85.所以不管第几次修改,old_priority值都是从80开始。


进入top命令修改

 按r输入进程号,再输入修改的值,按q退出

 用ps -al命令查看,修改成功 ,把优先级降低

但是优先级这个东西我们自己最好不要改。


nice值为什么是一个比较小的范围呢?

优先级的设置,只能是一种相对优先级,不能出现绝对的优先级。如果范围很大,能随意修改进程优先级,那么导致不调整优先级的进程长时间不能被执行,会出现很严重的进程“饥饿”问题

调度器的功能:较为均衡的让每个进程享受到CPU资源。

其他概念

  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
  • 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
  • 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
  • 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/485346
推荐阅读
相关标签
  

闽ICP备14008679号