一、背景
redis是内存数据库,并且是单线程,为什么单线程也能够这么快?
因为,所有线上请求的set、get操作都是在内存中,涉及到磁盘和网络的部分都是由后台线程执行,尽量减少了主线程的开销。单线程只是说对字典空间set、get时是单线程的,不需要同步机制,而将数据在用户空间和socket buffer之间的拷贝是由io_thread_list做的,其中主线程也算是其中一个io_thread。
二、epoll使用案例
为了降低redis理解时的复杂度,先给出一个epoll使用时的case,有助于后面的理解。
代码语言:c复制#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<errno.h>
#include<fcntl.h>
#define _BACKLOG_ 5
#define _BUF_SIZE_ 10240
#define _MAX_ 64
typedef struct _data_buf
{
int fd;
char buf[_BUF_SIZE_];
}data_buf_t,*data_buf_p;
static void usage(const char* proc)
{
printf("usage:%s[ip][port]n",proc);
}
static int start(int port,char *ip)
{
assert(ip);
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
exit(1);
}
struct sockaddr_in local;
local.sin_port=htons(port);
local.sin_family=AF_INET;
local.sin_addr.s_addr=inet_addr(ip);
int opt=1; //设置为接口复用
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
exit(2);
}
if(listen(sock,_BACKLOG_)<0)
{
perror("listen");
exit(3);
}
return sock;
}
static int epoll_server(int listen_sock)
{
int epoll_fd=epoll_create(256);//生成一个专用的epoll文件描述符
if(epoll_fd<0)
{
perror("epoll_create");
exit(1);
}
struct epoll_event ev;//用于注册事件
struct epoll_event ret_ev[_MAX_];//数组用于回传要处理的事件
int ret_num=_MAX_;
int read_num=-1;
ev.events=EPOLLIN;
ev.data.fd=listen_sock;
//注册监听套接字
if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listen_sock,&ev)<0)//用于控制某个文件描述符上的事件(注册,修改,删除)
{
perror("epoll_ctl");
return -2;
}
int done=0;
int i=0;
int timeout=5000;
struct sockaddr_in client;
socklen_t len=sizeof(client);
while(!done)
{
//ret_ev存放的是触发的事件
switch(read_num=epoll_wait(epoll_fd,ret_ev,ret_num,timeout))//用于轮寻I/O事件的发生
{
case 0:
printf("time outn");
break;
case -1:
perror("epoll");
exit(2);
default:
{
for(i=0;i<read_num; i)
{
//连接建立事件
if(ret_ev[i].data.fd==listen_sock&&(ret_ev[i].events&EPOLLIN))
{
int fd=ret_ev[i].data.fd;
int new_sock=accept(fd,(struct sockaddr*)&client,&len);
if(new_sock<0)
{
perror("accept");
continue;
}
ev.events=EPOLLIN;
ev.data.fd=new_sock;
epoll_ctl(epoll_fd,EPOLL_CTL_ADD,new_sock,&ev);
printf("get a new client...n");
}
else //normal sock
{
//读事件
if(ret_ev[i].events&EPOLLIN)
{
int fd=ret_ev[i].data.fd;
data_buf_p mem=(data_buf_p)malloc(sizeof(data_buf_t));
if(!mem){
perror("malloc failed...");
continue;
}
mem->fd=fd;
memset(mem->buf,'