linux篇【4】:进程与操作系统

目录

一.冯诺依曼体系结构

注意:

1.为什么要有内存?

a.技术角度

b.成本角度

2.存储器/内存 的意义

二.操作系统

1.管理的理解一:管理的本质-对数据做管理。

2.管理的理解二:如何管好大量的数据!

管理的核心理念:先描述(对象有各种信息),再组织(数据结构)

3.进程(和PCB)

程序是文件吗?——是的!文件在磁盘! !

进程与程序,作业

进程控制块-PCB

二.操作系统OS

1.OS介绍

类比后,操作系统也是通过给用户提供接口的方式!操作系统也是不相信任何人,

windows的系统接口和Linux的系统接口一样吗? ? 肯定不一样。

操作系统管理结构官方图:  

2.OS为什么要给我提供服务呢?

三.进程的查看

1.查看进程的第一种最常用的方式:ps axj | grep ' mytest'

ctrl+c和kill -9 进程pid 杀进程

2.pid意思是当前进程,ppid意思是父进程

bash进程和init进程:

3.代码创建子进程 fork()

例:printf打印2次

例2:

4.为什么 fork()给父进程返回子进程的pid,给子进程返回0

5.为什么fork会返回两次!

fork之后,OS做了什么?是不是系统多了一个进程

调用一个函数,当这个函数准备return的时候,这个函数的核心功能完成了吗? ?

fork会返回两次解释:

三.进程状态(运行态,终止态,阻塞态,挂起态)

1.操作系统原理上的进程状态

2.Linux内核中的进程状态

(1)R,S,D

(2)Z (zombie) 和 X(dead)

(3)孤儿进程

 S+:带+是前台进程。S:不带+是后台进程。

孤儿进程会被1号进程 (init进程) 领养:

(4)T和t

3.守护进程/精灵进程

(1)介绍PGID,SID等各个名称

(2)会话

4.操作系统和Linux内核中的进程状态对应关系

四.进程优先级

1.什么是优先级 优先级VS权限 (是什么)

2.为何会存在优先级? (为什么)——资源不够!

3.Linux下的优先级的相关概念和操作(怎么办) ps -la查看优先级

优先级由 priority+nice 两个值构成

PRI and NI

PRI vs NI

(3)修改优先级操作

五.进程间切换(寄存器)

1.上下文切换:

寄存器VS寄存器里面的临时数据:

2.竞争性与独立性,并行与并发

并行 解释::

并发 解释:(切换的过程是操作系统中的一个软件调度器做的)

3.抢占式内核

进程的优先级和队列

在抢占式多任务处理中,进程被抢占时,哪些运行环境需要被保存下来?

4.寄存器

寄存器上的数据的重要性:

要准确区分:寄存器vs寄存器里面的数据!


一.冯诺依曼体系结构

我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。

输入输出设备这些外设不和CPU直接打交道,而是把数据放到内存中。CPU也是直接从内存中读数据。

输入:键盘,话筒,摄像头,磁盘,网卡...
输出:显示器,音响,磁盘,网卡,显卡...(输入输出都是外设)
(运算器+控制器) [CPU]:算数计算+逻辑计算
存储器:就是内存

注意:

1.磁盘既可以从硬盘读取数据也可以向硬盘写入数据

2.冯诺依曼体系结构必要组成部分:

CPU   运算器与控制器,RAM 内存(存储器),ROM 磁盘(输入输出设备)

Cache 缓存是一种技术,不属于冯诺依曼体系结构必要组成部分。

3.冯诺依曼体系结构计算机的基本原理是存储程序和程序控制

1.为什么要有内存?

a.技术角度

cpu的运算速度 > 寄存器的速度 > L1~L3Cache > 内存 >> 外设(磁盘)  >>  光盘磁带
数据角度:外设不和CPU直接交互,而是和内存交互,CPU也是如此
        内存在我们看来,就是体系结构的一个大的缓存,适配外设和CPU速度不均的问题的!

b.成本角度

        寄存器>>内存>>磁盘(外设)

计算机蔓延全世界 ! 那计算机一定有2个特点:有效 ,便宜
 

2.存储器/内存 的意义

使用较低的钱的成本,能够获得较高的性能。平衡输入输出速度慢和cpu运行速度快

几乎所有的硬件,只能被动的完成某种功能,不能主动的完成某种功能,一般都是要配合软件完成的(OS+CPU)

我们自己写的软件,编译好之后,要运行,必须先加载到内存,为什么? ?
体系结构规定!局部性原理

二.操作系统

1.管理的理解一:管理的本质-对数据做管理。
 

操作系统是一款软件,搞管理的软件

管理的本质:对数据做管理。
 

校长连我的面都不见,如何管理我呢?.
管理你要和你打交道,要和你见面吗?
他是怎么做到的?
管理的本质:不是对被管理对象进行直接管理,而是只要拿到被管理对象的所有的相关数据,我们对数据的管理,就可以体现对人的管理! ! ! ! !

2.管理的理解二:如何管好大量的数据!

如何管好大量的数据!

人认识世界的方式:
通过属性认识世界的!
一切皆对象
一切事物都可以通抽取对象的属性,来达到描述对象的目的
class OBJ {
//成员属性        

}

管理的核心理念:先描述(对象有各种信息),再组织(数据结构)

 对进程的管理变成了对链表的增删查改

3.进程(和PCB)

什么是进程?进程是动态的过程,是一个运行起来的程序,进程=可执行程序(例如:内存中的test.exe)+该进程对应的内核数据结构(PCB)进程=内核的进程数据结构(task_struct, mm_ struct,page table) +进程的代码和数据

task_struct 是一个具体的PCB,PCB进程控制块在linux中叫 task_struct

程序是文件吗?——是的!文件在磁盘! !

操作系统里面可能同时存在大量的进程。操作系统要将所有的进程管理起来,
对进程的管理,本质就是对进程数据的管理。先描述(对象有各种信息),再组织(数据结构)

进程与程序,作业

  • 进程与程序不一定是一一对应的,一个程序可以同时运行多次,也就有了多个进程
  • 进程与作业不一定是一一对应的,因为一个作业任务的完成可由多个进程组成,且必须至少由一个进程组成

进程控制块-PCB

进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct
task_struct-PCB的一种
在Linux中描述进程的结构体叫做task_struct。
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
task_ struct内容分类
        标示符: 描述本进程的唯一标示符,用来区别其他进程。
        状态: 任务状态,退出代码,退出信号等。
        优先级: 相对于其他进程的优先级。
        程序计数器: 程序中即将被执行的下一条指令的地址。
        内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
        上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息
组织进程
可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。

二.操作系统OS

1.OS介绍

操作系统:内存管理,进程管理,文件管理,驱动管理

什么是操作系统(OS),类比银行管理系统,操作系统就是银行的行长。 

银行给所有人提供服务的方式是通过窗口提供的:

我们见到的所有的银行,都是一个封闭体,暴露出来一些窗口。因为银行是不相信任何人的!防止一些不法分子闯入,要防止少数人,给多数人提供服务。

类比后,操作系统也是通过给用户提供接口的方式!操作系统也是不相信任何人

要防止少数人恶意修改代码,要给多数人提供服务。内核是使用C语言写的!接口:给我们提供底层是C语言的函数调用。系统可以调用各种函数。

操作系统不相信任何人的!不会直接暴露自己的任何数据结构,代码逻辑,其他数据相关的细节!!

操作系统是通过系统调用的方式,对外提供接口服务的!
Linux操作系统是用C语言写的,这里所谓的“接口”,本质就是C函数!

我们学习系统编程本质就是在学习这里的系统接口

windows的系统接口和Linux的系统接口一样吗? ? 肯定不一样。

操作系统管理结构官方图:  

2.OS为什么要给我提供服务呢?

计算机和OS 设计出来是为了给人提供服务的

printf or cout 向显示器打印,显示器是硬件->所谓的打印,本质就是将数据写到硬件上
我们自己的C程序,没有资格向硬件写入
 

三.进程的查看

我们自己写的代码,编译成为可执行程序,启动之后就是一个进程!
那么别人写的呢??启动之后是不是进程呢??肯定算!!

1.查看进程的第一种最常用的方式:ps axj | grep ' mytest'

ps axj 选中所有进程,ps axj | grep 'mytest' :从所有进程中筛选出mytest的进程

ps axj | grep 'mytest' | grep -v grep :从所有进程中筛选出mytest的进程,同时把进程grep隐藏(我们正在使用grep,grep也是进程)

ps axj | head -l :从所有进程中筛选出第一行,第一行是PPID,PID....COMMAND,这些名称。

ps axj | grep 25979:从所有进程中筛选出进程25979

 

当前路径?:当前进程所在地路径,进程自己会维护。

每一个进程在系统中,都会存在一个唯一的标识符(pid)!就如同同学们在学校每个学生都有一个学号的东西!
pid (process id )

ps ajx | head -1 && ps axj | grep' mytest '| grep -v g

ctrl+c和kill -9 进程pid 杀进程

-9是9号命令,以后再谈

2.pid意思是当前进程,ppid意思是父进程

getpid():得到当前进程的标识,getppid():得到当前进程的父进程标识(想得到当前进程的子进程标识只能用fork)

几乎我们在命令行上所执行的所有的指令(你的cmd), 都是bash进程(bash的pid是29673)的子进程!
 

bash进程和init进程:

bash就是我们每打开一个终端后运行的第一个程序,他会捕捉你的键盘输入,看你输入了什么指令,则会解析执行对应的指令程序,在终端中运行的执行程序所对应进程默认父进程就是这个bash进程(因为这些指令程序对应的进程都是这个bash进程创建的)
init进程(也叫1号进程,就是操作系统)是系统内非常 重要的一个管理进程,系统中只有一个这个进程(bash进程是打开了多少终端就会运行多少个),进程PID为1
 

3.代码创建子进程 fork()

fork函数是用来创建子进程的,它有两个返回值:父进程返回子进程的pid,给子进程返回0

fork如何做到会有不同的返回值?

例:printf打印2次

printf为什么会打印两次?:fork之后,父进程和子进程会共享代码,一般都会执行后续的代码

 

例2:

fork之后,父进程和子进程会共享代码,一般都会执行后续的代码
fork之后,父进程和子进程返回值不同,可以通过不同的返回值,判断,让父子执行不同的代码块,即 if 和 else 都执行 

 

4.为什么 fork()给父进程返回子进程的pid,给子进程返回0

父亲:儿子 = 1:n  (n>=1)
一个父亲有可以多个儿子,所以父进程必须有标识子进程的方案,fork之后,给父进程返回子进程的pid!
子进程最重要的是要知道自己被创建成功了,因为子进程找父进程成本非常低!所以不需要标识符,返回0就行了,getppid()可以得到子进程的父进程

5.为什么fork会返回两次!

fork之后,OS做了什么?是不是系统多了一个进程

task_ struct + 进程代码和数据
task_ struct + 子进程的代码和数据
子进程的task_ stuct对象内部的数据从哪里来的:基本是从父进程继承下来的
子进程执行代码,计算数据的,子进程的代码从哪里来?:和父进程执行同样的代码,fork之后,父子进程代码共享!而数据要各自独立。
(父子进程代码共享:不同的返回值,让不同的进程执行不同的代码)

调用一个函数,当这个函数准备return的时候,这个函数的核心功能完成了吗? ?

已经完成!
1.子进程已经被创建了
2.将子进程放入运行队列!(就是进程被运行)

如何理解2.进程被运行:

运行队列runqueue是链式队列

fork会返回两次解释:

因为return之前子进程放入运行队列,就有父进程和子进程两个进程运行,因为return pid也是代码,父子共享代码,return pid会被父子各自执行一次,则有两个返回值

三.进程状态(运行态,终止态,阻塞态,挂起态

1.操作系统原理上的进程状态

运行态:进程只要在运行队列中叫做运行态,代表我已经准备好了,随时可以调度! !(不是正在CPU上运行的进程)

终止态:该进程还在,只不过永远不被调度和运行了,随时等待被释放!(不是已被释放的进程)

进程都终止了,为什么不立马释放对应的资源,而要维护一个终止态? ?
答:释放要花时间!当前你的操作系统有可能很忙,并不能立即处理。

阻塞态:进程等待某种资源(非CPU),资源没有就绪的时候,进程需要在该资源的等待队列中进行排队,此时进程的代码并没有运行,进程所处的状态就叫做阻塞!(访问外设才会阻塞,比如while(1) printf(“11”);访问显示器就是访问外设 会阻塞,单一个while(1)就不会阻塞)

1.一个进程,使用资源的时候,可不仅仅是在申请CPU资源!
2.进程可能申请更多的其他资源:磁盘,网卡,显卡,显示器资源,声卡/音响
如果我们申请CPU资源,暂时无法得到满足,需要排队的--运行队列
那么如果我们申请其他慢设备的资源呢?也是需要排队的! (task_ struct在 进程排队)

当进程访问某些资源(磁盘网卡),该资源如果暂时没有准备好,或者正在给其他进程提供服务,此时: 1.操作系统会将当前进程从runqueue中移除 2. 操作系统将当前进程放入对应设备的描述结构体中的等待队列!

比如:CPU在运行队列中执行一个进程A,执行进程A的代码中需要访问磁盘资源disk,但是磁盘暂时没有准备好,或者正在给其他进程提供服务,则进程A不会占着CPU的资源等磁盘,而是操作系统把进程A放进磁盘的等待队列去等待,当我们的进程此时在等待磁盘的时候,该进程的代码,不会被执行,我们看到的情况就是:我的进程卡住了,这叫做 进程阻塞

等到磁盘就绪后再重新放回CPU的运行队列去运行代码

挂起态

如何内存不足了怎么办? ?
操作系统就要帮我们进行辗转腾挪
短期内不会被调度(你等的资源,短期内不会就绪)的进程,它的代码和数据依旧在内存中!就是白白的浪费空间!OS就会把该进程的代码和数据置换到磁盘上(swap分区)!

 

2.Linux内核中的进程状态

R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。如果不访问外部资源(例如:printf访问显示屏这些外设)那么基本上就是在申请CPU资源,那就是R状态
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
(interruptible sleep))。S和D都是阻塞态,区别是S可以被唤醒/杀死,D只能等自己执行结束再唤醒
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态

(1)R,S,D

R运行态:如果不访问外部资源(例如:printf访问显示屏这些外设)那么基本上就是在申请CPU资源,那就是R状态,例如下面:

S阻塞态:申请外部资源,比如有一个printf,还是死循环,虽然屏幕上疯狂刷屏打印的内容,但实际上cpu打印是极快的,大部分时间都是在等 显示器 这个资源,所以状态是S阻塞状态,例如下图:  S状态也叫浅度睡眠/可中断睡眠,因为这个状态是可以被唤醒的(又操作系统把S态转为R态即为唤醒),也可以随时被我们用kill -9 (pid) 杀掉

D(disk sleep) :一般而言,linux中,如果我们等待的是磁盘资源(disk),我们进程阻塞所处的状态就是D

D解释:进程让磁盘把自己的数据写入磁盘,进程要等待磁盘写完返回一个值,代表写入成功或失败,此状态下进程在等待磁盘写入数据,操作系统无权杀死进程,否则当磁盘写完时找不到进程,会引起数据丢失,这个进程的状态就叫做D状态。(想要杀死D进程只能关机)

(2)Z (zombie) 和 X(dead)

X死亡状态就是之前说的终止态,可以随时把它的资源回收掉。

僵尸进程(zombie)简介:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

僵尸进程是指先于父进程退出的子进程程序已经不再运行,但是因为需要保存退出原因,因此僵尸进程不会完全释放资源退出

它不会自动退出释放所有资源,也不会被kill命令再次杀死

僵尸进程会产生资源泄露,需要避免僵尸进程的产生采用进程等待(wait/waitpid)方式完成

Z (zombie)僵尸状态:

当一个Linux中的进程退出的时候,一般不会直接进入x状态(x是死亡状态,资源可以立马回收),而是进入Z状态。过程:进程退出后进入僵尸状态,必须被父进程/操作系统回收,父进程/操作系统会读取该进程的退出信息后,该进程才能进入X状态 释放。
为什么?进程为什么被创建出来呢? ?答:因为一定是要有任务让这个进程执行,当该进程退出的时候,我们怎么知道,这个进程把任务给我们完成的如何了呢? ?答:一般需要将进程的 执行结果 告知给父进程。进程Z,就是为了 维护退出信息(放在task_ struct中,进程的代码和数据可以释放了,但是task_ struct不可释放),可以让父进程或者OS读取的!如何读取的?:通过进程等待来进行读取的!如何等待?后面讲

 —如何模拟僵尸进程?

如果创建子进程,子进程退出了,父进程不退出,也不等待子进程,子进程退出之后所处的状态就是Z

例子:下面子进程退出后就是僵尸状态了,只是父进程没有管,所以子进程一直处于僵尸状态


—长时间僵尸,有什么问题? ?

 如果没有人回收子进程的僵尸,该状态会一直维护! 该进程的相关资源 (task_ struct) 不会被释放!导致内存泄漏!一般必须要求父进程进行回收(后面说!)

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

—僵尸进程必须使用waitpid/wait接口进行等待

僵尸进程会造成资源泄露,必须使用wait/waitpid接口进行等待处理

(3)孤儿进程

孤儿进程简介:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。(孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作,孤儿进程不会造成资源浪费

————————————————————————————————————————

 S+:带+是前台进程。S:不带+是后台进程。

后台进程 ctrl+c 终止不掉,只能kill -9 pid 杀掉

————————————————————————————————————————

孤儿进程是运行在后台的,孤儿进程退出不会成为僵尸进程,因此也不会资源泄露

父进程为什么没有Z呢?而是直接没了? ?
父进程没有Z是因为父进程的父进程是bash,他被bash回收了(我们上面写的子进程没被回收是因为父进程没有写waitpid进行回收)。

孤儿进程会被1号进程 (init进程) 领养:

如果父进程提前退出,子进程还在运行,子进程会被1号进程 (init进程) 领养! ! 孤儿进程不会造成资源浪费(1号进程就是操作系统!)

孤儿进程示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
	while (1)
	{
		sleep(1);
		printf("hello bit!\n");
	}
	pid_t id = fork();
	if (id == 0)
	{
		//child
		int cnt = 5;
		while (1)
		{
			printf("我是子进程,我还剩下 %d S\n", cnt--);
			sleep(1);
		}
		printf("我是子进程,我已经僵尸了, 等待被检测\n");
		exit(0);
	}
	else {
		//father
		int cnt = 3;
		while (cnt)
		{
			printf("我是父进程,我:%d\n", cnt--);
			sleep(1);
		}
		exit(0);
	}
}

(4)T和t

T(stopped) 常规暂停,跟阻塞没关系

kill -19 pid 是暂停命令(19号sigstop);kill -18 pid 是唤醒命令(19号sigcont是continue意思);

可以通过信号控制进程的起停:

 t (tracing stop ) :(tracing-追踪)也是暂停 进程被调试的时候,遇到断点所处的状态

3.守护进程/精灵进程

这两种是同一种进程的不同翻译,是特殊的孤儿进程,不但运行在后台,最主要的是脱离了与终端和登录会话的所有联系,也就是默默的运行在后台不想受到任何影响,并且退出后不会成为僵尸进程。

一般以服务器的方式工作,对外提供服务的服务器,都是以守护进程(精灵进程)的方式在服务器中工作的,一旦启动之后,除非用户主动关闭,否则,一直会在运行
守护进程特点:①父进程是系统。②

(1)介绍PGID,SID等各个名称

PPID:父进程的id。PID:进程本身的id。PGID:当前进程所属的进程组。(同组进程的第一个进程一般是进程组的组长)SID:当前进程的会话id。

 COMMAND:命令。TIME:运行的时间。UID:用户名映射后的数字,用户UID。

TPGID:当前进程组和终端相关的信息,-1 说明进程和终端无关系,非-1说明有关。

TTY:哪一个终端

(2)会话

用户登录时会建立一个会话,会话内部会有一个前台进程组 和 0个或者多个后台进程组,前台进程在会给我们加载bash。

在会话中启动一个进程组(可以只有一个进程),来完成某种任务,所有会话内的进程fork创建子进程,一般依旧属于当前会话! !
让某个后台进程脱离当前会话,让它独立的在计算机中形成一个新会话,自成进程组,这种进程叫做守护进程/精灵进程。

4.操作系统和Linux内核中的进程状态对应关系

表格:

        操作系统                        Linux内核中的进程状态

        运行状态                                R状态

        终止状态                                Z 或者 X状态

        进程阻塞                                S 或者 D状态

        进程挂起                                S 或 D 或 T状态

四.进程优先级

1.什么是优先级 优先级VS权限 (是什么)

优先级是进程获取资源的先后顺序
权限:说的是 能还是不能的问题
优先级:说的是 你能,只不过是先还是后

2.为何会存在优先级? (为什么)——资源不够!

排队的本质叫做确认优先级
系统里面永远都是, 进程占大多数,而资源是少数!——>进程竞争资源是常态!——> 一定需要确认先后

3.Linux下的优先级的相关概念和操作(怎么办) ps -la查看优先级

优先级由 priority+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 值是进程优先级的修正修正数据

ps -la查看优先级

优先级由 priority+nice 两个值构成

priority(优先级)默认优先级是80,数字越小,优先级越高;数字越大,优先级越低

nice:进程优先级的修正数据!

要更改进程优先级,需要更改不是pri,而是NI,nice

(3)修改优先级操作

普通用户不能随意修改进程优先级,只能超级用户修改,输入超级用户命令 sudo top 运行起来,再输入 r ,再输入进程的pid后回车,输入想改成的优先级的值。(优先级的值最小是-20,最大19。)

Linux不允许进程无节制的设置优先级,nice[-20, 19],prio [60,99]
优先级值 prio = prio_old + nice
每次设置优先级,这个 prio_old 优先级都会被恢复成为80,因为nice[-20, 19],所以prio [60,99]。

五.进程间切换(寄存器)

进程切换主要手段是通过上下文切换来完成的。

1.上下文切换:

CPU内部有大量寄存器,寄存器中会存在很多临时数据,这些 寄存器里面的临时数据 就叫当前进程的上下文;上下文存入和移出寄存器,也就是上下文保护和恢复的过程叫做上下文切换

寄存器VS寄存器里面的临时数据:

寄存器是一堆硬件设备。

2.竞争性与独立性,并行与并发

其他概念
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
进程运行具有独立性,不会因为一个进程挂掉或者异常,而导致其他进程出现问题!
进程:内核结构+代码和数据
如何做到进程具有独立性的呢? ?——进程地址空间

 
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

并行 解释::

如果存在多个CPU的情况,在任何一个时刻,都有可能有两个进程在同时被运行(就是在
CPU上运行),这样进程在不同CPU下同时运行 叫做并行

并发 解释:(切换的过程是操作系统中的一个软件调度器做的)

我的电脑是单CPU的,但是我的电脑中有各种进程都可以在跑啊

——多个进程都在你的系统中运行 != 多个进程都在你的系统中同时运行
不要以为进程一旦占有CPU,就会一直执行到结束,才会释放CPU资源!我们遇到的大部分操作系统都是分时的!
操作系统会给每一个进程, 在一次调度周期中, 赋予一个时间片轮转的概念! 例如:进程1给20ms(20ms就是进程1的时间片),进程2给30ms
在一个时间段内,多个进程都会通过切换交叉的方式,让多个进程代码,在一段时间内都得到推进,这种现象,我们叫做并发。(比如每个进程走20ms就切换,5个进程就是100ms,1秒内能每个进程执行10次,所以是多个进程都得到推进)

3.抢占式内核

操作系统,就是简单的根据队列来进行先后调度的吗??有没有可能突然来了一个优先级更高的进程??
——现在的计算机支持 抢占式内核!
解释:正在运行的低优先级进程,但如果来个优先级更高的进程,我们的调度器会直接把进程从CPU上剥离,放上优先级更高的进程,这就叫做进程抢占
        

进程的优先级和队列

假设有5种优先级: a. 操作系统内允许不同优先级进程的存在。b: 相同优先级的进程,是可能存在多个的!

队列不能头插,如果来了个优先级更高的进程,如何体现优先级呢?

假设有5种优先级,底层就要维护一个指针数组:task struct * queue[ 5 ],一共5个元素,每个元素代表一个优先级,从上往下:优先级最高的为0,优先级为1的,优先级为2的,优先级为3的,优先级为4的;每一个元素存的都是类型是 task struct * 的指针,这个指针链着一个队列,根据不同的优先级,将特定的进程放入不同的队列中,CPU再从上往下,由优先级高到低遍历调用不为空的队列:O(1)调度算法,比如下图中遍历,发现优先级为0的队列不为空,就首先从优先级为0的队列取头部的进程

了解内容:实际上操作系统会把上面的哈希表task struct * queue[ 5 ]做成位图形式,通过扫描位图去遍历;并且运行队列会实现两个这样的哈希表位图,操作系统先处理第一个叫active的位图,新增的进程放入第二个叫 old 的位图,最后处理完第一个位图后,swap(active,old),再集中式处理新增的这一批进程

 struct runqueue

{
        hash_ queue* active
        hash queue* old
}

在抢占式多任务处理中,进程被抢占时,哪些运行环境需要被保存下来?

抢占是多个进程抢占cpu运行自己的程序,所以要保存什么?要保存的是cpu中关于当前进程的数据,比如寄存器中要处理的数据或指令及地址,而全局变量是什么?是个变量,数据在内存中,跟进程切换毫无关系,所以不在保存的范围内

所以要保存的有:所有cpu寄存器的内容( cpu上正在处理的数据),页表指针(程序切换时会将页表起始地址加载到寄存器中),程序计数器(下一步程序要执行的指令地址)

4.寄存器

CPU内的寄存器是一堆存储器,是组件,功能是:可以临时的存储数据(非常少,但是非常重要!)
寄存器:①可见寄存器(是给我们用的寄存器,例如:eax,ebx,ecx,edx这些通用寄存器;ebp,esp,eip这些栈中的等)
②不可见寄存器(给操作系统用的寄存器)

当进程在被执行的过程中,一定会存在大量的临时数据,会暂存在CPU内的寄存器中!

寄存器上的数据的重要性:

生活中的例子:上学上到一半去当兵,所以要保留学籍,当兵完回来继续上学,就恢复学籍
我们把进程在运行中产生的各种寄存器数据,我们叫做进程的硬件上下文数据
当进程被剥离:需要保存上下文数据(保留学籍
当进程恢复的时候:需要将曾经保存的上下文数据恢复到寄存器中(恢复学籍
进程的上下文数据保存在哪里呢?——保存在进程的PCB中,linux下的PCB叫task struct

要准确区分:寄存器vs寄存器里面的数据!

寄存器只有一套,寄存器里面的数据可以有多份。(图书馆是一个CPU,寄存器就是图书馆的每一个座位,数据像同学和他的书包等东西,自习完了就把东西收拾走人,这个座位还可以让成百上千的同学使用)

猜你喜欢

转载自blog.csdn.net/zhang_si_hang/article/details/126486650