Skip to content

Latest commit

 

History

History
174 lines (132 loc) · 3.86 KB

IO.md

File metadata and controls

174 lines (132 loc) · 3.86 KB

IO(輸入/輸出) Back

  • Linux系統下I/O模型:
    • 阻塞式I/O
    • 非阻塞式I/O
    • 多路複用I/O
    • 信號驅動I/O

1. 阻塞式IO

  • 優點: 結構簡單, 容易同步
  • 缺點: 阻塞導致CPU資源浪費
  • 通過調用alarm()來進行超時控制
    • 超時到達時產生SIGALARM信號中斷阻塞
    • 多次調用時則無法區分SIGALARM信號是哪一次超時引發, 從而導致無法實現超時控制
  • 通過設置socket選項進行超時控制
    • 設置SO_RCVTIMEO和SO_SNDTIMEO
    • 只需設置一次, 對後來的讀寫操作均有效
    • 不適用於accept和connect
1.1 Read

  • 產生阻塞的函數:

    • read()
    • readv()
    • recv()
    • recvfrom()
    • recvmsg()
  • 進程喚醒的狀態:

    • TCP以字節為單位, 只要接收緩衝區出現數據則喚醒
    • UDP以數據報為單位, 當完整的數據報到達才喚醒
1.2 Write

  • 產生阻塞的函數:

    • write()
    • writev()
    • send()
    • sendto()
    • sendmsg()
  • UDP協議寫操作永遠不會阻塞

1.3 Connect

  • 產生阻塞的函數:

    • connect()
  • TCP下, Client收到Server的SYN字段則表示connect()成功.

  • TCP連接操作至少需要一個往返時間

  • UDP沒有連接, 故不產生阻塞

1.4 Accpet

  • 產生阻塞的函數:
    • accept()

2. 非阻塞式IO

  • 優點: 效率提高, 不會產生阻塞
  • 缺點: 長期佔用CPU

  • 設置socket為非阻塞方式

    • fcntl()
     int flags;
     flag = fcntl(sockfd, F_GETFL, 0);
     fcntl(sockfd, F_SETFL, flag| O_NONBLOCK);
    • ioctl
     int on = 1;
     ioctl(sockfd, FIONBIO, &on);
  • return ERROR

    • read(): EWOULDBLOCK
    • write(): EWOULDBLOCK(無空間), 空間不夠則返回實際拷貝字節
    • connect(): EINPROGRESS(啟動三次握手後立即返回), 同一主機上鏈接則立即返回成功
    • accept(): EWOULDBLOCK(沒有鏈接立即返回)
  • 檢查操作是否完成

    • 輪詢
     for(;;)
     {
     	if(read(sockfd, buf, nbytes) < 0)
     	{
     		if(errno == EWOULDBLOCK) continue;
     		else
     		{
     			printf("read error\n");
     			break;
     		}
     	}
     	else
     		break;	//read complete
     }
    • select()
     // set the descriptors set for monitoring
     //...
     select(reset, wrset, exset, ...)
     //read, write ...
     //...

3. 多路服用IO

  • 只檢查一個socket描述符時和阻塞式IO模型類似, 效率低於阻塞式.
  • 檢查多個socket描述符效率則高於阻塞式IO.

  • socket描述符就緒的條件:
    • 讀就緒條件:
      • 接收緩衝區中數據量大於等於接受最低限度(默認接受最低限度是1: TCP為1個字節;UDP為1個數據報. 可通過SO_RCVLOWAT修改)
      • 讀通道被關閉, 收到FIN字段
      • 偵聽socket的完成鏈接隊列不為空
      • 非阻塞式socket的connect操作過程出現錯誤
    • 寫就緒條件:
      • 發送緩衝區可用空間大於等於發送最低限度(TCP默認發送最低限度是2048字節, UDP沒有實際的發送緩衝區, 因此總是写就绪. 可通過SO_SNDLOWAT修改)
    • 異常就緒條件(用於帶外數據)

4. 信號驅動IO

  • 非阻塞等待IO
  • 程序結構簡單, 更適用於UDP協議, 因為TCP在很多環節會產生SIGIO信號, 從而產生混淆, 而UDP只在收到數據報或錯誤時產生SIGIO信號.

  • 設置步驟:

      1. 設置SIGIO
     void sigio_handler(int signo)
     {
     	//...
     }
     int sockfd;
     int on = 1;
     signal(SIGIO, sigio_handler);
      1. 設置socket描述符所有者
     fcntl(sockfd, F_SETOWN, getpid());
      1. 允許socket進行信號驅動IO
     ioctl(sockfd, FIOASYNC, &on);