6.2POSIX线程间通信

6.2 POSIX线程间通信

使用 Xenomai 的 POSIX skin创建的POSIX线程,在同一进程内共享相同的地址空间,线程间直接共享同步对象,例如信号量,互斥锁,条件变量等等。

但是,Xenomai 不支持 POSIX 标准中的静态初始化。

1. POSIX 标准中的静态初始化

在 POSIX 标准里,为了方便静态互斥锁(mutex)和条件变量(condition variables)的初始化,定义了 PTHREAD_COND_INITIALIZER 和 PTHREAD_MUTEX_INITIALIZER 这两个初始化器。借助这两个初始化器,开发者能够在声明静态互斥锁和条件变量时直接完成初始化,示例如下:

#include <pthread.h>

// 静态初始化互斥锁
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
// 静态初始化条件变量
pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER;

这种静态初始化方式简洁高效,能在程序启动时就完成对象的初始化,避免了额外的函数调用开销。

2. Xenomai POSIX 层的特殊需求

然而,Xenomai POSIX 层由于其自身的特性,需要通过系统调用来初始化互斥锁和条件变量。这意味着上述 POSIX 标准的静态初始化方式在 Xenomai 环境下无法正常工作,因为 PTHREAD_COND_INITIALIZER 和 PTHREAD_MUTEX_INITIALIZER 无法完成 Xenomai 所需的系统调用初始化过程。

面对这个问题,有两种解决方案可供选择,最终Xenomai选择方案二。

  • 方案一: 首次调用其他服务时初始化对象

第一种方案是在首次调用与互斥锁或条件变量相关的其他服务(如 pthread_mutex_lock 或 pthread_cond_wait)时,触发对象的初始化操作。但这种方案存在明显的缺陷。以 pthread_mutex_lock 为例,如果在该函数内部调用初始化例程,会引入额外的不确定性。因为初始化过程可能涉及系统调用,这会增加函数执行时间的不可预测性,破坏了用户对 pthread_mutex_lock 这类服务确定性的预期。在实时系统中,这种不确定性是非常致命的,可能会导致系统性能下降甚至出现错误。

  • 方案二:要求用户调用初始化服务

第二种方案是要求用户主动调用 pthread_mutex_init 和 pthread_cond_init 函数来完成互斥锁和条件变量的初始化。这种方案的优点在于,用户可以在程序执行的非关键时期进行初始化操作,避免了在关键路径上引入额外的开销和不确定性。虽然这增加了用户的编程负担,但能保证系统的实时性和确定性。

综上,在使用 Xenomai POSIX 层的互斥锁和条件变量时,开发者需要仔细检查代码中所有使用静态初始化器的地方,并将其替换为在非关键时期调用 pthread_mutex_init 和 pthread_cond_init 函数。示例如下:

#include <pthread.h>

// 声明互斥锁和条件变量
pthread_mutex_t my_mutex;
pthread_cond_t my_cond;

int main() {
    // 在非关键时期初始化互斥锁
    if (pthread_mutex_init(&my_mutex, NULL) != 0) {
        // 处理初始化失败的情况
    }

    // 在非关键时期初始化条件变量
    if (pthread_cond_init(&my_cond, NULL) != 0) {
        // 处理初始化失败的情况
    }

    // 后续正常使用互斥锁和条件变量
    // ...

    // 程序结束时销毁互斥锁和条件变量
    pthread_mutex_destroy(&my_mutex);
    pthread_cond_destroy(&my_cond);

    return 0;
}

通过这种方式,可以确保在 Xenomai 环境下正确使用互斥锁和条件变量,同时保证系统的实时性和确定性。

3. Xenomai 实例

  • 互斥锁 mutex :testsuite/clocktest/clocktest.c
root@xeno-demo:~# clocktest
== Testing built-in CLOCK_REALTIME (0)
CPU      ToD offset [us] ToD drift [us/s]      warps max delta [us]
--- -------------------- ---------------- ---------- --------------
  0                  1.1           -0.024          0            0.0
  1                  1.2           -0.016          0            0.0
  2                  1.2            0.009          0            0.0
  3                  1.3           -0.002          0            0.0
  • 互斥量和条件变量:demo/posix/cyclictest/cyclictest.c

为什么使用了静态初始化?

static pthread_cond_t refresh_on_max_cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t refresh_on_max_lock = PTHREAD_MUTEX_INITIALIZER;

static pthread_mutex_t break_thread_id_lock = PTHREAD_MUTEX_INITIALIZER;
请帮我填充开发报告。1. 前言 1.1. 项目简要说明 本项目。 在此简要描述项目背景、需求。可参考立项报告或软件开发任务书,进行概括或补充。项目一般对应机型,如WR741N 1.0。 1.2. 任务概述 说明本任务是项目的全部,还是项目的子模块,是新设计模块,还是对原有模块或代码的修改,等等。任务一般对应模块,如WR741N 1.0的防火墙模块。 1.3. 可用资源 可选,说明不需要重新开发的可利用资源,如现成的模块、控件和软件等。 1.4. 术语定义 列举本文所用的专门术语的定义和英文缩写词的原文及解释。注意,术语之间如有引用关系,被引用者列在前面。 1.5. 参考资料 参考资料指概要设计中引用的开发流程文档或规范,或者有利于加深概要设计理解的资料(读者必须能容易获得)。请在下面列出资料,并给出这些文件资料的标题、作者、编号、版本号、发表日期和出版单位或资料的来源:  经批准的开发文档;  引用的软件开发标准和规范;  说明本文档中引用的其他文件和资料。 2. 需求分析 强烈鼓励单独撰写需求分析文档。 如果没有单独的需求分析文档,在此分层次对需求进行分析。主要是分析以下方面:  界定系统的对外提供的功能(将对应于概要设计中的模块接口或系统界面);  详细的功能项及功能的分类(将对应于概要设计中的模块划分);  功能分类之间的关系(将对应于概要设计中的模块关系);  功能的作用或流程(将对应于概要设计中的模块内部建模);  提供上述功能时需要达到的性能(将影响概要设计中协议、算法等的选择)。 3. 原理概述 本章分析任务的重点原理,如协议、算法等等。如果需要原理概述,本章为概要设计的重点内容。不需要时删除本章。 注意,原理不是实现的结果,而是实现的依据。如果属于协议设计,要说明为什么要这样设计。移植型的任务,原理着重讲移植要注意的平台差异。修改/增加型的任务,原理着重讲修改或增加的部分。 以下小节如果内容较多,请扩展成独立的一章。 3.1. 协议分析(或设计) 本节分析要使用并实现的协议标准(如PPTP协议),或根据需求设计新协议(如防火墙产品用户认证协议)及设计的出发点和思路。不需要时删除本节。 3.1.1. 协议描述 概述协议实现的目标、原理等。 3.1.2. 协议数据格式 协议使用的数据格式,如网络网络协议的帧格式。 3.1.3. 协议描述 描述协议的以下方面:  协议收发包的方式,如使用socket还是其他接口,是否阻塞,等等。  协议模块与任务(或线程)的关系,如果涉及多任务,说明任务间通信的方式。  双方或多方进行协议通信的互交图、状态图或其他形式的描述。 3.2. 重要算法与数据结构 本节分析要实现的重要算法(如防火墙项目策略模块查找算法设计)、相关数据定义以及算法依赖的数据结构。 4. 系统构架描述 4.1. 概述 概要地叙述模块划分的原则,如通过对需求进行分析得到几类功能,从而相应地将系统分成几个模块。 注意:这里说的模块,指的是本任务之下划分的模块。如果本任务是产品的一个模块,那么这里的模块实际上是产品模块中的子模块。 4.2. 模块结构 辅于模块结构图等形式,表述模块的划分情况(根据、命名、结果)和模块之间的关系,说明模块的相互关系是本节的重点。 说明:这是设计中对复杂系统“分而治之”的过程。注意“分”的合理性,并通过对模块关系的分析,保持系统的功能完整性。 4.3. 模块描述和建模 逐一说明每个模块的基本情况,包括:模块自身的功能;对项目以内或以外其他模块提供的功能;模块的简要流程或算法的名称;模块的重要性;等等。 如果划分的模块有比较复杂的(如存在多个对象),则对模块进行建模:  首先介绍模块及其包括对象的功能和属性。  其次可以描述对象之间的关系,如成绩管理模块中,学生、成绩、班级等对象的关系。  然后可以描述模块的主要流程,如加密过程。 5. 任务(或进程/线程)设计 必须描述的内容。 如果本任务需要单独的任务(或进程/线程)或采用多任务(进程/线程)、线程池等,在本章描述任务或进程/线程的使用设计(包括它们与模块的关系)。即使没有增加新的任务或进程/线程,也要描述代码的运行方式。如果涉及中断处理的,同样要说明。 5.1. 原因 说明使用新的任务(或进程/线程)、采用多个任务(或进程/线程)或线程池的原因。 5.2. 任务(或进程/线程)内部流程 依次说明每个任务/进程/线程实现什么流程,如果前面已经有流程,指出对应关系即可。 5.3. 任务(或进程/线程)间通信 每两个需要进行信息交互的任务,都要说明通信的信息内容、交互方式。 6. 内存管理 如果需要内存的特别使用和管理,在本章说明,否则删除本章。 6.1. 说明 说明哪些数据类型需要进行特别管理,如:  作用  被哪些模块、函数、任务(或线程)使用 6.2. 设计 如何对内存进行管理,包括但不限于以下内容:  是否要互斥和/或同步  是否要反复申请、释放  内存维护方式 7. 出错处理 可选。复杂项目需要统一的出错处理,否则删除本节。 8. 用户界面概要设计 可选,如有用户界面(包括GUI和CLI),在本章描述界面设计,否则删除本章节。先说明界面的关系,再对每个主要界面进行说明。 8.1. 界面组织 说明界面设计的指导原则。用图表描述不同界面间的组织关系。 8.2. 界面设计 对主要界面进行设计。 9. 接口概要设计 本章的接口指提供给其他模块调用的接口。如果需要给其他模块或对外提供接口,在本章说明,否则删除本章。 接口的设计要与使用模块的设计人进行协商。要注意完整性(能实现所有需要的功能)和易用性(不能让使用者需要经过复杂的准备或步骤才能调用)。 9.1. 概述 说明接口设计思想。要说明是否达到完整性,给出充分理由。 9.2. 接口分类与功能 接口设计,包括接口的分类及功能说明,大致的输入和输出,等等。如果接口提供给不同模块使用,或接口分不同的功能,要进行分类并说明。 如果一些对外功能需要调用若干个接口的组合才能实际,请说明。 10. 开发环境、测试环境及部署环境 10.1. 开发环境 指明开发所需环境,如:  硬件(交叉编译时包括宿主机和目标机)  OS(交叉编译时包括宿主机和目标机)  编译器、集成开发环境  其他 10.2. 测试环境 测试所需环境。如网络拓扑、测试设备等。 10.3. 部署环境 开发成果的部署环境。包括需要使用的硬件、软件、网络环境等。注意部署的层次性,如DHCP Client模块,部署到vxWorks平台的无线路由器(在WAN口使用),再描述无线路由器的部署。 《简要描述即可》项目具体如下,分别使用多线程,多进程,IO多路复用实现一个简单的web server(端口80)承载附件的web。对比这三种实现方式各自的优缺点。 下面给出了process多进程服务器的代码样例,其余两个改动不大。/* Copyright(c) Lianzhou International Co.Ltd. * * file process_server.c * brief This is a simple muti-process server. * * author Sun Haoming * version 1.0.0 * date 25Aug14 * * history * \arg 1.0.0, 25Aug14, Sun Haoming, Create the file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/stat.h> #include <fcntl.h> #include <arpa/inet.h> #include <signal.h> #include <sys/wait.h> /**************************************************************************************************/ /* DEFINES */ /**************************************************************************************************/ #define PORT 80 #define BUFFER_SIZE 2048 /**************************************************************************************************/ /* TYPES */ /**************************************************************************************************/ /**************************************************************************************************/ /* EXTERN_PROTOTYPES */ /**************************************************************************************************/ /**************************************************************************************************/ /* LOCAL_PROTOTYPES */ /**************************************************************************************************/ const char *get_content_type(const char *path); void send_response(int client_fd, const char *path); /**************************************************************************************************/ /* VARIABLES */ /**************************************************************************************************/ /**************************************************************************************************/ /* LOCAL_FUNCTIONS */ /**************************************************************************************************/ /* 根据拓展名返回类型 */ const char *get_content_type(const char *path) { const char *ext = strrchr(path, '.'); /* strrchr从末尾查找至. */ if (ext) { if (strcmp(ext, ".html") == 0) return "text/html"; if (strcmp(ext, ".css") == 0) return "text/css"; if (strcmp(ext, ".js") == 0) return "application/javascript"; if (strcmp(ext, ".png") == 0) return "image/png"; if (strcmp(ext, ".jpg") == 0) return "image/jpeg"; } return "application/octet-stream"; /*默认返回二进制流*/ } /* 发送HTTP响应给客户端 */ void send_response(int client_fd, const char *path) { char header[BUFFER_SIZE]; /*响应头buffer*/ char buffer[BUFFER_SIZE]; /*内容buffer*/ struct stat st; /*文件状态*/ /* 文件不存在 */ if (stat(path, &st) == -1) { printf("该文件未找到:%s\n", path); /* 404 文件头 */ snprintf(header, BUFFER_SIZE, "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html\r\n" "\r\n" "<h1>404 Not Found</h1>"); send(client_fd, header, strlen(header), 0); return; } /*读取文件*/ int fd = open(path, O_RDONLY); if (fd < 0) { printf("打开该文件失败: %s\n", path); return; } /* 200 OK 文件头 */ snprintf(header, BUFFER_SIZE, "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n" "\r\n", get_content_type(path), st.st_size); send(client_fd, header, strlen(header), 0); printf("发送文件: %s (大小: %ld 字节)\n", path, st.st_size); /*发送给客户端*/ ssize_t bytes_read; while ((bytes_read = read(fd, buffer, BUFFER_SIZE)) > 0) { send(client_fd, buffer, bytes_read, 0); } close(fd); } /*客户端请求线程*/ void handle_client(int client_fd) { /*直接接受int*/ char buffer[BUFFER_SIZE]; char path[BUFFER_SIZE] = "."; /* 默认./ */ char method[16] = {0}; /*存储请求方法*/ int content_length = 0; /* POST内容长度*/ char request_path[BUFFER_SIZE] = {0}; /* 存储原始请求路径*/ /*读取客户端请求*/ ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1); if (bytes_read <= 0) { printf("读取客户端请求失败\n"); close(client_fd); return; } buffer[bytes_read] = '\0'; /* 获取客户端IP */ struct sockaddr_in addr;/*地址结构*/ socklen_t addr_len = sizeof(addr);/*地址长度*/ getpeername(client_fd, (struct sockaddr*)&addr, &addr_len);/*获取当前socket的对端地址*/ char client_ip[INET_ADDRSTRLEN];/*存储IP*/ inet_ntop(AF_INET, &addr.sin_addr, client_ip, sizeof(client_ip));/*将二进制IP转为字符串*/ printf("收到来自 %s 的请求:\n%.*s\n", client_ip, (int)bytes_read, buffer); /* 解析请求方法 */ sscanf(buffer, "%s %s", method, request_path); /* 获取GET请求 */ if (strcmp(method, "GET") == 0) { /* 根目录请求*/ if (strcmp(request_path, "/") == 0) { strcat(path, "/Index.html"); } else { strncat(path, request_path, BUFFER_SIZE - strlen(path) - 1); } send_response(client_fd, path); } /* 处理POST请求 */ else if (strcmp(method, "POST") == 0) { char *body_start = strstr(buffer, "\r\n\r\n"); if (body_start) { body_start += 4; char *content_len_ptr = strstr(buffer, "Content-Length: "); if (content_len_ptr) { sscanf(content_len_ptr, "Content-Length: %d", &content_length); } /*处理表单提交*/ if (strstr(path, "/submit") != NULL) { char response[BUFFER_SIZE]; snprintf(response, BUFFER_SIZE, "HTTP/1.1 200 OK\r\n" "Content-Type: text/html\r\n\r\n" "<html><body>" "<h1>表单已提交</h1>" "<p>收到数据: %.*s</p>" "</body></html>", content_length, body_start); send(client_fd, response, strlen(response), 0); printf("处理POST数据: %.*s\n", content_length, body_start); } } } /* 其他请求方法*/ else { const char *resp = "HTTP/1.1 405 Method Not Allowed\r\n\r\n"; send(client_fd, resp, strlen(resp), 0); } close(client_fd); printf("请求处理完成\n"); return; } int main() { signal(SIGPIPE, SIG_IGN); /*忽略SIGPIPE管道信号*/ printf("Web服务器启动,监听端口 %d \n", PORT); /*创建TCP套接字*/ int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd < 0) { perror("创建套接字失败"); exit(EXIT_FAILURE); } /*允许快速重启,地址重用*/ int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /*配置服务器地址结构*/ struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(PORT), .sin_addr.s_addr = INADDR_ANY }; /* 绑定 */ if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("绑定端口失败"); exit(EXIT_FAILURE); } /* 监听 */ if (listen(server_fd, 10) < 0) { perror("监听失败"); exit(EXIT_FAILURE); } printf("服务器已启动\n"); /* 持续接收客户端连接*/ while (1) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); /*阻塞等到下一个客户端连接*/ int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); if (client_fd < 0) { perror("接受连接失败"); continue; } /*非阻塞的回收僵尸进程,WNOHANG无子进程退出立即返回*/ while (waitpid(-1, NULL, WNOHANG) > 0); /*创建子进程处理客户端请求*/ pid_t pid = fork(); if (pid < 0) { perror("fork失败"); close(client_fd); } else if (pid == 0) { close(server_fd);/*子进程不用监听服务端*/ handle_client(client_fd); /*子进程处理客户端请求*/ exit(0); } else { close(client_fd); /*父进程不要客户端*/ } } close(server_fd); return 0; }
08-16
<think>我们正在编写一个开发报告,需要填充各个部分。项目是使用多线程、多进程和IO多路复用三种方式实现一个简单的Web服务器(端口80),并对比它们的优缺点。下面给出了多进程服务器的代码样例,我们需要根据这个项目来填充报告。 报告结构如下: 1. 前言 2. 需求分析 3. 原理概述 4. 系统构架描述 5. 任务(或进程/线程)设计 6. 内存管理(可选,这里不需要,可以删除) 7. 出错处理(可选,这里简单处理,可以写也可以不写) 8. 用户界面概要设计(可选,这里没有GUI,主要是命令行,可以简单写) 9. 接口概要设计(可选,这里没有外部接口,可以删除) 10. 开发环境、测试环境及部署环境 由于项目比较简单,我们不需要写得太复杂,但需要覆盖报告要求的内容。 注意:我们只需要填充报告,不需要实际编写代码。代码已经给出多进程的样例,多线程和IO多路复用的代码改动不大,但我们需要在报告中描述三种实现方式。 下面开始填充报告内容:</think>### 开发报告填充 #### 1. 前言 **1.1 项目简要说明** 本项目实现一个基于C语言的简易Web服务器,支持HTTP协议(端口80),通过多进程、多线程和IO多路复用三种技术方案实现并发请求处理,并对比分析其性能差异。项目背景源于高并发网络服务的性能优化需求,需评估不同并发模型的资源开销与适用场景。 **1.2 任务概述** 本任务是完整项目开发,包含三种独立实现的子模块(多进程/多线程/IO多路复用),均需实现HTTP请求解析、静态资源响应(HTML/CSS/JS/图片)及POST表单处理功能。核心任务是对比不同方案的优缺点。 **1.3 可用资源** - 现成代码:提供多进程实现样例(`process_server.c`) - 开发库:POSIX线程库(pthread)、epoll系统调用 **1.4 术语定义** | 术语 | 解释 | |------|------| | IO多路复用 | 单线程监控多个Socket事件的技术(如epoll) | | 僵尸进程 | 子进程退出后未被父进程回收的残留进程 | | SIGPIPE | 写入已关闭Socket时触发的信号 | **1.5 参考资料** 1. 《UNIX网络编程》卷1, W.Richard Stevens, 人民邮电出版社 2. HTTP/1.1协议规范 RFC2616, IETF 3. Linux `epoll`手册页, kernel.org --- #### 2. 需求分析 | 需求类型 | 详细说明 | |----------|----------| | 功能需求 | 1. 响应HTTP GET/POST请求<br>2. 支持静态文件传输(HTML/CSS/JS/图片)<br>3. 根路径默认返回Index.html<br>4. 处理表单提交(POST) | | 性能需求 | 1. 并发处理≥100连接<br>2. 对比三种方案的资源占用(CPU/内存) | | 系统关系 | 模块划分:<br>- 网络监听模块(主进程)<br>- 请求处理模块(子进程/线程) | | 流程需求 | 客户端请求→解析方法(GET/POST)→路径映射→文件读取→HTTP响应 | --- #### 3. 原理概述 **3.1 HTTP协议实现** - **协议描述**:基于HTTP/1.1,支持短连接。GET请求返回文件,POST处理表单数据。 - **数据格式**:请求头解析`Method+Path`,响应头包含`Content-Type/Length`。 - **通信方式**:阻塞式Socket,主循环监听连接,子单元处理请求。 **3.2 并发模型对比** | 方案 | 核心数据结构 | 算法流程 | |------|--------------|----------| | 多进程 | `fork()`创建子进程 | 父进程监听→`fork()`→子进程响应→退出 | | 多线程 | `pthread_create()` | 主线程监听→创建线程→线程响应→`pthread_join()` | | IO多路复用 | `epoll`事件表 | 单线程`epoll_wait()`→事件分发→回调处理 | --- #### 4. 系统架构描述 **4.1 模块划分原则** 按功能解耦: - **网络层**:监听端口,接受连接(三方案共享) - **并发层**:进程/线程/epoll的事件调度 - **业务层**:解析HTTP、文件读写、响应生成 **4.2 模块关系图** ```plaintext +-----------------+ | 网络监听模块 |←(连接请求) +-----------------+ ↓ +-----------------+ (任务分配) | 并发调度模块 |←→[多进程/多线程/epoll] +-----------------+ ↓ +-----------------+ | HTTP处理模块 |→文件系统 +-----------------+ ``` **4.3 模块描述** - **网络监听模块**:创建Socket,绑定80端口,监听连接。 - **并发调度模块**: - 多进程:`fork()` + `waitpid(WNOHANG)`回收僵尸进程 - 多线程:线程池避免频繁创建销毁 - epoll:边缘触发(ET)模式高效就绪事件通知 - **HTTP处理模块**: - 解析请求头(`sscanf`提取Method/Path) - 动态路由(`/`→`Index.html`) - 文件响应(`sendfile()`优化传输) --- #### 5. 任务设计 **5.1 多任务必要性** - 多进程/线程:隔离客户端请求,避免阻塞主监听。 - epoll:单线程高并发,减少上下文切换开销。 **5.2 任务流程** | 方案 | 流程 | |------|------| | 多进程 | `accept()`→`fork()`→子进程`handle_client()`→`exit(0)` | | 多线程 | `accept()`→`pthread_create()`→线程调用`handle_client()` | | epoll | `epoll_ctl()`注册事件→`epoll_wait()`→回调`handle_client()` | **5.3 进程间通信** - 多进程:无直接通信(通过文件描述符传递Socket) - 多线程:共享内存(需互斥锁保护全局资源) --- #### 7. 出错处理 - **SIGPIPE信号**:忽略`signal(SIGPIPE, SIG_IGN)` - **文件缺失**:返回HTTP 404状态码 - **无效方法**:返回HTTP 405状态码 --- #### 8. 用户界面概要设计 **8.1 界面组织** 命令行操作: ```bash ./process_server # 启动多进程版本 ./thread_server # 启动多线程版本 ./epoll_server # 启动epoll版本 ``` **8.2 界面反馈** - 启动日志:`Web服务器启动,监听端口80` - 请求日志:`收到来自 [IP] 的请求` --- #### 10. 开发测试环境 | 环境类型 | 配置 | |----------|------| | 开发环境 | Ubuntu 22.04, GCC 11.4, GDB调试器 | | 测试环境 | ApacheBench (ab) 压测工具,并发100请求 | | 部署环境 | Linux内核≥5.4,支持epoll(如CentOS 8+) | --- ### 三种方案对比结论 | 方案 | 优点 | 缺点 | |------|------|------| | **多进程** | 稳定性高(进程隔离) | 资源开销大(内存复制) | | **多线程** | 资源共享高效 | 线程同步复杂(锁竞争) | | **IO多路复用** | 高并发(C10K问题) | 回调逻辑复杂,调试困难 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值