nginx数据读取
上篇说到nginx会分裂出若干个子进程来接受客户端请求。每个进程是竞争关系,谁抢到了连接事件谁来处理。网络模型和之前分析redis的实现是一样的,mac下是kqueue。当时没有仔细记录,现在记录一下kqueue的情况。
kqueue参数说明
kqueue 一共有如下三个函数:
int kqueue(void);
int kevent(int kq,
const struct kevent *changelist, int nchanges,
struct kevent *eventlist, int nevents,
const struct timespec *timeout);
int kevent64(int kq,
const struct kevent64_s *changelist, int nchanges,
struct kevent64_s *eventlist, int nevents,
unsigned int flags,
const struct timespec *timeout);
kevent64函数redis和nginx中都没有涉及,应该是event的扩展版本先略过。
-
int kqueue(void);
返回一个kqueue文件描述符。 -
int kevent(...)
参数中带指针的是输出参数,其他为输入参数,kevent
函数的功能有3个:(1)向kqueue中添加监听事件(2)从kqueue中删除监听事件(3)读取kqueue中的事件。下面解释一下各个参数的含义:
1.kq 即kqueue函数返回的文件描述符。
2.需要变更的event事件,我们看一下这个kevent结构体,参数含义为 ident
:socket文件描述符, filter
:代表了对应什么事件被触发了,socket编程中我们通常取值 EVFILT_READ和EVFILT_WRIT,flags
增删事件时使用,取值为EV_ADD/EV_DELETE/EV_ENABLE/EV_DISABLE
,前两个已经可以满足我们使用需要. fflags
和data
,用的不多,填0和null;udata
,用户数据。
struct kevent {
uintptr_t ident; /* identifier for this event */
int16_t filter; /* filter for event */
uint16_t flags; /* general flags */
uint32_t fflags; /* filter-specific flags */
intptr_t data; /* filter-specific data */
void *udata; /* opaque user data identifier */
};
3.nchanges changelist的个数。
4.eventlist 读取kqueue中事件时使用,返回事件数组。
5.nevents 最大读取数量。
6.timeout 等待时间,如果填null则是无限时间。
7.返回值 读取到的事件数量。
kqueue使用举例伪代码
int kq = kqueue();
int fd = newServerSocket();
kevent ke;
ke.ident=fd;
ke.filter=EVFILT_WRIT&EVFILT_READ;
ke.flags=EV_ADD;
kevent(kq,&ke,1,null,0,null);//添加到kq中。
...
kevent* lst;
int event_num = kevent(kq,null,0,lst,max_recv_num=512,0);
for(kevent ke:lst){
Onevent(event.fd);//doAccept or doRead ...
}
每个socket发生的事件,我们可以在外部维护一个socket文件描述符到处理read和write事件函数的映射关系,比如redis。 我们也可以采取nginx的处理方式,将处理函数放到udata中由kqueue保存。