性能下降原因分析
- 进程创建与销毁开销:进程创建需要分配内存空间、初始化进程控制块等,销毁时也要进行资源回收,随着进程数量增多,这些开销增大。
- 进程间通信开销:若采用低效的通信方式,如管道,数据拷贝次数多,进程间同步与互斥操作频繁,影响性能。
- 资源竞争:多个进程共享系统资源,如文件描述符、内存等,竞争激烈会导致等待,降低效率。
- 网络 I/O 模型局限:若使用 select 模型,其文件描述符集合大小受限,且每次调用需线性遍历所有描述符,随着进程增多,监控的描述符增多,性能下降。
优化方案
进程间通信方式优化
- 采用共享内存 + 信号量:共享内存直接在内存中开辟一块区域供多个进程访问,减少数据拷贝。配合信号量实现进程间同步与互斥,提高通信效率。
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>
// 信号量操作函数
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
void semaphore_operation(int semid, int num, int op) {
struct sembuf sb;
sb.sem_num = num;
sb.sem_op = op;
sb.sem_flg = 0;
semop(semid, &sb, 1);
}
int main() {
// 创建共享内存
key_t key = ftok(".", 1);
int shmid = shmget(key, 1024, IPC_CREAT | 0666);
char *shared_mem = (char *)shmat(shmid, NULL, 0);
// 创建信号量
int semid = semget(key, 1, IPC_CREAT | 0666);
union semun su;
su.val = 1;
semctl(semid, 0, SETVAL, su);
pid_t pid = fork();
if (pid == 0) {
semaphore_operation(semid, 0, -1); // 等待信号量
// 子进程使用共享内存
std::cout << "Child process reads: " << shared_mem << std::endl;
semaphore_operation(semid, 0, 1); // 释放信号量
shmdt(shared_mem);
} else if (pid > 0) {
semaphore_operation(semid, 0, -1); // 等待信号量
// 父进程使用共享内存
std::string message = "Hello from parent";
std::copy(message.begin(), message.end(), shared_mem);
shared_mem[message.size()] = '\0';
semaphore_operation(semid, 0, 1); // 释放信号量
wait(NULL);
shmdt(shared_mem);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID, 0);
}
return 0;
}
- 使用消息队列:消息队列可按消息类型进行接收,具有一定异步性,减少进程间等待时间。
资源分配策略优化
- 资源预分配:在程序初始化阶段,预先分配好进程所需的资源,如内存、文件描述符等,避免进程运行时频繁申请资源。
- 动态资源调整:根据进程负载动态调整资源分配,如使用负载均衡算法,将任务合理分配到各进程,使资源利用更均衡。
网络 I/O 模型优化
- 采用 epoll:epoll 适用于大量并发连接且活跃连接较少的场景。它使用事件驱动机制,通过 epoll_ctl 注册、修改、删除文件描述符,epoll_wait 等待事件发生,只返回有事件的描述符,减少无效遍历。
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <iostream>
#include <vector>
#define MAX_EVENTS 10
int main() {
// 创建 socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8080);
servaddr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(sockfd, 5);
// 创建 epoll 实例
int epollfd = epoll_create1(0);
struct epoll_event ev, events[MAX_EVENTS];
ev.events = EPOLLIN;
ev.data.fd = sockfd;
epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev);
while (true) {
int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
for (int i = 0; i < nfds; ++i) {
if (events[i].data.fd == sockfd) {
int connfd = accept(sockfd, NULL, NULL);
ev.events = EPOLLIN;
ev.data.fd = connfd;
epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev);
} else {
int connfd = events[i].data.fd;
char buffer[1024];
int n = read(connfd, buffer, sizeof(buffer));
if (n > 0) {
buffer[n] = '\0';
std::cout << "Received: " << buffer << std::endl;
} else if (n == 0) {
std::cout << "Connection closed" << std::endl;
epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, NULL);
close(connfd);
}
}
}
}
close(sockfd);
close(epollfd);
return 0;
}
- 结合多线程:在每个进程内使用多线程配合 epoll,利用线程的轻量级特性进一步提高并发处理能力,如主线程负责监听新连接,工作线程负责处理已连接的 I/O 操作。