I/O复用模型之select函数用法——服务器开发

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:58   2322   0

在上篇《多进程并发如何防止僵尸进程——服务器开发》中我们介绍了服务器开发中多进程并发的相关知识。

现在我们介绍另外一种常用并发服务器开发的技术——select函数I/O复用模型。

先来介绍select及相关的函数:

select函数的作用是监听指定的多个I/O的文件描述符,在设定的时间内阻塞,当有一个或者多个I/O端口满足某个“读”或者“写”的条件,则在fd_set类型参数中标记并返回。

int select (
int maxfdp1,      //要监测的I/O描述符范围:0~maxfdp1-1
fd_set *readset,   //readset是一个值—结果的参数,有一个默认大小为1024位的变量,在调用时用它设置哪些描述符需要监听“读”,返回时用它识别哪些描述符准备好“读"。
fd_set *writeset,   //同上这里监测的是“写”
fd_set *exceptset,  //同上这里检测的是“异常”
const struct timeval * timeout     //设置每次select的最大阻塞时间,若为NULL,则无限阻塞直到满足返回条件
);

fd_set类型是一个默认大小为1024位的类型,每一位代表一个I/0文件描述符。例如每个进程默认0、1、2分别表示标准输入、标准输出和标准错误输出,所以fd_set类型1024位中0、1、2位分别代表标准输入、标准输出和标准错误输出。同理假如进程里创建了一个监听socket文件描述符为3,则fd_set的第3位代表这个监听sockfd;假如用accept了客户端连接返回一个sockfd为n,那么fd_set的第n个位就代表这个sockfd。(因为在一个进程里,某个I/O口的文件描述符是唯一的。)

在调用时,将需要监听“读”的I/O文件描述符在readset参数对应的“位”里设为1,同理将需要监听“写”的I/O文件描述符在writeset参数对应的“位”里设为1,并在maxfdp1里设定要监听的最大的文件描述符+1指定好监听的范围。还可以在timeout参数中设定最大的阻塞时间。然后select函数就会在timeout的时间内阻塞,监听readset、writeset中标记的文件描述符。当有一个或者多个I/O端口满足某个“读”或者“写”的条件,则将满足“读”条件的描述表在readset中对应的位处标记为1。“写”同理在readset对应位里标记。

我们就可以根据writeset和readset来找出哪些I/O口准备“读”或者“写”了。

select.h头文件提供对fd_set“位“一系列操作函数:

void FD_ZERO(fd_set * fdset);                //将fdset所有“位”置0
void FD_SET(int fd,fd_set * fdset);          //将fdset第fd个位置1
void FD_CLR(int fd,fd_set * fdset);         //将fdset第fd个位置0
void FD_ISSET(int fd,fd_set * fdset);      //判断fdset第fd个位置是否为1

有了上面的函数,我们就可以这样操作:(例如想监听标准输入I/O和监听sockfd)

fd_set rdfdset;
int listenfd=socket(AF_INET,SOCK_STREAM,0);
bind(listenfd,(sockaddr *)&servaddr,sizeof(servaddr));
listen(listenfd,10);
FD_ZERO(&rdfdset);
FD_SET(STDIN_FILENO,&rdfdset);
FD_SET(listenfd,&rdfdset);
int maxfdp1=max(STDIN_FILENO,listenfd)+1;
select(maxfdp1,&rdfdset,0,0,0);
if(FD_ISSET(listenfd,&rdfdset))
{
       //do something
}
if(FD_ISSET(STDIN_FILENO,&rdfdset))
{
       //do something
}

我们知道了select函数监听到某个文件描述符满足“读”或“写”条件就会返回,那么怎样才称为满足“读”或“写”的条件呢。

一、文件描述符满足“读"条件:(此时用read、readv、recv、recvfrom、recvmsg等读这个I/O不会阻塞)

1、I/O口接收缓冲区中的数据字节数大于接收缓冲区低潮限度。(此时调用read等函数读I/O口会返回大于0值)默认低潮限度为1,我们可以利用SO_RCVLOWAT来设置接收缓冲区低潮限度。

2、TCP套接口连接“读”这一半关闭(利用shutdown),(此时调用read等函数读I/O口会返回等于0值,代表文件EOF)。

3、监听套接口准备好(已连接队列有可用连接,此时调用accept函数会马上返回已连接队列首部的对端套接口)。

4、套接口错误等待处理,(此时调用read等函数返回-1)。

二、文件描述符满足“写"条件:(此时用write、writev、send、sendto、sendmsg等读这个I/O不会阻塞)

1、I/O口发送缓冲区中的可用空间字节数大于发送缓冲区低潮限度。(此时调用write等函数写I/O口会返回大于0值)默认低潮限度为2048,我们可以利用SO_SNDLOWAT来设置发送缓冲区低潮限度。

2、TCP套接口连接“写”这一半关闭(利用shutdown)(此时调用write等函数写I/O口会返回等于-1值,并产生SIGPIPE信号)。

3、套接口错误等待处理,(此时调用read等函数返回-1)。

根据select函数的特点,简单的I/O复用并发服务器流程图如下:


详细代码待续...

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP