进程: 跑起来的程序、加载进内存的程序、动态的概念。
程序:代码指令、放在硬盘里、静态概念。
计算机cpu通过时分共享, 让一个进程只运行一个较小的时间片,然后切换到其他进程上,从而提供了多个虚拟cpu的假象。
这么做好处在:
可以多并发,可以让多个用户看起来都在==近乎同时==使用电脑,增强用户体验。即基本无延时体验。
但是,cpu共享逃不脱地让每个进程运行都慢一点。仔细想想,为什么?
要想实现进程间的切换需要一些底层机制,例如:上下文切换…
1.创建create
2.销毁destroy
3.等待wait
4.其他控制
5.状态status
进程创建过程
1.将磁盘中的【代码】+【静态数据】load进内存中,加载到进程的地址空间中。
注意:现代操作系统是【惰性】执行Load过程:仅在程序执行期间需要加载的代码或数据片才会加载。
2.分配栈和堆内存;
3.分配与输入输出相关的任务:进程的三个文件描述符,标准输入、输出和错误。
创建后的,进程【启动】。
功能:几乎克隆父进程
不同:返回值;父进程:返回子进程的PID;子进程:返回0
注意:子进程先运行完还是父进程先运行完,要看本身和OS的调度。
功能:阻塞当前进程
功能:让子进程执行与父进程不同的程序。
不同:他只是将父进程中的代码和静态数据覆盖为自己的,堆和栈也会重新初始化;其实是并没有创建新进程,而是直接运行当前的进程。
只有就绪和运行能相互可达:
就绪------调度--------> 运行
就绪<----时间片到-------就绪
举个例子:
Process0 需要从磁盘读取数据或者等待网络数据包,进程就会被blocked; OS发现Procees0不使用CPU并开始Process1;当Process1运行时,I/O完成,那么请问OS要切换回process0么?这涉及到什么?
OS的进程调度算法
答案是 【受限直接执行机制】
为什么要保持操作系统控制权?不放开权限给用户们的应用程序?
因为操作系统负责资源管理,如果没有控制权,一个进程可以简单地无限制运行接管机器,或访问没有权限访问的信息。这不就乱套了。
因此确保应用程序不做任何我们不希望他做的事,同时仍高效地运行它。
拆分概念
用户模式:用户态受限访问硬件资源。比如,不能发出I/O请求。
内核模式:内核态可访问全部资源。
【trap】陷阱指令:该指令使得可以跳入内核,并将特权级别提升到内核态。
完成后,OS调用【return-from-trap】指令,返回到发起调用的用户程序中,同时将特权级别降低,回到用户模式。
内核在启动的时会设置【陷阱表】.
答案是【不能】
这就是协作方式的问题,这种条件下只有一种方式:
重启计算机。
时钟每隔几毫秒产生中断,操作系统重新获得CPU控制权,然后调度算法决定接下来跑哪些应用进程。
以上的算法有个缺点,任务到来后必须要等待一段未知的运行时间。想想,你跑个程序,却要等别人跑一段时间后才轮到你,这互动性有多差。所有有以下算法,
基本思想很简单:RR在一个时间片(time slice,有时称为调度量子,scheduling quantum)内运行一个工作,然后切换到运行队列中的下一个任务,而不是运行一个任务直到结束。它反复执行,直到所有任务完成。因此,RR有时被称为时间切片(time-slicing)。请注意,时间片长度必须是时钟中断周期的倍数。因此,如果时钟中断是每10ms中断一次,则时间片可以是10ms、20ms或10ms的任何其他倍数。
那么如何优化?
● 规则1:如果A的优先级 > B的优先级,运行A(不运行B)。
● 规则2:如果A的优先级 = B的优先级,轮转运行A和B。
● 规则3:工作进入系统时,放在最高优先级(最上层队列)。
● 规则 4:一旦工作用完了其在某一层中的时间配额(无论中间主动放弃了多少次CPU),就降低其优先级(移入低一级队列)。
● 规则5:经过一段时间S,就将系统中所有工作重新加入最高优先级队列。
思考下,为什么要规则5?
设想下:
一个长工作,cpu密集型工作,在消耗几个时间片之后,优先级会降级再降低,但是假如仍旧还有很久的剩余时间才能完成,那么怎么办?那不得饿死?…
因此好处如下:
彩票调度算法
步骤:
1. 分配每个任务彩票数:A:100 B:50 D:250
2. 生成随机数,比如300
3. 设置一个柜面变量:counter = 0
4. 遍历任务列表,coutner += 任务X的tickets,只有counter >= 300者,中奖!跑它!
彩票货币(ticket currency)
这种方式允许拥有一组彩票的用户以他们喜欢的某种货币,将彩票分给自己的不同工作。之后操作系统再自动将这种货币兑换为正确的全局彩票。
彩票转让(ticket transfer)
通过转让,一个进程可以临时将自己的彩票交给另一个进程。
这种机制在客户端/服务端交互的场景中尤其有用,在这种场景中,客户端进程向服务端发送消息,请求其按自己的需求执行工作,为了加速服务端的执行,客户端可以将自己的彩票转让给服务端,从而尽可能加速服务端执行自己请求的速度。
服务端执行结束后会将这部分彩票归还给客户端。
彩票通胀(ticket inflation)
利用通胀,一个进程可以临时提升或降低自己拥有的彩票数量。当然在竞争环境中,进程之间互相不信任,这种机制就没什么意义。一个贪婪的进程可能给自己非常多的彩票,从而接管机器。但是,通胀可以用于进程之间相互信任的环境。在这种情况下,如果一个进程知道它需要更多CPU时间,就可以增加自己的彩票,从而将自己的需求告知操作系统,这一切不需要与任何其他进程通信。
缺点:
在单CPU系统中,存在多级的硬件缓存(hardware cache),一般来说会让处理器更快地执行程序。缓存是很小但很快的存储设备,通常拥有内存中最热的数据的备份。相比之下,内存很大且拥有所有的数据,但访问速度较慢。通过将频繁访问的数据放在缓存中,系统似乎拥有又大又快的内存。
程序第一次读取数据时,数据在内存中,因此需要花费较长的时间(可能数十或数百纳秒)。处理器判断该数据很可能会被再次使用,因此将其放入CPU缓存中。如果之后程序再次需要使用同样的数据,CPU会先查找缓存。因为在缓存中找到了数据,所以取数据快得多(比如几纳秒),程序也就运行更快。
缓存是基于局部性(locality)的概念,局部性有两种,即时间局部性和空间局部性。
时间局部性是指当一个数据被访问后,它很有可能会在不久的将来被再次访问,比如循环代码中的数据或指令本身。
空间局部性指的是,当程序访问地址为x的数据时,很有可能会紧接着访问x周围的数据,比如遍历数组或指令的顺序执行。
由于这两种局部性存在于大多数的程序中,硬件系统可以很好地预测哪些数据可以放入缓存,从而运行得很好。
核心冲突在:cpu修改之后的内容如果不及时写回内存,只放缓存。
例如,假设一个运行在CPU 1上的程序从内存地址A读取数据。由于不在CPU 1的缓存中,所以系统直接访问内存,得到值D。
程序然后修改了地址A处的值,只是将它的缓存更新为新值D’。将数据写回内存比较慢,因此系统(通常)会稍后再做。
假设这时操作系统中断了该程序的运行,并将其交给CPU 2,重新读取地址A的数据,由于CPU 2的缓存中并没有该数据,所以会直接从内存中读取,得到了旧值D,而不是正确的值D’。