linux 中select函数分析

合集下载

linux Select函数用法详解

linux  Select函数用法详解

Socket-SelectSelect在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 connect、accept、recv或recvfrom 这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。

可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

下面详细介绍一下。

Select的函数格式:int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set*errorfds,struct timeval *timeout);先说明两个结构体:第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。

fd_set集合可以通过一些宏由人为来操作,比如清空集合 FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。

Linux网络编程学习之select详细讲解

Linux网络编程学习之select详细讲解

Linux网络编程学习之select()详解select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。

程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。

文件在句柄在Linux里很多,如果你man某个函数,在函数返回值部分说到成功后有一个文件句柄被创建的都是的,如man socket可以看到“On success, a file descriptor for the new socket is returned.”而man 2 open可以看到“open() and creat() return the new file descriptor”,其实文件句柄就是一个整数,看socket函数的声明就明白了:int socket(int domain, int type, int protocol);当然,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。

0、1、2是整数表示的,对应的FILE *结构的表示就是stdin、stdout、stderr,0就是stdin,1就是stdout,2就是stderr。

比如下面这两段代码都是从标准输入读入9个字节字符:#include <stdio.h>#include <unistd.h>#include <string.h>int main(int argc, char ** argv){char buf[10] = "";read(0, buf, 9); /* 从标准输入0 读入字符*/fprintf(stdout, "%s\n", buf); /* 向标准输出stdout 写字符*/return 0;}/* **上面和下面的代码都可以用来从标准输入读用户输入的9个字符** */#include <stdio.h>#include <unistd.h>#include <string.h>int main(int argc, char ** argv){char buf[10] = "";fread(buf, 9, 1, stdin); /* 从标准输入stdin 读入字符*/write(1, buf, strlen(buf));return 0;}继续上面说的select,就是用来监视某个或某些句柄的状态变化的。

服务器模型的比较

服务器模型的比较

事件驱动为广大的程序员所熟悉,其最为人津津乐道的是在图形化界面编程中的应用;事实上,在网络编程中事件驱动也被广泛使用,并大规模部署在高连接数高吞吐量的服务器程序中,如http 服务器程序、ftp 服务器程序等。

相比于传统的网络编程方式,事件驱动能够极大的降低资源占用,增大服务接待能力,并提高网络传输效率。

关于本文提及的服务器模型,搜索网络可以查阅到很多的实现代码,所以,本文将不拘泥于源代码的陈列与分析,而侧重模型的介绍和比较。

使用libev 事件驱动库的服务器模型将给出实现代码。

本文涉及到线程/时间图例,只为表明线程在各个IO 上确实存在阻塞时延,但并不保证时延比例的正确性和IO 执行先后的正确性;另外,本文所提及到的接口也只是笔者熟悉的Unix/Linux 接口,并未推荐Windows 接口,读者可以自行查阅对应的Windows 接口。

阻塞型的网络编程接口几乎所有的程序员第一次接触到的网络编程都是从listen()、send()、recv() 等接口开始的。

使用这些接口可以很方便的构建服务器/客户机的模型。

我们假设希望建立一个简单的服务器程序,实现向单个客户机提供类似于“一问一答”的内容服务。

图1. 简单的一问一答的服务器/客户机模型我们注意到,大部分的socket 接口都是阻塞型的。

所谓阻塞型接口是指系统调用(一般是IO 接口)不返回调用结果并让当前线程一直阻塞,只有当该系统调用获得结果或者超时出错时才返回。

实际上,除非特别指定,几乎所有的IO 接口(包括socket 接口)都是阻塞型的。

这给网络编程带来了一个很大的问题,如在调用send() 的同时,线程将被阻塞,在此期间,线程将无法执行任何运算或响应任何的网络请求。

这给多客户机、多业务逻辑的网络编程带来了挑战。

这时,很多程序员可能会选择多线程的方式来解决这个问题。

多线程服务器程序应对多客户机的网络应用,最简单的解决方式是在服务器端使用多线程(或多进程)。

linux select区分读写

linux select区分读写

linux select区分读写Linux中的select函数是一个用于多路复用的系统调用,它可以同时监视多个文件描述符的状态,包括读、写和异常等事件。

在使用select函数时,我们可以通过设置相应的文件描述符集合来区分读和写。

首先,我们需要创建三个文件描述符集合:readfds、writefds和exceptfds。

readfds用于监视可读事件,writefds用于监视可写事件,exceptfds用于监视异常事件。

然后,我们需要将需要监视的文件描述符添加到相应的集合中。

对于需要监视读事件的文件描述符,我们可以使用FD_SET宏将其添加到readfds中;对于需要监视写事件的文件描述符,我们可以使用FD_SET宏将其添加到writefds中;对于需要监视异常事件的文件描述符,我们可以使用FD_SET宏将其添加到exceptfds中。

接下来,我们调用select函数,并传入最大文件描述符值加1、readfds、writefds和exceptfds作为参数。

select函数会阻塞程序执行,并等待指定的文件描述符集合中有事件发生。

当select函数返回时,我们可以通过检查相应的文件描述符集合来确定哪些文件描述符有可读或可写或异常事件发生。

对于每个文件描述符,在相应的集合中使用FD_ISSET宏进行检查即可。

如果返回值为真,则表示该文件描述符有相应类型的事件发生。

最后,我们可以根据检查结果进行相应处理。

例如,如果某个文件描述符在readfds中返回为真,则表示该文件描述符有可读事件发生,我们可以调用read函数来读取数据;如果某个文件描述符在writefds中返回为真,则表示该文件描述符有可写事件发生,我们可以调用write函数来写入数据;如果某个文件描述符在exceptfds中返回为真,则表示该文件描述符有异常事件发生,我们可以进行相应的处理。

总之,通过使用select函数和相应的文件描述符集合,我们可以区分读和写事件,并根据需要进行相应的处理。

select判断语句

select判断语句

select判断语句摘要:1.什么是select 判断语句2.select 判断语句的语法结构3.select 判断语句的实例分析4.select 判断语句的应用场景正文:一、什么是select 判断语句在编程语言中,select 判断语句是一种多分支选择结构,它允许程序根据某个条件表达式的值来选择执行不同的代码块。

这种结构可以帮助程序员减少代码的冗余,使代码更加简洁、易读。

二、select 判断语句的语法结构select 判断语句的语法结构如下:```switch (expression) {case constant1:// code block 1;break;case constant2:// code block 2;break;//...default:// code block n+1;}```其中,expression 表示条件表达式,它可以是一个整数、字符或布尔表达式。

case 后面跟的是一个常量表达式,它用于与条件表达式进行比较。

当条件表达式的值等于某个case 后面的常量表达式时,就会执行该case 对应的代码块。

break 语句用于跳出switch 结构。

default 语句是可选的,当条件表达式的值与所有case 后面的常量表达式都不匹配时,就会执行default 对应的代码块。

三、select 判断语句的实例分析假设有一个整数变量a,我们希望根据a 的值来输出相应的信息。

可以使用select 判断语句来实现这个功能:```cint a = 1;switch (a) {case 1:printf("a 等于1");break;case 2:printf("a 等于2");break;case 3:printf("a 等于3");break;default:printf("a 的值不在预期范围内");}```四、select 判断语句的应用场景select 判断语句在实际编程中应用广泛,它主要用于根据不同条件执行相应的操作。

linux 中select的基本用法

linux 中select的基本用法

linux 中select的基本用法深入了解Linux 中select 的基本用法导语:在Linux 中,select 是一个重要的系统调用,用于同时监视多个文件描述符的可读、可写和出错条件。

它是实现多路复用IO的一种常用方法,能够帮助提高系统的性能。

本文将介绍select 的基本用法,从基础概念到具体使用方法,一步一步讲解,帮助读者更好地理解和应用该系统调用。

第一部分:基础知识1. select 的定义和作用- select 是一个系统调用,用于在一组文件描述符上进行IO 复用- 它能够同时监视多个文件描述符,并在有可读、可写或出错事件发生时进行相应的处理- 使用select 可以减少系统资源的浪费,提高系统的性能2. 文件描述符- 在Linux 中,文件和设备都通过文件描述符来操作- 文件描述符是一个非负整数,用于标识一个打开的文件或设备- 标准输入、标准输出和标准错误输出的文件描述符分别为0、1 和23. fd_set 类型- fd_set 是用于表示文件描述符集合的数据类型- 它通过一个位图来表示文件描述符集合的状态,每个文件描述符占用一个位- 可以使用宏函数来操作fd_set,如FD_ZERO、FD_SET、FD_CLR 和FD_ISSET第二部分:使用步骤1. 初始化文件描述符集合- 使用FD_ZERO 宏函数将文件描述符集合清零- 使用FD_SET 宏函数将需要监视的文件描述符添加到集合中2. 设置超时时间- 声明并初始化timeval 结构体变量,用于指定select 的超时时间- 如果不需要设置超时时间,则将timeval 结构体中的字段都设为03. 调用select 函数- 调用select 函数并传入最大文件描述符数、可读文件描述符集合、可写文件描述符集合、出错文件描述符集合和超时时间- select 函数会阻塞进程,直到有事件发生或超时,返回时会修改文件描述符集合的状态4. 处理事件- 使用FD_ISSET 宏函数判断特定文件描述符是否在集合中- 根据返回的文件描述符集合的状态,进行相应的读、写或出错操作第三部分:注意事项和高级用法1. 最大文件描述符数- select 的第一个参数需要指定最大文件描述符数加1- 如果文件描述符超过了该值,在一些旧版本的Linux 中可能会导致select 函数调用失败2. 文件描述符集合的修改- 在调用select 函数后,文件描述符集合的状态会被修改,只保留了就绪的文件描述符- 因此,每次调用select 函数前都需要重新初始化文件描述符集合3. 非阻塞模式和异步通知- select 在默认情况下是阻塞的,即会一直等待事件发生- 可以通过将文件描述符设置为非阻塞模式来改变这一行为,使得select立即返回- 也可以使用其他方法,如信号处理和管道通信,实现异步通知机制4. poll 和epoll 函数- poll 和epoll 是select 的替代方案,可以更好地处理大量文件描述符- 它们采用事件驱动的方式,不需要每次都重新初始化文件描述符集合- 在需要处理大量并发连接的情况下,可以考虑使用poll 或epoll 函数结语:本文从基础知识到具体使用步骤,详细介绍了Linux 中select 的基本用法。

linux使用select实现精确定时器详解

linux使用select实现精确定时器详解

linux使⽤select实现精确定时器详解在编写程序时,我们经常会⽤到定时器。

⾸先看看select函数原型如下:复制代码代码如下:int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);参数说明:slect的第⼀个参数nfds为fdset集合中最⼤描述符值加1,fdset是⼀个位数组,其⼤⼩限制为__FD_SETSIZE(1024),位数组的每⼀位代表其对应的描述符是否需要被检查。

select的第⼆三四个参数表⽰需要关注读、写、错误事件的⽂件描述符位数组,这些参数既是输⼊参数也是输出参数,可能会被内核修改⽤于标⽰哪些描述符上发⽣了关注的事件。

所以每次调⽤select前都需重新初始化fdset。

timeout参数为超时时间,该结构会被内核修改,其值为超时剩余的时间。

利⽤select实现定时器,需要利⽤其timeout参数,注意到:1)select函数使⽤了⼀个结构体timeval作为其参数。

2)select函数会更新timeval的值,timeval保持的值为剩余时间。

如果我们指定了参数timeval的值,⽽将其他参数都置为0或者NULL,那么在时间耗尽后,select函数便返回,基于这⼀点,我们可以利⽤select实现精确定时。

timeval的结构如下:复制代码代码如下:struct timeval{long tv_sec;/*secons*long tv_usec;/*microseconds*/}我们可以看出其精确到microseconds也即微妙。

⼀、秒级定时器复制代码代码如下:void seconds_sleep(unsigned seconds){struct timeval tv;_sec=seconds;_usec=0;int err;do{err=select(0,NULL,NULL,NULL,&tv);}while(err<0 && errno==EINTR);}⼆、毫秒级别定时器复制代码代码如下:void milliseconds_sleep(unsigned long mSec){struct timeval tv;_sec=mSec/1000;_usec=(mSec%1000)*1000;int err;do{err=select(0,NULL,NULL,NULL,&tv);}while(err<0 && errno==EINTR);}三、微妙级别定时器复制代码代码如下:void microseconds_sleep(unsigned long uSec){struct timeval tv;_sec=uSec/1000000;_usec=uSec%1000000;int err;do{err=select(0,NULL,NULL,NULL,&tv);}while(err<0 && errno==EINTR);}现在我们来编写⼏⾏代码看看定时效果吧。

Linux socket select 函数用法详解

Linux socket select 函数用法详解

linux 的socket函数分为阻塞和非阻塞两种方式,比如accept函数,在阻塞模式下,它会一直等待有客户连接。

而在非阻塞情况下,会立刻返回。

我们一般都希望程序能够运行在非阻塞模式下。

一种方法就是做一个死循环,不断去查询各个socket的状态,但是这样会浪费大量的cpu时间。

解决这个问题的一个方法就是使用select函数。

使用select函数可以以非阻塞的方式和多个socket通信。

当有socket需要处理时,select函数立刻返回,期间并不会占用cpu时间。

例程分析:#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#define MYPORT 1234 // 侦听端口#define BACKLOG 5 // 最大可连接客户端数量#define BUF_SIZE 200int fd_A[BACKLOG]; // 连接的FD数组int conn_amount; // 当前连接的数量void showclient(){int i;printf("client amount: %d\n", conn_amount);for (i = 0; i < BACKLOG; i++){printf("[%d]:%d ", i, fd_A[i]);}printf("\n\n");}int main(void){int sock_fd, new_fd; // 侦听sock_fd, 新连接new_fdstruct sockaddr_in server_addr; // server address informationstruct sockaddr_in client_addr; // connector's address informationsocklen_t sin_size;int yes = 1;char buf[BUF_SIZE];int ret;int i;//创建侦听Socketif ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("Create listening socket error!");exit(1);}//配置侦听Socket//SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址捆绑。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

linux 中select函数分析
select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组,
每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他
文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,
当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执
行了select()的进程哪一Socket或文件可读,下面具体解释:
#include <sys/types.h>
#include <sys/times.h>
#include <sys/select.h>
int select(nfds, readfds, writefds, exceptfds, timeout)
int nfds;
fd_set *readfds, *writefds, *exceptfds;
struct timeval *timeout;
ndfs:select监视的文件句柄数,视进程中打开的文件数而定,一般设为呢要监视各文件中的最大文件号加一。

readfds:select监视的可读文件句柄集合。

writefds: select监视的可写文件句柄集合。

exceptfds:select监视的异常文件句柄集合。

timeout:本次select()的超时结束时间。

(见/usr/sys/select.h,
可精确至百万分之一秒!)
当readfds或writefds中映象的文件可读或可写或超时,本次select()
就结束返回。

程序员利用一组系统提供的宏在select()结束时便可判
断哪一文件可读或可写。

对Socket编程特别有用的就是readfds。

几只相关的宏解释如下:
FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。

FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。

FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。

FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否
可读写,>0表示可读写。

(关于fd_set及相关宏的定义见/usr/include/sys/types.h)
这样,你的socket只需在有东东读的时候才读入,大致如下:
...
int sockfd;
fd_set fdR;
struct timeval timeout = ..;
...
for(;;) {
FD_ZERO(&fdR);
FD_SET(sockfd, &fdR);
switch (select(sockfd + 1, &fdR, NULL, &timeout)) {
case -1:
error handled by u;
case 0:
timeout hanled by u;
default:
if (FD_ISSET(sockfd)) {
now u read or recv something;
/* if sockfd is father and
server socket, u can now
accept() */
}
}
}
所以一个FD_ISSET(sockfd)就相当通知了sockfd可读。

至于struct timeval在此的功能,请man select。

不同的timeval设置
使使select()表现出超时结束、无超时阻塞和轮询三种特性。

由于
timeval可精确至百万分之一秒,所以Windows的SetTimer()根本不算
什么。

你可以用select()做一个超级时钟。

FD_ACCEPT的实现?依然如上,因为客户方socket请求连接时,会发送
连接请求报文,此时select()当然会结束,FD_ISSET(sockfd)当然大
于零,因为有报文可读嘛!至于这方面的应用,主要在于服务方的父Socket,你若不喜欢主动accept(),可改为如上机制来accept()。

至于FD_CLOSE的实现及处理,颇费了一堆cpu处理时间,未完待续。

--
讨论关于利用select()检测对方Socket关闭的问题:
仍然是本地Socket有东东可读,因为对方Socket关闭时,会发一个关闭连接通知报文,会马上被select()检测到的。

关于TCP的连接(三次握手)和关
闭(二次握手)机制,敬请参考有关TCP/IP的书籍。

不知是什么原因,UNIX好象没有提供通知进程关于Socket或Pipe对方关闭的信号,也可能是cpu所知有限。

总之,当对方关闭,一执行recv()或read(),马上回返回-1,此时全局变量errno的值是115,相应的sys_errlist[errno] 为"Connect refused"(请参考/usr/include/sys/errno.h)。

所以,在上
篇的for(;;)...select()程序块中,当有东西可读时,一定要检查recv()或
read()的返回值,返回-1时要作出关断本地Socket的处理,否则select()会
一直认为有东西读,其结果曾几令cpu伤心欲断针脚。

不信你可以试试:不检
查recv()返回结果,且将收到的东东(实际没收到)写至标准输出...
在有名管道的编程中也有类似问题出现。

具体处理详见拙作:发布一个有用
的Socket客户方原码。

至于主动写Socket时对方突然关闭的处理则可以简单地捕捉信号SIGPIPE并作出相应关断本地Socket等等的处理。

SIGPIPE的解释是:写入无读者方的管道。

在此不作赘述,请详man signal。

以上是cpu在作tcp/ip数据传输实验积累的经验,若有错漏,请狂炮击之。

唉,昨天在hacker区被一帮孙子轰得差点儿没短路。

ren cpu(奔腾的心) z80
补充关于select在异步(非阻塞)connect中的应用,刚开始搞socket编程的时候
我一直都用阻塞式的connect,非阻塞connect的问题是由于当时搞proxy scan
而提出的呵呵
通过在网上与网友们的交流及查找相关FAQ,总算知道了怎么解决这一问题.同样用select可以很好地解决这一问题.大致过程是这样的:
1.将打开的socket设为非阻塞的,可以用fcntl(socket, F_SETFL, O_NDELAY)完
成(有的系统用FNEDLAY也可).
2.发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧在进行还没有完成.
3.将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视, 如果可写,用
getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int));
来得到error的值,如果为零,则connect成功.。

相关文档
最新文档