前言
IO操作在计算机系统中是比较耗时和占用资源的,所以提高IO操作性能显得尤为重要。这里介绍select和poll两种IO复用的方法。
一、select和poll 对比
select | poll | |
---|---|---|
数据结构 | 3个数组:读集合、写集合、出错集合 | 1个数组 |
维护fd数量 | 1024 | 自定义数量 |
拷贝 | 数组在用户态和内核态来回拷贝 | 数组在用户态和内核态来回拷贝 |
判断读写事件的方式 | 底层循环判断每个fd是否就绪 | 底层循环判断每个fd事件 |
缺点 | 数量越多,性能却差 | 数量越多,性能却差 |
二、两种IO实现
1.select
int main()
{
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9999);
if(0 > bind(server_fd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr))){
printf("bind failed:%s\n", strerror(errno));
return -1;
}
listen(server_fd, 128);
socklen_t len = sizeof(struct sockaddr);
struct sockaddr_in clientaddr;
fd_set rfds, rset; //用户读集合、传入内核的读集合
FD_ZERO(&rfds);
FD_SET(server_fd, &rfds); //服务端fd放进集合
int max_fd = server_fd;
int client_fd = -1;
while(1)
{
rset = rfds;
int nready = select(max_fd + 1, &rset, NULL, NULL, NULL);
//1.先判断服务端fd就绪状态
if (FD_ISSET(server_fd, &rset)){
client_fd = accept(server_fd, (struct sockaddr *)&clientaddr, &len);
printf("accept:%d\n", client_fd);
FD_SET(client_fd, &rfds);
if(max_fd < client_fd)
max_fd = client_fd;
if(--nready==0) continue; //只有一个服务端fd,终止此次循环
}
//2.再循环读取客户fd就绪状态
for (int i = server_fd + 1; i <= max_fd; i++)
{
if(FD_ISSET(i,&rset)){
char buf[BUF_SIZE]={0};
int n = read(client_fd, buf, BUF_SIZE);
if(n <= 0){
printf("read failed:%s\n", strerror(errno));
close(client_fd);
break;
}
printf("fd:%d,read buf:%s\n", i,buf);
}
}
}
return 0;
}
2.poll
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/errno.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#define BUF_SIZE 1024
int main()
{
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9999);
if(0 > bind(server_fd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr))){
printf("bind failed:%s\n", strerror(errno));
return -1;
}
listen(server_fd, 128);
socklen_t len = sizeof(struct sockaddr);
struct sockaddr_in clientaddr;
int max_fd = server_fd;
int client_fd = -1;
struct pollfd fds[BUF_SIZE];
fds[server_fd].fd = server_fd;
fds[server_fd].events = POLLIN;
while(1)
{
int nready = poll(fds, max_fd + 1, -1);
if(nready<=0) continue;
if(fds[server_fd].revents & POLLIN)
{
client_fd = accept(server_fd, (struct sockaddr*)&clientaddr, &len);
if(client_fd>0)
{
printf("accept:%d\n", client_fd);
fds[client_fd].fd = client_fd;
fds[client_fd].events = POLLIN;
if(max_fd<client_fd)
max_fd = client_fd;
}
if(--nready==0)
continue;
}
for (int i = server_fd + 1; i <= max_fd; i++){
if(fds[i].revents & POLLIN){
char buf[BUF_SIZE] = {0};
if(0 >= read(client_fd, buf, BUF_SIZE)){
printf("%d disconnect\n", client_fd);
fds[client_fd].fd = -1;
fds[client_fd].events = 0;
close(client_fd);
continue;
}
printf("read buf:%s\n", buf);
}
}
}
return 0;
}
总结
这次先总结下select和poll两种方式的区别和相同点,下次说下epoll优点和实现。