select、poll、epoll使用小結


Linuxでは異なるI/Oモデルを使用することができ、同期と非同期モデル、およびブロックと非ブロックモデルの一般的なI/Oモデルを下図で理解することができ、本稿では主にその中の非同期ブロックモデルを分析する.
一、select使用
このモデルでは、非ブロックI/Oを構成し、ブロックselectシステム呼び出しを使用して、I/O記述子がいつ動作するかを決定します.select呼び出しを使用すると、複数の記述子に通知を提供できます.各プロンプトについて、記述子の書き込み可能、読み取り可能、エラーの発生の有無を要求できます.非同期ブロックI/Oのシステムフローを下図に示します.
selectでよく使用されるいくつかの関数は、次のとおりです.
FD_ZERO(int fd, fd_set* fds) 
FD_SET(int fd, fd_set* fds) 
FD_ISSET(int fd, fd_set* fds) 
FD_CLR(int fd, fd_set* fds) 
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) 
fd_setタイプはbitビットでハンドルをマークするキューと簡単に理解できます.具体的なセット、検証はFD_を使用することができますSET,FD_ISSETなどのマクロ実装.select関数では、readfds、writefds、exceptfdsが入力パラメータと出力パラメータとして同時に使用され、readfdsが位置をマークしている場合、selectはマークビット読み取り可能であることを検出します.timeoutは設定されたタイムアウト時間です.
次にselectの使用方法について説明します.
	SOCKADDR_IN addrSrv;
	int reuse = 1;
	SOCKET sockSrv,connsock;
	SOCKADDR_IN addrClient;
	pool pool;
	int len=sizeof(SOCKADDR);
	/*  TCP*/
	sockSrv=socket(AF_INET,SOCK_STREAM,0);
	/*  、     */
	
	addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(port);
	
	if(bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR))<0)
	{
		fprintf(stderr,"Failed to bind");
		return ;
	}
	
	if(listen(sockSrv,5)<0)
	{
		fprintf(stderr,"Failed to listen socket");
		return ;
	}
	setsockopt(sockSrv,SOL_SOCKET,SO_REUSEADDR,(const char*)&reuse,sizeof(reuse));
	init_pool(sockSrv,&pool);
	while(1)
	{
		/*  selete       */
		pool.ready_set=pool.read_set;
		pool.nready=select(pool.maxfd+1,&pool.ready_set,NULL,NULL,NULL);
		if(FD_ISSET(sockSrv,&pool.ready_set))
		{
			connsock=accept(sockSrv,(SOCKADDR *)&addrClient,&len);
			//loadDeal()/*    */
			//printf("test
"); add_client(connsock,&pool);// } /* */ check_client(&pool); }
は、サーバコードの重要な部分であり、非同期モードに設定され、接続を受け入れて接続プールに追加します.リスナー記述子でselectを使用して、クライアントの接続要求を受け入れ、check_Client関数では、接続プールの記述子を巡り、イベントが発生しているかどうかを確認します.
二、poll使用
poll関数はselectに似ていますが、呼び出し形式は異なります.pollは、条件ごとに記述子セットを構築するのではなく、pollfd構造体配列を構築し、各配列要素に記述子記号とその関心条件を指定します.次のように定義します.
#include 
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};

各構造体のeventsドメインは、カーネルが注目していることを示すユーザによって設定され、reventsドメインは、ディスクリプタに対してどのようなイベントが発生したかを示すために返されるカーネルによって設定されます.この点はselectとは異なり、selectはパラメータを変更して、どの記述子が準備されているかを示します.「unix環境高度プログラミング」にはeventsの値を取るテーブルがあります.以下のようになります.
POLLIN:上位レベル以外のデータを読み取り、ブロックしない
POLLRDNORM:一般データを読み取り、ブロックしない
POLLRDBAND:読み取り可能なO優先データ、ブロックしない
POLLPRI:高優先データを読み取り、ブロックしない
POLLOUT:書き込み可能データ、ブロックしない
POLLWRNORM:POLLOUTと同じ
POLLWRBAND:0以外の優先データを書き込み、ブロックしない
次にreventsには次の値があります
POLLERR:エラー
POLLHUP:保留中であり、ディスクリプタで保留されると、ディスクリプタに書き込むことはできませんが、ディスクリプタからデータを読み取ることができます.
POLLNVAL:この記述子は開いているファイルを参照しません.
poll関数の場合、nfdsはfdsの要素数を表し、timeoutはタイムアウト設定を表し、単位がミリ秒で0の場合、待機しないことを表し、-1は記述子の1つが信号の戻りを準備したか、捉えたかを表し、0より大きい場合は記述子の準備ができているか、タイムアウトした戻りを表す.関数の戻り値の戻り値が0の場合、イベントが発生していないことを示し、-1はエラーを示し、errnoを設定します.0より大きい場合、いくつかの記述子にイベントがあることを示します.
pollの使用はselectと基本的に似ています.ここでは紹介しません.pollはselectに比べてリスニングされる記述子の数に制限がないという利点がある.
三、epoll学習
epollには2つのモードがあり、Edge Triggered(ETと略称する)とLevel Triggered(LTと略称する)がある.この2つのモードを採用する際に注意すべきことは、ETモードを採用すると、状態が変化した場合にのみ通知するが、LTモードを採用することは従来のselect/poll操作と同様であり、まだ処理されていないイベントがある限り常に通知することである.
1)epollデータ構造の紹介:
typedef union epoll_data
{
  void        *ptr;
  int          fd;
  __uint32_t   u32;
  __uint64_t   u64;
} epoll_data_t;

struct epoll_event
{
  __uint32_t   events; /* Epoll events */
  epoll_data_t data;   /* User data variable */
};
よくあるイベントは次のとおりです.
EPOLLIN:記述子の読み取り可能を示す
EPOLLOUT:記述子の書き込み可能を表す
EPOLLPRI:記述子に対する緊急データがあることを示す
EPOLLERR:エラー発生
EPOLLHOP:保留中
EPOLLET:エッジトリガ
EPOLLONESHOT:使い捨てで、今回のイベントをリスニングした後、このソケットをリスニングし続ける必要がある場合は、このソケットを再びEPOLLキューに追加する必要があります
2)関数の紹介
epollの3つの関数
int epoll_creae(int size);
機能:この関数はepoll専用のファイル記述子を生成します.
パラメータ:sizeはepollで注目できる最大記述子数です.
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
機能:epollファイル記述子の時間を制御するために使用され、登録、変更、削除が可能
パラメータ:epfdはepoll_からcreate生成epoll専用記述子
opアクション:EPOLL_CTL_ADD登録EPOLL_CTL_MOD修正EPOLL_DEL削除
fd:関連するファイル記述子
evnetはカーネルにどんなイベントを傍受するかを教えます
int epoll_wait(int epfd,struct epoll_event*events,int maxevents,int timeout);
機能:この関数はi/oイベントの発生を待つ.
パラメータ:epfdで検出するハンドル
events:処理待ち時間を再送するための配列
maxevents:カーネルというeventsの大きさを教えて、前のsizeを超えてはいけません
timeout:タイムアウト時間
使用方法:https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c

epollがサポートするFDの上限は、最大開くことができるファイルの数(selectがこのような問題に直面している)であり、IO効率はFDの数の増加に伴って線形に低下しない(select、pollが直面している問題)mmapを使用してカーネルとユーザ空間のメッセージングを加速させる.現在libeventはいくつかの実装をカプセル化しており,libeventを用いて多重化を実現することができる.
参考資料:https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/
   http://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/index.html?ca=drs-
             http://www.ibm.com/developerworks/cn/linux/l-async/