高級IO

幾種IO模型優缺點:

阻塞型:當資源臨時不可獲得時,調用者進程阻塞等待。節省系統資源,運行效率低。

非阻塞型(輪詢):當資源不可獲得時,系統調用出錯返回,影響浪費系統資源,運行效率高。

多路IO複用型:既節省系統資源,服務效率又高

異步IO:
                信號驅動IO:在不干擾主進程運行的情況下實現異步訪問IO;
                異步IO:類似於信號驅動IO,依賴底層驅動。
信號驅動I/O和異步I/O的主要區別在於,信號驅動I/O是由內核通知我們何時可以啓動一個I/O操作,此時數據仍在內核空間。而異步I/O有內核通知我們IO操作何時完成,此時數據已經拷貝到了我們的用戶空間。

上述五種不同I/O模型的比較:前四種I/O模型主要區別在於第一階段,有的阻塞,有的不阻塞。第二階段基本相同:在數據從內核空間拷貝到調用者的用戶空間時,進程阻塞與讀操作。而異步IO的兩個階段都沒有阻塞,在數據拷貝完成後,纔會讓內核通知我們。

一、非阻塞IO





二、多路複用IO

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
功能:實現IO多路轉接,先構造一張存有多個描述符的表,然後這個函數阻塞,並且指示內核等待當多個描述中的任意一個準備好時,該函數才返回。
參數:
nfds:要檢查的描述符數量,其值爲三個描述符表中最大描述符編號的值。然後再加1.
readfds:在調用函數以前,將此值設置爲要讓內核測試 讀是否準備好的描述符集。當函數返回時,這個值已經改變,用於指示哪些描述符字已經準備好讀。可以用FD_ISSET來測試。
writefds:在調用函數以前,將此值設置爲要讓內核測試 寫是否準備好的描述符集。當函數返回時,這個值已經改變,用於指示哪些描述符字已經準備好寫。可以用FD_ISSET來測試。
 exceptfds在調用函數以前,將此值設置爲要讓內核測試異常條件的描述符集。當函數返回時,這個值已經改變,用於指示哪些描述符字異常。可以用FD_ISSET來測試。
                    timeout:要等待的時間
                              struct    timeval
  {
                                    long  tv_sec;   表示要等待的秒數
                                    long  tv_usec;  表示要等待的微秒數
                                 } ; 
timeout==NULL時,此函數永遠等待,只有當指定描述符中的任意一個準備好或捕捉到一個信號時才返回。如果捕捉到一個信號。select返回-1
timeout == 0時,完全不等待,立即返回。    
timeout >0時,等待指定的秒數或微秒數。當指定時間已經超時時立即返回,如果超時時還沒有一個描述符準備好,則返回值是0.
 
返回值:
  • 返回值-1表示出錯。例如在所指定的描述符都沒有準備好時捕捉到了一個信號,此時出錯返回-1,這種情況下,不修改其中任何描述符集。
  • 返回值0表示所有描述符都沒有準備好,而且已經超時。此時,所有描述符集都被清爲0.
  • 返回值正值表示已經準備好的描述符個數,是這三個描述符集中已準備好的描述符之和。
注:
  • 有三個描述符集,readfds、writefds、exceptfds,每個描述符集存放在fd_set數據類型中,這種數據類型中的每一位對應一個描述符。所以內核每次檢查對該類型中的每一位(也就是每個描述符)進行檢查,來判斷當前有沒有準備好的描述符。所以nfds實質上就是定義了一個內核要檢查的範圍(也就是要檢查的描述符數量)。因爲描述符時從0開始,所以nfds爲描述符表中的最大描述符編號加一。
  • 描述符在什麼條件下準備好
對於普通文件描述符基本不會阻塞,但涉及到進程間或網絡間通信時,便會阻塞,這時必須引起對select返回“準備好”的條件說得明確些
            1、下列四個條件任何一個滿足時,套接字準備好讀:
                    a、可接收緩衝區的數據字節數大於0時,即有可讀的數據時,將不阻塞。對於套接口而言,可以使用套接口選項SO_RCVLOWAT來設置一個低潮限度,當接收數據緩衝區的字節數大於等於此低潮限度時,套接口讀操作將不阻塞並返回。
                    b、在面向連接的通信中,當連接到讀端的寫端關閉時,讀操作將不阻塞且返回0. UDP不是面向連接的,所以不會返回。
                    c、當讀操作出現錯誤時,這時讀操作將不阻塞且返回錯誤。
                    d、調用accept,套接口是一個監聽套接口且還有未完成的連接。
            2、下列三個條件任何一個滿足時。套接口準備好寫:
                    a、發送緩衝區仍有可用空間時,將不阻塞。對於套接口而言,可以使用套接口選項SO_SNDLOWAT來設置一個低潮限度,當發送數據緩衝區的字節數小於等於此低潮限度時,套接口讀操作將不阻塞並返回。實際上UDP套接口沒有發送緩衝區,內核只是拷貝應用進程數據並將其向協議棧的下層傳遞。因此一個阻塞UDP套接口上的輸出操作不會阻塞。
                    b、在面向連接的通信中,當連接到寫端的讀端關閉時,寫操作將產生信號SIGPIPE,終止進程。
                    c、當寫操作出現錯誤時,這時讀操作將出錯返回。
            3、如果一個套接口存在帶外數據或者仍處於帶外標記,那它有異常條件待處理
            注意當一個套接口出錯時,它被select標記爲即可讀又可寫。

            通過調用以下四個宏函數來處理fd_set描述符集:
                  void FD_CLR(int fd, fd_set *set);         將描述符fd從描述符集中清除。若fd在描述符集中則返回非0值,否則返回0
                  void FD_SET(int fd, fd_set *set);         將描述符fd添加到描述符集中
                  int  FD_ISSET(int fd, fd_set *set);        測試描述符fd是否被置位
                  void FD_ZERO(fd_set *set);                 將描述符集清0


int pselect(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, const struct timespec *timeout,const sigset_t *sigmask);
除以下幾點外,此函數和select相同:
  • select的超時用timeval結構指定,但pselect使用timespec結構,timespec結構以秒和納秒錶示超時,而非微妙和秒。
  • pselect的超時聲明爲const,保證了調用pselect不會改變此值
  • 對於pselect可使用信號屏蔽字,若sigmask爲空,那麼在信號有關方面,pselect的運行狀況和select相同。否則,sigmask指向一個信號屏蔽字,在調用pselect時,以原子方式安裝該信號屏蔽字。在返回時恢復以前的信號屏蔽字。


int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:
實現IO多路轉接,poll函數用於監測多個等待事件,若事件未發生,進程睡眠,放棄CPU控制權,若監測的任何一個事件發生,poll將喚醒睡眠的進程,並判斷是什麼等待事件發生,執行相應的操作。poll函數退出後,struct pollfd變量的所有值被清零,需要重新設置。
          參數:
                       fds:指定了一個被監視的文件描述符,一般設置爲一個結構體數組,指向結構體數組的第一個元素的指針。
                        struct pollfd { 
          int        fd;         /* file descriptor */ 描述符編號
          short    events; /* requested events to watch */ 要關心的描述符狀態,其值詳細見下圖。
                    short    revents; /* returned events witnessed */ 返回時,內核設置revents,以說明描述符發生了什麼
     } ;
                      nfds:說明了結構體數組中的個數,即描述符個數。
                      timeout:
        當timeout==-1時,此函數永遠等待,只有當指定描述符中的任意一個準備好或捕捉到一個信號時才返回。如果捕捉到一個信號。select返回-1
                  當timeout == 0時,完全不等待,立即返回。    
        當timeout >0時,等待指定的毫秒。當指定時間已經超時時立即返回,如果超時時還沒有一個描述符準備好,則返回值是0.
返回值 :
  • 返回值-1表示出錯。
  • 返回值0表示所有描述符都沒有準備好,而且已經超時。此時,所有描述符集都被清爲0.
  • 返回值正值表示已經準備好的描述符個數
  
注意,第三部分的三個常值在events中是不能設置的,但是當相應條件存在時就在revents中返回。



三、異步IO

使用異步I/O(SIGIO)需要進程執行以下三個步驟:
1、給SIGIO信號註冊處理函數。
2、設置要接受SIGIO信號的進程ID或進程組ID。以命令F_SETOWN調用fcntl函數來設置進程或進程組ID。
3、激活套接口的信號驅動

注:
對於管道文件和普通文件等用OPEN打開的文件,當以只讀方式打開時,對描述符執行寫操作會引起SIGIO信號。當以只寫方式打開時,對描述符執行讀操作,會引起SIGIO信號。(讀寫操作必須已經完成,即數據已經全部被讀取到內核空間或者從內核空間將數據全部取出,纔會發生信號)

UDP套接口上的SIGIO信號,當下述事件發生時產生SIGIO信號:
  • 數據包到達套接口
  • 套接口上發生異步錯誤

TCP套接口上的SIGIO信號,當下述事件發生時產生SIGIO信號:
  • 信號驅動對TCP套接口幾乎時沒用的,原因是該信號產生過於頻繁,並且該信號並沒有告訴我們發生了什麼事件。下列條件均可在TCP套接口上產生SIGIO信號。
  • 在監聽套接口上有一個連接請求已經完成
  • 發起來一個連接拆除請求
  • 一個連接請求拆除請求已經完成
  • 一個連接的一半已經關閉
  • 數據到達了套接口
  • 數據已從套接口上發出(即輸出緩衝區有空閒空間)
  • 發生了一個異步錯誤


















發佈了44 篇原創文章 · 獲贊 14 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章