当前位置:首页 » 操作系统 » selectlinux

selectlinux

发布时间: 2022-11-14 18:26:40

1. 关于linux下的select/epoll

select这个系统调用的原型如下

第一个参数nfds用来告诉内核 要扫描的socket fd的数量+1 ,select系统调用最大接收的数量是1024,但是如果每次都去扫描1024,实际上的数量并不多,则效率太低,这里可以指定需要扫描的数量。 最大数量为1024,如果需要修改这个数量,则需要重新编译Linux内核源码
第2、3、4个参数分别是readfds、writefds、exceptfds,传递的参数应该是fd_set 类型的引用,内核会检测每个socket的fd, 如果没有读事件,就将对应的fd从第二个参数传入的fd_set中移除,如果没有写事件,就将对应的fd从第二个参数的fd_set中移除,如果没有异常事件,就将对应的fd从第三个参数的fd_set中移除 。这里我们应该 要将实际的readfds、writefds、exceptfds拷贝一份副本传进去,而不是传入原引用,因为如果传递的是原引用,某些socket可能就已经丢失
最后一个参数是等待时间, 传入0表示非阻塞,传入>0表示等待一定时间,传入NULL表示阻塞,直到等到某个socket就绪

FD_ZERO()这个函数将fd_set中的所有bit清0,一般用来进行初始化等。
FD_CLR()这个函数用来将bitmap(fd_set )中的某个bit清0,在客户端异常退出时就会用到这个函数,将fd从fd_set中删除。
FD_ISSET()用来判断某个bit是否被置1了,也就是判断某个fd是否在fd_set中。
FD_SET()这个函数用来将某个fd加入fd_set中,当客户端新加入连接时就会使用到这个函数。

epoll_create系统调用用来创建epfd,会在开辟一块内存空间(epoll的结构空间)。size为epoll上能关注的最大描述符数,不够会进行扩展,size只要>0就行,早期的设计size是固定大小,但是现在size参数没什么用,会自动扩展。
返回值是epfd,如果为-1则说明创建epoll对象失败

第一个参数epfd传入的就是epoll_create返回的epfd。
第二个参数传入对应操作的宏,包括 增删改(EPOLL_CTL_ADD、EPOLL_CTL_DEL、EPOLL_CTL_MOD)
第三个参数传入的是 需要增删改的socket的fd
第四个参数传入的是 需要操作的fd的哪些事件 ,具体的事件可以看后续。
返回值是一个int类型,如果为-1则说明操作失败

第一个参数是epfd,也就是epoll_create的返回值。
第二个参数是一个epoll_event类型的指针,也就是传入的是一个数组指针。 内核会将就绪的socket的事件拷贝到这个数组中,用户可以根据这个数组拿到事件和消息等
第三个参数是maxevents,传入的是 第二个参数的数组的容量
第四个参数是timeout, 如果设为-1一直阻塞直到有就绪数据为止,如果设为0立即返回,如果>0那么阻塞一段时间
返回值是一个int类型,也就是就绪的socket的事件的数量(内核拷贝给用户的events的元素的数量),通过这个数量可以进行遍历处理每个事件

一般需要传入 ev.data.fd 和 ev.events ,也就是fd和需要监控的fd的事件。事件如果需要传入多个,可以通过按位与来连接,比如需要监控读写事件,只需要像如下这样操作即可: ev.events=EPOLLIN | EPOLLOUT 。

LT(水平触发), 默认 的工作模式, 事件就绪后用户可以选择处理和不处理,如果用户不处理,内核会对这部分数据进行维护,那么下次调用epoll_wait()时仍旧会打包出来
ET(边缘触发),事件就绪之后, 用户必须进行处理 ,因为内核把事件打包出来之后就把对应的就绪事件给清掉了, 如果不处理那么就绪事件就没了 。ET可以减少epoll事件被重复触发的次数,效率比LT高。
如果需要设置为边缘触发只需要设置事件为类似 ev.events=EPOLLIN | EPOLLET 即可

select/poll/epoll是nio多路复用技术, 传统的bio无法实现C10K/C100K ,也就是无法满足1w/10w的并发量,在这么高的并发量下,在进行上下文切换就很容易将服务器的负载拉飞。

1.将fd_set从用户态拷贝到内核态
2.根据fd_set扫描内存中的socket的fd的状态,时间复杂度为O(n)
3.检查fd_set,如果有已经就绪的socket,就给对应的socket的fd打标记,那么就return 就绪socket的数量并唤醒当前线程,如果没有就绪的socket就继续阻塞当前线程直到有socket就绪才将当前线程唤醒。
4.如果想要获取当前已经就绪的socket列表,则还需要进行一次系统调用,使用O(n)的时间去扫描socket的fd列表,将已经打上标记的socket的fd返回。

CPU在同一个时刻只能执行一个程序,通过RR时间片轮转去切换执行各个程序。没有被挂起的进程(线程)则在工作队列中排队等待CPU的执行,将进程(线程)从工作队列中移除就是挂起,反映到Java层面的就是线程的阻塞。

什么是中断?当我们使用键盘、鼠标等IO设备的时候,会给主板一个电流信号,这个电流信号就给CPU一个中断信号,CPU执行完当前的指令便会保存现场,然后执行键盘/鼠标等设备的中断程序,让中断程序获取CPU的使用权,在中断程序后又将现场恢复,继续执行之前的进程。

如果第一次没检测到就绪的socket,就要将其进程(线程)从工作队列中移除,并加入到socket的等待队列中。

socket包含读缓冲区+写缓冲区+等待队列(放线程或eventpoll对象)

当从客户端往服务器端发送数据时,使用TCP/IP协议将通过物理链路、网线发给服务器的网卡设备,网卡的DMA设备将接收到的的数据写入到内存中的一块区域(网卡缓冲区),然后会给CPU发出一个中断信号,CPU执行完当前指令则会保存现场,然后网卡的中断程序就获得了CPU的使用权,然后CPU便开始执行网卡的中断程序,将内存中的缓存区中的数据包拿出,判断端口号便可以判断它是哪个socket的数据,将数据包写入对应的socket的读(输入)缓冲区,去检查对应的socket的等待队列有没有等待着的进程(线程),如果有就将该线程(进程)从socket的等待队列中移除,将其加入工作队列,这时候该进程(线程)就再次拥有了CPU的使用权限,到这里中断程序就结束了。

之后这个进程(线程)就执行select函数再次去检查fd_set就能发现有socket缓冲区中有数据了,就将该socket的fd打标记,这个时候select函数就执行完了,这时候就会给上层返回一个int类型的数值,表示已经就绪的socket的数量或者是发生了错误。这个时候就再进行内核态到用户态的切换,对已经打标记的socket的fd进行处理。

将原本1024bit长度的bitmap(fd_set)换成了数组的方式传入 ,可以 解决原本1024个不够用的情况 ,因为传入的是数组,长度可以不止是1024了,因此socket数量可以更多,在Kernel底层会将数组转换成链表。

在十多年前,linux2.6之前,不支持epoll,当时可能会选择用Windows/Unix用作服务器,而不会去选择Linux,因为select/poll会随着并发量的上升,性能变得越来越低,每次都得检查所有的Socket列表。

1.select/poll每次调用都必须根据提供所有的socket集合,然后就 会涉及到将这个集合从用户空间拷贝到内核空间,在这个过程中很耗费性能 。但是 其实每次的socket集合的变化也许并不大,也许就1-2个socket ,但是它会全部进行拷贝,全部进行遍历一一判断是否就绪。

2.select/poll的返回类型是int,只能代表当前的就绪的socket的数量/发生了错误, 如果还需要知道是哪些socket就绪了,则还需要再次使用系统调用去检查哪些socket是就绪的,又是一次O(n)的操作,很耗费性能

1.epoll在Kernel内核中存储了对应的数据结构(eventpoll)。我们可以 使用epoll_create()这个系统调用去创建一个eventpoll对象 ,并返回eventpoll的对象id(epfd),eventpoll对象主要包括三个部分:需要处理的正在监听的socket_fd列表(红黑树结构)、socket就绪列表以及等待队列(线程)。

2.我们可以使用epoll_ctl()这个系统调用对socket_fd列表进行CRUD操作,因为可能频繁地进行CRUD,因此 socket_fd使用的是红黑树的结构 ,让其效率能更高。epoll_ctl()传递的参数主要是epfd(eventpoll对象id)。

3.epoll_wait()这个系统调用默认会 将当前进程(线程)阻塞,加入到eventpoll对象的等待队列中,直到socket就绪列表中有socket,才会将该进程(线程)重新加入工作队列 ,并返回就绪队列中的socket的数量。

socket包含读缓冲区、写缓冲区和等待队列。当使用epoll_ctl()系统调用将socket新加入socket_fd列表时,就会将eventpoll对象引用加到socket的等待队列中, 当网卡的中断程序发现socket的等待队列中不是一个进程(线程),而是一个eventpoll对象的引用,就将socket引用追加到eventpoll对象的就绪列表的尾部 。而eventpoll对象中的等待队列存放的就是调用了epoll_wait()的进程(线程),网卡的中断程序执行会将等待队列中的进程(线程)重新加入工作队列,让其拥有占用CPU执行的资格。epoll_wait()的返回值是int类型,返回的是就绪的socket的数量/发生错误,-1表示发生错误。

epoll的参数有传入一个epoll_event的数组指针(作为输出参数),在调用epoll_wait()返回的同时,Kernel内核还会将就绪的socket列表添加到epoll_event类型的数组当中。

2. linux下select用阻塞的情况怎么返回

既然select已经阻塞了,它就不会返回,除非有信号.你要非得让它返回的话就用信号吧.
不过一般用select时都不会设为阻塞,因为你设为阻塞不是跟没用select一样吗?recv本身也是阻塞的,
而且主动close套接字时还会返回0,可以判断

3. linux下的select函数是干嘛的

不是用得很好吗?精确延时功能。
此外,可以用来判断读写操作是否在指定时间内就绪。

4. linux下select用阻塞的情况怎么返回

既然select已经阻塞了,它就不会返回,除非有信号.你要非得让它返回的话就用信号吧.
不过一般用select时都不会设为阻塞,因为你设为阻塞不是跟没用select一样吗?recv本身也是阻塞的,而且主动close套接字时还会返回0,可以判断.

5. linux select 怎么理解

linux select函数详解
在Linux中,我们可以使用select函数实现I/O端口的复用,传递给 select函数的参数会告诉内核:
•我们所关心的文件描述符
•对每个描述符,我们所关心的状态。(我们是要想从一个文件描述符中读或者写,还是关注一个描述符中是否出现异常)
•我们要等待多长时间。(我们可以等待无限长的时间,等待固定的一段时间,或者根本就不等待)
从 select函数返回后,内核告诉我们一下信息:
•对我们的要求已经做好准备的描述符的个数
•对于三种条件哪些描述符已经做好准备.(读,写,异常)
有了这些返回信息,我们可以调用合适的I/O函数(通常是 read 或 write),并且这些函数不会再阻塞.
#include <sys/select.h>
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
返回:做好准备的文件描述符的个数,超时为0,错误为 -1.
首先我们先看一下最后一个参数。它指明我们要等待的时间:
struct timeval{
long tv_sec; /*秒 */
long tv_usec; /*微秒 */
}
有三种情况:
timeout == NULL 等待无限长的时间。等待可以被一个信号中断。当有一个描述符做好准备或者是捕获到一个信号时函数会返回。如果捕获到一个信号, select函数将返回 -1,并将变量 erro设为 EINTR。
timeout->tv_sec == 0 &&timeout->tv_usec == 0不等待,直接返回。加入描述符集的描述符都会被测试,并且返回满足要求的描述符的个数。这种方法通过轮询,无阻塞地获得了多个文件描述符状态。
timeout->tv_sec !=0 ||timeout->tv_usec!= 0 等待指定的时间。当有描述符符合条件或者超过超时时间的话,函数返回。在超时时间即将用完但又没有描述符合条件的话,返回 0。对于第一种情况,等待也会被信号所中断。

6. Linux下select函数文件描述符状态的问题

当然是在有输入或者输出时文件描述符的读写状态改变咯,比如标准输入的文件描述符是0,如果用select来等待0号文件描述符,那么当在键盘上敲字符时开始,就是文件描述符的读写状态改变之时,这时select函数就会返回;对于套接字描述符来说也是这样,用select来等待一个服务器描述符,那么当有新的连接请求时(服务器描述符等待请求时是一个读描述符,当有新请求时实际上是有一个输入),服务器描述符的读写状态改变,select函数返回。顺便说一下,检查哪个文件描述符发生改变,可以用FD_ISSET宏来进行检测。

7. linux select函数中所指满足可读文件描述的条件是什么

select在串口编程这里是实现监听串口的数据功能的,如果串口中有接收到数据,select就会返回一个大于0的数,select会把读文件集合(fd_set)中的其他文件描述符清掉,只留下有数据的串口文件描述符,用FD_ISSET()可以判断该文件描述符是否在集合中,从而执行相应的代码。

8. linux select检测为啥是监听套接字

select系统调用的作用就是让程序在多个文件描述符上进行等待,而套接字也是属于文件描述符的一种,所以服务器程序就可以利用select系统调用,在多个套接字上等待客户端请求,从而达到同时处理多个客户端的效果,而等待客户端请求就需要用listen调用对客户端进行监听,select调用检测的当然就是监听套接字咯。
select系统调用是检测一个已打开的文件描述符的集合(这个集合是一个fd_set类型的数据结构),服务器程序需要创建这个集合,创建时需要用listen调用让套接字处于监听状态,只有这样当有一个新的连接发生时,描述符才会有活动发生,才能够被检测到。

9. linux下的select函数是干嘛的

select是用来设置超时时间的,其第一个参数本来是一个文件号,假如读取该文件长时间没有返回则超时跳出,而这部分代码将文件号设置为0,说明只是为了控制延时
不过看你这部分代码,明显只是实现一个比较精确定时的sleep
这段代码之所以这么做,是因为linux本身的sleep函数非常不准(windows也是一样),在线程较多,cpu任务较重的时候,sleep函数的精确度根本无法达到要求
于是你这段代码使用select来代替sleep更为精准,其精准程度和内核相关,如果内核的滴答频率决定的,一般是100HZ也有1000hz的(因内核版本不同而不同),也就是说select做多可以精确到10ms,或者1ms,而sleep就做不到
于是
这段函数最重要的作用就是用高精确的select函数来代替低精确度的sleep函数,实现时间较为精准的延时

10. 谁能告诉我 linux下select函数到底是干什么用的 貌似我不用它也可以得到我想要的结果啊

select是用来设置超时时间的,其第一个参数本来是一个文件号,假如读取该文件长时间没有返回则超时跳出,而这部分代码将文件号设置为0,说明只是为了控制延时不过看你这部分代码,明显只是实现一个比较精确定时的sleep这段代码之所以这么做,是因为linux本身的sleep函数非常不准(windows也是一样),在线程较多,cpu任务较重的时候,sleep函数的精确度根本无法达到要求于是你这段代码使用select来代替sleep更为精准,其精准程度和内核相关,如果内核的滴答频率决定的,一般是100HZ也有1000hz的(因内核版本不同而不同),也就是说select做多可以精确到10ms,或者1ms,而sleep就做不到于是这段函数最重要的作用就是用高精确的select函数来代替低精确度的sleep函数,实现时间较为精准的延时。可查阅《Linux就该这么学》了解更多Linux介绍。

热点内容
内置存储卡可以拆吗 发布:2025-05-18 04:16:35 浏览:336
编译原理课时设置 发布:2025-05-18 04:13:28 浏览:378
linux中进入ip地址服务器 发布:2025-05-18 04:11:21 浏览:612
java用什么软件写 发布:2025-05-18 03:56:19 浏览:32
linux配置vim编译c 发布:2025-05-18 03:55:07 浏览:107
砸百鬼脚本 发布:2025-05-18 03:53:34 浏览:944
安卓手机如何拍视频和苹果一样 发布:2025-05-18 03:40:47 浏览:741
为什么安卓手机连不上苹果7热点 发布:2025-05-18 03:40:13 浏览:803
网卡访问 发布:2025-05-18 03:35:04 浏览:511
接收和发送服务器地址 发布:2025-05-18 03:33:48 浏览:372