今天就跟大家聊聊有關(guān)Linux網(wǎng)絡(luò)編程中IO模型指的是什么,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
公司主營(yíng)業(yè)務(wù):成都網(wǎng)站制作、做網(wǎng)站、外貿(mào)營(yíng)銷(xiāo)網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶(hù)真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。成都創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶(hù)帶來(lái)驚喜。成都創(chuàng)新互聯(lián)公司推出烏什免費(fèi)做網(wǎng)站回饋大家。
基于IO訪問(wèn)中存在的兩個(gè)階段詳細(xì)介紹了Linux產(chǎn)生的五種IO模型。
同步是指一個(gè)任務(wù)的完成需要依賴(lài)另外一個(gè)任務(wù)時(shí),只有等待被依賴(lài)的任務(wù)完成后,依賴(lài)的任務(wù)才能算完成。
異步是指不需要等待被依賴(lài)的任務(wù)完成,只是通知被依賴(lài)的任務(wù)要完成什么工作,依賴(lài)的任務(wù)也立即執(zhí)行,只要自己完成了整個(gè)任務(wù)就算完成了,異步一般使用狀態(tài)、通知和回調(diào)。
阻塞與非阻塞
阻塞是指調(diào)用結(jié)果返回之前,當(dāng)前線程會(huì)被掛起,一直處于等待消息通知,不能夠執(zhí)行其他業(yè)務(wù)。
非阻塞是指在不能立刻得到結(jié)果之前,該函數(shù)不會(huì)阻塞當(dāng)前線程,而會(huì)立刻返回。
對(duì)于一次IO訪問(wèn),數(shù)據(jù)會(huì)先被拷貝到內(nèi)核的緩沖區(qū)中,然后才會(huì)從內(nèi)核的緩沖區(qū)拷貝到應(yīng)用程序的地址空間。需要經(jīng)歷兩個(gè)階段:
1)準(zhǔn)備數(shù)據(jù)
2)將數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到進(jìn)程地址空間
由于存在這兩個(gè)階段,Linux產(chǎn)生了下面五種IO模型。
阻塞IO
當(dāng)用戶(hù)進(jìn)程調(diào)用了recvfrom調(diào)用時(shí),內(nèi)核進(jìn)入IO的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)(內(nèi)核需要等待足夠的數(shù)據(jù)再拷貝),這個(gè)過(guò)程需要等待,用戶(hù)進(jìn)程會(huì)被阻塞,等內(nèi)核將數(shù)據(jù)準(zhǔn)備好,然后拷貝到用戶(hù)地址空間,內(nèi)核返回結(jié)果,用戶(hù)進(jìn)程才從阻塞態(tài)進(jìn)入就緒態(tài)。
Linux中,默認(rèn)情況下所有的socket都是阻塞的。
非阻塞IO
當(dāng)用戶(hù)進(jìn)程發(fā)出read操作時(shí),如果kernel中的數(shù)據(jù)還沒(méi)有準(zhǔn)備好,那么它并不會(huì)block用戶(hù)進(jìn)程,而是立刻返回一個(gè)error。用戶(hù)進(jìn)程判斷結(jié)果是一個(gè)error時(shí),它就知道數(shù)據(jù)還沒(méi)有準(zhǔn)備好,于是它可以再次發(fā)送read操作。一旦kernel中的數(shù)據(jù)準(zhǔn)備好了,并且又再次收到了用戶(hù)進(jìn)程的system call,那么它馬上就將數(shù)據(jù)拷貝到了用戶(hù)內(nèi)存,然后返回。
非阻塞IO模式下用戶(hù)進(jìn)程需要不斷地詢(xún)問(wèn)內(nèi)核的數(shù)據(jù)準(zhǔn)備好了沒(méi)有。
Linux下可以通過(guò)設(shè)置socket使其變?yōu)閚on-blocking。
IO多路復(fù)用
通過(guò)一種機(jī)制,一個(gè)進(jìn)程可以監(jiān)視多個(gè)文件描述符(套接字描述符),一旦某個(gè)文件描述符就緒(一般是讀就緒或者寫(xiě)就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫(xiě)操作。這樣就不需要每個(gè)用戶(hù)進(jìn)程不斷的詢(xún)問(wèn)內(nèi)核數(shù)據(jù)準(zhǔn)備好了沒(méi)有。
常用的IO多路復(fù)用方式有select、poll和epoll。
kernel會(huì)“監(jiān)視”所有select負(fù)責(zé)的socket,當(dāng)任何一個(gè)socket中的數(shù)據(jù)準(zhǔn)備好了,select就會(huì)返回。這個(gè)時(shí)候用戶(hù)進(jìn)程再調(diào)用read操作,將數(shù)據(jù)從kernel拷貝到用戶(hù)進(jìn)程。
int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
select 函數(shù)監(jiān)視的文件描述符分3類(lèi),分別是writefds、readfds、和exceptfds。調(diào)用后select函數(shù)會(huì)阻塞,直到有描述副就緒(有數(shù)據(jù)可讀、可寫(xiě)、或者有except),或者超時(shí)(timeout指定等待時(shí)間,如果立即返回設(shè)為null即可),函數(shù)返回。當(dāng)select函數(shù)返回后,可以通過(guò)遍歷fdset,來(lái)找到就緒的描述符。
select的一個(gè)缺點(diǎn)在于單個(gè)進(jìn)程能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,在Linux上一般為1024。
poll使用一個(gè) pollfd的指針實(shí)現(xiàn)。
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
pollfd結(jié)構(gòu)包含了要監(jiān)視的event和發(fā)生的event
struct pollfd { int fd; /* file descriptor */short events; /* requested events to watch */short revents; /* returned events witnessed */ };
和select函數(shù)一樣,poll返回后,需要遍歷pollfd來(lái)獲取就緒的描述符。poll沒(méi)有監(jiān)聽(tīng)最大數(shù)量限制。
epoll使用一個(gè)文件描述符管理多個(gè)描述符,將用戶(hù)關(guān)心的文件描述符的事件存放到內(nèi)核的一個(gè)事件表中,采用監(jiān)聽(tīng)回調(diào)的機(jī)制,這樣在用戶(hù)空間和內(nèi)核空間的copy只需一次,避免再次遍歷就緒的文件描述符列表。
epoll的操作過(guò)程需要三個(gè)接口:
int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
int epoll_create(int size):
創(chuàng)建一個(gè)epoll的句柄,size用來(lái)告訴內(nèi)核這個(gè)監(jiān)聽(tīng)的數(shù)目一共有多大。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event):
對(duì)指定描述符fd執(zhí)行op操作。
- epfd:是epoll_create()的返回值。
- op:表示op操作,用三個(gè)宏來(lái)表示:添加EPOLL_CTL_ADD,刪除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。分別添加、刪除和修改對(duì)fd的監(jiān)聽(tīng)事件。
- fd:是需要監(jiān)聽(tīng)的fd(文件描述符)
- epoll_event:是告訴內(nèi)核需要監(jiān)聽(tīng)什么事,struct epoll_event結(jié)構(gòu)如下:
struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; //events可以是以下幾個(gè)宏的集合:EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀(包括對(duì)端SOCKET正常關(guān)閉); EPOLLOUT:表示對(duì)應(yīng)的文件描述符可以寫(xiě); EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來(lái)); EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤; EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷; EPOLLET: 將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式,這是相對(duì)于水平觸發(fā)(Level Triggered)來(lái)說(shuō)的。 EPOLLONESHOT:只監(jiān)聽(tīng)一次事件,當(dāng)監(jiān)聽(tīng)完這次事件之后,如果還需要繼續(xù)監(jiān)聽(tīng)這個(gè)socket的話(huà),需要再次把這個(gè)socket加入到EPOLL隊(duì)列里
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout):
等待epfd上的io事件,最多返回maxevents個(gè)事件。
參數(shù)events用來(lái)從內(nèi)核得到事件的集合,maxevents告之內(nèi)核這個(gè)events有多大,這個(gè)maxevents的值不能大于創(chuàng)建epoll_create()時(shí)的size,參數(shù)timeout是超時(shí)時(shí)間(毫秒,0會(huì)立即返回,-1將不確定,也有說(shuō)法說(shuō)是永久阻塞)。該函數(shù)返回需要處理的事件數(shù)目,如返回0表示已超時(shí)。
LT(level trigger,水平觸發(fā))模式:當(dāng)epoll_wait檢測(cè)到描述符就緒,將此事件通知應(yīng)用程序,應(yīng)用程序可以不立即處理該事件。下次調(diào)用epoll_wait時(shí),會(huì)再次響應(yīng)應(yīng)用程序并通知此事件。LT模式是默認(rèn)的工作模式。
LT模式同時(shí)支持阻塞和非阻塞socket。
ET(edge trigger,邊緣觸發(fā))模式:當(dāng)epoll_wait檢測(cè)到描述符就緒,將此事件通知應(yīng)用程序,應(yīng)用程序必須立即處理該事件。如果不處理,下次調(diào)用epoll_wait時(shí),不會(huì)再次響應(yīng)應(yīng)用程序并通知此事件。
ET是高速工作方式,只支持非阻塞socket。ET模式減少了epoll事件被重復(fù)觸發(fā)的次數(shù),因此效率要比LT模式高。
異步IO
用戶(hù)進(jìn)程發(fā)起read操作之后,立刻就可以開(kāi)始去做其它的事。內(nèi)核收到一個(gè)異步IO read之后,會(huì)立刻返回,不會(huì)阻塞用戶(hù)進(jìn)程。內(nèi)核會(huì)等待數(shù)據(jù)準(zhǔn)備完成,然后將數(shù)據(jù)拷貝到用戶(hù)內(nèi)存,當(dāng)這一切都完成之后,內(nèi)核會(huì)給用戶(hù)進(jìn)程發(fā)送一個(gè)signal,告訴它read操作完成了。
信號(hào)驅(qū)動(dòng)IO
內(nèi)核文件描述符就緒后,通過(guò)信號(hào)通知用戶(hù)進(jìn)程,用戶(hù)進(jìn)程再通過(guò)系統(tǒng)調(diào)用讀取數(shù)據(jù)。此方式屬于同步IO,因?yàn)閷?shí)際讀取數(shù)據(jù)到用戶(hù)進(jìn)程緩存的工作仍然是由用戶(hù)進(jìn)程自己負(fù)責(zé)的。
看完上述內(nèi)容,你們對(duì)Linux網(wǎng)絡(luò)編程中IO模型指的是什么有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。