linux的select函數

我們先來看一段阻塞的read代碼:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
	int fd;
	char buf[11];
	int ret;

	fd=0;

	while(1){
		//set the buf to 0
		memset((void *)buf,0,11);
		ret=read(fd,(void *)buf,10);
		printf("ret=%d\n",ret);

		if(ret!=-1){
			buf[10]='\0';
			printf("  buf=%s\n",buf);
		}
	}
	return 0;
}

我們先用memset初始化數組:

void *memset(void *s, int c, size_t n);

DESCRIPTION
The memset() function fills the first n bytes of the memory area pointed to by s with the constant byte c.

也就是說,我們把buf全部設置爲0。

然後使用read函數:

 ssize_t read(int fd, void *buf, size_t count);

read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than
the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are
reading from a pipe, or from a terminal), or because read() was interrupted by a signal. On error, -1 is returned, and errno is set appropriately. In this case it is left
unspecified whether the file position (if any) changes.

read會從文件描述符fd中讀取內容,並讀取count個字節,並將內容讀到buf中。

它會返回讀到的字節數。如果是0,則是讀到末尾了。如果是-1,那就是出錯了。

在沒有出錯的情況下,我們打印buf的內容(讀取到的內容)。

'\0'標誌着字符串的結尾。

我們運行該程序:

I love linux programming
ret=10
  buf=I love lin
ret=10
  buf=ux program
ret=5
  buf=ming

我們發現,程序阻塞了,它一直在等待輸入。這是因爲它在read處阻塞了。

但這是不好的,因爲如果這是一個網絡程序,如果客戶端一直沒有消息發過來的話,難道就要一直阻塞嗎?這是很浪費資源的。

於是,我們要用到select


int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

DESCRIPTION
select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become “ready” for some class of I/O operation
(e.g., input possible). A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.

select會輪詢監聽多個文件描述符,如果有文件描述符準備好了io操作,那麼這個文件描述符就會被激活。

Three independent sets of file descriptors are watched. Those listed in readfds will be watched to see if characters become available for reading (more precisely, to see if a
read will not block; in particular, a file descriptor is also ready on end-of-file), those in writefds will be watched to see if a write will not block, and those in exceptfds
will be watched for exceptions. On exit, the sets are modified in place to indicate which file descriptors actually changed status. Each of the three file descriptor sets may be
specified as NULL if no file descriptors are to be watched for the corresponding class of events.

fd_set *readfds, fd_set *writefds,fd_set *exceptfds是三個傳入傳出參數。它們分別監聽讀事件、寫事件和異常事件。fd_set *readfds表示監控有讀數據到達文件描述符集合,我們將會使用這個參數,因爲我們要監聽讀事件。

如果設置爲NULL就表示不需要監控文件描述符的該事件。

nfds is the highest-numbered file descriptor in any of the three sets, plus 1.

nfds爲要監控的文件描述符的最大數+1。因爲文件描述符的下標是從0開始的。

The timeout argument specifies the minimum interval that select() should block waiting for a file descriptor to become ready


           struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };

timeout就是要阻塞的時間,它由秒和微秒組成。

RETURN VALUE
On success, select() and pselect() return the number of file descriptors contained in the three returned descriptor sets (that is, the total number of bits that are set in
readfds, writefds, exceptfds) which may be zero if the timeout expires before anything interesting happens

select返回激活的文件描述符的總數。如果timeout時間到了,就會返回0。

另外:

Four macros are provided to manipulate the sets. FD_ZERO() clears a set. FD_SET() and FD_CLR() respectively add and remove a given file descriptor from a set. FD_ISSET() tests
to see if a file descriptor is part of the set; this is useful after select() returns.

FD_ZERO()清空一個集合。

FD_SET()從一個集合中增加給定的文件描述符。

FD_CLR()從一個集合中移除給定的文件描述符。

FD_ISSET()測試一個文件描述符是否在一個集合中。


有了以上的知識,我們來看代碼:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>

int main(int argc, char *argv[]) {
	int fd;
	char buf[11];
	int ret, sret;

	fd = 0;

	fd_set readfds;
	struct timeval timeout;

	while (1) {

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

		timeout.tv_sec = 5;
		timeout.tv_usec = 0;

		sret = select(1, &readfds, NULL, NULL, &timeout);

		if (sret == 0) {
			printf("sret=%d\n", sret);
			printf("   timeout\n");
		} else {
			printf("sret=%d\n", sret);
			//set the buf to 0
			memset((void *) buf, 0, 11);
			ret = read(fd, (void *) buf, 10);
			printf("ret=%d\n", ret);

			if (ret != -1) {
				buf[10] = '\0';
				printf("  buf=%s\n", buf);
			}

		}
	}
	return 0;
}

這時候,如果客戶端沒有發消息過來,服務器端只會等5秒,5秒之後可以處理其他的事。之後如果發消息來了,還是能夠收到,因爲服務端一直在循環,重要的是,select並不阻塞。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章