linux高性能服务器编程之epoll




一.概述:

epoll是多路复用的一种,但它比select和poll更加高效。具体体现在以下几个方面:

(1).select能打开的文件描述符是有一定限制的,默认情况下是2048,这对应那些大型服务器来说h是不足的。但
epoll则没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左
右,具体数目可以cat  /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

(2).因为文件描述符是内核管理的,所以每次调用select或poll,都需要把fd集合从用户态拷贝到内核态,并且每次检查文件描述符的状态时,都要在内核遍历所有文件描述符,这个开销在fd很多时会很大。而epoll采用了nmap(内存映射)(和共享内存一样),内核和用户空间共用一行份fd集。

(3).另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。



其它优化:

(4).epoll会给所有要关注的文件描述符建立一个红黑树,这样在查找某一个文件描述符时效率会有所提升。

(5).epoll会给准备好的文件描述符建立一个链表,这样查找一个已准备好的文件描述符时就不用在以前所有要关注的fd集中查找了。




二.epoll用法篇:

epoll是什么?按照man手册的说法:是为处理大批量句柄而作了改进的poll。当然,这不是2.6内核才有的,它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel2.5.44),它几乎具备了之前所说select和poll的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

epoll有epoll_create,epoll_ctl,epoll_wait 3个系统调用。



(1).epoll_create:

int   epoll_create(int size);

创建一个epoll的句柄(后面会根据这个句柄创建红黑树)。自从linux2.6.8之后,size参数是被忽略的(也就是说,可以为任意值)。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

返回值:成功返回一个epoll句柄,失败返回-1;

(2).epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

函数描述:epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。(一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。)

返回值:成功返回0,失败返回-1;

epfd参数:epoll_create创建的一个epoll句柄。

op参数:表示要执行的动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;

fd参数:需要监听的文件描述符。

event参数:告诉内核需要监听什么事。struct epoll_event结构如下:

 The event argument describes the object linked to the file descriptor fd.  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_data_t data;        /* User data variable */
           };

events有如下值:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);

EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水水平触发(Level
Triggered)来说的。(epoll默认为水平触发)

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

(3).epoll_wait:

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

函数功能:监听在epoll监控的事件中已经发送的事件。

返回值:成功返回监听文件描述符集中已经准备好的文件描述符,返回0代表timeout,失败返回-1。

epoll参数:epoll_create创建的epoll句柄。

events参数:输出型参数,保存监听文件描述符集中已经准备好的文件描述符集。

maxevents参数:events数组的大小。

timeout参数:超时时间。单位为毫秒。




三.LT模式下的阻塞模式。

相关代码:

server.c:

 1 /****************************************                                                                                                 
  2     > File Name:epoll_server.c
  3     > Author:xiaoxiaohui
  4     > mail:[email protected]
  5     > Created Time:2016年05月28日 星期六 15时38分17秒
  6 ****************************************/
  7 
  8 #include<stdio.h>
  9 #include<stdlib.h>
 10 #include<sys/types.h>
 11 #include<sys/socket.h>
 12 #include<arpa/inet.h>
 13 #include<netinet/in.h>
 14 #include<string.h>
 15 #include<unistd.h>
 16 #include<sys/epoll.h>
 17 
 18 #define LEN 1024地
 19 const char* IP = "127.0.0.1";
 20 const int PORT = 8080;
 21 const int BACKLOG = 5;
 22 int timeout = 5000;
 23 const int MAXEVENTS = 64;
 24 struct sockaddr_in local;
 25 struct sockaddr_in clien153                 }
154                 break;
155         }
156     }
157 }
158 
159 int main()
160 {
161     int listenSock = ListenSock();
162     epoll_fd(listenSock);
163     close(listenSock);
164     return 0;
165 }                                t;
 26 int SIZE_CLIENT = sizeof(client);
 27 
 28 typedef struct data_buf     //用于存储epoll_event中的data中的不同元素
 29 {
 30     int fd;
 31     char buf[LEN];
 32 }data_buf_t, *data_buf_p;地
 33 
 34 
 35 int ListenSock()
 36 {
 37     int listenSock = socket(AF_INET, SOCK_STREAM, 0);
 38     if(listenSock < 0)
 39     {
 40         perror("socket");
 41         exit(1);
 42     }
 43 
 44     local.sin_family = AF_INET;
 45     local.sin_port = htons(PORT);
 46     local.sin_addr.s_addr = inet_addr(IP);
 47     if( bind(listenSock, (struct sockaddr*)&local, sizeof(local)) < 0)
 48     {
 49         perror("bind");
 50         exit(2);
 51     }
 52 
 53     if( listen(listenSock, BACKLOG) < 0)
 54     {
 55         perror("listen");
 56         exit(3);
 57     }
 58 
 59     return listenSock;
 60 }
 61 
 62 static int epoll_fd(int listenSock)
 63 {
 64 
 65     int epoll_fd = epoll_create(256);           //size随便选一个值
 66 
 67     struct epoll_event ev;          //把listenSock设置进epoll_fd中
 68     ev.events = EPOLLIN;
 69     ev.data.fd = listenSock;
 70     epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenSock, &ev);        //系统会维护一个红黑树
 71 
 72     struct epoll_event ev_outs[MAXEVENTS];      //准备好的队列
 73     int max = MAXEVENTS;
 74     
 75     while(1)                                                                                                                              
 76     {
 77         int num = -1;
 78         switch( num = epoll_wait(epoll_fd, ev_outs, max, timeout))
 79         {
 80             case 0:     //timeout                                                                                                         
 81                 printf("timeout.....\n");
 82                 break;
 83             case -1:    //error
 84                 perror("epoll_wait");
 85                 break;
 86             default:
 87                 for(int index = 0; index < num; index++)   
 88                 {
 89                     if(ev_outs[index].data.fd == listenSock && (ev_outs[index].events & EPOLLIN))      //监听套接字准备就绪
 90                     {
 91                         printf("accept is ready\n");
 92                         int linkSock = accept(listenSock, (s地truct sockaddr*)&client, &SIZE_CLIENT);
 93                         if(linkSock < 0)
 94                         {
 95                             perror("accept");
 96                             continue;           //这次可能是一个新客户端的请求,所以后面可能还有文件描述符准备就绪了
 97                         }地
 98 
 99                         ev.events = EPOLLIN;     //把新套接字放到红黑树中
100                         ev.data.fd = linkSock;
101                         epoll_ctl(epoll_fd, EPOLL_CTL_ADD, linkSock, &ev);
102                     }
103                     else         //已链接套接字准备就绪
104                     {
105                         if(ev_outs[index].events & EPOLLIN)      //读事件准备就绪
106                         {
107                             data_buf_p mem = (data_buf_p)malloc(sizeof(data_buf_t));
108                             memset(mem->buf, ‘\0‘, sizeof(mem->buf));
109                             mem->fd = ev_outs[index].data.fd;
110 
111                             int ret = read(mem->fd, mem->buf, sizeof(mem->buf));
112                             if(ret > 0)
113                             {
114                                 mem->buf[ret] = ‘\0‘;
115                                 printf("client# %s\n", mem->buf);
116                                                                                                                                           
117                                 ev.data.ptr = mem;         //mem中即保持了fd,又保持了buf数据
118                                 ev.events = EPOLLOUT;    //读事件已经完成,现在要关心写事件
119                                 epoll_ctl(epoll_fd, EPOLL_CTL_MOD, mem->fd, &ev);
120                             }
121                             else if(ret == 0 )      //客户端已关闭
122                             {
123                                 printf("client is closed\n");
124                                 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ev_outs[index].data.fd, NULL);     //把该文件描述符从红黑树中移除
125                                 close(ev_outs[index].data.fd);
126                                 free(mem);
127                             }
128                             else
129                             {
130                                 perror("read");
131                                 continue;
132                             }
133                         }
134                         else if(ev_outs[index].events & EPOLLOUT)   //写事件准备就绪
135                         {
136                             data_buf_p mem = (data_buf_p)ev_outs[index].data.ptr;
137                             int fd = mem->fd;
138                             char* buf = mem->buf;
139 
140                             if( write(fd, buf, strlen(buf)) < 0)
141                             {
142                                 perror("write");
143                                 continue;
144                             }
145 
146                             ev.events = EPOLLIN;       //这个文件描述符的写事件已完成,下次关心读事件
147                             ev.data.fd = mem->fd;
148                             epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
149                         }
150                         else   //DoNothing
151                         {}
152                     }
153                 }
154                 break;
155         }
156     }
157 }
158 
159 int main()
160 {
161     int listen地Sock = ListenSock();
162     epoll_fd(listenSock);
163     close(listenSock);
164     return 0;
165 }

client.c:

1 /****************************************                                                                                                 
  2     > File Name:client.c
  3     > Author:xiaoxiaohui
  4     > mail:[email protected]
  5     > Created Time:2016年05月23日 星期一 12时30分01秒
  6 ****************************************/
  7 
  8 #include<stdio.h>
  9 #include<stdlib.h>
 10 #include<string.h>
 11 #include<sys/types.h>
 12 #include<sys/socket.h>
 13 #include<netinet/in.h>
 14 #include<arpa/inet.h>
 15 #include<sys/time.h>
 16 #include<unistd.h>
 17 
 18 #define LEN 1024
 19 const int PORT = 8080;
 20 const char* IP = "127.0.0.1";
 21 struct sockaddr_in server;
 22 int clientSock;
 23 char buf[LEN];
 24 
 25 int main()
 26 {
 27     clientSock = socket(AF_INET, SOCK_STREAM, 0);
 28     if(clientSock < 0)
 29     {
 30         perror("socket");
 31         exit(1);
 32     }
 33 
 34     server.sin_family = AF_INET;
 35     server.sin_addr.s_addr = inet_addr(IP);
 36     server.sin_port = htons(PORT);
 37 
 38     if ( connect(clientSock, (struct sockaddr*)&server, sizeof(server)) < 0)
 39     {
 40         perror("connect");
 41         exit(2);
 42     }
 43 
 44     while(1)
 45     {
 46         memset(buf, ‘\0‘, LEN);
 47         printf("please input: ");
 48         gets(buf);
 49         write(clientSock, buf, strlen(buf));
 50 
 51         memset(buf, ‘地\0‘, LEN);
 52         int ret = read(clientSock, buf, LEN);
 53         buf[ret] = ‘\0‘;
 54         printf("echo: %s\n", buf);  
 55     }
 56 
 57     return 0;
 58 }

执行结果:




四.ET模式下的非阻塞模式:

(1).概述:

ET模式要在epoll_ctl中进行设置(具体设置看代码),并且要把套接字设置为非阻塞模式。

下面概况以下ET与LT的区别:

ET (edge-triggered)是高效的工作方式,只支持no-block socket,它效率要比LT更高。ET与LT的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。而LT模式下,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。
因此,LT模式下开发基于epoll的应用要简单些,不太容易出错。而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。

所以在下面代码中要封装一个read_data函数,来确保一次把数据缓冲区内的数据读取完。因为ET模式下只支持非阻塞模式,所以还要把每个套接字设置为非阻塞的。

下面代码实现      在浏览器访问服务器程序,并在浏览器中打印hello world :)         ,当服务器程序发送完给浏览器的数据时,服务器程序关闭链接。

相关代码:

1 /****************************************                                                                                                 
  2     > File Name:epoll_server.c
  3     > Author:xiaoxiaohui
  4     > mail:[email protected]
  5     > Created Time:2016年05月28日 星期六 15时38分17秒
  6 ****************************************/
  7 
  8 #include<stdio.h>
  9 #include<stdlib.h>
 10 #include<sys/types.h>
 11 #include<sys/socket.h>
 12 #include<arpa/inet.h>
 13 #include<netinet/in.h>
 14 #include<string.h>
 15 #include<unistd.h>
 16 #include<sys/epoll.h>
 17 #include<fcntl.h>
 18 #include<errno.h>
 19 
 20 #define LEN 1024
 21 const char* IP = "127.0.0.1";
 22 const int PORT = 8080;
 23 const int BACKLOG = 5;
 24 int timeout = 5000;
 25 const int MAXEVENTS = 64;
 26 struct sockaddr_in local;
 27 struct sockaddr_in client;
 28 int SIZE_CLIENT = sizeof(client);
 29 
 30 typedef struct data_buf     //用于存储epoll_event中的data中的不同元素
 31 {
 32     int fd;
 33     char buf[LEN];
 34 }data_buf_t, *data_buf_p;
 35 
 36 static int set_no_block(int fd)       //把fd设置为非阻塞
 37 {
 38     int oldfd = fcntl(fd, F_GETFL);
 39     if(oldfd < 0)
 40     {
41         perror("fcntl");
 42         return -1;
 43     }
 44 
 45     if( fcntl(fd, F_SETFL, oldfd | O_NONBLOCK))
 46     {
 47         perror("fcntl");
 48         return -1;
 49     }
 50     return 0;
 51 }
 52 
 53 int read_data(int fd, char* buf, int len)      //ET模式下读取数据,因为ET模式下只通知一次,所以要保证把所有数据都读完
 54 {                                              //成功返回读取的个数,失败返回-1,返回0代表读到文件尾
 55     int index = 0;
 56     int ret = -1;
 57 
 58     while(index < len)
 59     {
 60         ret = read(fd, buf + index, len - index);
 61         printf("the read return ret is %d\n", ret);
 62         if(ret > 0)
 63         {
 64             index += ret;
 65         }
 66         else if(ret < 0)
 67         {
 68             printf("the errno is %d\n",errno);
 69             if(errno == EAGAIN)
 70             {
 71                 break;
 72             }
 73         }
 74         else
 75         {                                                                                                                                 
 76             return 0;
 77         }
 78     }
 79 
 80     return index;
 81 }                                                                                                                                         
 82 
 83 
 84 
 85 
 86 static int ListenSock()
 87 {
 88     int listenSock = socket(AF_INET, SOCK_STREAM, 0);
 89     if(listenSock < 0)
 90     {
 91         perror("socket");
 92         exit(1);
 93     }
 94 
 95     int opt = 1;
 96     if( setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))     //设置端口复用   
 97     {
 98         perror("sersockopt");
 99         exit(2);
100     }
101     if( set_no_block(listenSock) != 0)      //设置为非阻塞    .............修改
102     {
103         printf("set_non_block is error\n");
104         exit(3);
105     }
106 
107     local.sin_family = AF_INET;
108     local.sin_port = htons(PORT);
109     local.sin_addr.s_addr = inet_addr(IP);
110     if( bind(listenSock, (struct sockaddr*)&local, sizeof(local)) < 0)
111     {
112         perror("bind");
113         exit(4);
114     }
115 
116     if( listen(listenSock, BACKLOG) < 0)
117     {
118         perror("listen");
119         exit(5);
120     }
121                                                                                                                                           
122     return listenSock;
123 }
124 
125 static int epoll_fd(int listenSock)
126 {
127 
128     int epoll_fd = epoll_create(256);           //size随便选一个值
129 
130     struct epoll_event ev;          //把listenSock设置进epoll_fd中
131     ev.events = EPOLLIN | EPOLLET;    //....................修改
132     ev.data.fd = listenSock;
133     epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenSock, &ev);        //系统会维护一个红黑树
134 
135     struct epoll_event ev_outs[MAXEVENTS];      //准备好的队列
136     int max = MAXEVENTS;
137     
138     while(1)
139     {
140         int num = -1;
141         switch( num = epoll_wait(epoll_fd, ev_outs, max, timeout))
142         {
143             case 0:     //timeout
144                 printf("timeout.....\n");
145                 break;
146             case -1:    //error
147                 perror("epoll_wait");
148                 break;
149             default:
150                 for(int index = 0; index < num; index++)   
151                 {
152                     if(ev_outs[index].data.fd == listenSock && (ev_outs[index].events & EPOLLIN))      //监听套接字准备就绪
153                     {
154                         printf("accept is ready\n");
155                         int linkSock = accept(listenSock, (struct sockaddr*)&client, &SIZE_CLIENT);
156                         if(linkSock < 0)
157                         {
158                             perror("accept");
159                             continue;           //这次可能是一个新客户端的请求,所以后面可能还有文件描述符准备就绪了
160                         }
161                                                                                                                                           
162                         if( set_no_block(linkSock) != 0)      //设置为非阻塞    .............修改
163                         {
164                             printf("set_non_block is error\n");
165                             exit(3);
166                         }
167 
168                         ev.events = EPOLLIN | EPOLLET;     //把新套接字放到红黑树中   ...................修改
169                         ev.data.fd = linkSock;
170                         epoll_ctl(epoll_fd, EPOLL_CTL_ADD, linkSock, &ev);
171                     }
172                     else         //已链接套接字准备就绪
173                     {
174                         if(ev_outs[index].events & EPOLLIN)      //读事件准备就绪
175                         {
176                             data_buf_p mem = (data_buf_p)malloc(sizeof(data_buf_t));
177                             memset(mem->buf, ‘\0‘, sizeof(mem->buf));
178                             mem->fd = ev_outs[index].data.fd;
179 
180                             printf("read is ready\n");
181                             int ret = read_data(mem->fd, mem->buf, sizeof(mem->buf)); //.........................修改
182                             printf("read is over, the ret is %d\n", ret);
183                             if(ret > 0)
184                             {
185                                 mem->buf[ret] = ‘\0‘;
186                                 printf("client# %s\n", mem->buf);
187 
188                                 ev.data.ptr = mem;         //mem中即保持了fd,又保持了buf数据
189                                 ev.events = EPOLLOUT;    //读事件已经完成,现在要关心写事件
190                                 epoll_ctl(epoll_fd, EPOLL_CTL_MOD, mem->fd, &ev);
191                             }
192                             else if(ret == 0 )      //客户端已关闭
193                             {
194                                 printf("client is closed\n");
195                                 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, ev_outs[index].data.fd, NULL);     //把该文件描述符从红黑树中移除
196                                 close(ev_outs[index].data.fd);
197                                 free(mem);
198                             }
199                             else
200                             {
201                                 perror("read");                                                                                           
202                                 continue;
203                             }
204                         }
205                         else if(ev_outs[index].events & EPOLLOUT)   //写事件准备就绪
206                         {
207                             data_buf_p mem = (data_buf_p)ev_outs[index].data.ptr;
208                             int fd = mem->fd;
209                             char* buf = mem->buf;
210 
211                             char *msg = "HTTP/1.0 200 OK\r\n\r\nhello world:)\r\n"; //.....................修改
212                             if( write(fd, msg, strlen(msg)) < 0)                //.........................修改
213                             {
214                                 perror("write");
215                                 continue;
216                             }
217 
218                             epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);     //把该文件描述符从红黑树中移除    ............修改
219                             close(fd);                                       //.............................................修改
220                             free(mem);                                       //.............................................修改
221                             mem = NULL;
222 
223                 //          ev.events = EPOLLIN;       //这个文件描述符的写事件已完成,下次关心读事件  ..................修改
224                 //          ev.data.fd = mem->fd;     //........................................修改
225                 //          epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);  //....................修改
226                         }
227                         else   //DoNothing
228                         {}
229                     }
230                 }
231                 break;
232         }
233     }
234 }
235 
236 int main()
237 {
238     int listenSock = ListenSock();
239     epoll_fd(listenSock);
240     close(listenSock);
241     return 0;
242 }

执行结果:




六.总结:

epoll目前为止最为高效的多路复用接口,它比select和poll高效的本质原因在于epoll采用的nmap技术和使用了基于事件的触发机制。

另外,epoll会给所有要监听的文件描述符创建一个红黑树以方便操作,并且会把已经准备好的文件描述符单独拿出来放到一个链表中,来提高效率。

epoll默认的工作模式是LT模式,当ET模式要比LT模式更高效,所以,要提高epoll效率,可以使epoll工作在ET模式下,在ET模式下,要把所有的套接字都设置为非阻塞模式。

使epoll工作在ET模式下,就要封装一个read函数,来保证能把I/O缓冲区内的数据一次读取完。

时间: 2024-10-18 16:56:13

linux高性能服务器编程之epoll的相关文章

linux高性能服务器编程之poll

一.概述: 和select不同的是,poll使用一个pollfd来指向所要监听的fd,事件,返回事件.(pollfd下面详细讲.) 并且poll没有最大的文件描述符数量的限制,是自己定义一个pollfd数组来实现的. 它的缺点和select差不多,即 (1)每次调用poll,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大(2)当要确定一个文件描述符的状态时,都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大 二.poll介绍篇:  int poll(struct p

linux/unix网络编程之epoll

转载自 Linux epoll模型 ,这篇文章讲的非常详细! 定义: epoll是Linux内核为处理大批句柄而作改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著的减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率.因为它会复用文件描述符集合来传递结果而不是迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一个原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符

服务器编程之路:进无止境(下)

(接上文) 为了找到第二个命题的解决方法,我们可以再回过头来看看本文中第一版的服务器程序.前面也说了,第一版程序的问题在于,一条线程服务一个连接,而OS切换线程的开销很大,所以造成性能上不去.但第一版程序绝对是愉快的顺序编程.如果我们想保留顺序编程,那应该怎么克服性能方面的缺陷呢? 问题被直接导向为:既然OS调度线程很吃力,那是否存在一种"用户态线程",由程序自己调度,让OS一边玩儿去? 先抛出答案,所谓的"用户态线程",我们一般的实现就是"协程(coro

Linux高性能服务器编程——定时器

 定时器 服务器程序通常管理着众多定时事件,因此有效组织这些定时事件,使之能在预期的时间点被触发且不影响服务器的主要逻辑,对于服务器的性能有着至关重要的影响.位置我们要将每个定时事件封装成定时器,并使用某种容器类型的数据结构,比如链表.排序链表和时间轮将所有定时器串联起来,以实现对定时事件的统一管理. Linux提供三种定时方法: 1.socket选项SO_RECVTIMEO和SO_SNDTIMEO. 2.SIGALRM信号 3.I/O复用系统调用的超时参数 socket选项SO_RCVTI

Linux高性能服务器编程——信号及应用

 信号 信号是由用户.系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常.Linux信号可由如下条件产生: 对于前台进程,用户可以通过输入特殊的终端字符来给它发送信号.比如输入Ctrl+C通常会给进程发送一个终端信号. 2.系统异常 系统状态变化 运行kill命令或调用kill函数 Linux信号概述 发送信号 Linux下,一个进程给其他进程发送信号的API是kill函数.其定义如下: #include <sys/types.h> #include <sign

Linux高性能服务器编程——多进程编程

多进程编程 多进程编程包括如下内容: 复制进程影映像的fork系统调用和替换进程映像的exec系列系统调用. 僵尸进程以及如何避免僵尸进程 进程间通信(Inter-Process Communication,IPC)最简单的方式:管道 3种进程间通信方式:信号量,消息队列和共享内存 fork系统调用 #include<unistd.h> pid_tfork(void); 该函数的每次都用都返回两次,在父进程中返回的是子进程的PID,在子进程中返回的是0.该返回值是后续代码判断当前进程是父进程还

Linux高性能服务器编程——进程池和线程池

进程池和线程池 池的概念 由于服务器的硬件资源"充裕",那么提高服务器性能的一个很直接的方法就是以空间换时间,即"浪费"服务器的硬件资源,以换取其运行效率.这就是池的概念.池是一组资源的集合,这组资源在服务器启动之初就完全被创建并初始化,这称为静态资源分配.当服务器进入正是运行阶段,即开始处理客户请求的时候,如果它需要相关的资源,就可以直接从池中获取,无需动态分配.很显然,直接从池中取得所需资源比动态分配资源的速度要快得多,因为分配系统资源的系统调用都是很耗时的.当

Linux高性能服务器编程——I/O复用

 IO复用 I/O复用使得程序能同时监听多个文件描述符,通常网络程序在下列情况下需要使用I/O复用技术: 客户端程序要同时处理多个socket 客户端程序要同时处理用户输入和网络连接 TCP服务器要同时处理监听socket和连接socket,这是I/O复用使用最多的场合 服务器要同时处理TCP请求和UDP请求.比如本章将要讨论的会社服务器 服务器要同时监听多个端口,或者处理多种服务. I/O复用虽然能同时监听多个文件描述符,但它本身是阻塞的.并且当多个文件描述符同时就绪时,如果不采用额外措施

Linux/Unix C编程之的perror函数,strerror函数,errno

#include <stdio.h> // void perror(const char *msg); #include <string.h> // char *strerror(int errnum); #include <errno.h> //errno ? errno 是错误代码,在 errno.h头文件中: perror是错误输出函数,输出格式为:msg:errno对应的错误信息(加上一个换行符): strerror?是通过参数 errnum (就是errno)