目录
一、 进程调度
要想多个进程交替运行,操作系统必须对这些进程进行调度,这个调度也不是随即进行的,需要遵循一定法则,由此就有了进程的调度算法。
# 1 先来先服务
先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。FCFS算法比较有利于长作业(进程),而不利于短作业(进程)。由此可知,本算法适合于CPU繁忙型作业,而不利于I/O繁忙型的作业(进程)。
# 2 短作业优先
短作业(进程)优先调度算法(SJ/PF)是指对短作业或短进程优先调度的算法,该算法既可用于作业调度,也可用于进程调度。但其对长作业不利;不能保证紧迫性作业(进程)被及时处理;作业的长短只是被估算出来的。
# 3 时间片轮转
时间片轮转(Round Robin,RR)法的基本思路是让每个进程在就绪队列中的等待时间与享受服务的时间成比例。在时间片轮转法中,需要将CPU的处理时间分成固定大小的时间片,例如,几十毫秒至几百毫秒。如果一个进程在被调度选中之后用完了系统规定的时间片,但又未完成要求的任务,则它自行释放自己所占有的CPU而排到就绪队列的末尾,等待下一次调度。同时,进程调度程序又去调度当前就绪队列中的第一个进程。
# 4 多级反馈队列
前面介绍的各种用作进程调度的算法都有一定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程,而且如果并未指明进程的长度,则短进程优先和基于进程长度的抢占式调度算法都将无法使用。
而多级反馈队列调度算法则不必事先知道各种进程所需的执行时间,而且还可以满足各种类型进程的需要,因而它是目前被公认的一种较好的进程调度算法。在采用多级反馈队列调度算法的系统中,调度算法的实施过程如下所述。
(1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。
(2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。
(3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。
易语言
二、 僵尸进程与孤儿进程
#1 僵尸进程:进程结束了,资源还没来得及回收
#2 孤儿进程:主进程挂了,子进程还没结束,它就会被专门的进程接管
三、 进程对象及其他方法
# 1 windows:tasklist |findstr 进程id号
#windows:Ctrl+alt+delect ==cmd--》tasklist 查看进程id号
# 2 mac,Linux:ps aux | grep 进程id号
# 3 进程对象:t=Process(target=task, )或者是在进程内部:current_process()
# 4 t.pid或者current_process().pid 获取进程id号
# 5 os.getpid() 同上,获取进程id号
# 6 os.getppid() 获取父进程id号,子进程中获取父进程id,等于父进程的id号
# 7 t.is_alive()或者current_process().is_alive() 查看进程是否存活
# 8 t.terminate() 关闭进程,在主进程关闭
from multiprocessing import Process,current_process
import time
import os
# 每个进程都会有自己的id号pid
def task():
print('子进程')
# 当前进程的id号
print(current_process().pid)
print(os.getpid()) # 跟上面打印出来是一模一样的
# 取出该进程父id号
print('-----',os.getppid())
# current_process()当前进程对象
print(current_process().is_alive())
time.sleep(2)
print('子进程结束')
if __name__ == '__main__':
t = Process(target=task, )
t.start()
# t1 = Process(target=task, )
# t1.start()
# t.is_alive() # t这个进程是否存活
# print('主进程打印的结果',t.is_alive())
print(t.pid)
time.sleep(0.5)
# t.terminate() # 把t进程关闭
# time.sleep(0.1)
print('主进程打印的结果', t.is_alive())
四、守护进程
守护进程(子进程)会随着主进程结束而结束
from multiprocessing import Process,current_process
import time
import os
def task():
print(os.getpid())
print('子进程')
time.sleep(200)
print('子进程结束')
if __name__ == '__main__':
t = Process(target=task, )
# 守护进程:主进程一旦结束,子进程也结束
# t.daemon=True # 一定要加在启动之前
t.start()
time.sleep(1)
print('主进程结束')
五、互斥锁
进程之间数据不共享,但是同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是混乱,如何控制,就是加锁。
ticket.py文件内容:
{"ticket_count": 1}
# 同时只有一个人能拿到,必须释放,其他人才能再次获取到
from multiprocessing import Process, Lock
import json
import time
import random
def search():
# 查票的函数
# 打开文件,读出ticket_count
with open('ticket', 'r', encoding='utf-8') as f:
dic = json.load(f)
print('余票还有:', dic.get('ticket_count'))
def buy():
with open('ticket', 'r', encoding='utf-8') as f:
dic = json.load(f)
time.sleep(random.randint(1, 3)) # 模拟一下网络延迟
if dic.get('ticket_count') > 0:
# 能够买票
dic['ticket_count'] -= 1
# 保存到文件中去
with open('ticket', 'w', encoding='utf-8') as f:
json.dump(dic, f)
print('买票成功')
else:
# 买票失败
print('买票失败')
# 写一个函数,先查票,再买票
def task(mutex):
search()
# 买票过程要加锁
# 买前加锁
# mutex.acquire()
# buy() # 10个进程变成了串行执行
# # 买后释放锁
# mutex.release()
with mutex:
buy()
if __name__ == '__main__':
# 锁的创建,在哪?主进程创建锁
mutex = Lock() # 创建一把锁
# 模拟十个人买票(开10个进程)
for i in range(10):
t = Process(target=task, args=(mutex,))
t.start()
六、 队列介绍
创建共享是进程队列,Queue多进程安全的队列,可以使用Queue实现多进程之间数据传递,大白话总结一下就是,队列支持多个人从队列的一端存放数据,同样支持多个人从队列的一端取数据
# 基本用法
from multiprocessing import Queue
# 实例化得到要给对象
q=Queue(5) # 默认很大,可以放很多,写了个5,只能放5个
# 往管道中放值
q.put(1)
q.put('lqz')
q.put(18)
q.put(19)
# q.put(20)
# q.put(21)
# q.put_nowait(100)
# 从管道中取值
# print(q.get())
# print(q.get())
# print(q.get())
# print(q.get(timeout=100)) # 等0.1s还没有值,就结束
# print(q.get_nowait()) # 不等了,有就是有,没有就没有
print(q.empty()) # 看一下队列是不是空的
print(q.full()) # 看一下队列是不是满的
# 总结:
'''
q=Queue(队列大小)
# 放值
q.put(asdf)
q.put_nowait(asdf) # 队列满了,放不进去就不放了,报错
# 取值
q.get() # 从队列头部取出一个值
q.get_nowait() # 从队列头部取值,没有就抛错
# 队列是否为空,是否满
print(q.empty()) # 看一下队列是不是空的
print(q.full()) # 看一下队列是不是满的
'''
七、 IPC机制(进程间通信)
我们知道进程之间数据是相互隔离的,要想实现进程间的数据通信IPC机制,就必须借助于一些技术才行,比如:multiprocess模块中的:对列和管道,这两种方式都能实现进程之间的数据传输,由于队列是管道+锁 的方式,所以我们着重研究队列即可。
# Inter-Process Communication,进程间通信
from multiprocessing import Process, current_process, Queue
import time
import os
def task1(q):
print('我是task1进程,我的id号是:%s'%os.getpid())
q.put('lqz is handsome')
def task2(q):
# res=q.get()
# print('我是task2进程,我的id号是:%s'%os.getpid(),res)
print('我是task2进程,我的id号是:%s'%os.getpid())
if __name__ == '__main__':
q = Queue(5)
t1 = Process(target=task1, args=(q,))
t1.start()
t2 = Process(target=task2, args=(q,))
t2.start()
print(q.get())
八、面向对象高级:魔法方法(_ _ 开头的),_ _ enter_ _ 和 _ _ exit_ _,上下文管理器
# 自己写一个类,实现类似于打开文件 with open 的功能
# with MyClass('文件名','方式','编码') as f:
# f.read()
#在这写代码,f就关闭了
ticket.py文件内容:
{"ticket_count": 0}
class MyClass():
def __init__(self,file_name,mode,encoding):
self.file_name=file_name
self.mode=mode
self.encoding=encoding
def __enter__(self):
print('只要有with,就会执行我')
self.file=open(self.file_name,self.mode,encoding=self.encoding)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
# 只要顶格写代码,就会执行我
print('只要顶格写代码,就会执行我')
self.file.close()
with MyClass('ticket','r','utf-8') as f:
print(f.read())
print('xxss')
print("sdfadasf")
# a=MyClass('ticket','r','utf-8')