成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

系統 Linux 通信技術
任何編程語言都會遇到這種CPU處理速度和I/O速度不匹配的問題!在網絡編程中如何進行網絡I/O優化: 怎么高效地利用CPU進行網絡數據處理? ? ?

I/O( INPUT OUTPUT),包括文件I/O、網絡I/O。

計算機世界里的速度鄙視:

  • 內存讀數據:納秒級別。
  • 千兆網卡讀數據: 微秒級別。 1微秒 =1000納秒,網卡比內存慢了千倍。
  • 磁盤讀數據:毫秒級別。1毫秒=10萬納秒 ,硬盤比內存慢了10萬倍。
  • CPU一個時鐘周期1納秒上下,內存算是比較接近CPU的,其他都等不起。

CPU 處理數據的速度遠大于I/O準備數據的速度 。

任何編程語言都會遇到這種CPU處理速度和I/O速度不匹配的問題!

在網絡編程中如何進行網絡I/O優化: 怎么高效地利用CPU進行網絡數據處理? ? ?

[[285393]]

一、相關概念

從操作系統層面怎么理解網絡I/O呢? 計算機的世界有一套自己定義的概念。如果不明白這些概念,就無法真正明白技術的設計思路和本質。所以在我看來,這些概念是了解技術和計算機世界的基礎。

1. 同步與異步,阻塞與非阻塞

理解網絡I/O避不開的話題:同步與異步,阻塞與非阻塞。

拿山治燒水舉例來說,(山治的行為好比用戶程序,燒水好比內核提供的系統調用),這兩組概念翻譯成大白話可以這么理解。

  • 同步/異步關注的是水燒開之后需不需要我來處理。
  • 阻塞/非阻塞關注的是在水燒開的這段時間是不是干了其他事。

(1) 同步阻塞

點火后,傻等,不等到水開堅決不干任何事(阻塞),水開了關火(同步)。

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

(2)  同步非阻塞

點火后,去看電視(非阻塞),時不時看水開了沒有,水開后關火(同步)。

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

(3) 異步阻塞

按下開關后,傻等水開(阻塞),水開后自動斷電(異步)。

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

網絡編程中不存在的模型。

(4) 異步非阻塞

按下開關后,該干嘛干嘛 (非阻塞),水開后自動斷電(異步)。

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

2.  內核空間 、用戶空間

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

內核負責網絡和文件數據的讀寫。

用戶程序通過系統調用獲得網絡和文件的數據。

(1) 內核態 用戶態

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

程序為讀寫數據不得不發生系統調用。

通過系統調用接口,線程從用戶態切換到內核態,內核讀寫數據后,再切換回來。

進程或線程的不同空間狀態。

(2) 線程的切換

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

用戶態和內核態的切換耗時,費資源(內存、CPU)

優化建議:

  • 更少的切換
  • 共享空間

3. 套接字 – socket

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

有了套接字,才可以進行網絡編程。

應用程序通過系統調用socket(),建立連接,接收和發送數據(I / O)。

SOCKET 支持了非阻塞,應用程序才能非阻塞調用,支持了異步,應用程序才能異步調用

4.  文件描述符 –FD 句柄

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

網絡編程都需要知道FD? ??FD是個什么鬼???

Linux:萬物都是文件,FD就是文件的引用。像不像JAVA中萬物都是對象?程序中操作的是對象的引用。JAVA中創建對象的個數有內存的限制,同樣FD的個數也是有限制的。

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

Linux在處理文件和網絡連接時,都需要打開和關閉FD。

每個進程都會有默認的FD:

  • 0 標準輸入 stdin
  • 1 標準輸出 stdout
  • 2 錯誤輸出 stderr

5. 服務端處理網絡請求的過程

  • 連接建立后
  • 等待數據準備好(CPU 閑置)
  • 將數據從內核拷貝到進程中(CPU閑置)

怎么優化呢?

對于一次I/O訪問(以read舉例),數據會先被拷貝到操作系統內核的緩沖區,然后才會從操作系統內核的緩沖區拷貝到應用程序的地址空間。

所以說,當一個read操作發生時,它會經歷兩個階段:

  • 等待數據準備 (Waiting for the data to be ready)。
  • 將數據從內核拷貝到進程中 (Copying the data from the kernel to the process)。

正是因為這兩個階段,Linux系統升級迭代中出現了下面三種網絡模式的解決方案。

二、I/O模型

1. 阻塞 I/O - Blocking I/O

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

簡介:最原始的網絡I/O模型。進程會一直阻塞,直到數據拷貝完成。

缺點:高并發時,服務端與客戶端對等連接,線程多帶來的問題:

  • CPU資源浪費,上下文切換。
  • 內存成本幾何上升,JVM一個線程的成本約1MB。
  1. public static void main(String[] args) throws IOException { 
  2.  ServerSocket ss = new ServerSocket(); 
  3.  ss.bind(new InetSocketAddress(Constant.HOST, Constant.PORT)); 
  4.  int idx =0
  5.  while (true) { 
  6.  final Socket socket = ss.accept();//阻塞方法 
  7.  new Thread(() -> { 
  8.  handle(socket); 
  9.  },"線程["+idx+"]" ).start(); 
  10.  } 
  11.  } 
  12.  
  13.  static void handle(Socket socket) { 
  14.  byte[] bytes = new byte[1024]; 
  15.  try { 
  16.  String serverMsg = " server sss[ 線程:"+ Thread.currentThread().getName() +"]"; 
  17.  socket.getOutputStream().write(serverMsg.getBytes());//阻塞方法 
  18.  socket.getOutputStream().flush(); 
  19.  } catch (Exception e) { 
  20.  e.printStackTrace(); 
  21.  } 
  22.  } 

2. 非阻塞 I/O - Non Blocking IO

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

簡介: 進程反復系統調用,并馬上返回結果。

缺點: 當進程有1000fds,代表用戶進程輪詢發生系統調用1000次kernel,來回的用戶態和內核態的切換,成本幾何上升。

  1. public static void main(String[] args) throws IOException { 
  2.  ServerSocketChannel ss = ServerSocketChannel.open(); 
  3.  ss.bind(new InetSocketAddress(Constant.HOST, Constant.PORT)); 
  4.  System.out.println(" NIO server started ... "); 
  5.  ss.configureBlocking(false); 
  6.  int idx =0
  7.  while (true) { 
  8.  final SocketChannel socket = ss.accept();//阻塞方法 
  9.  new Thread(() -> { 
  10.  handle(socket); 
  11.  },"線程["+idx+"]" ).start(); 
  12.  } 
  13.  } 
  14.  static void handle(SocketChannel socket) { 
  15.  try { 
  16.  socket.configureBlocking(false); 
  17.  ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 
  18.  socket.read(byteBuffer); 
  19.  byteBuffer.flip(); 
  20.  System.out.println("請求:" + new String(byteBuffer.array())); 
  21.  String resp = "服務器響應"
  22.  byteBuffer.get(resp.getBytes()); 
  23.  socket.write(byteBuffer); 
  24.  } catch (IOException e) { 
  25.  e.printStackTrace(); 
  26.  } 
  27.  } 

3. I/O 多路復用 - IO multiplexing

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

簡介: 單個線程就可以同時處理多個網絡連接。 內核負責輪詢所有socket,當某個socket有數據到達了,就通知用戶進程。 多路復用在Linux內核代碼迭代過程中依次支持了三種調用,即SELECT、POLL、EPOLL三種多路復用的網絡I/O模型。 下文將畫圖結合Java代碼解釋。

(1) I/O 多路復用- select

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

簡介: 有連接請求抵達了再檢查處理。

缺點:

  • 句柄上限- 默認打開的FD有限制,1024個。
  • 重復初始化-每次調用 select(),需要把 fd 集合從用戶態拷貝到內核態,內核進行遍歷。
  • 逐個排查所有FD狀態效率不高。

服務端的select 就像一塊布滿插口的插排,client端的連接連上其中一個插口,建立了一個通道,然后再在通道依次注冊讀寫事件。 一個就緒、讀或寫事件處理時一定記得刪除,要不下次還能處理。

  1. public static void main(String[] args) throws IOException { 
  2.  ServerSocketChannel ssc = ServerSocketChannel.open();//管道型ServerSocket 
  3.  ssc.socket().bind(new InetSocketAddress(Constant.HOST, Constant.PORT)); 
  4.  ssc.configureBlocking(false);//設置非阻塞 
  5.  System.out.println(" NIO single server started, listening on :" + ssc.getLocalAddress()); 
  6.  Selector selector = Selector.open(); 
  7.  ssc.register(selector, SelectionKey.OP_ACCEPT);//在建立好的管道上,注冊關心的事件 就緒 
  8.  while(true) { 
  9.  selector.select(); 
  10.  Set<SelectionKey> keys = selector.selectedKeys(); 
  11.  Iterator<SelectionKey> it = keys.iterator(); 
  12.  while(it.hasNext()) { 
  13.  SelectionKey key = it.next(); 
  14.  it.remove();//處理的事件,必須刪除 
  15.  handle(key); 
  16.  } 
  17.  } 
  18.  } 
  19.  private static void handle(SelectionKey key) throws IOException { 
  20.  if(key.isAcceptable()) { 
  21.  ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); 
  22.  SocketChannel sc = ssc.accept(); 
  23.  sc.configureBlocking(false);//設置非阻塞 
  24.  sc.register(key.selector(), SelectionKey.OP_READ );//在建立好的管道上,注冊關心的事件 可讀 
  25.  } else if (key.isReadable()) { //flip 
  26.  SocketChannel sc = null
  27.  sc = (SocketChannel)key.channel(); 
  28.  ByteBuffer buffer = ByteBuffer.allocate(512); 
  29.  buffer.clear(); 
  30.  int len = sc.read(buffer); 
  31.  if(len != -1) { 
  32.  System.out.println("[" +Thread.currentThread().getName()+"] recv :"+ new String(buffer.array(), 0, len)); 
  33.  } 
  34.  ByteBuffer bufferToWrite = ByteBuffer.wrap("HelloClient".getBytes()); 
  35.  sc.write(bufferToWrite); 
  36.  } 
  37.  } 

2.3.2 I/O 多路復用 – poll

從操作系統層面理解Linux下的網絡IO模型,一篇抵十篇

簡介: 設計新的數據結構(鏈表)提供使用效率。

poll和select相比在本質上變化不大,只是poll沒有了select方式的最大文件描述符數量的限制。

缺點: 逐個排查所有FD狀態效率不高。

2.3.3 I/O 多路復用- epoll

簡介: 沒有fd個數限制,用戶態拷貝到內核態只需要一次,使用事件通知機制來觸發。 通過epoll_ctl注冊fd,一旦fd就緒就會通過callback回調機制來激活對應fd,進行相關的I/O操作。

缺點:

  • 跨平臺,Linux 支持最好
  • 底層實現復雜
  • 同步
  1. public static void main(String[] args) throws Exception { 
  2.  final AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open() 
  3.  .bind(new InetSocketAddress(Constant.HOST, Constant.PORT)); 
  4.  serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() { 
  5.  @Override 
  6.  public void completed(final AsynchronousSocketChannel client, Object attachment) { 
  7.  serverChannel.accept(null, this); 
  8.  ByteBuffer buffer = ByteBuffer.allocate(1024); 
  9.  client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() { 
  10.  @Override 
  11.  public void completed(Integer result, ByteBuffer attachment) { 
  12.  attachment.flip(); 
  13.  client.write(ByteBuffer.wrap("HelloClient".getBytes()));//業務邏輯 
  14.  } 
  15.  @Override 
  16.  public void failed(Throwable exc, ByteBuffer attachment) { 
  17.  System.out.println(exc.getMessage());//失敗處理 
  18.  } 
  19.  }); 
  20.  } 
  21.  
  22.  @Override 
  23.  public void failed(Throwable exc, Object attachment) { 
  24.  exc.printStackTrace();//失敗處理 
  25.  } 
  26.  }); 
  27.  while (true) { 
  28.  //不while true main方法一瞬間結束 
  29.  } 
  30.  } 

當然上面的缺點相比較它優點都可以忽略。 JDK提供了異步方式實現,但在實際的Linux環境中底層還是epoll,只不過多了一層循環,不算真正的異步非阻塞。 而且就像上圖中代碼調用,處理網絡連接的代碼和業務代碼解耦得不夠好。 Netty提供了簡潔、解耦、結構清晰的API。

  1. public static void main(String[] args) { 
  2.  new NettyServer().serverStart(); 
  3.  System.out.println("Netty server started !"); 
  4.  } 
  5.  
  6.  public void serverStart() { 
  7.  EventLoopGroup bossGroup = new NioEventLoopGroup(); 
  8.  EventLoopGroup workerGroup = new NioEventLoopGroup(); 
  9.  ServerBootstrap b = new ServerBootstrap(); 
  10.  b.group(bossGroup, workerGroup) 
  11.  .channel(NioServerSocketChannel.class) 
  12.  .childHandler(new ChannelInitializer<SocketChannel>() { 
  13.  @Override 
  14.  protected void initChannel(SocketChannel ch) throws Exception { 
  15.  ch.pipeline().addLast(new Handler()); 
  16.  } 
  17.  }); 
  18.  try { 
  19.  ChannelFuture f = b.localAddress(Constant.HOST, Constant.PORT).bind().sync(); 
  20.  f.channel().closeFuture().sync(); 
  21.  } catch (InterruptedException e) { 
  22.  e.printStackTrace(); 
  23.  } finally { 
  24.  workerGroup.shutdownGracefully(); 
  25.  bossGroup.shutdownGracefully(); 
  26.  } 
  27.  } 
  28.  
  29. class Handler extends ChannelInboundHandlerAdapter { 
  30.  @Override 
  31.  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
  32.  ByteBuf buf = (ByteBuf) msg; 
  33.  ctx.writeAndFlush(msg); 
  34.  ctx.close(); 
  35.  } 
  36.  
  37.  @Override 
  38.  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
  39.  cause.printStackTrace(); 
  40.  ctx.close(); 
  41.  } 

bossGroup 處理網絡請求的大管家(們),網絡連接就緒時,交給workGroup干活的工人(們)。

三、總結

  • 同步/異步,連接建立后,用戶程序讀寫時,如果最終還是需要用戶程序來調用系統read()來讀數據,那就是同步的,反之是異步。 Windows實現了真正的異步,內核代碼甚為復雜,但對用戶程序來說是透明的。
  • 阻塞/非阻塞,連接建立后,用戶程序在等待可讀可寫時,是不是可以干別的事兒。 如果可以就是非阻塞,反之阻塞。 大多數操作系統都支持的。

Redis,Nginx,Netty,Node.js 為什么這么香?

這些技術都是伴隨Linux內核迭代中提供了高效處理網絡請求的系統調用而出現的。 了解計算機底層的知識才能更深刻地理解I/O,知其然,更要知其所以然。

 

責任編輯:趙寧寧 來源: 今日頭條
相關推薦

2021-06-30 15:25:13

操作系統Java

2019-11-25 09:46:32

Linux操作系統管理

2019-11-07 11:08:16

Linux操作系統目錄

2021-07-01 11:56:35

操作系統Java IO

2024-04-17 09:52:00

操作系統多線程內存

2022-03-11 10:21:30

IO系統日志

2022-03-10 08:31:51

REST接口規范設計Restful架構

2023-05-11 20:15:36

2023-09-11 07:47:25

2022-12-20 08:22:42

CommitMuation

2021-06-01 08:37:45

Linuxdrm內存

2021-09-06 06:31:40

理解動態規劃

2020-11-27 08:02:41

Promise

2023-11-06 07:27:38

模型NLP領域

2023-06-26 00:26:40

I/OJava字節流

2021-12-04 22:05:02

Linux

2021-10-29 07:35:32

Linux 命令系統

2023-02-27 10:17:05

EventBus觀察者模式

2023-04-26 08:50:23

IO模型操作系統Java

2018-05-31 09:44:01

微服務架構數據
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品亚洲一区 | 日本三级视频 | h视频在线免费观看 | 国产99视频精品免费视频7 | 一区二区三区四区在线 | 人人99| 一区二区三 | 91精品国产欧美一区二区成人 | 天天操天天摸天天干 | 亚洲综合在线视频 | 免费观看国产视频在线 | av日日操 | 久久一级免费视频 | 午夜日韩 | 精品一区二区三区四区 | 欧美一区二区三区视频在线观看 | 欧美一区二区视频 | 欧美综合一区二区三区 | 91电影| 91欧美| 亚洲久草 | 国产一区二区三区四区三区四 | 成人不卡 | 亚洲精品电影网在线观看 | 成人免费视屏 | 久久国产精品免费 | 天堂久久av | 国产成人99久久亚洲综合精品 | 第一区在线观看免费国语入口 | 国产欧美性成人精品午夜 | 国产区视频在线观看 | 99久久久国产精品 | 欧美一区2区三区4区公司二百 | 午夜不卡一区二区 | 国产精品高清在线 | 欧美精品网站 | 超碰在线97国产 | 亚洲一区二区在线播放 | 伊人二区 | 精品综合久久久 | 成人深夜福利 |