高級JAVA,Linux---高級IO

 2023-10-15 阅读 29 评论 0

摘要:IO過程 發起IO調用,等待IO條件就緒,將數據拷貝到緩沖區中進行處理。 等待+數據拷貝 兩個主要的操作 五種IO模型 阻塞IO:發起IO調用,不具備IO條件,則一直阻塞等待,操作是順序執行。非阻塞IO:發起IO調用,不具備IO條

IO過程

發起IO調用,等待IO條件就緒,將數據拷貝到緩沖區中進行處理。
等待+數據拷貝 兩個主要的操作

五種IO模型

  • 阻塞IO:發起IO調用,不具備IO條件,則一直阻塞等待,操作是順序執行。
  • 非阻塞IO:發起IO調用,不具備IO條件,則立即返回,操作需要循環調用。
  • 信號驅動IO:定義一個IO就緒信號,收到信號則立即發送IO調用,操作不是順序進行。
  • 異步IO:定義IO完成信號,發起異步IO調用,IO由系統完成。
  • 多路轉接IO:對大量描述符集中事件監控,讓進車可以只針對就緒的描述符進行操作,提高了效率,避免對未就緒的操作符進行操作,導致阻塞。

阻塞IO

為了完成某個功能,發起IO調用如果當前不具備IO條件,則一直等待。
在這里插入圖片描述
優點:流程非常簡單
缺點:一個IO完成以后,才能執行下一個IO,資源沒有充分利用,大量的時間在等待。

非阻塞IO

為了完成一個IO,發起調用,如果當前不具備IO條件,則立即返回,但是需要循環發起IO。
在這里插入圖片描述
優點:流程相對復雜一點,對資源的利用更加充分。
缺點:IO操作不夠實時。

信號驅動IO

定義一個IO信號處理方式,在處理方式中進行IO操作,IO就緒時信號通知進程,進程在IO就緒的時候去進行IO操作。

在這里插入圖片描述
優點:IO操作更加實時,對資源的利用更加高效。
缺點:操作復雜。

異步IO

例如:排隊買票的例子。
假期出去游玩的人比較多,因此異步IO的操作,就類似于,你讓別人幫你排隊,并且幫你把票買了,最終將票給你就可以了。
因此異步IO的本質就是,其所有的IO操作都是讓別人幫忙完成,自身只是發起一個調用,告訴別人需要從哪里開始拷貝數據,以及拷貝多少數據即可。IO的等待與操作都是由別人完成。
在這里插入圖片描述
優點:對資源利用更加充分
缺點:流程更加負責

同步與互斥

同步

這里的同步與之前進程中的同步不同,這里的同步指的是處理流程中,順序處理,一個完成之后再完成下一個,并且所有的功能都是由進程自身完成的。

異步
處理流程順序不定,主要的功能都是由別人完成或者由操作系統完成。

  • 異步阻塞:功能是由別人完成,但是在調用的時候是等著別人完成。
  • 異步非阻塞:功能由別人完成,調用之后立即返回,不用等待。

高級IO中的同步與互斥的主要區別:

判斷主要的功能是不是自己完成的,完成的順序是否確定。

多路轉接IO

對大量的描述符進行IO事件監控,可以告訴我們,當前有哪些描述符已經就緒了哪些事件,進程就可以針對就緒了指定事件的進程進行響應操作,避免了對沒有就緒的描述符進行操作導致的效率降低。

IO事件
可讀事件、可寫事件、異常事件

多路轉接IO模型:

  • 作用:用于對描述符事件進行監控的。
  • 實現方式:select、poll、epoll

select模型

操作流程:

  • 定義某個事件的描述符集合 (可讀、可寫、異常事件集合),初始化集合。
  • 對哪個描述符關心,就把這個描述符添加到指定的相應事件中。
  • 將集合拷貝到內核中進行監控。監控的原理是輪詢監控。(輪詢遍歷判斷)
    可讀事件的就緒:接收緩沖區中的數據大小大于低水位標記,通常默認為一個字節。
    可寫事件的就緒:發送緩沖區中剩余空間的大小大于低水位標記,通常默認為一個字節。
    異常事件的就緒:描述符是否產生了某個異常。
  • 監控調用的返回,表示監控出錯、描述符已經就緒、監控超時。在調用返回的時候,將事件監控的描述符集合中的未就緒的描述符從集合中移除,在集合中僅僅保留就緒的描述符
  • 輪詢判斷哪個描述符處于哪個集合中,就確定這個描述符是否就緒了某個事件,然后進行對應事件的操作。(如果一個描述符處于可讀集合中,表述當前描述符已經滿足可讀事件,然后進行具體的操作)

注意

在select中,并不會直接返回給用戶就緒的描述符進行操作,而是返回的就緒描述符的集合,因此需要我們自己進行判斷。
因為,select在返回的時候,將沒有就緒的描述符已經移除,因此在下次監控的時候,就需要重新向集合中添加描述符。

代碼操作:

  1. 定義集合(struct fd_set),該成員只有一個數組,在該數組中是以二進制比特位進行使用的,添加描述符的就是將對應的二進制比特位置一。默認大小為1024個描述符。
  2. 初始化清空集合:void FD_ZERO(fd_set *set)
  3. 添加描述符到指定集合中。void FD_SET(int fd,fd_set* set),將fd描述符添加到set集合中。
  4. 開發發起監控調用。
    int select(int nfds,fd_set * readfds,fd_set* writefds,fd_set *exceptfds,struct timeval *timeout);
  • nfds:當前監控集合中最大的描述符+1。因為集合默認監控的描述符大小為1024個,避免遍歷沒用的描述符,減少遍歷的次數。
  • readfds:可讀事件描述符集合,如果不需要則置空。
  • writedfs:可寫事件描述符集合,如果不需要則置空。
  • exceptfds:異常事件描述符集合,如果不需要則置空。
  • timeout:時間結構體,通過時間決定select阻塞、非阻塞、限制超時的阻塞,如果timeout為空,則表示阻塞監控,直到有描述符就緒或者監控出錯才會返回。 如果timeout中的成員數據為0,則表示非阻塞,監控的時候如果沒有描述符就緒,則會立即超時返回。如果timeout中的成員數據不為0,則在指定的時間之內,沒有就緒則超時返回。
    返回值:返回值大于0,表示就緒描述符個數;返回值如果為0,表示沒有描述符就緒,超時返回;返回值小于0,表示監控出錯。
  1. 調用返回,返回就緒的描述符,遍歷判斷哪個描述符還在集合中,就是哪個事件已經就緒。
    int FD_ISSET(int fd,fd_set *set),判斷fd描述符是否在集合中。本質就是對二進制比特位查看的封裝。

  2. 如果對描述符不進行監控,則移除描述符。
    void FD_CLR(int fd,fd_set* set)在set集合中刪除fd描述符

int main()
{fd_set redfs;while(1){cout<<"開始監控"<<endl;struct timeval tv;tv.tv_sec = 3;tv.tv_usec = 0;FD_ZERO(&redfs)FD_SET(0,&redfs); //將0號標準輸入描述符添加到集合中int res = select(1,redfs,NULL,NULL,&tv); if(res<0){perror("select error")return -1;}else if(res == 0){cout<<"監控超時"<<endl;continue;}if(FD_ISSET(0,&redfs)){cout<<"監控成功,0號事件已經響應"<<endl;}}
}

優缺點:

  • 跨平臺移植性比較好。
  • select對描述符的監控由最大數量上限。FD_SETSIZE 。
  • 在內核中進行監控數通過輪詢遍歷實現的,因此性能會隨著描述符的增多而下降。
  • 只能返回就緒的集合,需要進程進行輪詢判斷才能知道哪個描述符就緒了哪個事件。
  • 每次監控都需要將描述符添加到集合中,每次監控都需要將集合拷貝到內核中。

poll模型

操作流程:

  • 定義監控的描述符時間的結構體數組,將需要監控的描述符以及事件標識信息,添加到數組的各個節點中。
  • 發起調用開始監控,將監控事件的描述符結構體數組,拷貝到內核中進行輪詢遍歷判斷,如果有就緒/等待超時則調用返回,并且在每個描述符對應的事件結構體中,標識當前就緒的事件。
  • 進程輪詢遍歷數組,判斷數組中的每個節點中的就緒事件是哪個事件,再決定對描述符如何進行操作。

接口:

  • int poll (struct pollfd* arry,nfds_t nfds,int timeout)
    監控采用事件結構體。
    struct pollfd {int fd; shor events; shor revents}
    fd要監控的描述符、events要監控的事件(POLLIN可讀事件、POLLOUT可寫事件)、revents哪個監控事件就緒了就將其放在其中。
    nfds:數組中有效節點的個數
    timeout:監控的超時等待時間:毫秒
    返回值:>0表示監控的就緒事件的個數;返回值==0表示等待超時,返回值<0監控出錯。

優缺點:

  • 使用事件結構體進行監控,簡化了select中三種事件集合的操作流程。
  • 監控數量不做限制。
  • 不需要每次重新定義事件節點。
  • 跨平臺移植性差。
  • 每次監控依然需要向內核中拷貝數據。
  • 在內核中監控,依然采用輪詢遍歷。

epoll模型

操作流程:

  • 在內核中創建epoll句柄(epollevent結構體。紅黑樹+雙向鏈表)
  • 對內核中的epollevent結構添加、刪除、修改
  • 開始監控,發起調用,在內核中采用異步阻塞實現監控,等待超時或者描述符就緒了事件后調用返回,返回給用戶就緒描述符事件的結構信息。
  • 進行直接對就緒的事件結構體中的描述符成員進行操作即可

接口信息:

  • int epoll_create(int size)創建epoll句柄,size只要大于0即可。
    返回值:一個文件描述符,
  • int epoll_ctl(int epfd,int cmd,int fd,struct epoll_event* ev)
    epfd:epoll_create返回的操作句柄
    cmd:針對fd描述符的監控信息進行操作,添加/刪除/修改操作。EPOLL_CTL_ADD /EPOLL_CTL_DEL / EPOLL_CTL_MOD
    fd:要監控的描述符
    ev:描述符對應的事件結構體信息。當epoll開始監控的時候,描述符如果就緒了進程關心的事件,則會給用戶返回我們添加對應事件結構體信息,通過事件結構體信息中包含的描述符進行操作,所以fd與ev結構體中的fd是同一個描述符。
  • int epoll_wait(int epfd,struct epoll_events *evs,int max_event,int timeout)
    epfd:epoll操作句柄
    evs:struct epoll_events結構體數組的首地址,用于接受就緒的描述符對應的事件結構體信息。
    max_event:本次監控想要獲取就緒事件的最大數量,不能大于evs數組大小。
    timeout:超時等待時間,單位毫秒。
    返回值:>0表示就緒事件的個數 =0表示等待超時,<0監控出錯

監控原理

異步阻塞操作。
監控由系統完成,用戶添加監控的描述符以及對應事件結構體會被添加到內核的eventpoll結構體中的紅黑樹中,一旦進程發起調用,操作系統為每個描述符的事件進行回調。當描述符就緒了哪個事件,則將描述符對應的事件結構體添加到雙向鏈表中。
進程自身,每隔一段事件,判斷雙向鏈表是否為NULL,決定是否有事件就緒。
在這里插入圖片描述

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/140442.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息