[コンピュータ ネットワーク] 高度な IO 選択

1.IOとは何ですか?

IO: 入力と出力を表します


相手が接続を確立したがデータを送信せず、
私が read を呼び出したスレッドである場合、ブロックしてデータが送信されるのを待ちます。つまり、読み取り条件が満たされない
場合、read または recv はただ待つだけ。


データがあるときにコピーする場合でも、データがない場合に待機する場合
でも、両方の時間コストはすべてユーザー側で計算されます。

ユーザーの観点から見ると、IO=etc+データコピー

効率的な IO とは何ですか?

単位時間当たりの待機の割合が低いほど、IO 効率は高くなります。

IO 条件が満たされると、 IO イベント準備完了と呼ばれます。

2. IOの5つのモデル

5 つの IO モデルの概念的な理解

例: 釣りを 2 つのステップに分けて行うと仮定します。釣り = 待つ + 釣り

釣り竿の針にウキを掛けて水面に浮かべると、釣り竿の深さが安定し、
ウキが上下に揺れると魚が掛かっていることが分かります。


1.張三さんは基本的に一つのことに集中するのが好きなので、張三さんが釣りをして待っているときは、餌を食べる魚がいないかウキを見つめ続けます。しばらくすると、ウキが動きます
。サンは釣り竿を引いて魚を捕まえ、バケツに入れて
釣りを続けます
が、釣りをしている間、張三はただ魚の浮きを見つめるだけで、他には何もしませんでした。


2.李四はもともと活発なので、釣りをしているときは魚の浮きにはあまり注意を払わず、左右を見て、釣り針に
掛かっている魚を見つけると、魚を捕まえます。それをバケツに入れて
釣りを続けます。
釣りをしている間、リー・シーは魚が浮かぶのを見る以外にも他のことをしていました。


3.王武は特別です。王武は釣りをするとき、釣り竿の上に鈴を付けて、魚が餌を取るのを待ちます。待っている間、
王武は自分のことをしています。
王武がその声を聞くと、鐘を鳴らして魚を引っ張り、竿を立てて魚を捕まえ、バケツに入れ、
先ほどの釣りを続けました
。釣りの過程で、王武さんは魚の浮きを見ずに、鐘の音だけを聞いていました。餌を食べる魚がいるかどうかを判断します。


4. Zhao Liu はこの地域で一番裕福な男です。彼はピックアップ トラックを運転して釣りをしています。ピックアップ トラックには 10 本の釣り竿が取り付けられています (最も裕福な男は人生を体験するためにここにいます)。彼は 10 本の釣り竿を使って一緒に釣りをします
。趙劉さんは浮いている魚がいないか前から後ろまでチェックします。


5. Tian Qi は半径 500 キロメートル以内で最も裕福な男です(Tian Qi は Zhao Liu よりも裕福です)。Tian
Qi はとても忙しく、毎日さまざまな会議を開催しています。さらに、Tian Qi は釣りをしたくありませんが、釣りが好きです。魚を食べたかったので、運転
手シャオ・ワンに釣りに行くのを手伝ってもらいました。魚のバケツがいっぱいになったら、ティアン・チーに電話すると、誰かが魚を取りに来ます。シャオ・ワンが釣りをしている間、ティアン・チーも会議中です。


張三と李四は同じように釣りをしています。違いは、
魚が餌を食べるまでの待ち方です。張三は魚が浮いているときは動かないですが、
李四は魚が浮いていたらすぐに戻って他のことをします。魚が浮かない。


Zhang San のフィッシング方法はブロッキング IOと呼ばれます
(データの準備ができていないため、呼び出された読み取りインターフェイスは返されません)。


Li Siのフィッシング方法はノンブロッキングIOと呼ばれます(一度テストしてデータがなければすぐに戻り、一定時間後に再度テストできます)


Wang Wu は、魚を捕まえる前に、ベルが鳴ったら釣り竿を引く必要があることを知っています。Wang
Wu の釣り方法は、信号駆動型 IO と呼ばれます。


趙立は一度に複数の釣り竿を管理し、趙立の漁法はマルチプレキシングまたはマルチプレキシングと呼ばれます。

これらの人々の中で、趙劉の釣り効率は比較的高い。
なぜなら、趙劉はより多くの釣り竿を持っているため、魚がかかる確率が高い、つまり待ち時間が短いため、
趙劉の釣り効率が相対的に高い。


同期IOと非同期IO

最初の 4 人はフィッシング プロセスを実行する必要があるため、全員が同期 IO と呼ばれます。

Tianqi はフィッシング プロセスに参加せず、待機せず、フィッシング プロセスを開始しただけであり、
Tianqi のフィッシング メソッドは非同期 IO と呼ばれます。


全体的な理解

釣りはデータのコピーとして見ることができ、Zhang San、Li Si らはプロセスとして見ることができ、 Tian Qi はプロセスとして見ることができ、ドライバーの Xiao Wang はオペレーティング システムとして見ることができ、釣り竿はファイルとして見ることができます。記述子で魚をデータとして見ることができる魚の食い込みや浮きの動きやベルの鳴動をIOイベント準備完了とみなすことができる





プロセスがファイル記述子のデータを読み取るときに、データの準備ができていない場合、現在のプロセスはハングして IO 時間が利用可能になるまで待機することしかできず
、その後、データを対応する上位層にコピーできます。

3. IO のブロック

ブロッキング IO: データの準備ができていないため、呼び出された読み取りインターフェイスは戻りません。


read関数を使ってキーボードから読み込むと、コードを書くときに何も入力されていなければ何も表示されず、IOがブロックされてしまいます。

man 2 を入力して読んでください

ファイル記述子からカウント データを buf バッファに読み取ります
。取得が成功すると、バイト データが返されます。
取得が 0 の場合は、ファイルの終わりが読み込まれたことを意味します
。取得が -1 の場合、失敗を意味します。とエラーコードが設定されます。


0は標準入力ストリームを示し、
標準入力ストリームからバッファ配列サイズのデータ​​を読み出してバッファに送信します。

実行可能プログラムの実行後、入力がないため、読み取りはデータが入力されるまで待機してからデータをコピーします。

4. ノンブロッキング IO

ノンブロッキングIO: 一度テストしてデータがなければ、すぐに他の作業に戻り、しばらくしてから再度テストできます。



read関数を使ってキーボードから読み込むことで、コードを書いた時点では入力がなく、読み込み条件が満たされない場合のみ読み込みを待つ状況をシミュレートします。

上記のブロッキング IO コードに基づいて変更します。


setノンブロック関数

man fcntlと入力してください

最初のパラメータはファイル記述子です。2
番目のパラメータはファイル記述子で何をしたいかを示します。
ファイル状態フラグを取得/設定します (cmd= F_GETFLまたはF_SETFL )

ファイルステータスフラグを設定することで、ファイル記述子をノンブロッキングにすることができます。

F_GETFLを使用して現在のファイル記述子の属性を取得します。F_SETFL使用してファイル記述子のステータスを設定し、O_NONBLOCK (非ブロッキング) パラメータを追加します。

関数が -1 を返した場合、失敗を示します。


ファイル記述子を非ブロッキング状態に設定する関数setnonblockを作成します。まず、F_GETFLを使用して、対応するファイル記述子の属性を取得します。取得が失敗した場合は、エラー理由とエラー コードが返されます。取得が成功した場合は、次を使用します。F_SETFL:ファイル記述子のステータスを非ブロッキング状態に設定します。



ノンブロッキング IO で読み取りエラーが発生するのはなぜですか?

メイン関数 main では、標準入力ストリームを非ブロッキング状態に変更し
、読み取りの 3 つの戻り値 (読み取り成功、ファイルの終わり、読み取りエラー) に従ってリターン プロンプトを設定します。


標準入力ストリームがノンブロッキング状態に設定され、
実行可能プログラムが再度実行されると、読み取りは直接失敗し、
read を呼び出すと、データの準備ができていないことがわかります(現在の読み取り検出速度が速すぎるため、入力がある前にエラーが報告されます)

したがって、基になるデータの準備ができていない場合は、エラーの形式で返されますが、それは実際のエラーとはみなされません。


しかし、このままでは本当にエラーがあるのか​​、最下層にデータがないのか区別する方法がないので、エラーコードによってさらに判断することになります

エラーコードのさらなる判定

EAGAINEWOULDBLOCKは両方ともシステムによって設定されます。エラー コードは 11 で
、エラーが存在しないことを判断するために使用されます。ただし、エラーの形式で返されたエラー コードが
true の場合は、次のエラー コードの検出を続けることができます。時間。


IO が信号によって中断された場合は、再検出します


データの準備ができていないことが検出された場合は、他の作業に戻ります。

ノンブロッキング IO は、検出されたデータの準備ができていないときに戻って他の作業を行うことができます。


パラメータが void で戻り値が void のラッパーを定義し、その名前を func_t 型に変更し、
func_t 型の vetcor 配列を定義します。


3 つのタスク、つまり PrintLog OperMysql CheckNet をセットアップします。


LoadTask 関数を作成するときは、タスクを funcs 配列に挿入します。


メイン関数 main で、LoadTask 関数を呼び出してタスクをロードします。


ベクトル配列を走査する HandlerALLTask 関数を作成します。配列要素はタスクです。
データの準備ができていない場合は、処理タスクが返されます。


完全なコード

mytest.cc

#include<iostream>
#include<unistd.h>
#include<fcntl.h>
#include<cstdio>
#include<cstring>
#include<vector>
#include<functional>
using namespace std;

//任务
void PrintLog()//打印日志
{
    
    
   cout<<"这是一个打印日志例程"<<endl;
}

void OperMysql()
{
    
    
  cout<<"这是一个操作数据库的例程"<<endl;
}

void CheckNet()
{
    
    
    cout<<"这是一个检测网络状态的例程"<<endl;
}

using func_t =function<void(void)>;
vector<func_t>  funcs;

void LoadTask()
{
    
    
    funcs.push_back(PrintLog);
    funcs.push_back( OperMysql);
    funcs.push_back(CheckNet);
}

void HandlerALLTask()
{
    
    
    //遍历vector数组
  for(auto& func:funcs)
  {
    
    
    func();
  }
}

void SetNonBlock(int fd)//将文件描述符设为非阻塞
{
    
    
   int fl=fcntl(fd,F_GETFL);//获取当前文件描述符的指定状态标志位
    if(fl<0)//获取失败
    {
    
    
          cerr<<"error string: "<<strerror(errno)<<"error code: "<<errno<<endl;
    }
    fcntl(fd,F_SETFL,fl | O_NONBLOCK);//将文件描述符状态设为非阻塞状态
}

int main()
{
    
    
    char buffer[64];
    SetNonBlock(0);//将标准输入流 改为非阻塞状态
    LoadTask();//加载任务
   while(true)
   {
    
    
    
      //0表示标准输入流
      ssize_t n=read(0,buffer,sizeof(buffer)-1);//检测条件是否就绪
      if(n>0)//读取成功
      {
    
    
        buffer[n-1]=0;    
        cout<<"echo# "<<buffer<<endl; 
      }
      else if(n==0)//读到文件结尾
      {
    
    
        cout<<"end file"<<endl;
      }
      else//读取失败 
      {
    
    
        if(errno==EAGAIN || errno ==EWOULDBLOCK)
        {
    
    
          //若为真,说明没出错,只是以出错返回
          //底层数据没有准备好,下次继续检测

          HandlerALLTask();//遍历数组 处理任务
          sleep(1);
          cout<<"data not  ready"<<endl;
          continue;
          
        }
        else if(errno == EINTR)
        {
    
    
            //IO被信号中断,需要重新检测
            continue;
        }
        else //真正的错误
        {
    
    
             cout<<"read error"<<"error string: "<<strerror(errno)<<"error code: "<<errno<<endl;
             break;
        }
      }
      sleep(1);

   }
    return 0;
}

 

メイクファイル

mytest:mytest.cc
	g++ -o $@ $^ -std=c++11
	
.PHONY:clean
clean:
	rm -f mytest

5.選択

なぜ選択肢があるのでしょうか?

read/recv などのファイル インターフェイスには、ファイル記述子が 1 つしかあり
ません。複数のファイル記述子を待機するインターフェイスが必要な場合、read などのインターフェイスにはこの機能がありません。オペレーティング システムは、多重化用のインターフェイス選択
を設計します


select の機能は次のとおりです。
1. 複数のファイル記述子を待機します
。 2. 待機のみを担当します (データをコピーする機能はありません)。


インターフェイスの選択

男性選択を入力してください

select は待機のみを担当し、コピーは行わないため、バッファはありません。

最初のパラメータ nfds についての理解

最初のパラメータ nfds は入力パラメータで、select が待機している複数のファイル記述子 (fd) の最大数 + 1 を表します
(ファイル記述子の本質は配列の添字であり、複数のファイルの中で最大の値です) descriptors はファイル記述子の値 + 1 (nfds)

入力パラメータと出力パラメータとは何ですか

ユーザーはデータをオペレーティング システムに渡し、オペレーティング システムもこれらの出力パラメーターを通じて結果をユーザーに渡します。
ユーザーとオペレーティング システムの間で情報を転送できるようにするために、パラメーターは入力パラメーターおよび出力パラメーターとして設定されます。

最後のパラメータのタイムアウトについての理解

timeout は入出力パラメータです


タイムアウトのデータ型はstruct timevalです。

時間構造体にすることができます。tv_sec は秒を表し、tv_usec はマイクロ秒を表します


struct timevalのオブジェクトには3つの値を設定可能

最初のタイプのオブジェクトはNULL に設定されます。これは、選択の待機をブロックすることを意味します
(複数のファイル記述子のいずれも準備ができていないため、選択は返されません)。

2 番目の struct timeval オブジェクトが定義され、変数は 0 に設定されます。これは、
選択をノンブロッキングで待機することを意味します
(複数のファイル記述子のいずれかが準備されていないため、選択はすぐにエラーになり、戻ります)。

3 番目の struct timeval オブジェクトが定義され、変数は 5 および 0 に設定されて 5
秒以内のブロッキング待機を示します。それ以外の場合はタイムアウトになります (非ブロッキング待機)。3
秒でファイル記述子の準備ができている場合、select はパラメーターを返しますtimeout は残り時間 2 秒を表します (5-3=2)


readfds writefds 以外の fds パラメータについて

readfds writefds Belowfdsこれら 3 つのパラメータは同種です
readfds は読み取りイベントを表しますwritefds は書き込みイベントを表しますExcellenttfdsは異常イベントを表します

3 つのタイプはすべてfd_setです


fd_set は、ファイル記述子の値を表すビットを通じて複数のファイル記述子の位置を表すビットマップ構造です。


ビットマップ構造に対してビット単位の AND 演算やビット単位の OR 演算を使用する場合は、オペレーティング システムが提供するインターフェイスを使用する必要があります。

FD_CLR:指定されたセットから指定されたファイル記述子をクリアします

FD_ISSET:ファイル記述子がセットに追加されるかどうかを決定します。

FD_SET:ファイル記述子を対応するセットコレクションに追加します。

FD_ZERO:ファイル記述子全体をクリアします


readfds 読み取りイベントを例に挙げます。

readfds コレクションに配置された場合、ユーザーは、それらのファイル記述子に対応する読み取りイベントをカーネルが処理する必要があることをカーネルに伝えます。戻るとき、カーネルは、それらのファイル記述子の読み取りイベントの準備ができている
ことをユーザーに通知する必要があります。


オペレーティング システムに 8 つのファイル記述子に対応するイベントを処理させたいとします。

ユーザーがカーネルに通知したい場合、ユーザーは fd_set オブジェクト rfds を定義する必要があります。この中で 8 ビットが 1 に設定され、ビット位置がファイル記述子
番号を示します
。ビットが 1 に設定されている場合、オペレーティング システムは対応するファイル記述子の番号を考慮する必要があります。
例: ファイル記述子番号 1 ~ 8 を考慮する必要があります。つまり、それらが準備ができているかどうかを確認する必要があります。


select が戻ると、カーネルは rfds がリセットされ、準備完了ファイル記述子に対応するビット位置が 1 に設定されたことをユーザーに通知します。

例: No. 3 と No. 5 が準備完了した場合、対応するビット位置が 1 に設定され、ファイル記述子 No. 3 と No. 5 に対応するコンテンツが準備完了であることを示します。


選択の戻り値

select の戻り値にも 3 つの状況があり、
最初の状況は 0 より大きく
、複数のファイル記述子の準備ができていることを示します。

2 番目のタイプは 0 に等しく
、タイムアウト状態に入ります。つまり、5 秒以内に準備が整ったファイル記述子がありません。

3 番目のタイプは 0 より小さく
、待機が失敗した場合は -1 を返します。
例: 添字 1 と 2 を持つファイル記述子を待機したいが、添字 2 を持つファイル記述子がまったく存在しない場合、待機は失敗。


おすすめ

転載: blog.csdn.net/qq_62939852/article/details/133524323