Linux应用编程---select

select 的用法

细节去问那个男人

 #include <sys/select.h>
 int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

1.nfds :其值应该设置为三个集合中编号最高的文件描述符,再加上1。glibc有文件描述符的上限1024,大于1023描述符的用poll代替监听。
2.fd_set类型表示一种fd的集合每一bit代表一个描述符,FD_SET将及想要监听的fd放到一个组里面
3.readfds:表示监听可读的fd集合,同理后面为可写以及出现异常的集合
4. timout 表示超时时间,监听多久没响应退出返回timeout
struct timeval {
time_t tv_sec; /* seconds /
suseconds_t tv_usec; /
microseconds */
};
返回值:成功返回3个集合中包含的fd总数,如果超时可能为0,出现错误返回-1,用errno表示错误。
-----------------------------------------------
每个bit位带表一个文件描述符类似的操作在内核输入子系统中对事件的描述也是使用类似的想法
其实现为一个大的数组。

void FD_CLR(int fd, fd_set *set);//在集合中clear fd 表示的那位
int  FD_ISSET(int fd, fd_set *set);//判断当前集合中fd是否置位
void FD_SET(int fd, fd_set *set);//将集合中表示fd的那位置位1
void FD_ZERO(fd_set *set);//将整个集合置位0

注意点
1.timeout 为传入的指针,每次调用后select内部实现会修改这个timeout的值表示剩余时间,比如select提前有消息被监听到退出timeout时间会越来越少,在循环中必须每次调用前重置timeout
eg:
(ii) select() may update the timeout argument to indicate how much time was left. pselect() does not change this
argument.
2.在select之后集合里面的值会被置位所以每次select之前需要clear那一位,特别是在循环当中
eg:
On exit, each of the file descriptor sets is modified in place to indicate which file descriptors actually changed sta‐
tus. (Thus, if using select() within a loop, the sets must be reinitialized before each call.)

bugs:

POSIX allows an implementation to define an upper limit, advertised via the constant FD_SETSIZE, on the  range  of  file
       descriptors  that  can  be  specified  in a file descriptor set.  The Linux kernel imposes no fixed limit, but the glibc
       implementation makes fd_set a fixed-size type, with FD_SETSIZE defined as 1024, and the FD_*() macros operating  accord‐
       ing to that limit.  To monitor file descriptors greater than 1023, use poll(2) instead.

       According  to  POSIX,  select() should check all specified file descriptors in the three file descriptor sets, up to the
       limit nfds-1.  However, the current implementation ignores any file descriptor in these sets that is  greater  than  the
       maximum  file  descriptor number that the process currently has open.  According to POSIX, any such file descriptor that
       is specified in one of the sets should result in the error EBADF.

       Glibc 2.0 provided a version of pselect() that did not take a sigmask argument.

       Starting with version 2.1, glibc provided an emulation of  pselect()  that  was  implemented  using  sigprocmask(2)  and
       select().   This  implementation  remained vulnerable to the very race condition that pselect() was designed to prevent.
       Modern versions of glibc use the (race-free) pselect() system call on kernels where it is provided.

       Under Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a  subsequent  read
       blocks.   This  could for example happen when data has arrived but upon examination has wrong checksum and is discarded.
       There may be other circumstances in which a file descriptor is spuriously reported as ready.  Thus it may  be  safer  to
       use O_NONBLOCK on sockets that should not block.

       On  Linux, select() also modifies timeout if the call is interrupted by a signal handler (i.e., the EINTR error return).
       This is not permitted by POSIX.1.  The Linux pselect() system call has the same behavior, but the  glibc  wrapper  hides
       this behavior by internally copying the timeout to a local variable and passing that variable to the system call.

样例代码

#include <sys/select.h>
struct timeval tv;
int ret;
fd_set fds;
struct timeval select_timeout;
int fd_tty;
fd_tty = open("/dev/tty0",O_RDWR,0)
while(1)
{
    
    
	FD_ZERO(&fds);
	FD_SET(fd_tty , &fds);
	select_timeout.tv_sec = 3;
	ret = select(fd_tty + 1,&fds,NULL,NULL, &select_timeout);
	switch (ret) {
    
    
		case -1 :
		    printf("select ret error -1 \n");
		    break;
		case 0 :
			gettimeofday(&tv,NULL);
		    printf("[%ld:%ld]select timeout \n",tv.tv_sec,tv.tv_usec);
		    break;
		default:
		    if (FD_ISSET(fd_tty , &fds)) {
    
    
				gettimeofday(&tv,NULL);
		        printf("[%ld:%ld]ready [%d]\n",tv.tv_sec,tv.tv_usec,ret );
		    }
			break;
		}
	sleep(1);
}

猜你喜欢

转载自blog.csdn.net/weixin_41884251/article/details/110956623