面试题答案
一键面试常见性能瓶颈
- 文件描述符管理:传统的
select
和poll
在处理大量文件描述符时,性能会随着描述符数量增加而显著下降。 - 阻塞I/O:阻塞I/O操作会导致线程等待,降低并发处理能力。
- 频繁系统调用:如每次I/O操作都进行系统调用,会增加上下文切换开销。
- 缓冲区不合理:缓冲区过小会导致频繁读写,过大则浪费内存。
- 内存分配与释放:频繁的内存分配和释放会导致内存碎片,降低内存使用效率。
通过epoll
机制提升性能
epoll
是Linux下高效的I/O事件通知机制,采用事件驱动,避免遍历所有文件描述符。
关键代码片段:
#include <sys/epoll.h>
// 创建epoll实例
int epollFd = epoll_create1(0);
if (epollFd == -1) {
perror("epoll_create1");
return -1;
}
// 添加文件描述符到epoll实例
struct epoll_event event;
event.data.fd = sockfd;
event.events = EPOLLIN | EPOLLET; // 边缘触发模式
if (epoll_ctl(epollFd, EPOLL_CTL_ADD, sockfd, &event) == -1) {
perror("epoll_ctl: add");
close(epollFd);
return -1;
}
// 等待事件发生
struct epoll_event events[max_events];
int nfds = epoll_wait(epollFd, events, max_events, -1);
for (int i = 0; i < nfds; ++i) {
if (events[i].data.fd == sockfd) {
// 处理新连接
} else {
// 处理数据
}
}
合理设置缓冲区大小
- 接收缓冲区:设置合适大小避免数据丢失和频繁系统调用。
- 发送缓冲区:根据网络带宽和数据发送频率设置。
关键代码片段:
// 设置接收缓冲区大小
int recvBuffSize = 8192;
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvBuffSize, sizeof(recvBuffSize));
// 设置发送缓冲区大小
int sendBuffSize = 8192;
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendBuffSize, sizeof(sendBuffSize));
优化内存管理
- 内存池:预先分配一块较大内存,避免频繁的内存分配和释放。
- 对象复用:对于一些可复用的对象,如连接对象、数据包对象等,复用而不是每次创建和销毁。
关键代码片段(简单内存池示例):
#include <stdlib.h>
#define MEM_POOL_SIZE 1024 * 1024
char memPool[MEM_POOL_SIZE];
char *memPtr = memPool;
void* myMalloc(size_t size) {
if (memPtr + size <= memPool + MEM_POOL_SIZE) {
void* ret = memPtr;
memPtr += size;
return ret;
}
return NULL;
}