操作系統(tǒng)介紹
- 程序員無(wú)法把所有的硬件操作細(xì)節(jié)都了解到既穆,管理這些硬件并且加以優(yōu)化使用是非常繁瑣的工作准脂,這個(gè)繁瑣的工作就是操作系統(tǒng)來(lái)干的饿悬,有了他刹枉,程序員就從這些繁瑣的工作中解脫了出來(lái)叽唱,只需要考慮自己的應(yīng)用軟件的編寫就可以了,應(yīng)用軟件直接使用操作系統(tǒng)提供的功能來(lái)間接使用硬件微宝。
- 操作系統(tǒng)就是一個(gè)協(xié)調(diào)尔觉、管理和控制計(jì)算機(jī)硬件資源和軟件資源的控制程序。
- 隱藏了丑陋的硬件調(diào)用接口芥吟,為應(yīng)用程序員提供調(diào)用硬件資源的更好侦铜,更簡(jiǎn)單,更清>晰的模型(系統(tǒng)調(diào)用接口)钟鸵。
- 將應(yīng)用程序?qū)τ布Y源的競(jìng)態(tài)請(qǐng)求變得有序化
操作系統(tǒng)發(fā)展史
-
第一代計(jì)算機(jī)(1940~1955):真空管和穿孔卡片
-
第二代計(jì)算機(jī)(1955~1965):晶體管和批處理系統(tǒng)
-
第三代計(jì)算機(jī)(1965~1980):集成電路芯片和多道程序設(shè)計(jì)
-
多道技術(shù):
多道技術(shù)中的多道指的是多個(gè)程序钉稍,多道技術(shù)的實(shí)現(xiàn)是為了解決多個(gè)程序競(jìng)爭(zhēng)或者說(shuō)共享同一個(gè)資源(比如cpu)的有序調(diào)度問(wèn)題,解決方式即多路復(fù)用棺耍,多路復(fù)用分為時(shí)間上的復(fù)用和空間上的復(fù)用贡未。
- 空間上的復(fù)用:將內(nèi)存分為幾部分,每個(gè)部分放入一個(gè)程序蒙袍,這樣俊卤,同一時(shí)間內(nèi)存中就有了多道程序。
- 時(shí)間上的復(fù)用:當(dāng)一個(gè)程序在等待I/O時(shí)害幅,另一個(gè)程序可以使用cpu消恍,如果內(nèi)存中可以同時(shí)存放足夠多的作業(yè),則cpu的利用率可以接近100%
-
-
第四代計(jì)算機(jī)(1980~至今):個(gè)人計(jì)算機(jī)
操作系統(tǒng)總結(jié)
- 即使可以利用的cpu只有一個(gè)(早期的計(jì)算機(jī)確實(shí)如此)以现,也能保證支持(偽)并發(fā)的能力狠怨。將一個(gè)單獨(dú)的cpu變成多個(gè)虛擬的cpu(多道技術(shù):時(shí)間多路復(fù)用和空間多路復(fù)用+硬件上支持隔離),沒有進(jìn)程的抽象邑遏,現(xiàn)代計(jì)算機(jī)將不復(fù)存在佣赖。
一 操作系統(tǒng)的作用:
1:隱藏丑陋復(fù)雜的硬件接口,提供良好的抽象接口
2:管理记盒、調(diào)度進(jìn)程憎蛤,并且將多個(gè)進(jìn)程對(duì)硬件的競(jìng)爭(zhēng)變得有序
二 多道技術(shù):
1.產(chǎn)生背景:針對(duì)單核,實(shí)現(xiàn)并發(fā)
ps:
現(xiàn)在的主機(jī)一般是多核纪吮,那么每個(gè)核都會(huì)利用多道技術(shù)
有4個(gè)cpu俩檬,運(yùn)行于cpu1的某個(gè)程序遇到io阻塞栏豺,會(huì)等到io結(jié)束再重新調(diào)度,會(huì)被調(diào)度到4個(gè)
cpu中的任意一個(gè)豆胸,具體由操作系統(tǒng)調(diào)度算法決定奥洼。
2.空間上的復(fù)用:如內(nèi)存中同時(shí)有多道程序
3.時(shí)間上的復(fù)用:復(fù)用一個(gè)cpu的時(shí)間片
強(qiáng)調(diào):遇到io切,占用cpu時(shí)間過(guò)長(zhǎng)也切晚胡,核心在于切之前將進(jìn)程的狀態(tài)保存下來(lái)灵奖,這樣才能保證下次切換回來(lái)時(shí),能基于上次切走的位置繼續(xù)運(yùn)行
進(jìn)程
- 進(jìn)程:正在進(jìn)行的一個(gè)過(guò)程或者說(shuō)一個(gè)任務(wù)估盘。而負(fù)責(zé)執(zhí)行任務(wù)則是cpu瓷患。
- 進(jìn)程與程序的區(qū)別
- 程序僅僅只是一堆代碼而已,而進(jìn)程指的是程序的運(yùn)行過(guò)程遣妥。
需要強(qiáng)調(diào)的是:同一個(gè)程序執(zhí)行兩次擅编,那也是兩個(gè)進(jìn)程
-
并發(fā)與并行
- 并發(fā):是偽并行,即看起來(lái)是同時(shí)運(yùn)行箫踩。單個(gè)cpu+多道技術(shù)就可以實(shí)現(xiàn)并發(fā)
- 并行:同時(shí)運(yùn)行爱态,只有具備多個(gè)cpu才能實(shí)現(xiàn)并行
-
進(jìn)程的創(chuàng)建
新進(jìn)程的創(chuàng)建都是由一個(gè)已經(jīng)存在的進(jìn)程執(zhí)行了一個(gè)用于創(chuàng)建進(jìn)程的系統(tǒng)調(diào)用而創(chuàng)建的:
在UNIX中該系統(tǒng)調(diào)用是:fork,fork會(huì)創(chuàng)建一個(gè)與父進(jìn)程一模一樣的副本境钟,二者有相同的存儲(chǔ)映像锦担、同樣的環(huán)境字符串和同樣的打開文件(在shell解釋器進(jìn)程中,執(zhí)行一個(gè)命令就會(huì)創(chuàng)建一個(gè)子進(jìn)程)
在windows中該系統(tǒng)調(diào)用是:CreateProcess慨削,CreateProcess既處理進(jìn)程的創(chuàng)建洞渔,也負(fù)責(zé)把正確的程序裝入新進(jìn)程。
關(guān)于創(chuàng)建的子進(jìn)程缚态,UNIX和windows
1.相同的是:進(jìn)程創(chuàng)建后磁椒,父進(jìn)程和子進(jìn)程有各自不同的地址空間(多道技術(shù)要求物理層面實(shí)現(xiàn)進(jìn)程之間內(nèi)存的隔離),任何一個(gè)進(jìn)程的在其地址空間中的修改都不會(huì)影響到另外一個(gè)進(jìn)程玫芦。
2.不同的是:在UNIX中浆熔,子進(jìn)程的初始地址空間是父進(jìn)程的一個(gè)副本,提示:子進(jìn)程和父進(jìn)程是可以有只讀的共享內(nèi)存區(qū)的姨俩。但是對(duì)于windows系統(tǒng)來(lái)說(shuō)蘸拔,從一開始父進(jìn)程與子進(jìn)程的地址空間就是不同的师郑。
-
進(jìn)程的終止
- 正常退出(自愿环葵,如用戶點(diǎn)擊交互式頁(yè)面的叉號(hào),或程序執(zhí)行完畢調(diào)用發(fā)起系統(tǒng)調(diào)用正常退出宝冕,在linux中用exit张遭,在windows中用ExitProcess)
- 出錯(cuò)退出(自愿,python a.py中a.py不存在)
- 嚴(yán)重錯(cuò)誤(非自愿地梨,執(zhí)行非法指令菊卷,如引用不存在的內(nèi)存缔恳,1/0等,可以捕捉異常洁闰,try...except...)
- 被其他進(jìn)程殺死(非自愿歉甚,如kill -9)
-
進(jìn)程的層次結(jié)構(gòu)
- 無(wú)論UNIX還是windows,進(jìn)程只有一個(gè)父進(jìn)程扑眉,不同的是:
- 在UNIX中所有的進(jìn)程纸泄,都是以init進(jìn)程為根,組成樹形結(jié)構(gòu)腰素。父子進(jìn)程共同組成一個(gè)進(jìn)程組聘裁,這樣,當(dāng)從鍵盤發(fā)出一個(gè)信號(hào)時(shí)弓千,該信號(hào)被送給當(dāng)前與鍵盤相關(guān)的進(jìn)程組中的所有成員衡便。
- 在windows中,沒有進(jìn)程層次的概念洋访,所有的進(jìn)程都是地位相同的镣陕,唯一類似于進(jìn)程層次的暗示,是在創(chuàng)建進(jìn)程時(shí)姻政,父進(jìn)程得到一個(gè)特別的令牌(稱為句柄),該句柄可以用來(lái)控制子進(jìn)程茁彭,但是父進(jìn)程有權(quán)把該句柄傳給其他子進(jìn)程,這樣就沒有層次了扶歪。
- 無(wú)論UNIX還是windows,進(jìn)程只有一個(gè)父進(jìn)程扑眉,不同的是:
-
進(jìn)程的狀態(tài)
-
進(jìn)程并發(fā)的實(shí)現(xiàn)
-
進(jìn)程并發(fā)的實(shí)現(xiàn)在于理肺,硬件中斷一個(gè)正在運(yùn)行的進(jìn)程,把此時(shí)進(jìn)程運(yùn)行的所有狀態(tài)保存下來(lái)善镰,為此妹萨,操作系統(tǒng)維護(hù)一張表格,即進(jìn)程表(process table)炫欺,每個(gè)進(jìn)程占用一個(gè)進(jìn)程表項(xiàng)(這些表項(xiàng)也稱為進(jìn)程控制塊)
該表存放了進(jìn)程狀態(tài)的重要信息:程序計(jì)數(shù)器乎完、堆棧指針、內(nèi)存分配狀況品洛、所有打開文件的狀態(tài)树姨、帳號(hào)和調(diào)度信息,以及其他在進(jìn)程由運(yùn)行態(tài)轉(zhuǎn)為就緒態(tài)或阻塞態(tài)時(shí)桥状,必須保存的信息帽揪,從而保證該進(jìn)程在再次啟動(dòng)時(shí),就像從未被中斷過(guò)一樣辅斟。
-
multiprocessing模塊
multiprocessing模塊介紹
python中的多線程無(wú)法利用多核優(yōu)勢(shì)转晰,如果想要充分地使用多核CPU的資源(os.cpu_count()查看),在python中大部分情況需要使用多進(jìn)程。
Python提供了multiprocessing查邢。 multiprocessing模塊用來(lái)開啟子進(jìn)程蔗崎,并在子進(jìn)程中執(zhí)行我們定制的任務(wù)(比如函數(shù)),該模塊與多線程模塊threading的編程接口類似扰藕。multiprocessing模塊的功能眾多:支持子進(jìn)程缓苛、通信和共享數(shù)據(jù)、執(zhí)行不同形式的同步邓深,>提供了Process他嫡、Queue、Pipe庐完、Lock等組件钢属。
需要再次強(qiáng)調(diào)的一點(diǎn)是:與線程不同,進(jìn)程沒有任何共享狀態(tài)门躯,進(jìn)程修改的數(shù)據(jù)淆党,改動(dòng)僅限于該進(jìn)程內(nèi)。
Process類的介紹
Process([group [, target [, name [, args [, kwargs]]]]])讶凉,由該類實(shí)例化得到的對(duì)象染乌,可用來(lái)開啟一個(gè)子進(jìn)程
強(qiáng)調(diào):
1. 需要使用關(guān)鍵字的方式來(lái)指定參數(shù)
2. args指定的為傳給target函數(shù)的位置參數(shù),是一個(gè)元組形式懂讯,必須有逗號(hào)
參數(shù)介紹:
group參數(shù)未使用荷憋,值始終為None
target表示調(diào)用對(duì)象,即子進(jìn)程要執(zhí)行的任務(wù)
args表示調(diào)用對(duì)象的位置參數(shù)元組褐望,args=(1,2,'egon',)
kwargs表示調(diào)用對(duì)象的字典,kwargs={'name':'egon','age':18}
name為子進(jìn)程的名稱
方法介紹:
p.start():?jiǎn)?dòng)進(jìn)程勒庄,并調(diào)用該子進(jìn)程中的p.run()
p.run():進(jìn)程啟動(dòng)時(shí)運(yùn)行的方法,正是它去調(diào)用target指定的函數(shù)瘫里,我們自定義類的類中一定要實(shí)現(xiàn)該方法
p.terminate():強(qiáng)制終止進(jìn)程p实蔽,不會(huì)進(jìn)行任何清理操作,如果p創(chuàng)建了子進(jìn)程谨读,該子進(jìn)程就成了僵尸進(jìn)程局装,使用該方法需要特別小心這種情況。如果p還保存了一個(gè)鎖那么也將不會(huì)被釋放劳殖,進(jìn)而導(dǎo)致死鎖
p.is_alive():如果p仍然運(yùn)行铐尚,返回True
p.join([timeout]):主線程等待p終止(強(qiáng)調(diào):是主線程處于等的狀態(tài),而p是處于運(yùn)行的狀態(tài))哆姻。timeout是可選的超時(shí)時(shí)間宣增。
屬性介紹:
p.daemon:默認(rèn)值為False,如果設(shè)為True填具,代表p為后臺(tái)運(yùn)行的守護(hù)進(jìn)程统舀,當(dāng)p的父進(jìn)程終止時(shí)匆骗,p也隨之終止劳景,并且設(shè)定為True后誉简,p不能創(chuàng)建自己的新進(jìn)程,必須在p.start()之前設(shè)置
p.name:進(jìn)程的名稱
p.pid:進(jìn)程的pid
Process類的使用
注意:在windows中Process()必須放到# if name == 'main':下
from multiprocessing import Process
import time
# 開啟進(jìn)程方式一
# def task(name):
# print(f"{name} is running...")
# time.sleep(2)
# print(f"{name} is done...")
# if __name__ == '__main__':
# p = Process(target=task, args=("子進(jìn)程1",))
# p.start()
# print("主進(jìn)程結(jié)束....")
# 開啟進(jìn)程方式二
class Myprocess(Process):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
print(f"{self.name} is running...")
time.sleep(2)
print(f"{self.name} is done...")
if __name__ == '__main__':
p = Myprocess("子進(jìn)程1")
p.start()
print("主進(jìn)程結(jié)束....")
os.getpid() 查看當(dāng)前進(jìn)程的進(jìn)程ID號(hào)
os.getppid()查看當(dāng)前進(jìn)程的父進(jìn)程ID號(hào)
p.pid 也可以查看當(dāng)前p進(jìn)程的pid
練習(xí)
1盟广、思考開啟進(jìn)程的方式一和方式二各開啟了幾個(gè)進(jìn)程闷串?
各開啟了5各進(jìn)程,1個(gè)父進(jìn)程,4個(gè)子進(jìn)程
2、進(jìn)程之間的內(nèi)存空間是共享的還是隔離的筋量?下述代碼的執(zhí)行結(jié)果是什么烹吵?
from multiprocessing import Process
n=100 #在windows系統(tǒng)中應(yīng)該把全局變量定義在if __name__ == '__main__'之上就可以了
def work():
global n
n=0
print('子進(jìn)程內(nèi): ',n)
if __name__ == '__main__':
p=Process(target=work)
p.start()
print('主進(jìn)程內(nèi): ',n)
進(jìn)程和進(jìn)程之間內(nèi)存空間是隔離的,運(yùn)行結(jié)果為
主進(jìn)程內(nèi) 100
子進(jìn)程內(nèi) 0
3、基于多進(jìn)程實(shí)現(xiàn)并發(fā)的套接字通信桨武?
server
from multiprocessing import Process
import socket
IP_PORT = ('127.0.0.1', 8080)
def server1(IP_PORT):
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(IP_PORT)
server.listen(5)
while True:
conn, client_addr = server.accept()
p = Process(target=talk, args=(conn,))
p.start()
server1.closer()
def talk(conn):
while True:
try:
msg = conn.recv(1024)
if not msg:
continue
except ConnectionResetError as e:
print(e)
break
conn.send(msg.upper())
if __name__ == '__main__':
server1(IP_PORT)
客戶端
import socket
IP_PORT = ('127.0.0.1', 8080)
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
client.connect(IP_PORT)
while True:
msg = input('>>>:').strip()
if not msg:
continue
client.send(msg.encode('utf-8'))
data_recv = client.recv(1024)
print(data_recv.decode('utf-8'))
4肋拔、思考每來(lái)一個(gè)客戶端,服務(wù)端就開啟一個(gè)新的進(jìn)程來(lái)服務(wù)它呀酸,這種實(shí)現(xiàn)方式有無(wú)問(wèn)題凉蜂?
客戶端無(wú)法正常結(jié)束,導(dǎo)致占用系統(tǒng)資源無(wú)法釋放....
僵尸進(jìn)程和孤兒進(jìn)程
- 僵尸進(jìn)程是子進(jìn)程結(jié)束后,留下的一些狀態(tài)信息,以供父進(jìn)程隨時(shí)查看這些子進(jìn)程的狀態(tài),如果父進(jìn)程一直運(yùn)行,會(huì)產(chǎn)生很多僵尸進(jìn)程,導(dǎo)致pid被占用
- 孤兒進(jìn)程是子進(jìn)程還未結(jié)束的情況下,父進(jìn)程已經(jīng)結(jié)束,在linux中由init進(jìn)程來(lái)負(fù)責(zé)回收,是無(wú)害的.
Process對(duì)象的join方法
在主進(jìn)程運(yùn)行過(guò)程中如果想并發(fā)地執(zhí)行其他的任務(wù),我們可以開啟子進(jìn)程性誉,此時(shí)主進(jìn)程的任務(wù)與子進(jìn)程的任務(wù)分兩種情況
情況一:在主進(jìn)程的任務(wù)與子進(jìn)程的任務(wù)彼此獨(dú)立的情況下窿吩,主進(jìn)程的任務(wù)先執(zhí)行完畢后,主進(jìn)程還需要等待子進(jìn)程執(zhí)行完畢错览,然后統(tǒng)一回收資源纫雁。
情況二:如果主進(jìn)程的任務(wù)在執(zhí)行到某一個(gè)階段時(shí),需要等待子進(jìn)程執(zhí)行完畢后才能繼續(xù)執(zhí)行倾哺,就需要有一種機(jī)制能夠讓主進(jìn)程檢測(cè)子進(jìn)程是否運(yùn)行完畢轧邪,在子進(jìn)程執(zhí)行完畢后才繼續(xù)執(zhí)行,否則一直在原地阻塞羞海,這就是join方法的作用
from multiprocessing import Process
import time
import random
import os
def task():
print('%s is piaoing' %os.getpid())
time.sleep(random.randrange(1,3))
print('%s is piao end' %os.getpid())
if __name__ == '__main__':
p=Process(target=task)
p.start()
p.join() #等待p停止,才執(zhí)行下一行代碼
print('主')
Process對(duì)象的其他屬性或方法
進(jìn)程對(duì)象的其他方法一:terminate與is_alive
from multiprocessing import Process
import time
import random
def task(name):
print('%s is piaoing' %name)
time.sleep(random.randrange(1,5))
print('%s is piao end' %name)
if __name__ == '__main__':
p1=Process(target=task,args=('egon',))
p1.start()
p1.terminate()#關(guān)閉進(jìn)程,不會(huì)立即關(guān)閉,所以is_alive立刻查看的結(jié)果可能還是存活
print(p1.is_alive()) #結(jié)果為True
print('主')
print(p1.is_alive()) #結(jié)果為False
進(jìn)程對(duì)象的其他屬性:name與pid
from multiprocessing import Process
import time
import random
def task(name):
print('%s is piaoing' %name)
time.sleep(random.randrange(1,5))
print('%s is piao end' %name)
if __name__ == '__main__':
p1=Process(target=task,args=('egon',),name='子進(jìn)程1') #可以用關(guān)鍵參數(shù)來(lái)指定進(jìn)程名
p1.start()
print(p1.name,p1.pid,)
三 練習(xí)題
1闲勺、改寫下列程序,分別別實(shí)現(xiàn)下述打印效果
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
-------->1
-------->3
-------->2
# 直接運(yùn)行
效果二:保證最后輸出-------->4
-------->2
-------->3
-------->1
-------->4
# 在print('-------->4') 之前添加
p1.join()
p2.join()
p3.join()
效果三:保證按順序輸出
-------->1
-------->2
-------->3
-------->4
# 在p1.start()下面添加p1.join()
# 在p2.start()下面添加p2.join()
# 在p3.start()下面添加p3.join()
2扣猫、判斷上述三種效果菜循,哪種屬于并發(fā),哪種屬于串行申尤?
效果一,二屬于并發(fā),效果三輸入串行.