各I/O模型 对应Web服务应用模型(select,poll,epoll,kevent,"/dev/poll")
一、利用select多路复用I/O的Web服务应用模型
/* 可读、可写、异常三种文件描述符集的申明和初始化。*/ fd_set readfds, writefds, exceptionfds; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptionfds); int max_fd; /* socket配置和监听。*/ sock = socket(...); bind(sock, ...); listen(sock, ...); /* 对socket描述符上发生关心的事件进行注册。*/ FD_SET(&readfds, sock); max_fd = sock; while(1) { int i; fd_set r,w,e; /* 为了重复使用readfds 、writefds、exceptionfds,将它们拷贝到临时变量内。*/ memcpy(&r, &readfds, sizeof(fd_set)); memcpy(&w, &writefds, sizeof(fd_set)); memcpy(&e, &exceptionfds, sizeof(fd_set)); /* 利用临时变量调用select()阻塞等待,等待时间为永远等待直到发生事件。*/ select(max_fd + 1, &r, &w, &e, NULL); /* 测试是否有客户端发起连接请求,如果有则接受并把新建的描述符加入监控。*/ if(FD_ISSET(&r, sock)){ new_sock = accept(sock, ...); FD_SET(&readfds, new_sock); FD_SET(&writefds, new_sock); max_fd = MAX(max_fd, new_sock); } /* 对其它描述符发生的事件进行适当处理。描述符依次递增,最大值各系统有所不同(比如在作者系统上最大为1024),在linux可以用命令ulimit -a查看(用ulimit命令也对该值进行修改)。在freebsd下,用sysctl -a | grep kern.maxfilesperproc来查询和修改。*/ for(i= sock+1; i <max_fd+1; ++i) { if(FD_ISSET(&r, i)) doReadAction(i); if(FD_ISSET(&w, i)) doWriteAction(i); } }
二、利用poll多路复用I/O的Web服务应用模型
/* 新建并初始化文件描述符集。*/ struct pollfd fds[MAX_NUM_FDS]; int max_fd; /* socket配置和监听。*/ sock = socket(...); bind(sock, ...); listen(sock, ...); /* 对socket描述符上发生关心的事件进行注册。*/ fds[0].fd = sock; fds[0].events = POLLIN; max_fd = 1; while(1) { int i; /*调用poll()阻塞等待,等待时间为永远等待直到发生事件。*/ poll(fds, max_fd, -1); /* 测试是否有客户端发起连接请求,如果有则接受并把新建的描述符加入监控。*/ if(fds[0].revents & POLLIN){ new_sock = accept(sock, ...); fds[max_fd].fd = new_sock; fds[max_fd].events = POLLIN | POLLOUT; ++ max_fd; } /* 对其它描述符发生的事件进行适当处理。*/ for(i=1; i <max_fd+1; ++i) { if(fds.revents & POLLIN) doReadAction(i); if(fds.revents & POLLOUT) doWriteAction(i); } } 利用epoll多路复用I/O的Web服务应用模型 /* 新建并初始化文件描述符集。*/ struct epoll_event ev; struct epoll_event events[MAX_EVENTS]; /* 创建epoll句柄。*/ int epfd = epoll_create(MAX_EVENTS); /* socket配置和监听。*/ sock = socket(...); bind(sock, ...); listen(sock, ...); /* 对socket描述符上发生关心的事件进行注册。*/ ev.events = EPOLLIN; ev.data.fd = sock; epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev); while(1) { int i; /*调用epoll_wait()阻塞等待,等待时间为永远等待直到发生事件。*/ int n = epoll_wait(epfd, events, MAX_EVENTS, -1); for(i=0; i <n; ++i) { /* 测试是否有客户端发起连接请求,如果有则接受并把新建的描述符加入监控。*/ if(events.data.fd == sock) { if(events.events & POLLIN){ new_sock = accept(sock, ...); ev.events = EPOLLIN | POLLOUT; ev.data.fd = new_sock; epoll_ctl(epfd, EPOLL_CTL_ADD, new_sock, &ev); } }else{ /* 对其它描述符发生的事件进行适当处理。*/ if(events.events & POLLIN) doReadAction(i); if(events.events & POLLOUT) doWriteAction(i); } } }
三、利用kqueue多路复用I/O的Web服务应用模型
/* 新建并初始化文件描述符集。*/ struct kevent changelist[MAX_EVENTS]; struct kevent eventlist[MAX_EVENTS]; int count = 0; /* 创建kqueue句柄。*/ int kqfd = kqueue(); /* socket配置和监听。*/ sock = socket(...); bind(sock, ...); listen(sock, ...); /* 对socket描述符上发生关心的事件进行注册。*/ EV_SET(&changelist[0], sock, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); ++ count; while(1) { int i; /*调用kevent()阻塞等待,等待时间为永远等待直到发生事件。*/ int n = kevent(kqfd, changelist, count, eventlist, count, NULL); for(i=0; i <n; ++i) { /* 测试是否有客户端发起连接请求,如果有则接受并把新建的描述符加入监控。*/ if(eventlist.ident == sock) { new_sock = accept(sock, ...); EV_SET(&changelist[count], new_sock, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); ++ count; }else{ /* 对其它描述符发生的事件进行适当处理。*/ doReadAction(i); } } }
四、利用/dev/poll多路复用I/O的Web服务应用模型
/* 新建并初始化文件描述符集。*/ struct pollfd pfd; struct pollfd pollfds[MAX_EVENTS]; struct dvpoll dopoll; int count = 0; /* 打开/dev/poll设备,创建poll句柄。*/ int dpfd = open("/dev/poll", O_RDWR); /* socket配置和监听。*/ sock = socket(...); bind(sock, ...); listen(sock, ...); /* 对socket描述符上发生关心的事件进行注册。*/ pfd.fd = sock; pfd.events = EPOLLIN; pfd.revents = 0; write(dpfd, pfd, sizeof(pfd)); ++ count; while(1) { int i; /*调用ioctl()阻塞等待,等待时间为永远等待直到发生事件。*/ dopoll.dp_timeout = -1; dopoll.dp_nfds = count; dopoll.dp_fds = &pollfds; int n = ioctl(dpfd, DP_POLL, &dopoll); for(i=0; i <n; ++i) { /* 测试是否有客户端发起连接请求,如果有则接受并把新建的描述符加入监控。*/ if(pollfds.fd == sock) { if(pollfds.revents & POLLIN){ new_sock = accept(sock, ...); pfd.fd = new_sock; pfd.events = EPOLLIN | POLLOUT; pfd.revents = 0; write(dpfd, pfd, sizeof(pfd)); ++ count; } }else{ /* 对其它描述符发生的事件进行适当处理。*/ if(pollfds.revents & POLLIN) doReadAction(i); if(pollfds.revents & POLLOUT) doWriteAction(i); } } }
五、利用rtsig多路复用I/O的Web服务应用模型
/* 新建并初始化关注信号。*/ sigset_t sigset; siginfo_t siginfo; sigemptyset(&sigset); sigaddset(&sigset, SIGRTMIN + 1); sigaddset(&sigset, SIGIO); /* socket配置和监听。*/ sock = socket(...); bind(sock, ...); listen(sock, ...); /* 重新设置描述符可读写时发送的信号值。*/ fcntl(sock, F_SETSIG, SIGRTMIN + 1); /* 对socket描述符设置所有者。*/ fcntl(sock, F_SETOWN, getpid()); /* 启用描述符的信号驱动I/O模式。*/ fcntl(sock, F_SETFL, O_ASYNC | O_NONBLOCK | O_RDWR); while(1) { struct timespec ts; ts.tv_sec = 1; ts.tv_nsec = 0; /*调用sigtimedwait()阻塞等待,等待时间1秒。*/ sigtimedwait(&sigset, &siginfo, &ts); /* 测试是否有客户端发起连接请求,如果有则接受并把新建的描述符加入监控。*/ if(siginfo.si_fd == sock) { new_sock = accept(sock, ...); fcntl(new_sock , F_SETSIG, SIGRTMIN + 1); fcntl(new_sock , F_SETOWN, getpid()); fcntl(new_sock , F_SETFL, O_ASYNC | O_NONBLOCK | O_RDWR); }else { /* 对其它描述符发生的事件进行适当处理。*/ doReadAction(i); } }