第三十天

2019-08-14

操作系统发展史

手工操作的穿孔卡片

  程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把程序和数据输入计算机内存,接着通过控制台开关启动程序针对数据运行;计算完毕,打印机输出计算结果;用户取走结果并卸下纸带(或卡片)后,才让下一个用户上机。

  手工操作方式两个特点:

  (1)用户独占全机。不会出现因资源已被其他用户占用而等待的现象,但资源的利用率低。
  (2)CPU 等待手工操作。CPU的利用不充分。
人机矛盾:手工操作的慢速度和计算机的高速度之间形成了尖锐矛盾,手工操作方式已严重损害了系统资源的利用率(使资源利用率降为百分之几,甚至更低),不能容忍。唯一的解决办法:只有摆脱人的手工操作,实现作业的自动过渡。这样就出现了成批处理。
 
 

批处理——磁带存储

主机与输入机之间增加一个存储设备——磁带,在运行于主机上的监督程序的自动控制下,计算机可自动完成:成批地把输入机上的用户作业读入磁带,依次把磁带上的用户作业读入主机内存并执行并把计算结果向输出机输出。完成了上一批作业后,监督程序又从输入机上输入另一批作业,保存在磁带上,并按上述步骤重复处理。

监督程序不停地处理各个作业,从而实现了作业到作业的自动转接,减少了作业建立时间和手工操作时间,有效克服了人机矛盾,提高了计算机的利用率。
但是,在作业输入和结果输出时,主机的高速CPU仍处于空闲状态,等待慢速的输入/输出设备完成工作: 主机处于“忙等”状态。

 

 

多道程序设计技术

  所谓多道程序设计技术,就是指允许多个程序同时进入内存并运行。即同时把多个程序放入内存,并允许它们交替在CPU中运行,它们共享系统中的各种硬、软件资源。当一道程序因I/O请求而暂停运行时,CPU便立即转去运行另一道程序。

分享图片

 

  在A程序计算时,I/O空闲, A程序I/O操作时,CPU空闲(B程序也是同样);必须A工作完成后,B才能进入内存中开始工作,两者是串行的,全部完成共需时间=T1+T2。

 

 

分享图片

 

将A、B两道程序同时存放在内存中,它们在系统的控制下,可相互穿插、交替地在CPU上运行:当A程序因请求I/O操作而放弃CPU时,B程序就可占用CPU运行,这样 CPU不再空闲,而正进行A I/O操作的I/O设备也不空闲,显然,CPU和I/O设备都处于“忙”状态,大大提高了资源的利用率,从而也提高了系统的效率,A、B全部完成所需时间<<T1+T2。

多道程序设计技术不仅使CPU得到充分利用,同时改善I/O设备和内存的利用率,从而提高了整个系统的资源利用率和系统吞吐量(单位时间内处理作业(程序)的个数),最终提高了整个系统的效率。

   单处理机系统中多道程序运行时的特点:
  (1)多道:计算机内存中同时存放几道相互独立的程序;
  (2)宏观上并行:同时进入系统的几道程序都处于运行过程中,即它们先后开始了各自的运行,但都未运行完毕;
  (3)微观上串行:实际上,各道程序轮流地用CPU,并交替运行。
分享图片
 

多道优点:

1.空间上的复用:

多个程序共用一套计算机硬件

2.时间上的复用(切换+保存状态)

①.当一个程序遇到IO操作,操作系统会剥夺该程序的CPU执行权限(提高了CPU的利用率,并且不影响程序的执行效率)

②.当一个程序长时间占用CPU操作系统也会剥夺该程序的CPU执行权限(降低程序的执行效率)

 

总结:

  程序员无法把所有的硬件操作细节都了解到,管理这些硬件并且加以优化使用是非常繁琐的工作,这个繁琐的工作就是操作系统来干的,有了他,程序员就从这些繁琐的工作中解脱了出来,只需要考虑自己的应用软件的编写就可以了,应用软件直接使用操作系统提供的功能来间接使用硬件。

细说的话,操作系统应该分成两部分功能:

 

#一:隐藏了丑陋的硬件调用接口,为应用程序员提供调用硬件资源的更好,更简单,更清晰的模型(系统调用接口)。
应用程序员有了这些接口后,就不用再考虑操作硬件的细节,专心开发自己的应用程序即可。
例如:操作系统提供了文件这个抽象概念,对文件的操作就是对磁盘的操作,有了文件我们无需再去考虑关于磁盘的读写控制
(比如控制磁盘转动,移动磁头读写数据等细节),
#二:将应用程序对硬件资源的竞态请求变得有序化 例如:很多应用软件其实是共享一套计算机硬件,比方说有可能有三个应用程序同时需要申请打印机来输出内容,

那么a程序竞争到了打印机资源就打印,然后可能是b竞争到打印机资源,也可能是c,这就导致了无序,
打印机可能打印一段a的内容然后又去打印c...,操作系统的一个功能就是将这种无序变得有序。

 

 

进程理论

进程和程序

程序:就是一坨代码

进程:就是正在运行的程序

 

多个进程执行时的进程调度

多个进程交替运行,操作系统必须对这些进程进行调度,这个调度也不是随机进行的,而是需要遵循一定的法则,于是有了进程的调度算法。

1.先来先服务:谁先来就给谁先服务,先来的程序先进行运行。

2.短作业优先调度:谁消耗的时间比较短,先运行谁。

3.时间片轮转法和4.多级反馈队列

分享图片

 

 

 

同步异步、阻塞非阻塞

同步异步:表示的是程序的运行状态

同步:任务提交之后,原地等待的任务执行并拿到返回结果才走,期间不做任何事情(在代码层面看就是卡住了)

异步:任务提交之后,不再原地等待,而是继续执行下一行代码(通过其它的方式拿到结果)

阻塞和非阻塞:表示的程序的运行状态

阻塞:阻塞态

非阻塞:就绪态,运行态

分享图片

(1)就绪(Ready)状态

  当进程已分配到除了CPU外的所有资源时,只要获得处理机便可以立即执行,这是的进程状态就是就绪态。

 (2)运行(Running)状态

  当进程已获得处理机制,其它程序正在处理机上执行,这时的进程状态被称为就绪态。

(3)阻塞(Blocked)状态

  正在执行的状态,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可能有很多种,比如等待I\O才做、申请缓冲区不能满足、等待信号等。

 分享图片

 

 

创建进程的两种方式

 multiprocess模块

multiprocess并不是一个模块,而是python中一个操作、管理进程的包。之所以叫mulit是取自multiple的多功能的意思,这个包几乎包含了和进程有关的所有子模块。

multiprocess.process模块

Process模块

process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建。

#使用process模块创建进程
from multiprocessing import Process import time def test(name): print(%s is running%name) print(%s is over%name) # windows创建进程一定要在if __name__ == ‘__main__‘:代码块内创建 否则报错
if __name__ == __main__: p = Process(target=test,args=(egon,))  #创建一个进程对象
    p.start()  # 告诉操作系统帮你创建一个进程
    time.sleep(1) print()

 

 

 

windows创建进程一定要在__main__中写,因为windows会将代码以模块的方式,从上往下执行一遍,而linux会直接将代码完完整整的拷贝一份。

#以继承Process类的形式开启进程的方式
from multiprocessing import Process import time class MyProcess(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print(%s is running% self.name) time.sleep(3) print(%s is over%self.name) if __name__ ==__main__: p = MyProcess(egon) p.start() print()

 

创建进程就是在内存中重新开辟一块内存空间,将允许产生的代码丢进去,一个进程对应在内存里的就是一块独立的内存空间。

#进程间的数据是隔离的
from multiprocessing import Process import time money = 100

def test(): global money  # 修改全局变量,通过这个来进行测试
    money = 99999

if __name__ == __main__: p = Process(target = test) p.start() p.join() print(money) >>>:100
#创建的子进程和主进程是两个独立的空间,代码块是在子进程中执行的,所以只能修改子进程空间中的变量,主进程空间不受到影响

 

 

 

进程对象以及一些方法

join方法

def f(name): print(hello,name) time.sleep(1) print(子进程在此) if __name__ == __main__: p = Process(target=f,args=(bob,)) p.start() p.join() print(父进程在此) #start()它起到的作用的作用是让操作系统帮你创建一个进程,至于什么时候创建,由操作系统随机决定 #当我们想让子进程先运行的时候我们可以用join,它的作用是让主进程等待子进程的运行结束

 

terminate和is_alive

from multiprocessing import Process,current_process import os import time def test(name): print(%s is running%name,子进程%s%s.getpid(),父进程%s%os.getppid) time.sleep(3) print(%s is over%name) if __name__ == __main__: p = Process(targs=test,args=(egon,)) p.start() p .termainate() # 杀死当前进程
    time.sleep(0.1) print(p.si_alive) time.sleep(0.1) print(p.is_alive)  # 判断是否存活
    print(,os.getpid(),主主进程%s%os.getppid())

 

守护进程

from multiprocessing import Process import time def test(name): print(%s总管正常活着%name) time.sleep(1) print(%s总管正常死亡%name) if __name__ == __main__: p = Process(target=test,args=(egon,)) #p.daemon = True # 守护进程,当主程序代码运行结束之后,守护进程随机终止
 p.start() time.sleep(0.1) print(皇帝jason寿终正寝)

 

僵尸进程和孤儿进程

僵尸进程:当进程结束之后还保留着一些信息以及它占用的PID,所有的进程都步入成僵尸进程。

  父进程回收子进程资源的两种方式

    1.join方法

    2.父进程正常死亡

孤儿进程:子进程没死,但父进程意外死亡

     针对linux会有儿童福利院(init)如果父进程意外死亡他创建的子进程都会被福利院收养

 

 

互斥锁

#写一个简单的抢票系统
from multiprocessing import Process,Lock import time import json #查询余票
def search(i): with open(data,r,encoding=utf-8) as f: data = f.read() t_d = json.loads(data) print(用户%s查询余票为:%s%(i,t_d.get(ticket))) #买票
def buy(i): with open(data,r,encoding=utf-8) as f: data = f.read() t_d = json.loads(data) time.sleep(1) if t_d.get(ticket) > 0: t_d[ticket] -= 1 with open(data,w,encoding=utf-8) as f: json.dump(t_d,f) print(用户%s抢票成功%i) else: print(没票了) def run(i): search(i) buy(i) if __name__ == __main__: for i in range(10):  # 生成10个子进程去抢票
        p = Process(target=run,args=(i,)) p.start() #这段代码运行之后会出现10个用户都抢到了票的情况,原因是每个用户拿的并不是一个实时的数据,所以在对数据进行修改时,所有人都在对拿到的数据进行修改,所以每个人都将自己拿到的票取了出来(程序中就是将数字减一)

#怎么避免这种情况
from multiprocessing import Process,Lock import time import json # 查票
def search(i): with open(data,r,encoding=utf-8) as f: data = f.read() t_d = json.loads(data) print(用户%s查询余票为:%s%(i,t_d.get(ticket))) # 买票
def buy(i): with open(data,r,encoding=utf-8) as f: data = f.read() t_d = json.loads(data) time.sleep(1) if t_d.get(ticket) > 0: # 票数减一
        t_d[ticket] -= 1
        # 更新票数
        with open(data,w,encoding=utf-8) as f: json.dump(t_d,f) print(用户%s抢票成功%i) else: print(没票了) def run(i,mutex): search(i) mutex.acquire() # 抢锁 只要有人抢到了锁 其他人必须等待该人释放锁
 buy(i) mutex.release() # 释放锁


if __name__ == __main__: mutex = Lock()  # 生成了一把锁
    for i in range(10): p = Process(target=run,args=(i,mutex)) p.start()

加锁之后,当一个进程拿到了这个数据的操作权限时,其它进程是无法对这个数据进行操作,甚至无法查看(就是当一个人抢到票在支付的时候其它人是看不到这张票的),其它进程只能等到这个进程的修改操作结束之后才能看到这个数据,然后也是要先抢到锁(所以一次只能有一个人),才能对数据进行修改,当这个进程结束对数据的操作要退出时,会释放锁,这时别的程序才能来抢锁,谁抢到谁来操作。