ネットワークIOからIO多重化への詳細な説明

I.はじめに

この記事では、LinuxのBIO、NIO、およびIO多重化に焦点を当てます。

第二に、Netcatソフトウェアの基本的な使用法

Netcat(ncと略記)は、ポートスキャン、ポートリダイレクト、ポートモニタリング、さらにはリモート接続など、LinuxでTCPおよびUDP関連の操作を実行できる強力なネットワークコマンドツールです。

ここでは、ncを使用して、メッセージを受信するサーバーとメッセージを送信するクライアントをシミュレートします。

1.ncソフトウェアをインストールします

sudo yum install -y nc

2. ncを使用して、ポート9999でリッスンするサーバーを作成します

nc -l -p 9999     # -l表示listening,监听

正常に起動すると、ncは
3をブロックします。新しいbashを作成し、ncを使用してメッセージを送信するクライアントを作成します。

nc localhost 9999

コンソールに送信する情報を入力し、サーバーがそれを受信したかどうかを
確認します。4。上記のncプロセスでファイル記述子を確認します。

ps -ef | grep nc  # 查看nc的进程号,这里假设是2603

ls /proc/2603/fd  # 查看2603进程下的文件描述符


このプロセスの下にソケットがあることがわかります。これは、ncのクライアントとサーバーの間に作成されたソケットです。
この一連の操作の後、Netcatソフトウェアの基本的な理解ができたと思います。BIOを紹介しましょう。

3、straceトラッキングシステムコール

Straceソフトウェアの説明:システムコールと信号を追跡できるソフトウェアであり、BIOについて学ぶことができます。

環境の説明:ここでのデモンストレーションは古いバージョンのLinuxに基づいています。新しいバージョンのLinuxはBIOを使用していないため、デモンストレーションを表示できません。


1.straceを使用してシステムコールを追跡します

sudo yum install -y strace  			# 安装strace软件

mkdir ~/strace  						# 新建一个目录,存放追踪的信息

cd ~/strace                 			# 进入到这个目录

strace -ff -o out nc -l -p 8080   # 使用strace追踪后边的命令进行的系统调用
								  # -ff 表示追踪后面命令创建的进程及子进程的所有系统调用,
                                  # 并根据进程id号分开输出到文件
                                  # -o  表示追踪到的信息输出到指定名称的文件,这里是out                                  


2.サーバーによって作成されたシステムコールを表示します

前の手順で入力したディレクトリに、out.pidファイルが表示されます。このファイルの内容は、このコマンドnc -l -p 9999の実行後のすべてのシステムコールプロセスです。vimコマンドを使用して表示します。

vim out.92459    # nc进程id为92459

ここでaccept()メソッドはブロックされており、他のソケットがそれに接続するのを待つ必要があります

3.クライアント接続、システムコールの確認

vimを終了し、テールを使用して表示します

tail -f out.92459

-fパラメーター:ファイルに追加のコンテンツがある場合、ファイルをコンソールにリアルタイムで印刷できるため、クライアントが接続された後に行われたシステムコールを表示するのに非常に便利です。

nc localhost 8080


システムコールを表示する

  • クライアントが接続された後、accept()メソッドはクライアント接続を取得し、ファイル記述子4を返します。この4は、クライアントと通信するためにサーバー上に新しく作成されたソケットです。
  • 次に、マルチプレクサポーリングを使用して、サーバー上のファイル記述子4と0を監視します。0は標準入力ファイル記述子です。イベントが発生した場合に読み取られるファイル記述子と、イベントが発生しなかった場合はブロックされます。

4.クライアントは、システムコールを表示するためのメッセージを送信します

クライアントはサーバーにデータを送信し、サーバーはソケットからのイベントの発生を監視し、対応する処理を実行し、処理後にブロックを継続し、次のイベントが発生するのを待つことができます


5.サーバーは、システムコールを表示するためにクライアントにデータを送信します

サーバーはデータを送信します。データはキーボードから入力する必要があります。これは標準入力0であり、0から読み取られ、データをソケット4に送信します。

Linux、Nginx、ZeroMQ、MySQL、 Redis、fastdfs、MongoDBを含む学習資料、完全なテクノロジースタック、コンテンツ知識を強化するための、知識と学習ネットワークの基礎となる原則のC / C ++ Linuxバックエンド開発の詳細を共有するZK、ストリーミングメディア、オーディオおよびビデオ開発、Linuxカーネル、CDN、P2P、K8S、Docker、TCP / IP、coroutine、DPDKなど。

ビデオ学習資料をクリックしてください:C / C ++ Linuxサーバー開発/ Linuxバックグラウンドアーキテクト-学習ビデオ

 

4、BIO(ブロッキングIO)

3番目のセクションでは、straceツールを使用してncソフトウェアの使用中のシステムコールを表示しました。実際、前のセクションはBIOを反映しています。BIOの直感的な理解に基づいて、上記の一連のシステムコールを要約します。

1.シングルスレッドモード

1.1、プロセスのデモンストレーション

1.サーバーの起動


サーバーを起動し、ソケット接続を待ちます。accept()メソッドは
2をブロックし、クライアントは接続します。データは送信されません。


クライアントを接続し、accept()メソッドを実行し、client1から送信されたデータを受信せず、read()メソッドが
3をブロックします。別のクライアントが接続します


read()メソッドがブロックされているため、accept()メソッドを実行できません。そのため、CPUは一度に1つのソケットしか処理できません。

1.2。既存の問題

上記のモデルには大きな問題があります。クライアントがサーバーとの接続を確立し、クライアントがデータの送信を遅らせると、プロセスは常にread()メソッドでブロックされるため、他のクライアントは接続できません。つまり、1つのクライアントしか接続できません。一度に処理されるため、顧客にとって非常に不親切です

1.3解決方法

実際、この問題を解決するのは非常に簡単です。複数のスレッドを使用できます。ソケットが接続されている限り、オペレーティングシステムは処理するスレッドを割り当てるため、read()メソッドは各スレッドでブロックされます。メインスレッドをブロックすることなく操作を実行できます。ソケット、スレッドにデータがあるソケット、読み取るソケット

2.マルチスレッドモード

1.1プロセスのデモンストレーション

  • プログラムサーバーは、accept()を使用してブロックし、クライアント接続があるかどうかの監視のみを担当します。
  • クライアント1はサーバーに接続し、スレッド(thread1)を開いてread()メソッドを実行し、プログラムサーバーは引き続き監視します。
  • クライアント2はサーバーに接続し、read()メソッドを実行するためのスレッドも開きます
  • 任意のスレッドの任意のソケットにデータが送信され、read()によってすぐに読み取ることができ、CPUがそれを処理できます。

1.2。既存の問題

上記のマルチスレッドモデルは完璧に見えますが、大きな問題もあります。クライアントが来るたびに、1つのスレッドを開く必要があります。10,000のクライアントがある場合は、10,000のスレッドを開く必要があります。オペレーティングシステムでは、ユーザーモードでスレッドを直接開くことはできません。CPUの80ソフト割り込みを呼び出して、カーネルにスレッドを作成させる必要があります。これには、非常にリソースであるユーザー状態の切り替え(コンテキスト切り替え)も含まれます。集中的な。

1.3解決方法

最初の方法:スレッドプールを使用します。これは、クライアント接続が少ない場合に使用できますが、ユーザー数が多い場合は、スレッドプールの大きさがわかりません。大きすぎる場合は、メモリが十分ではないかもしれない、それは現実的ではありません
解決策:。read()メソッドがブロックされているので、複数のスレッドを開くことが必要である任意の方法は、(読み取りを行うことができる場合)メソッドはブロックできない場合、何もありません複数のスレッドを開く必要があります。これは、別のIOモデルであるNIO(非ブロッキングIO)を使用します。

5、NIO(ノンブロッキングIO)

1.プロセスのデモンストレーション


1.サーバーが作成されたばかりで、クラ​​イアント接続がありません


NIOでは、accept()メソッドも非ブロッキングです。whileループ
2にあります。クライアントが接続するとき

3.接続する2番目のクライアントがある場合

2.まとめ


NIOモードでは、すべてが非ブロッキングです。

  • accept()メソッドは非ブロッキングであり、クライアント接続がない場合はエラーを返します
  • read()メソッドは非ブロッキングです。read()メソッドがデータを読み取れない場合はエラーを返します。データが読み取られた場合は、read()メソッドがデータを読み取る時間をブロックするだけです。

NIOモードでは、スレッドは1つだけです。

  • クライアントがサーバーに接続すると、ソケットが配列に追加され、ソケットのread()メソッドがデータを読み取れるかどうかを確認するために毎回トラバースされます。
  • このようなスレッドは、複数のクライアントの接続と読み取りを処理できます

3.既存の問題

NIOは、BIOがマルチスレッドを開く必要があるという問題を正常に解決しました。NIOの1つのスレッドで複数のソケットを解決できるため、完璧に見えますが、まだ問題があります。
このモデルは、クライアントが少ない場合に非常に便利ですが、接続しているクライアントが10,​​000の場合など、クライアントが多い場合は、各ループで10,000のソケットがトラバースされます。10,000のソケットのうち10のソケットしかない場合、データがある場合は10,000ソケットが変更され、多くの無駄な作業が行われます。そして、このトラバーサルプロセスはユーザーモードで実行されます。ユーザーモードは、ソケットにデータがあるか、カーネルのread()メソッドを呼び出すかを決定します。これには、ユーザーモードとカーネルモードの切り替えが含まれます。各トラバーサルは1回切り替える必要があります。
これらの問題のためにオーバーヘッドが大きく、IO多重化が発生しました

Linux、Nginx、ZeroMQ、MySQL、 Redis、fastdfs、MongoDBを含む学習資料、完全なテクノロジースタック、コンテンツ知識を強化するための、知識と学習ネットワークの基礎となる原則のC / C ++ Linuxバックエンド開発の詳細を共有するZK、ストリーミングメディア、オーディオおよびビデオ開発、Linuxカーネル、CDN、P2P、K8S、Docker、TCP / IP、coroutine、DPDKなど。

ビデオ学習資料をクリックしてください:C / C ++ Linuxサーバー開発/ Linuxバックグラウンドアーキテクト-学習ビデオ

 

6、IO多重化(IO多重化)


IO多重化には、select、poll、epollの3つの実装があります。次に、これら3つの実装の実際の色を見てみましょう。

1、選択

selectコードによって実装されたコード例もあります

1.1利点

Selectは、NIOのユーザーモードでトラバースするfd配列を実際にカーネルモードにコピーし、ユーザーモードがソケットにデータがあるか、カーネルモードが呼び出されるかを判断するため、カーネルモードをトラバースさせます。カーネルモードにコピーされると、このように判断がトラバースされます
ユーザーモードとカーネルモードを頻繁に切り替える必要はありません。コードからわかるように、selectシステム呼び出しの後、セット&rsetが返されるため、ユーザーはモードは単純なバイナリ比較を実行するだけでよく、どのソケットがデータを読み取る必要があるかをすばやく知ることができるため、効率が効果的に向上します。

1.2既存の問題


1.ビットマップの最大ビット数は1024ビットで、プロセスは1024クライアントしか処理でき
ません。2。&rsetは再利用できません。対応するビットはソケットにデータがあるたびに設定されます。3。
ファイル記述子配列はにコピーされますカーネルの状態であり、まだオーバーヘッドが
あります。4。Selectは、どのソケットにデータがあるかをユーザーに通知せず、O(n)トラバーサルが引き続き必要です。

2、世論調査

2.1コード例


pollでは、ファイル記述子は独立したデータ構造pollfdを持ち、pollfd配列はpollに渡され、他の実装ロジックはselectと同じです。

2.2利点

1. Pollは、selectのビットマップの代わりにpollfd配列を使用します。配列には1024の制限がなく、一度により多くの
クライアントを管理できます。2。pollfds配列でイベントが発生すると、対応するrevensが1に設定されます。トラバースすると再び設定されます。0に戻り、pollfd配列の再利用を実現します。

2.3デメリット

ポーリングは、selectの最初の2つの欠点を解決します。本質的な原則は依然としてselectの方法です
。selectには元の問題がまだあります。1。pollfds配列がカーネル状態にコピーされ、オーバーヘッドが残ります
。2。ポーリングは、どのソケットが状態にあるかをユーザーに通知しません。データ、引き続きO(n)トラバーサルが必要です。

3、epoll

3.1コード例

3.2イベント通知メカニズム

1.ネットワークカードにデータがある場合、最初にDMAに配置されます(メモリ内のバッファ。ネットワークカードはこのデータ領域に直接アクセスできます)
。2。ネットワークカードがcpuへの割り込みを開始し、 cpuは最初にネットワークカードを処理します。3
。割り込み番号はメモリ内のコールバックにバインドされます。どのソケットにデータがあり、コールバック関数はどのソケットを準備完了リストに入れます。

3.3詳細なプロセス

まず、epoll_createはepollインスタンスを作成します。これにより、必要な赤黒木、準備ができたリンクリスト、およびepollインスタンスを表すファイルハンドルが作成されます。実際、カーネル内のメモリスペースと、サーバーに接続されているすべてのソケットが開かれます。これらのソケットは赤黒木の形で存在し、準備ができたリンクリストを格納するためのスペースがあります。赤黒木は監視対象のファイル記述子のノードデータを格納します。準備完了リンクリストには、準備完了ファイル記述子のノードデータが格納されます
。epoll_ctlはnewを追加します。最初に、このファイル記述子ノードが赤黒木にあるかどうかを判断し、ある場合はすぐに戻ります。そうでない場合は、トランクに新しいノードを挿入し、カーネルにコールバック関数を登録するように指示します。ファイル記述子からデータを受信すると、カーネルはノードを準備完了リンクリストに挿入します。
epoll_waitはメッセージを受信し、データをユーザースペースにコピーして、リンクリストをクリアします。

3.4水平トリガーとエッジトリガー

Level_triggered:監視対象のファイル記述子で読み取り/書き込みイベントが発生すると、epoll_wait()はハンドラーに読み取りと書き込みを通知します。今回一度にすべてのデータの読み取りと書き込みを行わなかった場合(たとえば、読み取りと書き込みのバッファーが小さすぎる場合)、次にepoll_wait()を呼び出すと、ファイルの読み取りと書き込みを続行するように通知されます。読み書きされていない記述子。もちろん、読み書きを続けると、情報が得られます。システム内に読み取りまたは書き込みの必要がない多数の準備完了ファイル記述子があり、それらが毎回返される場合、これにより、処理プログラムが関心のある準備完了ファイル記述子を取得する効率が大幅に低下します。 !!
Edge_triggered:監視対象のファイル記述子で読み取り/書き込みイベントが発生すると、epoll_wait()はハンドラーに読み取りと書き込みを通知します。今回すべてのデータの読み取りと書き込みを行わなかった場合(読み取りと書き込みのバッファーが小さすぎるなど)、次にepoll_wait()を呼び出したときに通知されません。つまり、通知されるのは1回だけです。ファイル記述子まで2番目の読み取り/書き込みイベントが発生すると通知されます!このモードは水平トリガーよりも効率的であり、システムはあなたが気にしない多数の準備ができたファイル記述子で溢れることはありません!

3.5利点

Epollは現在最も高度なIOマルチプレクサです。LinuxのRedis、Nginx、およびJava NIOはすべてepollを使用します
。1 ソケットのライフサイクルでユーザーモードからカーネルモードへのコピーは1つだけであり、オーバーヘッドは小さいです。
2、イベント通知メカニズムを使用して、ソケットにデータがあるたびに、すべてのソケットをトラバースすることなく、カーネルにアクティブに通知し、それを準備完了リストに追加します

 

おすすめ

転載: blog.csdn.net/Linuxhus/article/details/114414682