I/O多路复用
1.什么是I/O多路复用
I/O 多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;
一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;
没有文件句柄就绪就会阻塞应用程序,交出CPU。
2.为什么会有I/O多路复用机制
没有IO多路复用机制时,有BIO、NIO两种实现方式,但它们都有一些问题
1.同步阻塞
服务端采用单线程,当 accept 一个请求后,在 recv 或 send 调用阻塞时,将无法 accept 其他请求(必须等上一个请求处理 recv 或 send 完 )(无法处理并发)
服务端采用多线程,当 accept 一个请求后,开启线程进行 recv,可以完成并发处理,但随着请求数增加需要增加系统线程,大量的线程占用很大的内存空间,并且线程切换会带来很大的开销,10000个线程真正发生读写实际的线程数不会超过20%,每次accept都开一个线程也是一种资源浪费。
2.异步阻塞
服务器端当 accept 一个请求后,加入 fds 集合,每次轮询一遍 fds 集合 recv (非阻塞)数据,没有数据则立即返回错误,每次轮询所有 fd (包括没有发生读写实际的 fd)会很浪费 CPU资源
3.实现I/O多路复用的几种方式
1.select实现I/O多路复用
1.1.涉及的api
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | #include <sys/select.h>
 int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set exceptfds,struct timeval *timeout);
 int pselect(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout,sigset_t sigmask);
 
 
 
 
 
 
 
 
 
 
 FD_ZERO(fd_set *set);
 
 
 FD_SET(int fd,fd_set *set);
 
 
 FD_CLR(int fd,fd_set *set);
 
 
 | 
1.2.操作实例
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 
 | #include <stdio.h>#include <sys/socket.h>
 #include <netinet/in.h>
 #include <unistd.h>
 #include <string.h>
 #include <sys/select.h>
 #include <arpa/inet.h>
 int main(int argc, char** argv)
 {
 int fd = New_Socket(argv[1], argv[2], 10);
 if (fd == -1) {
 printf("Create Socket error!\n");
 exit(EXIT_FAILURE);
 }
 fd_set fdset, rset;
 
 FD_ZERO(&fdset);
 
 FD_SET(fd, &fdset);
 .....
 int maxfd = fd + 1;
 struct timeval timeout = { 20, 20 };
 while (1) {
 rset = fdset;
 int nfds = select(maxfd, &rset, NULL, NULL, &timeout);
 
 
 if (nfds < 0) {
 perror("select");
 exit(EXIT_FAILURE);
 } else if (nfds == 0) {
 printf("select timeout\n");
 continue;
 } else {
 if (FD_ISSET(fd, &rset)) {
 Handler(fd, &fdset);
 }
 }
 }
 }
 
 | 
2.使用poll实现I/O多路复用
2.1.涉及的api
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | #include <poll.h>
 struct pollfd {
 int   fd;
 short events;
 short revents;
 };
 
 
 
 
 int poll(struct pollfd *fds,nfds_t nfds,int timeout);
 int ppoll(struct pollfd *fds,nfds_t nfds,cont struct timespec *tmo_p,const sigset_t *sigmask);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 | 
2.2.操作实例
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | struct pollfd{
 int fd;
 short event;
 short revent;
 }
 #define MAX_FD 100
 int main(int argc,char **argv)
 {
 struct pollfd fd[MAX_FD];
 
 fd[i].event = POLLIN
 
 int nfds = poll(struct pollfd fds[], nfds_t nfds, int timeout);
 
 for(int i = 0;i<MAX_FD,i++)
 {
 
 if(fd[i].revent & POLLIN)
 .....
 }
 }
 
 | 
3.使用epoll实现I/O多路复用
3.1.涉及的api
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | int epoll_create(int size);
 
 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
 
 
 
 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);
 
 
 | 
3.2.操作实例
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 
 | #include <sys/epoll.h>#include <sys/socket.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 int create_bind_listen(char *address,char *port,unsigned int backlog)
 {
 
 }
 int main(int argc,char **argv)
 {
 int fd = create_bind_listen(argv[1],argv[2],10);
 int epfd = epoll_create(100);
 struct epoll_event epoll_fd;
 struct epoll_event event_epoll[10];
 epoll_fd.fd = fd;
 epoll_fd.events = EPOLLIN;
 epoll_fd.data.fd = fd;
 epoll_ctl(epfd,EPOLL_CTL_ADD,&epoll_fd);
 int event_count = epoll_wait(epfd,event_epoll,10);
 for(int i = 0;i<event_count;i++)
 {
 
 }
 }
 
 |