曹工说Redis源码(6)-- redis server 主循环大体流程解析 (2)

6处和7处,这是另外一个else分支,从2处分出来的,如果没找到最近的周期任务,则进入这里;根据参数flags中是否设置了AE_DONT_WAIT选项,分出2个分支,一个设为0,一个设为null。

select函数简介

说到网络编程中的多路复用,select几乎是绕不开的话题,在没有epoll之前,基本就是使用select。当然,select有它的缺点,那就是:

select总是去线性扫描所有的文件描述符,看看哪个文件描述符是ready的;怎么算作ready,读或者写,不用阻塞,就算是ready;

select最大支持的文件描述符数量有限制,默认为1024.

下面,大家看看select的api,大家也可以自行在linux机器上执行:man select 查看。

select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

上面的第一行,是select的简单说明,其中一个词是,synchronous,同步的意思,说明select是同步的,不是异步的,只是进行了io多路复用。

下面那个是其api,简单解释三个参数:

参数fd_set *readfds

Those listed in readfds will be watched to see if characters become available for reading (more
precisely, to see if a read will not block

也就是说,这个集合中的fd,会被监测,看看哪些fd可以无阻塞地读取;怎么才能无阻塞地读取,那肯定是这个fd的输入缓冲区有内容啊,比如,客户端发了数据过来

参数fd_set *writefds

those in writefds will be watched to see if a write will not block

这个集合,会被监测,看看是否可以对这个fd,进行无阻塞地写;什么时候,不能无阻塞地写呢?肯定是缓冲区满了的时候。这种应该常见于:给对端发数据时,对方一直不ack这些数据,所以我方的缓冲区里,一直不能删这些数据,导致缓冲区满。

struct timeval *timeout

The timeout argument specifies the minimum interval that select() should block waiting for a file descriptor to become ready. If both fields of the timeval structure are zero, then select() returns immediately. (This is useful for polling.) If timeout is NULL (no timeout), select() can block
indefinitely.

这个timeout参数,指定了select()操作,等待文件描述符变成ready过程中,需要等待多长时间。如果这个timeout的两个字段,都被设为了0,则select()会马上返回。如果timeout是null,这个操作会无限阻塞。

所以,select我就算大家了解了,其中的timeout参数,简单来说,就是调用select时,最大阻塞多久就要返回。

如果设为0,则马上返回;如果为null,则无限阻塞;如果为正常的大于0的值,则阻塞对应的时长。

和前面的部分,联系起来,就是说:

假设没有周期任务,则,无限阻塞;

如果有周期任务,且时间已经到达,则马上返回;

如果有周期任务,且时间未到,则阻塞对应时长后返回。

linux下不是用epoll吗,为啥还讲select

有的函数,天生适合拿来讲课。epoll,kqueue等,会单独拿来讲。

获取到ready的文件描述符后,处理该文件描述符 // 1 处理文件事件,阻塞时间由 tvp 决定,tvp:timevalue pointer numevents = aeApiPoll(eventLoop, tvp); for (j = 0; j < numevents; j++) { // 2 从已就绪数组中获取事件 aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd]; int mask = eventLoop->fired[j].mask; int fd = eventLoop->fired[j].fd; int rfired = 0; // 读事件 if (fe->mask & mask & AE_READABLE) { // rfired 确保读/写事件只能执行其中一个 rfired = 1; fe->rfileProc(eventLoop, fd, fe->clientData, mask); } // 写事件 if (fe->mask & mask & AE_WRITABLE) { if (!rfired || fe->wfileProc != fe->rfileProc) fe->wfileProc(eventLoop, fd, fe->clientData, mask); } processed++; }

1处,这里就会根据当前的操作系统,决定调用select或是epoll,或是其他的实现。(通过条件编译实现)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zydfpf.html