操作系统为程序员操作硬件提供了接口。程序员不需要直接写程序操作硬件,只需要按照一定的规范,把数据提交给操作系统,操作系统回去操作硬盘,CPU和内存
"特点"
没有操作系统的概念,所有程序都是直接操控硬件
"工作过程"
程序员预约一段时间,在这段时间一个程序员独享计算机,其他人等待
"优点"
程序员在申请的时间内独享资源,可以及时调整自己的程序
"缺点"
浪费资源,一段时间内只有一个人使用
"工作过程"
多个程序员的代码放在一起进行提交,然后机器顺序计算,得到多个程序员代码的输出
"优点"
批处理代码,不再是一个程序员独享计算机,节省了时间
"缺点"
1.整个流程需要人的参与,把多个程序员的代码在机器之间搬动,进行执行和得到输出
2.计算机仍然是顺序执行的
3.程序员不能独享计算机,不能即使调试得到结果,需要等到大家都提交完成,一起处理后得到结果,有问题不能立刻处理
"解决第二代计算机中需要人参与的问题"
使用SPOOLING技术,就不需要人在中间搬动磁带了
"解决第二代计算机顺序执行的问题"
多道技术
多道技术中的多道指的是多个程序,多道技术的实现是为了解决多个程序竞争或共享一个CPU的有序调度问题,解决方式是多路复用,分为时间上的复用和空间上的复用
"空间上的复用:"将内存分为几部分,每部分放入一个程序,这样,同一时间内存中就有了多个程序
"空间上复用的问题:"程序之间的内存必须是分割开的,这种分割需要在硬件层面操作,由操作系统控制。如果内存彼此不分割,则一个程序可以访问另外一个程序的内存。
首先丧失的是安全性,如qq程序可以访问操作系统的内存,这就拿到了操作系统的权限。
其次丧失的是稳定性,如果qq程序退出,可能会导致操作系统的程序退出。
"时间上的复用:"当一个程序在等待I/O时,或一个程序运行了一段时间,CPU就切换,运行其他程序。
"解决单个程序员不能独享计算机的问题"
"分时操作系统:"多个终端+多道技术,可以多个程序员连接计算机,操作系统采用多道的技术,处理多个程序员的任务。所有的用户以为自己独享了计算机资源。
第三代计算机广泛采用了必须的保护硬件(程序之间的内存彼此隔离后),分时系统才开始流行。
一:操作系统的作用
1.隐藏复杂的硬件接口,提供良好的抽象接口。
2.管理、调度进程,并且将多个进程多硬件的竞争变得有序。
二:多道技术
1.产生背景:针对单核,实现并发
现在的主机一般是多核,多核都会使用多道技术
有4个CPU,运行在cpu1上的某个程序遇到io阻塞,会等到io结束再重新调度,对被调度到4个cpu中的任意一个,具体由操作系统的调度算法决定。
2.空间上的复用:内存中同时存在多个程序,需要硬件支持,将多个程序的内存空间分隔开来。
3.时间上的复用:复用一个cpu时间片。
如果程序在进行io处理,或一个程序占用cpu较长时间,就会吧进程的状态保存下来,去处理别的程序。下次CPU切换回来,继续上次的位置,继续运行。
进程是正在运行的代码程序。负责运行程序,执行任务的是CPU。
程序仅仅是一堆代码
进程是程序的运行过程,运行起来的代码会在操作系统中产生一个进程
同一个程序运行两次,是两个进程。
无论是并发还是并行,在用户看来都是'同时'运行的
不管是进程还是线程,都只是一个任务而已,真正做事的是CPU,但同一个CPU在同一时刻只能执行一个任务
"并发":是伪并行,即看起来是同时运行。单个cpu+多道技术可以实现并发
例如:你可能正在学习,但突然有人找你聊天,你可能聊一会,看一会视频,等对方有回复了,你在回复。你就做到了做两件事,看似同时进行,但实际上你是一会学习,一会回复,在同一时间实际只做了一件事
"并行":同时运行,需要具备多个CPU才能实现并行。
单核下,可以使用多道技术,多核下,每个CPU也可以使用多道技术。
例如:有四个CPU,6个任务,在同一时间有四个任务在执行,分别分配给了cpu0,cpu1,cpu2,cpu3
如果cpu0上的任务遇到I/O,cpu0就会处理任务5,这就是单核下的多道技术
如果cpu0上的I/O结束了,那么操作系统会重新进行分配,决定调度给那个CPU是操作系统算法和任务优先级决定的。
"进程创建的四种形式"
1.启动操作系统,会创建一些进程。linux中ps -aux查看进程,windows中tasklist查看
2.一个进程在运行过程中开启子进程。(os.fork,subprocess.Popen都可以开启子进程)
3.windows上用户双击某个软件,Linux中启动某个服务,都可以创建一个进程。
4.批处理作业的初始化(只在大型机的批处理系统中应用。)
"linux与Windows中的进程的创建"
1.Windows中调用CreateProcess来创建进程。
2.Unix中调用fork,fork会创建一个与父进程一模一样的副本
"两者的相同与不同"
相同的是:进程创建后,父进程和子进程有个字不同的地址空间(多道技术要求进程之间内存是相互隔离的),任何一个进程中的数据要和其他进程的数据相互隔离,互不影响。
不同的是:在Unix中,子进程的初始地址空间是父进程的一个副本,子进程中的数据来源于父进程,与父进程中的数据是完全相同的,但windows中子进程与父进程并不是完全相同。
1.程序正常退出
2.报错退出
3.被kill掉
无论UNIX还是Windows,进程只有一个父进程,不同的是
1.Unix中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号,该信号会被发送给与键盘相关的进程组中的所有成员。
2.在windows中,没有进程层次的概念,所有的进程地位相同,唯一类似于进程层次的,是在创建进程时,父进程会得到一个特别的令牌(称为句柄),该句柄可以用于控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。
运行中的进程被调度到就绪态的原因
1.进程自身遇到I/O阻塞,便要让出CPU让其他进程去执行,这样就保证了CPU一直爱工作
2.与进程无关,操作系统原因,如果一个进程占用时间过多,或者优先级原因,而调用其他进程去使用CPU
进程并发的实现在于,硬件中断一个正在运行的进程,会把该进程的状态信息进行保存,为此,操作系统维护一个表格,即进程表,每个进程占用一个进程表项
该表存放了进程状态的重要信息,这样当CPU再次调用时,能从该进程的上次终端位置继续执行,就好像没有中断过一样。
python中的"多线程"是无法利用多核优势的
python中的"多进程"是可以充分的利用多核CPU的资源,可以使用os.cpu_count()来查看cpu数
python中使用multiprocessing模块来开启多个子进程,使用threading来开启多个线程。
multiprocessing模块功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process,Queue,Pipe,Lock等组件。
"进程与线程不同,进程没有任何共享状态,进程修改的数据,改动的仅限于该进程内。"
Process([group [, target [, name [, args [, kwargs]]]]])
由该类实例化得到的对象,可用来开启一个子进程
"注意"
1.需要使用关键字的方式来制定参数
2.args指定的是传给target函数的位置参数,是一个元组形式,设置方式args=("a",2)
3.kwargs指定的是传给target函数的位置参数,是一个字典的形式,设置方式kwargs={a:2,b:"test"}
"参数介绍"
group:未使用,为None
target:表示调用对象,即子进程要执行的任务,指向一个函数
args:表示target函数的位置参数,元组形式,args=(1,2,'vita',)
kwargs:表示target函数的位置参数,字典形式,kwargs={'name':'vita','age':18}
name:表示子进程的名称
"方法介绍"
p.start():启动进程,并调用该子进程中的run()方法
p.run():进程启动时运行的方法,正是该方法去调用target指定的函数,自定义的继承自Process的类,需要实现该方法
p.terminate():强制终止进程p,不会做任何的清理工作,
如果p创建了子进程,该子进程就成了僵尸进程
如果p还保存了一个锁,那么锁也不会被释放,进而导致死锁
p.is_alive():如果p在运行,就返回True
p.join([timeout]):主线程等待p终止
"注意"是主线程处于等待状态,而p处于运行状态
"属性介绍"
p.daemon:默认为False,
如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止
设置为True后,p不能创建自己的新进程,必须在p.start之后设置
p.name:进程的名称
p.pid:进程的pid
注意:在windows中Process()必须放到# if __name__ == '__main__':下
"方式一"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process
def target_func(name):
print("子进程:",name)
if __name__ == '__main__':
# 实例化两个对象,开启两个子进程
p1=Process(name="p1",target=target_func,args=("p1",))# 这里一定要加',',否则TypeError: target_func() takes 1 positional argument but 2 were given
p2=Process(name="p2",target=target_func,args=("p2",))
p1.start()
p2.start()
print("主进程") #会发现创建了子进程,主进程依旧在继续运行,所以先输出了主进程的内容,然后运行子进程,
D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
主进程
子进程: p1
子进程: p2
Process finished with exit code 0
"方式二"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process
# 这里要继承Process
class Func(Process):
# 重用父类中的init方法,并加入新参数
def __init__(self,name):
super().__init__()
self.nm=name
# 一定要写run 方法,start方法会自动调用run方法
def run(self): #
print("子进程",self.nm)
if __name__ == '__main__':
# 实例化两个对象,开启两个子进程
func = Func("p1")
func.start()
func1 = Func("p2")
func1.start()
print("主进程")
D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
主进程
子进程 p1
子进程 p2
Process finished with exit code 0
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process
import os
# 这里要继承Process
class Func(Process):
def __init__(self,name):
super().__init__()
self.nm=name
def run(self): #
# 子进程的ppid就是main的pid
print("子进程:%s,pid:%s,ppid:%s"%(self.nm,os.getpid(),os.getppid()))
if __name__ == '__main__':
func = Func("p1")
func.start()
func1 = Func("p2")
func1.start()
# 主进程的ppid就是你运行py文件的程序的进程号,
# 如果使用pycharm运行,就是pycharm的进程号
# 如果使用cmd终端运行,就是终端的进程号
print("主进程:,pid:%s,ppid:%s"%(os.getpid(),os.getppid()))
D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
主进程:,pid:6100,ppid:11200
子进程:p1,pid:8252,ppid:6100
子进程:p2,pid:10784,ppid:6100
Process finished with exit code 0
在主进程运行过程中,可以开启子进程,主进程与子进程的任务分为
1.在主进程的任务与子进程的任务彼此独立时,主进程先执行完成,还要等待子进程执行完成后才统一回收资源
2.如果主进程的任务需要在执行到某个阶段,需要等待子进程执行完成后继续执行,就可使用join()
"join"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process
import os
import time
# 这里要继承Process
class Func(Process):
def __init__(self,name):
super().__init__()
self.nm=name
def run(self): #
print("子进程:%s is running"%(self.nm))
time.sleep(3)
print("子进程:%s is finishing" % (self.nm))
if __name__ == '__main__':
p_list=[]
p1 = Func("p1")
# 只是发送信号给操作系统,治愈操作系统何时执行,还要看系统内部的调用
p1.start()
p_list.append(p1)
p2 = Func("p2")
p2.start()
p_list.append(p2)
# 方法哦一个列表中,for循环来执行join
for p in p_list:
p.join()
print("主进程运行完成")
D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
子进程:p1 is running
子进程:p2 is running
子进程:p1 is finishing
子进程:p2 is finishing
主进程运行完成
Process finished with exit code 0
进程执行了start()后,信号就已经发送给了操作系统,操作系统会根据自己的调度规则,进行运行
join()是让主进程等待多个子进程的运行,此时所有的子进程还是并发执行的,
所有的子进程的运行时间是运行时间最长的子进程的运行时间。当所有子进程运行完成后,继续执行主进程的代码
"p.terminate(),p.is_alive,p.name,p.pid"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process
import os
import time
# 这里要继承Process
class Func(Process):
def __init__(self,name):
super().__init__()
self.nm=name
def run(self): #
print("子进程:%s is running"%(self.nm))
time.sleep(3)
print("子进程:%s is finishing" % (self.nm))
if __name__ == '__main__':
p1 = Func("p1")
# 只是发送信号给操作系统,至于操作系统何时执行,还要看系统内部的调用
p1.start()
# 执行了terminate(),就没有执行子进程的代码了
p1.terminate()
# 执行terminate()也是发送给操作系统一个进程终止信号,但不会立刻终止
print(p1.is_alive())
print("主进程")
print("子进程名:%s,子进程pid:%s" %(p1.name,p1.pid))
time.sleep(2)
# 休息2秒后,才终止
print(p1.is_alive())
D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
True
主进程
子进程名:Func-1,子进程pid:14768
False
Process finished with exit code 0
总共有主进程+开启的子进程
"进程间的数据是相互隔离的,所以主进程仍然输出100,子进程是0"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from multiprocessing import Process
def work():
global n
n=0
print('子进程内: ',n)
if __name__ == '__main__':
n = 100 # 在windows系统中应该把全局变量定义在if __name__ == '__main__'之上就可以了
p=Process(target=work)
p.start()
p.join()
print('主进程内: ',n)
D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
子进程内: 0
主进程内: 100
Process finished with exit code 0
"server"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from socket import *
from multiprocessing import Process
def talk(conn):
while True:
try:
data = conn.recv(1024)
if not data: break
conn.send(data.upper())
except ConnectionResetError:
break
conn.close()
def server(ip,port):
server = socket(AF_INET, SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.bind((ip,port))
server.listen(5)
while True:
conn, addr = server.accept()
p = Process(target=talk, args=(conn,))
p.start()
server.close()
if __name__ == '__main__':
server('127.0.0.1', 8080)
"client"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
如果客户端过多,服务端就会开启很多个子进程,会耗尽资源
1、改写下列程序,分别别实现下述打印效果
from multiprocessing import Process
import time
import random
def task(n):
time.sleep(random.randint(1,3))
print('-------->%s' %n)
if __name__ == '__main__':
p1=Process(target=task,args=(1,))
p2=Process(target=task,args=(2,))
p3=Process(target=task,args=(3,))
p1.start()
p2.start()
p3.start()
print('-------->4')
效果一:保证最先输出-------->4
"上面的代码不做更改,就是首先输出4"
-------->4
-------->1
-------->3
-------->2
效果二:保证最后输出-------->4
"p1.join()
p2.join()
p3.join()
这样就能让主进程等待子进程执行完成后,继续执行主进程,就可最后输出4"
-------->2
-------->3
-------->1
-------->4
效果三:保证按顺序输出
" p1.start()
p1.join()
p2.start()
p2.join()
p3.start()
p3.join()"
-------->1
-------->2
-------->3
-------->4
2、判断上述三种效果,哪种属于并发,哪种属于串行?
"前两种是并发执行,最后一个是串行"
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。