用戶態與內核態的切換
用戶運行一程序,該程序所創建的進程開始是運行在用戶態的,如果要執行文件操作,網絡數據發送等操作,必須通過write,send等系統調用,這些系統會調用內核中的代碼來完成操作,這時,必須切換到Ring0,然后進入3GB-4GB中的內核地址空間去執行這些代碼完成操作,完成后,切換Ring3,回到用戶態。這樣,用戶態的程序就不能隨意操作1內核地址空間,具有一定的安全保護作用。
用戶態切換到內核態的3種方式 a.系統調用 這是用戶進程主動要求切換到內核態的一種方式,用戶進程通過系統調用申請操作系統提供的服務程序完成工作。 而系統調用的機制其核心還是使用了操作系統為用戶特別開放的一個中斷來實現,例如Linux的ine 80h中斷。 b.異常 當CPU在執行運行在用戶態的程序時,發現了某些事件不可知的異常,這是會觸發由當前運行進程切換到處理此 異常的內核相關程序中,也就到了內核態,比如缺頁異常。 c.外圍設備的中斷 當外圍設備完成用戶請求的操作之后,會向CPU發出相應的中斷信號,這時CPU會暫停執行下一條將要執行的指令 轉而去執行中斷信號的處理程序,如果先執行的指令是用戶態下的程序,那么這個轉換的過程自然也就發生了有 用戶態到內核態的切換。比如硬盤讀寫操作完成,系統會切換到硬盤讀寫的中斷處理程序中執行后續操作等。 ========================================== 具體的切換操作 從出發方式看,可以在認為存在前述3種不同的類型,但是從最終實際完成由用戶態到內核態的切換操作上來說, 涉及的關鍵步驟是完全一樣的,沒有任何區別,都相當於執行了一個中斷響應的過程,因為系統調用實際上最 終是中斷機制實現的,而異常和中斷處理機制基本上是一樣的,用戶態切換到內核態的步驟主要包括: (1)從當前進程的描述符中提取其內核棧的ss0及esp0信息。 (2)使用ss0和esp0指向的內核棧將當前進程的cs,eip,eflags,ss,esp信息保存起來,這個過程也完成了由用 戶棧找到內核棧的切換過程,同時保存了被暫停執行的程序的下一條指令。 (3)將先前由中斷向量檢索得到的中斷處理程序的cs,eip信息裝入相應的寄存器,開始執行中斷處理程序,這 時就轉到了內核態的程序執行了。
IO同步、IO異步、阻塞、非阻塞
POSIX(可移植操作系統接口)把同步IO操作定義為導致進程阻塞直到IO完成的操作,反之則是異步IO
IO同步可分為(阻塞和非阻塞類型)
- 阻塞IO模型(進程在內核狀態下等待)
使用recv的默認參數一直等數據直到拷貝到用戶空間,這段時間內進程始終阻塞。A同學用杯子裝水,打開水龍頭裝滿水然后離開。這一過程就可以看成是使用了阻塞IO模型,因為如果水龍頭沒有水,他也要等到有水並裝滿杯子才能離開去做別的事情。很顯然,這種IO模型是同步的。
- 非阻塞IO模型
改變flags,讓recv不管有沒有獲取到數據都返回,如果沒有數據那么一段時間后再調用recv看看,如此循環。B同學也用杯子裝水,打開水龍頭后發現沒有水,它離開了,過一會他又拿着杯子來看看……在中間離開的這些時間里,B同學離開了裝水現場(回到用戶進程空間),可以做他自己的事情。這就是非阻塞IO模型。但是它只有是檢查無數據的時候是非阻塞的,在數據到達的時候依然要等待復制數據到用戶空間(等着水將水杯裝滿),因此它還是同步IO。
注:紅色字體是與異步IO的最大區別
- IO復用模型
這里在調用recv前先調用select或者poll,這2個系統調用都可以在內核准備好數據(網絡數據到達內核)時告知用戶進程,這個時候再調用recv一定是有數據的。因此這一過程中它是阻塞於select或poll,而沒有阻塞於recv,有人將非阻塞IO定義成在讀寫操作時沒有阻塞於系統調用的IO操作(不包括數據從內核復制到用戶空間時的阻塞,因為這相對於網絡IO來說確實很短暫),如果按這樣理解,這種IO模型也能稱之為非阻塞IO模型,但是按POSIX來看,它也是同步IO,那么也和樓上一樣稱之為同步非阻塞IO吧。
這種IO模型比較特別,分個段。因為它能同時監聽多個文件描述符(fd)。這個時候C同學來裝水,發現有一排水龍頭,舍管阿姨告訴他這些水龍頭都還沒有水,等有水了告訴他。於是等啊等(select調用中),過了一會阿姨告訴他有水了,但不知道是哪個水龍頭有水,自己看吧。於是C同學一個個打開,往杯子里裝水(recv)。這里再順便說說鼎鼎大名的epoll(高性能的代名詞啊),epoll也屬於IO復用模型,主要區別在於舍管阿姨會告訴C同學哪幾個水龍頭有水了,不需要一個個打開看(當然還有其它區別)。
- 信號驅動IO模型
通過調用sigaction注冊信號函數,等內核數據准備好的時候系統中斷當前程序,執行信號函數(在這里面調用recv)。D同學讓舍管阿姨等有水的時候通知他(注冊信號函數),沒多久D同學得知有水了,跑去裝水。是不是很像異步IO?很遺憾,它還是同步IO(省不了裝水的時間啊)。
- 異步IO模型
調用aio_read,讓內核等數據准備好,並且復制到用戶進程空間后執行事先指定好的函數。E同學讓舍管阿姨將杯子裝滿水后通知他。整個過程E同學都可以做別的事情(沒有recv),這才是真正的異步
總結 IO分兩階段: 1.數據准備階段 2.內核空間復制回用戶進程緩沖區階段 一般來講:阻塞IO模型、非阻塞IO模型、IO復用模型(select/poll/epoll)、信號驅動IO模型都屬於同步IO,因為階段2是阻塞的(盡管時間很短)。只有異步IO模型是符合POSIX異步IO操作含義的,不管在階段1還是階段2都可以干別的事。
詳細內容請查看:http://www.cnblogs.com/euphie/p/6376508.html
select、poll、epoll之間的區別總結
select,poll,epoll都是IO多路復用的機制。I/O多路復用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒后自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需自己負責進行讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間。
關於這三種IO多路復用的用法,前面三篇總結寫的很清楚,並用服務器回射echo程序進行了測試。連接如下所示:
select:http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html
poll:http://www.cnblogs.com/Anker/archive/2013/08/15/3261006.html
epoll:http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html
總結:
(1)select,poll實現需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實也需要調用epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設備就緒時,調用回調函數,把就緒fd放入就緒鏈表中,並喚醒在epoll_wait中進入睡眠的進程。雖然都要睡眠和交替,但是select和poll在“醒着”的時候要遍歷整個fd集合,而epoll在“醒着”的時候只要判斷一下就緒鏈表是否為空就行了,這節省了大量的CPU時間。這就是回調機制帶來的性能提升。
(2)select,poll每次調用都要把fd集合從用戶態往內核態拷貝一次,並且要把current往設備等待隊列中掛一次,而epoll只要一次拷貝,而且把current往等待隊列上掛也只掛一次(在epoll_wait的開始,注意這里的等待隊列並不是設備等待隊列,只是一個epoll內部定義的等待隊列)。這也能節省不少的開銷。