select迈嘹、poll、epoll區(qū)別:詳解三者區(qū)別
select全庸,poll秀仲,epoll都是IO多路復(fù)用的機(jī)制。I/O多路復(fù)用就通過一種機(jī)制壶笼,可以監(jiān)視多個(gè)描述符神僵,一旦某個(gè)描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫操作覆劈。但select保礼,poll,epoll本質(zhì)上都是同步I/O责语,因?yàn)樗麄兌夹枰谧x寫事件就緒后自己負(fù)責(zé)進(jìn)行讀寫炮障,也就是說這個(gè)讀寫過程是阻塞的,而異步I/O則無需自己負(fù)責(zé)進(jìn)行讀寫坤候,異步I/O的實(shí)現(xiàn)會負(fù)責(zé)把數(shù)據(jù)從內(nèi)核拷貝到用戶空間胁赢。
select:
(1)使用copy_from_user從用戶空間拷貝fd_set到內(nèi)核空間
(2)注冊回調(diào)函數(shù)__pollwait
(3)遍歷所有fd,調(diào)用其對應(yīng)的poll方法(對于socket铐拐,這個(gè)poll方法是sock_poll徘键,sock_poll根據(jù)情況會調(diào)用到tcp_poll,udp_poll或者datagram_poll)
(4)以tcp_poll為例练对,其核心實(shí)現(xiàn)就是__pollwait,也就是上面注冊的回調(diào)函數(shù)吹害。
(5)__pollwait的主要工作就是把current(當(dāng)前進(jìn)程)掛到設(shè)備的等待隊(duì)列中螟凭,不同的設(shè)備有不同的等待隊(duì)列,對于tcp_poll來說它呀,其等待隊(duì)列是sk->sk_sleep(注意把進(jìn)程掛到等待隊(duì)列中并不代表進(jìn)程已經(jīng)睡眠了)螺男。在設(shè)備收到一條消息(網(wǎng)絡(luò)設(shè)備)或填寫完文件數(shù)據(jù)(磁盤設(shè)備)后,會喚醒設(shè)備等待隊(duì)列上睡眠的進(jìn)程纵穿,這時(shí)current便被喚醒了下隧。
(6)poll方法返回時(shí)會返回一個(gè)描述讀寫操作是否就緒的mask掩碼,根據(jù)這個(gè)mask掩碼給fd_set賦值谓媒。
(7)如果遍歷完所有的fd淆院,還沒有返回一個(gè)可讀寫的mask掩碼,則會調(diào)用schedule_timeout是調(diào)用select的進(jìn)程(也就是current)進(jìn)入睡眠句惯。當(dāng)設(shè)備驅(qū)動發(fā)生自身資源可讀寫后土辩,會喚醒其等待隊(duì)列上睡眠的進(jìn)程。如果超過一定的超時(shí)時(shí)間(schedule_timeout指定)抢野,還是沒人喚醒拷淘,則調(diào)用select的進(jìn)程會重新被喚醒獲得CPU,進(jìn)而重新遍歷fd指孤,判斷有沒有就緒的fd启涯。
(8)把fd_set從內(nèi)核空間拷貝到用戶空間。
select的幾大缺點(diǎn):
(1)每次調(diào)用select恃轩,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài)结洼,這個(gè)開銷在fd很多時(shí)會很大
(2)同時(shí)每次調(diào)用select都需要在內(nèi)核遍歷傳遞進(jìn)來的所有fd,這個(gè)開銷在fd很多時(shí)也很大
(3)select支持的文件描述符數(shù)量太小了详恼,默認(rèn)是1024
poll實(shí)現(xiàn):poll的實(shí)現(xiàn)和select非常相似补君,只是描述fd集合的方式不同,poll使用pollfd結(jié)構(gòu)而不是select的fd_set結(jié)構(gòu)昧互,其他的都差不多挽铁。
epoll是對select和poll的改進(jìn)。
Linux進(jìn)程間通信
1.管道
管道是Linux支持的最初Unix IPC形式之一敞掘,具有以下特點(diǎn):
(1)管道是半雙工的叽掘,數(shù)據(jù)只能向一個(gè)方向流動;需要雙方通信時(shí)玖雁,需要建立起兩個(gè)管道更扁;
(2)只能用于父子進(jìn)程或者兄弟進(jìn)程之間(具有親緣關(guān)系的進(jìn)程);
(3)單獨(dú)構(gòu)成一種獨(dú)立的文件系統(tǒng):管道對于管道兩端的進(jìn)程而言,就是一個(gè)文件浓镜,但它不是普通的文件溃列,它不屬于某種文件系統(tǒng),而是自立門戶膛薛,單獨(dú)構(gòu)成一種文件系統(tǒng)听隐,并且只存在與內(nèi)存中。
(4)數(shù)據(jù)的讀出和寫入:一個(gè)進(jìn)程向管道中寫的內(nèi)容被管道另一端的進(jìn)程讀出哄啄。寫入的內(nèi)容每次都添加在管道緩沖區(qū)的末尾雅任,并且每次都是從緩沖區(qū)的頭部讀出數(shù)據(jù)。
2.有名管道
FIFO不同于管道之處在于它提供一個(gè)路徑名與之關(guān)聯(lián)咨跌,以FIFO的文件形式存在于文件系統(tǒng)中沪么。這樣,即使與FIFO的創(chuàng)建進(jìn)程不存在親緣關(guān)系的進(jìn)程锌半,只要可以訪問該路徑禽车,就能夠彼此通過FIFO相互通信(能夠訪問該路徑的進(jìn)程以及FIFO的創(chuàng)建進(jìn)程之間),因此拳喻,通過FIFO不相關(guān)的進(jìn)程也能交換數(shù)據(jù)哭当。
3. 信號
信號是在軟件層次上對中斷機(jī)制的一種模擬猪腕,在原理上冗澈,一個(gè)進(jìn)程收到一個(gè)信號與處理器收到一個(gè)中斷請求可以說是一樣的。信號是異步的陋葡,一個(gè)進(jìn)程不必通過任何操作來等待信號的到達(dá)亚亲,事實(shí)上,進(jìn)程也不知道信號到底什么時(shí)候到達(dá)腐缤。信號事件的發(fā)生有兩個(gè)來源:硬件來源(比如我們按下了鍵盤或者其它硬件故障)捌归;軟件來源,最常用發(fā)送信號的系統(tǒng)函數(shù)是kill, raise,alarm和setitimer以及sigqueue函數(shù)岭粤,軟件來源還包括一些非法運(yùn)算等操作惜索。
4.消息隊(duì)列
與命名管道相比,消息隊(duì)列的優(yōu)勢在于剃浇,1巾兆、消息隊(duì)列也可以獨(dú)立于發(fā)送和接收進(jìn)程而存在,從而消除了在同步命名管道的打開和關(guān)閉時(shí)可能產(chǎn)生的困難虎囚。2角塑、同時(shí)通過發(fā)送消息還可以避免命名管道的同步和阻塞問題,不需要由進(jìn)程自己來提供同步方法淘讥。3圃伶、接收程序可以通過消息類型有選擇地接收數(shù)據(jù),而不是像命名管道中那樣,只能默認(rèn)地接收窒朋。
5.信號量
信號量與其他進(jìn)程間通信方式不大相同搀罢,它主要提供對進(jìn)程間共享資源訪問控制機(jī)制。相當(dāng)于內(nèi)存中的標(biāo)志侥猩,進(jìn)程可以根據(jù)它判定是否能夠訪問某些共享資源魄揉,同時(shí),進(jìn)程也可以修改該標(biāo)志拭宁。除了用于訪問控制外洛退,還可用于進(jìn)程同步。信號量有以下兩種類型:
二值信號量:最簡單的信號量形式杰标,信號燈的值只能取0或1兵怯,類似于互斥鎖。
計(jì)算信號量:信號量的值可以取任意非負(fù)值(當(dāng)然受內(nèi)核本身的約束)腔剂。
6.套接字(unix域協(xié)議)
socket API原本是為網(wǎng)絡(luò)通訊設(shè)計(jì)的媒区,但后來在socket的框架上發(fā)展出一種IPC機(jī)制,就是UNIX Domain Socket掸犬。雖然網(wǎng)絡(luò)socket也可用于同一臺主機(jī)的進(jìn)程間通訊(通過loopback地址127.0.0.1)袜漩,但是UNIX Domain Socket用于IPC更有效率:不需要經(jīng)過網(wǎng)絡(luò)協(xié)議棧,不需要打包拆包湾碎、計(jì)算校驗(yàn)和宙攻、維護(hù)序號和應(yīng)答等,只是將應(yīng)用層數(shù)據(jù)從一個(gè)進(jìn)程拷貝到另一個(gè)進(jìn)程介褥。UNIX域套接字與TCP套接字相比較座掘,在同一臺傳輸主機(jī)的速度前者是后者的兩倍。這是因?yàn)槿崽希琁PC機(jī)制本質(zhì)上是可靠的通訊溢陪,而網(wǎng)絡(luò)協(xié)議是為不可靠的通訊設(shè)計(jì)的。UNIX Domain Socket也提供面向流和面向數(shù)據(jù)包兩種API接口睛廊,類似于TCP和UDP形真,但是面向消息的UNIX Domain Socket也是可靠的,消息既不會丟失也不會順序錯(cuò)亂超全。