Linux下select()システム呼び出しノート

5812 ワード

一、select()関数機能
The select system call allows a program to wait for input to arrive (or output to complete) on a number of low-level file descriptors at once.
selectシステム呼び出しにより、プログラムは同時に複数の下位ファイル記述子上で、入力の到着または出力の完了を待つことができる.
二、関数の意味
        A server can deal with multiple clients by waiting for a request on many open sockets at the same time.
1つのサーバは、複数のオープンソケットが要求の到来を同時に待つ方法で複数のクライアントを処理することができる.(具体的なアプリケーションの1つにすぎません)
自分の理解では、プログラムが実行中にブロック(条件が発生しないと実行しない)に遭遇する場合は、CPUリソースを浪費します.プログラムはCPUの時間を消費しているため、1つのイベントの発生を待っていて、仕事をしないのを待っていて、茅の穴を占めて糞をしない......しかしselect()関数があって、1つのプロセスで同時に多くのファイル記述子を入力、出力、エラーに監視することができます.select関数が戻るとFD_ISSET()関数は,どのファイル記述子の状態が変化したかを検出し,対応する操作を行うことで,そのようなファイル記述子に対する待機操作を省き,CPUの利用率を向上させる.
三、関連する関数の原型
#include
#include
void FD_ZERO(fd_set *fdset); fdsetコレクションをクリア(初期化)
void FD_CLR(int fd, fd_set *fdset); fdファイル記述子のクリア
void FD_SET(int fd, fd_set *fdset); fdファイル記述子の追加
int FD_ISSET(int fd, fd_set *fdset); fdがfdsetファイル記述子セットの要素であるかどうかを検出し、0以外の値を返し、0を返します.
ここで、データ構造「fd_set」は、開いているファイル記述子からなる集合である.上の4つの関数はこの集合を制御するために使用されます.
#include
#include
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
パラメータ:
nfds:テストする記述子範囲、0~(nfds-1)
戻り値:
        1.readfdsセットには記述子が読み取り可能であり、writefdsセットには記述子が書き込み可能であり、errorfdsセットに記述子がエラー条件に遭遇するとselectは1を返し、失敗は-1を返す.
        2.3つの状況が発生していない場合、selectはtimeoutタイムアウト時間に達した後、0を返します.
    3.timeoutが0の場合、selectは状況が発生するまでブロックされます.(前に言ったように、ブロックはCPUリソースの浪費ですが、ここのブロックは、個人を犠牲にして、私を大きくしました.ブロックはすべてのファイル記述子の変化を同時に監視しているので、すべての記述子がブロックされているのに比べて、効率は高いです......)
四、実例
ルーチン1.
#include 
#include 
#include 

#include 
#include 
#include 
#include 

int main(void)
{
	char buffer[128];
	int result, nread;
	fd_set inputs, testfds;
	struct timeval timeout;
	
	FD_ZERO(&inputs);
	FD_SET(0,&inputs);

	while(1) 
	{
		testfds = inputs;
		timeout.tv_sec = 2;
		timeout.tv_usec = 500000;
		result = select(FD_SETSIZE, &inputs, (fd_set *)NULL, (fd_set *)NULL, &timeout);

		switch(result)
		{
			case 0:printf("timeout
");break; case -1:perror("select");exit(1); default: if(FD_ISSET(0,&inputs)) { ioctl(0,FIONREAD,&nread); if(nread == 0) { printf("keyboard done
"); exit(0); } nread = read(0,buffer,nread); buffer[nread] = 0; printf("read %d from keyboard: %s", nread, buffer); } break; } } }
このプログラムはキーボード入力(標準入力-ファイル記述子は0)を読み出し、タイムアウト時間は2.5 sに設定する
インスタンス2:
server.c
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main()
{
	int server_sockfd, client_sockfd;
	int server_len, client_len;
	struct sockaddr_in server_address;
	struct sockaddr_in client_address;
	int res;
	fd_set readfds, testfds;

	server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
	server_address.sin_family = AF_INET;
	server_address.sin_addr.s_addr = inet_addr("192.168.0.21");
	server_address.sin_port = htons(8080);
	server_len = sizeof(server_address);

	int on=1;
	if((setsockopt(server_sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)  
	{  
		perror("set sock opt"); 
		exit(1);
	} 

	bind(server_sockfd, (struct sockaddr*)&server_address, server_len);

	listen(server_sockfd, 5);

	FD_ZERO(&readfds);
	FD_SET(server_sockfd, &readfds);

	while(1)
	{
		char ch;
		int fd;
		int nread;

		testfds = readfds;

		printf("server is waiting!
"); res = select(FD_SETSIZE, &testfds, (fd_set*)0, (fd_set*)0, (struct timeval*)0); printf("res=%d
",res); if(res < 0) { perror("server"); exit(1); } for(fd = 0; fd

client.c
/*  Make the necessary includes and set up the variables.  */

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
    int sockfd;
    int len;
    struct sockaddr_in address;
    int result;
    char ch = 'A';

/*  Create a socket for the client.  */

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

/*  Name the socket, as agreed with the server.  */

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("192.168.0.21");
    address.sin_port = htons(8080);
    len = sizeof(address);

/*  Now connect our socket to the server's socket.  */

    result = connect(sockfd, (struct sockaddr *)&address, len);

    if(result == -1) {
        perror("oops: client3");
        exit(1);
    }

/*  We can now read/write via sockfd.  */

    write(sockfd, &ch, 1);
    read(sockfd, &ch, 1);
    printf("char from server = %c
", ch); close(sockfd); exit(0); }

このプログラムはselectシステム呼び出しでサーバサービスをマルチクライアントに実現し、マルチプロセスを採用していない.サーバはselectにリスニングソケットとクライアントの接続ソケットを同時にチェックさせることができます.selectがアクティビティが発生したことを示すと、FDを使用できます.ISSET()関数は、どのアクティビティが発生したかを確認するために、可能なすべてのファイル記述子を巡回します.
リスニングソケットが読める場合は、接続を試みているお客様がいることを示します.この場合、acceptで接続要求を受け入れることができ、ブロックされません.ある顧客がソケットに接続する準備ができている場合、このソケットの顧客が処理する必要があることを示します.ioctl()を呼び出し、0を返すと、顧客プロセスが終了し、ソケットを閉じてディスクリプタセットから削除すればいいです.