此文件实现I/O多路复用的功能(select,kqueue,poll,devpoll),提供抽象的操作接口,从而保证了接口的单一性和可移植性。
接口:
/* Figure out how many file descriptors the system allows, and
** initialize the fdwatch data structures. Returns -1 on failure.
*/
int fdwatch_get_nfiles( void );
/* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */
void fdwatch_add_fd( int fd, void* client_data, int rw );
/* Delete a descriptor from the watch list. */
void fdwatch_del_fd( int fd );
/* Do the watch. Return value is the number of descriptors that are ready,
** or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means
** wait indefinitely.
*/
int fdwatch( long timeout_msecs );
/* Check if a descriptor was ready. */
int fdwatch_check_fd( int fd );
/* Get the client data for the next returned event. Returns -1 when there
** are no more events.
*/
void* fdwatch_get_next_client_data( void );
/* Generate debugging statistics syslog message. */
void fdwatch_logstats( long secs );
//该函数用于获得当前可以复用的fd的最大个数,并初始化数据结构
int fdwatch_get_nfiles( void )
{
int i;
#ifdef RLIMIT_NOFILE
struct rlimit rl;
#endif /* RLIMIT_NOFILE */
/* Figure out how many fd's we can have. */
进程所能打开的最大文件描述符数
nfiles = getdtablesize();
#ifdef RLIMIT_NOFILE
/* If we have getrlimit(), use that, and attempt to raise the limit. */
//设置资源限制的最大fd值
if ( getrlimit( RLIMIT_NOFILE, &rl ) == 0 )
{
nfiles = rl.rlim_cur;
if ( rl.rlim_max == RLIM_INFINITY )
rl.rlim_cur = 8192; /* arbitrary */
else if ( rl.rlim_max > rl.rlim_cur )
rl.rlim_cur = rl.rlim_max;
if ( setrlimit( RLIMIT_NOFILE, &rl ) == 0 )
nfiles = rl.rlim_cur;
}
#endif /* RLIMIT_NOFILE */
#if defined(HAVE_SELECT) && ! ( defined(HAVE_POLL) || defined(HAVE_DEVPOLL) || defined(HAVE_KQUEUE) )
/* If we use select(), then we must limit ourselves to FD_SETSIZE. */
//select I/O,不能超过FD_SETSIZE(32)
nfiles = MIN( nfiles, FD_SETSIZE );
#endif /* HAVE_SELECT && ! ( HAVE_POLL || HAVE_DEVPOLL || HAVE_KQUEUE ) */
/* Initialize the fdwatch data structures. */
//初始化数据结构
nwatches = 0;
fd_rw = (int*) malloc( sizeof(int) * nfiles );
fd_data = (void**) malloc( sizeof(void*) * nfiles );
if ( fd_rw == (int*) 0 || fd_data == (void**) 0 )
return -1;
for ( i = 0; i < nfiles; ++i )
fd_rw[i] = -1;
if ( INIT( nfiles ) == -1 )
return -1;
return nfiles;
}
//添加需要监控的fd,rw为FDW_READ(0)或FDW_WRITE(1)
void fdwatch_add_fd( int fd, void* client_data, int rw )
{
//检查fd是否在范围之内,并且检查fd是否已经存在
if ( fd < 0 || fd >= nfiles || fd_rw[fd] != -1 )
{
syslog( LOG_ERR, "bad fd (%d) passed to fdwatch_add_fd!", fd );
return;
}
ADD_FD( fd, rw );
fd_rw[fd] = rw;
fd_data[fd] = client_data;
}
#def
ine ADD_FD( fd, rw ) select_add_fd( fd, rw )//select I/O
//select I/O 添加fd
static void select_add_fd( int fd, int rw )
{
if ( nselect_fds >= nfiles )
{
syslog( LOG_ERR, "too many fds in select_add_fd!" );
return;
}
//index-->fd(通过index找到fd)
select_fds[nselect_fds] = fd;
switch ( rw )
{
case FDW_READ: FD_SET( fd, &master_rfdset ); break;
case FDW_WRITE: FD_SET( fd, &master_wfdset ); break;
default: break;
}
if ( fd > maxfd )
maxfd = fd;
//fd-->index 通过fd找到数组(select_fds)位置index
select_fdidx[fd] = nselect_fds;
++nselect_fds;
}
//删除监测的fd
void fdwatch_del_fd( int fd )
{
if ( fd < 0 || fd >= nfiles || fd_rw[fd] == -1 )
{
syslog( LOG_ERR, "bad fd (%d) passed to fdwatch_del_fd!", fd );
return;
}
//重点在DEL_FD
DEL_FD( fd );
fd_rw[fd] = -1;
fd_data[fd] = (void*) 0;
}
#define DEL_FD( fd ) select_del_fd( fd ) //select I/O
//select I/O 删除fd
static void select_del_fd( int fd )
{
int idx = select_fdidx[fd]; //获取fd在select_fds的位置
if ( idx < 0 || idx >= nfiles )
{
syslog( LOG_ERR, "bad idx (%d) in select_del_fd!", idx );
return;
}
--nselect_fds;//select_add_fd函数中末尾++nselect_fds;
//把最后的fd,和fd对应的index替换要删除的fd和fd的index//比较绕
select_fds[idx] = select_fds[nselect_fds];
select_fdidx[select_fds[idx]] = idx;
select_fds[nselect_fds] = -1;
select_fdidx[fd] = -1;
FD_CLR( fd, &master_rfdset );
FD_CLR( fd, &master_wfdset );
if ( fd >= maxfd )
maxfd_changed = 1;
}
//在大循环中,将master_fdset的值赋值给working_fdset然后调用select传入working_fdset进行检测,检测的时候由参数timeout_msecs决定
int fdwatch( long timeout_msecs )
{
++nwatches;
nreturned = WATCH( timeout_msecs );
next_ridx = 0;
return nreturned;
}
#define WATCH( timeout_msecs ) select_watch( timeout_msecs )
static int select_watch( long timeout_msecs )
{
int mfd;
int r, idx, ridx;
working_rfdset = master_rfdset;
working_wfdset = master_wfdset;
mfd = select_get_maxfd();
if ( timeout_msecs == INFTIM )
r = select(
mfd + 1, &working_rfdset, &working_wfdset, (fd_set*) 0,
(struct timeval*) 0 );
else
{
struct timeval timeout;
timeout.tv_sec = timeout_msecs / 1000L;
timeout.tv_usec = ( timeout_msecs % 1000L ) * 1000L;
r = select(
mfd + 1, &working_rfdset, &working_wfdset, (fd_set*) 0, &timeout );
}
if ( r <= 0 )
return r;
ridx = 0;
for ( idx = 0; idx < nselect_fds; ++idx )
if ( select_check_fd( select_fds[idx] ) )
{
select_rfdidx[ridx++] = select_fds[idx];
if ( ridx == r )
break;
}
return ridx; /* should be equal to r */
}
可以看出,fdwatch.c文件是对多路复用I/O的封装,提供抽象接口,,从而可以正常的处理小规模的服务器并发。