0%

linux_select_epoll

select和epoll的不同点

epoll和select的区别:

  • 从使用和例子等可以看到,select每次都要循环遍历所有的监听的fd,然后判断是否已经准备好了;
    而epoll是返回已经准备好的event数组,这样就不用循环遍历所有的fd
  • 从实现来看:
    select和epoll两者的底层都是一样,但是处理方式不同,导致一个效率高一个低
    select每次都要去调用poll_wait设置fd和相关唤醒结构,而epoll只有在新fd才去调用poll_wait
    select每次都要去设置poll_table等相关结构,然后销毁,而epoll_wait不会

select和epoll的底层实现

eventpoll结构:

eventpoll中的fd红黑树

内核代码架构:新标签查看或保存图片放大查看

select和epoll的使用例子

select的使用例子:
1
2
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
select实现的流程:
{
while(1)
{
sockets = select();
for(socket in sockets)
{
if(can_read(socket))
{
read(socket, buffer);
process(buffer);
}
}
}
}
select的使用:

#include <sys/time.h>
#include <sys/select.h>

int select(int nfds, fd_set *rdfds, fd_set *wtfds,
fd_set *exfds, struct timeval *timeout)
参数说明:
nfds:select中监视的文件句柄数,一般设为要监视的文件中的最大文件号加一。该参数让select变得更有效率,因为此时内核就不用去检查大于这个值的文件描述符是否属于这些文件描述符集合。文件描述符集合有一个最大容量限制,由常量FD_SETSIZE来决定,一般最大位1024个描述符
返回值
rdfds:检测输入是否就绪的文件描述符集合
wtfds:检测输出是否就绪的文件描述符集合
exfds:检测异常情况是否发生的文件描述符集合
1、信包模式下伪终端主设备上从设备状态发生改变;2、流式套接字接收到了外带数据)
struct timeval {
time_t tv_sec; 秒
suseconds_t tv_usec 微秒
}
超时时间:NULL为阻塞;
什么时候返回:timeout为NULL时,阻塞,等到有fd准备好了才返回,不会NULL时,则超时时间内有fd准备好了返回或者超时后返回;
数据类型fd_set以位掩码的形式来实现,通过四个宏来完成:
FD_CLR(inr fd,fd_set* set);将描述符fd从fdset所指向的集合中移除
FD_SET(int fd,fd_set*set);将描述符fd添加到fdset所指向的集合中
FD_ZERO(fd_set *set); 将fdset指向的集合初始化位空
FD_ISSET(int fd,fd_set *set);测试描述词组set中相关fd 的位是否为真
eg:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MYPORT 1234 // the port users will be connecting to
#define BACKLOG 5 // how many pending connections queue will hold
#define BUF_SIZE 200
int fd_A[BACKLOG]; // accepted connection fd
int conn_amount; // current connection amount
void showclient()
{
int i;
printf("client amount: %d\n", conn_amount);
for (i = 0; i < BACKLOG; i++) {
printf("[%d]:%d ", i, fd_A[i]);
}
printf("\n\n");
}
int main(void)
{
int sock_fd, new_fd; // listen on sock_fd, new connection on new_fd
struct sockaddr_in server_addr; // server address information
struct sockaddr_in client_addr; // connector's address information
socklen_t sin_size;
int yes = 1;
char buf[BUF_SIZE];
int ret;
int i;
if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
server_addr.sin_family = AF_INET; // host byte order
server_addr.sin_port = htons(MYPORT); // short, network byte order
server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));
if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
exit(1);
}
if (listen(sock_fd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
printf("listen port %d\n", MYPORT);

fd_set fdsr;
int maxsock;
struct timeval tv;

conn_amount = 0;
sin_size = sizeof(client_addr);
maxsock = sock_fd;
while (1) {
// initialize file descriptor set
FD_ZERO(&fdsr);
FD_SET(sock_fd, &fdsr);

// timeout setting
tv.tv_sec = 30;
tv.tv_usec = 0;

// add active connection to fd set
for (i = 0; i < BACKLOG; i++) {
if (fd_A[i] != 0) {
FD_SET(fd_A[i], &fdsr);
}
}

ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);
if (ret < 0) {
perror("select");
break;
} else if (ret == 0) {
printf("timeout\n");
continue;
}

// check every fd in the set
for (i = 0; i < conn_amount; i++) {
if (FD_ISSET(fd_A[i], &fdsr)) {
ret = recv(fd_A[i], buf, sizeof(buf), 0);
if (ret <= 0) { // client close
printf("client[%d] close\n", i);
close(fd_A[i]);
FD_CLR(fd_A[i], &fdsr);
fd_A[i] = 0;
} else { // receive data
if (ret < BUF_SIZE)
memset(&buf[ret], '\0', 1);
printf("client[%d] send:%s\n", i, buf);
}
}
}

// check whether a new connection comes
if (FD_ISSET(sock_fd, &fdsr)) {
new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
if (new_fd <= 0) {
perror("accept");
continue;
}

// add to fd queue
if (conn_amount < BACKLOG) {
fd_A[conn_amount++] = new_fd;
printf("new connection client[%d] %s:%d\n", conn_amount,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
if (new_fd > maxsock)
maxsock = new_fd;
}
else {
printf("max connections arrive, exit\n");
send(new_fd, "bye", 4, 0);
close(new_fd);
break;
}
}
showclient();
}

// close other connections
for (i = 0; i < BACKLOG; i++) {
if (fd_A[i] != 0) {
close(fd_A[i]);
}
}

exit(0);
}
epoll的使用例子:

服务端:

1
2
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include<sys/socket.h>
#include<sys/epoll.h>
#include<netinet/in.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#define MAX_FD 500 //声明epollfd最大的监听fd数量

int main(int argc,char **argv)
{
unsigned short port = 12345;
int epollFd;
if(argc == 2)
{
port = atoi(argv[1]);
}
//创建epollFd
epollFd = epoll_create(MAX_FD);
if (epollFd<=0)
{
printf("create epoll failed for fd\n");
exit(0);
}
//常规操作,创建socket套接字用于监听客户端连接;
int sfd,s;
sfd = socket(AF_INET,SOCK_STREAM,0);
fcntl(sfd,F_SETFL,O_NONBLOCK);
//绑定地址和端口,并设置监听队列数量;
struct sockaddr_in sin;
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
bind(sfd,(const struct sockaddr*)&sin,sizeof(sin));
listen(sfd,5);

//注册epoll监听事件类型为IN,读,监听的fd是socketfd;
struct epoll_event event;
struct epoll_event events[MAX_FD];
event.data.fd = sfd;
event.events = EPOLLIN|EPOLLET;
s=epoll_ctl(epollFd,EPOLL_CTL_ADD,sfd,&event);
if(s == -1)
{
perror("epoll_ctl\n");
exit(0);
}
int j=0;
//开始等待;
while(1)
{
int n,i;
int infd;
j++;
n = epoll_wait(epollFd,events,MAX_FD,10000);//这里超时时间为10s,则若10s内没有事件出现,则会返回到这里,若无while1,则退出;
for(i=0; i<n;i++)//遍历返回的要处理的事件数量
{
if((events[i].events &EPOLLERR) || (events[i].events&EPOLLHUP) || (!(events[i].events & EPOLLIN)))
{
printf("error for event\n");
close(events[i].data.fd);
continue;
}
else if (sfd == events[i].data.fd)//有客户端连接socketfd事件
{
while(1)
{
struct sockaddr in_addr;
socklen_t in_len;

in_len = sizeof(in_addr);
infd = accept(sfd,&in_addr,&in_len);//接受客户端连接
if (infd ==-1)
{
if((errno == EAGAIN)||(errno == EWOULDBLOCK))
{
printf("break for acceptn");
break;
}
}
else //并将客户端和服务器通信的fd,注册到epoll中,这样一旦有通信,则可以知道;
{
event.data.fd = infd;
event.events= EPOLLIN;//|EPOLLET;
epoll_ctl(epollFd,EPOLL_CTL_ADD,infd,&event);
}
}
continue;
}else if((events[i].events & EPOLLIN )&& (event.events&EPOLLIN))//若是客户端和服务器的非连接事件,则加强判断是否该通信事件,这里是读
{
ssize_t count;
char buf[512];
count = read(events[i].data.fd,buf,sizeof(buf));//接收客户端事件
if(count >0)
{
printf("read: %s\n",buf);//打印出来;
write(events[i].data.fd,buf,count);//发送相同内容给客户端;
}
}
}
}
return 0;
}