redis实现了单线程处理高并发。其底层实现原理使用了linux kernel的epoll多路复用器!
epoll是什么?epoll是linux kernel函数,是一些API。而伴随着kernel2.5.4的发布,Linux首次将操作系统的io事件的异常控制流暴露给了进程。也就是说,当发生网络io发生连接、读、写事件时,进程可以通过调用API来获取到这些事件,然后进行一系列的数据读写等操作。
epoll工作机制?
其工作机制是,使用"事件"的就绪通知方式,通过epoll_ctl注册文件描述符fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd, epoll_wait便可以收到通知, 并通知应用程序。而且epoll使用一个文件描述符管理多个描述符,将用户进程的文件描述符的事件存放到内核的一个事件表中, 这样数据只需要从内核缓存空间拷贝一次到用户进程地址空间。而且epoll是通过内核与用户空间共享内存方式来实现事件就绪消息传递的,其效率非常高。但是epoll是依赖系统的(Linux)。
事件驱动的实现方式?
事件驱动的本质是通过CPU来提供的,因为CPU需要随时响应意外事件,如键盘、网络 。CPU对于意外事件的响应是依靠异常控制流来实现的。所以,异常控制流是CPU的核心功能。
CPU的时间片分配也是通过异常控制流来实现的;
进程的创建、管理和销毁全部都是基于异常控制流实现的,其生命周期的钩子函数也是操作系统依赖异常控制流实现的。线程在 Linux 上和进程几乎没有功能上的区别;
操作系统本身就是事件驱动的,所以 epoll 并不是什么新发明,而只是把本来不给用户空间用的 api 暴露在了用户空间而已;---JohnLui
NAME
epoll - I/O event notification facility (epoll是一个io事件通知功能)SYNOPSIS
#includeDESCRIPTION
epoll is a variant of poll(2) that can be used either as an edge-triggered or a level-triggered
interface and scales well to large numbers of watched file descriptors. The following system
calls are provided to create and manage an epoll instance:
(epoll 可以监视管理大量的文件描述符)
接下来,strace -ff -o ~/strace-redis/redis ./redis-server 该命令启动一个Redis服务,会将Redis主线程以及其他线程做了什么事记录在文件中,查看文件
查看redis.6032主线程文件:搜索epoll关键字
第209行调用了epoll_create函数,接下来查看这个函数的作用:
epoll_create的描述大致意思是,创建一个epoll实例,并请求kernel创建一块内存用于存储事件备份的文件描述符,并返回epoll实例的文件描述符。也就是说,要内核开辟快空间来管理将要创建的文件描述符。为什么是将要呢,因为有客户端来通过socket连接Redis时,操作系统会创建一个fd,将其存储在这块空间里。
(epoll使用一个文件描述符管理多个描述符,将用户进程的文件描述符的事件存放到内核的一个事件表中, 这样数据只需要从内核缓存空间拷贝一次到用户进程地址空间)
(每一个网络连接其实都对应一个文件描述符)
211行:调用socket函数,创建一个serversocket,返回fd=6;
212行:bind绑定端口号;
224行:listen监听
249行:调用epoll_ctl函数,看下epoll_ctl是啥玩意
大概意思就是,将文件描述符与事件,register 注册到epoll实例中,交由epoll来管理。
(epoll_ctl注册文件描述符fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知, 并通知应用程序)
308行:调用了epoll_wait函数,看下它又是啥玩意
SYNOPSIS
#include
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event *events,
int maxevents, int timeout,
const sigset_t *sigmask);
DESCRIPTION
The epoll_wait() system call waits for events on the epoll instance referred to by the file
descriptor epfd. The memory area pointed to by events will contain the events that will be avail-
able for the caller. Up to maxevents are returned by epoll_wait(). The maxevents argument must
be greater than zero.
The call waits for a maximum time of timeout milliseconds. Specifying a timeout of -1 makes
epoll_wait() wait indefinitely, while specifying a timeout equal to zero makes epoll_wait() to
return immediately even if no events are available (return code equal to zero).
....
RETURN VALUE
When successful, epoll_wait() returns the number of file descriptors ready for the requested I/O,
or zero if no file descriptor became ready during the requested timeout milliseconds. When an
error occurs, epoll_wait() returns -1 and errno is set appropriately.
意思是,调用这个函数,可以查看注册在epoll 实例中的文件描述符,是否有事件发生。若有n个事件发生时,则返回n,否则为0。所以,当n>0时,epoll_event *events数组中就保存了发生的事件,以及文件描述符。有了这个数组,就可对其进行读、写或者连接的工作了。基于事件驱动,是多路复用的重要原因!看下epoll_event:
The struct epoll_event is defined as :
typedef union epoll_data {
void *ptr;
int fd; //文件描述符
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events epoll事件*/
epoll_data_t data; /* User data variable */
};
画个流程图: