# 18. 高并发模型 ## 一、概述 高并发模型是服务器处理大量客户端连接的核心架构。Linux下主要有以下几种经典模型: ## 二、fork多进程模型 ### 2.1 原理 每个新连接fork一个子进程处理,父进程继续监听。 ### 2.2 API详解 ```c #include pid_t fork(void); ``` - 返回值:父进程返回子进程PID,子进程返回0,失败返回-1 ### 2.3 代码示例 ```c #include #include #include #include #include #include #include #include void handle_client(int client_fd) { char buf[1024]; ssize_t n; while ((n = recv(client_fd, buf, sizeof(buf), 0)) > 0) { send(client_fd, buf, n, 0); } close(client_fd); exit(0); } int main() { int server_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(8080), .sin_addr.s_addr = INADDR_ANY }; bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)); listen(server_fd, 128); signal(SIGCHLD, SIG_IGN); // 自动回收子进程 while (1) { int client_fd = accept(server_fd, NULL, NULL); if (fork() == 0) { close(server_fd); handle_client(client_fd); } close(client_fd); } return 0; } ``` **编译命令:** ```bash gcc -o fork_server fork_server.c ./fork_server ``` ## 三、多线程模型 ### 3.1 原理 每个连接创建一个新线程处理,共享进程地址空间。 ### 3.2 API详解 ```c #include int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg); void pthread_exit(void *retval); ``` ### 3.3 代码示例 ```c #include #include #include #include #include #include #include void* handle_client(void* arg) { int client_fd = *(int*)arg; free(arg); char buf[1024]; ssize_t n; while ((n = recv(client_fd, buf, sizeof(buf), 0)) > 0) { send(client_fd, buf, n, 0); } close(client_fd); return NULL; } int main() { int server_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(8080), .sin_addr.s_addr = INADDR_ANY }; bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)); listen(server_fd, 128); while (1) { int client_fd = accept(server_fd, NULL, NULL); int *fd = malloc(sizeof(int)); *fd = client_fd; pthread_t tid; pthread_create(&tid, NULL, handle_client, fd); pthread_detach(tid); } return 0; } ``` **编译命令:** ```bash gcc -o thread_server thread_server.c -lpthread ./thread_server ``` ## 四、线程池模型 ### 4.1 原理 预创建固定数量线程,任务队列分发连接。 ### 4.2 代码示例 ```c #include #include #include #include #include #include #include #define THREAD_POOL_SIZE 4 #define TASK_QUEUE_SIZE 128 typedef struct { int client_fd; } task_t; typedef struct { task_t queue[TASK_QUEUE_SIZE]; int front, rear, count; pthread_mutex_t mutex; pthread_cond_t cond; pthread_t threads[THREAD_POOL_SIZE]; } thread_pool_t; void* worker(void* arg) { thread_pool_t *pool = (thread_pool_t*)arg; while (1) { pthread_mutex_lock(&pool->mutex); while (pool->count == 0) { pthread_cond_wait(&pool->cond, &pool->mutex); } task_t task = pool->queue[pool->front]; pool->front = (pool->front + 1) % TASK_QUEUE_SIZE; pool->count--; pthread_mutex_unlock(&pool->mutex); char buf[1024]; ssize_t n; while ((n = recv(task.client_fd, buf, sizeof(buf), 0)) > 0) { send(task.client_fd, buf, n, 0); } close(task.client_fd); } return NULL; } void pool_init(thread_pool_t *pool) { pool->front = pool->rear = pool->count = 0; pthread_mutex_init(&pool->mutex, NULL); pthread_cond_init(&pool->cond, NULL); for (int i = 0; i < THREAD_POOL_SIZE; i++) { pthread_create(&pool->threads[i], NULL, worker, pool); } } void pool_add_task(thread_pool_t *pool, int client_fd) { pthread_mutex_lock(&pool->mutex); pool->queue[pool->rear].client_fd = client_fd; pool->rear = (pool->rear + 1) % TASK_QUEUE_SIZE; pool->count++; pthread_cond_signal(&pool->cond); pthread_mutex_unlock(&pool->mutex); } int main() { thread_pool_t pool; pool_init(&pool); int server_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(8080), .sin_addr.s_addr = INADDR_ANY }; bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)); listen(server_fd, 128); while (1) { int client_fd = accept(server_fd, NULL, NULL); pool_add_task(&pool, client_fd); } return 0; } ``` **编译命令:** ```bash gcc -o thread_pool_server thread_pool_server.c -lpthread ./thread_pool_server ``` ## 五、IO多路复用+多线程 ### 5.1 原理 epoll监听所有fd,事件触发后分发给工作线程池处理。 ### 5.2 API详解 ```c #include int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); ``` ### 5.3 代码示例 ```c #include #include #include #include #include #include #include #include #define MAX_EVENTS 128 #define THREAD_POOL_SIZE 4 typedef struct { int client_fd; } task_t; typedef struct { task_t queue[128]; int front, rear, count; pthread_mutex_t mutex; pthread_cond_t cond; pthread_t threads[THREAD_POOL_SIZE]; } thread_pool_t; void* worker(void* arg) { thread_pool_t *pool = (thread_pool_t*)arg; while (1) { pthread_mutex_lock(&pool->mutex); while (pool->count == 0) { pthread_cond_wait(&pool->cond, &pool->mutex); } task_t task = pool->queue[pool->front]; pool->front = (pool->front + 1) % 128; pool->count--; pthread_mutex_unlock(&pool->mutex); char buf[1024]; ssize_t n; while ((n = recv(task.client_fd, buf, sizeof(buf), 0)) > 0) { send(task.client_fd, buf, n, 0); } close(task.client_fd); } return NULL; } void pool_init(thread_pool_t *pool) { pool->front = pool->rear = pool->count = 0; pthread_mutex_init(&pool->mutex, NULL); pthread_cond_init(&pool->cond, NULL); for (int i = 0; i < THREAD_POOL_SIZE; i++) { pthread_create(&pool->threads[i], NULL, worker, pool); } } void pool_add_task(thread_pool_t *pool, int client_fd) { pthread_mutex_lock(&pool->mutex); pool->queue[pool->rear].client_fd = client_fd; pool->rear = (pool->rear + 1) % 128; pool->count++; pthread_cond_signal(&pool->cond); pthread_mutex_unlock(&pool->mutex); } int main() { thread_pool_t pool; pool_init(&pool); int server_fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(8080), .sin_addr.s_addr = INADDR_ANY }; bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)); listen(server_fd, 128); int epfd = epoll_create1(0); struct epoll_event ev = { .events = EPOLLIN, .data.fd = server_fd }; epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &ev); struct epoll_event events[MAX_EVENTS]; while (1) { int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); for (int i = 0; i < nfds; i++) { if (events[i].data.fd == server_fd) { int client_fd = accept(server_fd, NULL, NULL); ev.events = EPOLLIN; ev.data.fd = client_fd; epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &ev); } else { epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL); pool_add_task(&pool, events[i].data.fd); } } } return 0; } ``` **编译命令:** ```bash gcc -o epoll_thread_server epoll_thread_server.c -lpthread ./epoll_thread_server ``` ## 六、各模型对比 | 模型 | 资源占用 | 并发数 | 实现复杂度 | 适用场景 | | ----------------- | ------------------ | ---------------- | ---------- | ------------------ | | fork多进程 | 高(独立地址空间) | 低(进程数限制) | 低 | 小型服务、连接数少 | | 多线程 | 中(共享地址空间) | 中(线程数限制) | 中 | 中等并发、CPU密集 | | 线程池 | 中(复用线程) | 中高 | 中 | 连接数多但处理快 | | IO多路复用+多线程 | 低(事件驱动) | 高(万级连接) | 高 | 高并发、IO密集 | ## 七、嵌入式选型建议 1. **资源受限设备(RAM<64MB)**:线程池模型,避免fork开销 2. **实时性要求高**:IO多路复用+专用处理线程 3. **简单控制逻辑**:fork模型,代码简单易维护 4. **网络设备(路由器/交换机)**:epoll+线程池,高并发低延迟 5. **音频/视频流**:IO多路复用+环形缓冲区,避免拷贝 ## 八、注意事项 1. fork后注意文件描述符继承和清理 2. 线程间共享数据需加锁保护 3. 线程池任务队列满时的处理策略 4. epoll的ET和LT模式选择 5. 信号处理与线程安全的冲突 ## 九、面试要点 1. **fork与线程的区别**:地址空间、文件描述符、通信方式 2. **epoll的ET/LT区别**:边缘触发需一次性读完,水平触发缓冲区空则通知 3. **线程池为何比多线程高效**:避免频繁创建/销毁线程开销 4. **高并发服务器的设计模式**:Reactor、Proactor模式 5. **如何处理C10K问题**:IO多路复用+非阻塞IO