活动介绍
file-type

进程线程与混合线程:用户级、内核级和通信机制

PPT文件

下载需积分: 15 | 254KB | 更新于2024-08-19 | 95 浏览量 | 1 下载量 举报 收藏
download 立即下载
"混合线程-thread and process" 在操作系统中,线程和进程是两种基本的执行单元。进程是资源分配的基本单位,拥有独立的内存空间,包括代码段、数据段、栈段以及进程控制块(PCB)。而线程是执行调度的基本单位,线程共享同一进程的资源,如内存空间,但每个线程有自己的栈段,用于存储局部变量和函数调用的状态。 标题中的“混合线程”指的是结合了用户级别线程和内核级别线程的模型。在这种模型中,用户线程由库调度器管理,运行在用户空间,而内核线程由操作系统内核调度,运行在内核空间。库调度器负责用户线程的创建、销毁和上下文切换,而系统调度线程池则处理内核线程的调度。 用户线程是轻量级的,创建和销毁速度快,但它们的调度不直接由内核控制,可能导致线程阻塞(例如等待I/O操作)时,整个进程会被挂起,即使其他用户线程准备就绪。内核线程则可以直接由内核调度,当一个内核线程阻塞时,其他内核线程仍可继续执行,提供了更好的并发性能。 标签“进程和线程”强调了两者之间的区别和联系。进程之间通过进程间通信(IPC)进行通信,包括管道、FIFO、互斥量、条件变量、读写锁、消息队列、信号量等机制。线程间通信则相对简单,通常通过共享内存或直接传递数据结构来实现,因为它们共享进程的地址空间。 并发任务的通信和同步是多线程编程中的关键问题。正确地同步和通信能避免数据竞争,确保数据的一致性和完整性。例如,互斥量用于保证同一时间只有一个线程访问临界区,条件变量允许线程等待特定条件满足后再继续执行,而信号量可以控制资源的访问数量。 在并发模型中,"boss/worker"方法是一种常见的设计模式。Boss线程负责接收新任务,创建工作线程来执行任务,这种方式使得任务分配和执行解耦,提高了系统并发处理能力。线程间使用ITC机制进行通信和同步,确保数据的正确传递和处理顺序。 总结来说,混合线程模型结合了用户级和内核级线程的优点,提供了一种灵活的并发执行方式。了解进程和线程的概念,掌握它们的通信与同步机制,对于编写高效、可靠的并发程序至关重要。

相关推荐

filetype

def run(self): cap = cv2.VideoCapture(self.capTarget) cap.set(cv2.CAP_PROP_FPS, 30) if self.save_video: fourcc = cv2.VideoWriter_fourcc(*'XVID') out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480)) # 启动识别线程 thread = threading.Thread(target=self.recognition_thread, daemon=True) print(f'[主线程] 启动子线程 | 线程ID: {thread.ident} | 名称: {thread.name} | 存活状态: {thread.is_alive()}') thread.start() while True: ret, frame = cap.read() if not ret: break # 更新识别线程 current_time = time.time() # 时间未到1秒则等待 if current_time - self.last_process_time >= 0.5 and self.frame_queue.empty(): self.last_process_time = time.time() self.frame_queue.put(frame.copy(), block=False) # 绘制人脸框和轨迹 for name, (left, top, right, bottom), center in self.current_faces: # 添加记录 if name != "未知": self.record_list.append({ "name": name, "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), }) cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2) # 绘制姓名 # cv2.putText(frame, name, (left + 6, bottom - 6), # cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) # 绘制中文姓名 # 转换BGR到RGB(PIL使用RGB) frame_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(frame_pil) # 加载中文字体(需提供字体文件路径) font = ImageFont.truetype("simhei.ttf", 20) # 例如使用黑体 # 计算文字位置(调整坐标) text_position = (left + 6, top + 6) # 建议顶部显示而非底部 # 绘制中文 draw.text(text_position, name, font=font, fill=(255, 255, 255)) # 转换回OpenCV格式 frame = cv2.cvtColor(np.array(frame_pil), cv2.COLOR_RGB2BGR) # 图片保存逻辑 if self.save_img and len(self.current_faces) > 0: save_flag = False update_names = [] check_time = time.time() # 检查所有人脸状态 for face in self.current_faces: name = face[0] if name == "未知": save_flag = True break else: last_time = self.face_last_save_time.get(name, 0) if check_time - last_time >= 60: save_flag = True update_names.append(name) # 更新已知人脸时间戳 for name in update_names: self.face_last_save_time[name] = check_time # 执行保存操作 if save_flag: os.makedirs(self.save_dir, exist_ok=True) filename = f"{datetime.now().strftime('%Y%m%d%H%M')}_{uuid.uuid4().hex[:3]}.jpg" cv2.imwrite(os.path.join(self.save_dir, filename), frame) cv2.imshow('Face Tracking', frame) if self.save_video: out.write(frame) if cv2.waitKey(1) & 0xFF == ord('q'): self.running = False saveRecordsNow(self.record_list) break if self.save_video: out.release() cap.release() cv2.destroyAllWindows()。如果我想要吧多线程改为并行,把识别功能单独拆猪来独立运行,然后当前程序调用识别服务功能并返回输入该如何实现

filetype

Copied C:\1wjzl\PROCESS/.\o2mfr3.c to libudf\src (system "copy "C:\0rjaz\ansys\ANSYSI~1\v221\fluent"\fluent22.1.0\src\udf\sconstruct.udf "libudf\win64\3ddp_host\SConstruct" ") ????? 1 ??????? (system "copy "C:\0rjaz\ansys\ANSYSI~1\v221\fluent"\fluent22.1.0\src\udf\scons_test.bat "libudf\win64\3ddp_host\scons_test.bat" ") ????? 1 ??????? (chdir "libudf")(chdir "win64\3ddp_host")Compiler used is "C:\0rjaz\ansys\ANSYSI~1\v221\fluent"\ntbin\clang\bin\clang-cl Linker used is "C:\0rjaz\ansys\ANSYSI~1\v221\fluent"\ntbin\clang\bin\lld-link Copy("C:\1wjzl\PROCESS\libudf\win64\3ddp_host\resolve.exe", "C:\0rjaz\ansys\ANSYSI~1\v221\fluent\ntbin\win64\resolve.exe") Copy("C:\1wjzl\PROCESS\libudf\win64\3ddp_host\o2mfr3.c", "C:\1wjzl\PROCESS\libudf\src\o2mfr3.c") c_sources ['o2mfr3.c', 'udf_names.c'] c_sources_ ['o2mfr3.c'] o2mfr3.c(1,1): warning: type specifier missing, defaults to 'int' [-Wimplicit-int] DEFINE_EXECUTE_AT_END(calc_interior_o2_flux) ^ o2mfr3.c(3,5): error: use of undeclared identifier 'Domain' Domain* domain = Get_Domain(1); ^ o2mfr3.c(3,13): error: use of undeclared identifier 'domain' Domain* domain = Get_Domain(1); ^ o2mfr3.c(4,5): error: use of undeclared identifier 'Thread' Thread* tf = Lookup_Thread(domain, ZONE_ID); ^ o2mfr3.c(4,13): error: use of undeclared identifier 'tf' Thread* tf = Lookup_Thread(domain, ZONE_ID); ^ o2mfr3.c(4,32): error: use of undeclared identifier 'domain' Thread* tf = Lookup_Thread(domain, ZONE_ID); ^ o2mfr3.c(4,40): error: use of undeclared identifier 'ZONE_ID' Thread* tf = Lookup_Thread(domain, ZONE_ID); ^ o2mfr3.c(6,28): error: use of undeclared identifier 'tf' if (!tf || THREAD_TYPE(tf) != THREAD_F_INTERIOR) { ^ o2mfr3.c(6,10): error: use of undeclared identifier 'tf' if (!tf || THREAD_TYPE(tf) != THREAD_F_INTERIOR) { ^ o2mfr3.c(6,35): error: use of undeclared identifier 'THREAD_F_INTERIOR' if (!tf || THREAD_TYPE(tf) != THREAD_F_INTERIOR) { ^ o2mfr3.c(7,49): error: use of undeclared identifier 'ZONE_ID' Error("Invalid Interior Thread! ID=%d", ZONE_ID); ^ o2mfr3.c(11,57): error: use of undeclared identifier 'tf' int o2_index = Mixer_Species_Index(Get_Thread_Mixer(tf), "o2"); ^ o2mfr3.c(17,5): error: use of undeclared identifier 'face_t' face_t f; ^ o2mfr3.c(18,5): error: use of undeclared identifier 'real' real total_flux = 0.0; ^ o2mfr3.c(20,18): error: use of undeclared identifier 'f' begin_f_loop(f, tf) ^ o2mfr3.c(20,21): error: use of undeclared identifier 'tf' begin_f_loop(f, tf) ^ o2mfr3.c(41,5): error: use of undeclared identifier 'FILE' FILE* fp = fopen("interior_o2_flux.csv", "a"); ^ o2mfr3.c(41,11): error: use of undeclared identifier 'fp' FILE* fp = fopen("interior_o2_flux.csv", "a"); ^ o2mfr3.c(41,16): warning: declaration of built-in function 'fopen' requires inclusion of the header <stdio.h> [-Wbuiltin-requires-header] FILE* fp = fopen("interior_o2_flux.csv", "a"); ^ o2mfr3.c(42,9): error: use of undeclared identifier 'fp' if (fp) { ^ o2mfr3.c(43,9): warning: declaration of built-in function 'fprintf' requires inclusion of the header <stdio.h> [-Wbuiltin-requires-header] fprintf(fp, "%.6f, %.12e\n", CURRENT_TIME, total_flux); ^ o2mfr3.c(43,17): error: use of undeclared identifier 'fp' fprintf(fp, "%.6f, %.12e\n", CURRENT_TIME, total_flux); ^ fatal error: too many errors emitted, stopping now [-ferror-limit=] 3 warnings and 20 errors generated. scons: *** [o2mfr3.obj] Error 1 (system "copy "C:\0rjaz\ansys\ANSYSI~1\v221\fluent"\fluent22.1.0\src\udf\sconstruct.udf "libudf\win64\3ddp_node\SConstruct" ") ????? 1 ??????? (system "copy "C:\0rjaz\ansys\ANSYSI~1\v221\fluent"\fluent22.1.0\src\udf\scons_test.bat "libudf\win64\3ddp_node\scons_test.bat" ") ????? 1 ??????? (chdir "libudf")(chdir "win64\3ddp_node")Compiler used is "C:\0rjaz\ansys\ANSYSI~1\v221\fluent"\ntbin\clang\bin\clang-cl Linker used is "C:\0rjaz\ansys\ANSYSI~1\v221\fluent"\ntbin\clang\bin\lld-link Copy("C:\1wjzl\PROCESS\libudf\win64\3ddp_node\resolve.exe", "C:\0rjaz\ansys\ANSYSI~1\v221\fluent\ntbin\win64\resolve.exe") Copy("C:\1wjzl\PROCESS\libudf\win64\3ddp_node\o2mfr3.c", "C:\1wjzl\PROCESS\libudf\src\o2mfr3.c") c_sources ['o2mfr3.c', 'udf_names.c'] c_sources_ ['o2mfr3.c'] o2mfr3.c(1,1): warning: type specifier missing, defaults to 'int' [-Wimplicit-int] DEFINE_EXECUTE_AT_END(calc_interior_o2_flux) ^ o2mfr3.c(3,5): error: use of undeclared identifier 'Domain' Domain* domain = Get_Domain(1); ^ o2mfr3.c(3,13): error: use of undeclared identifier 'domain' Domain* domain = Get_Domain(1); ^ o2mfr3.c(4,5): error: use of undeclared identifier 'Thread' Thread* tf = Lookup_Thread(domain, ZONE_ID); ^ o2mfr3.c(4,13): error: use of undeclared identifier 'tf' Thread* tf = Lookup_Thread(domain, ZONE_ID); ^ o2mfr3.c(4,32): error: use of undeclared identifier 'domain' Thread* tf = Lookup_Thread(domain, ZONE_ID); ^ o2mfr3.c(4,40): error: use of undeclared identifier 'ZONE_ID' Thread* tf = Lookup_Thread(domain, ZONE_ID); ^ o2mfr3.c(6,28): error: use of undeclared identifier 'tf' if (!tf || THREAD_TYPE(tf) != THREAD_F_INTERIOR) { ^ o2mfr3.c(6,10): error: use of undeclared identifier 'tf' if (!tf || THREAD_TYPE(tf) != THREAD_F_INTERIOR) { ^ o2mfr3.c(6,35): error: use of undeclared identifier 'THREAD_F_INTERIOR' if (!tf || THREAD_TYPE(tf) != THREAD_F_INTERIOR) { ^ o2mfr3.c(7,49): error: use of undeclared identifier 'ZONE_ID' Error("Invalid Interior Thread! ID=%d", ZONE_ID); ^ o2mfr3.c(11,57): error: use of undeclared identifier 'tf' int o2_index = Mixer_Species_Index(Get_Thread_Mixer(tf), "o2"); ^ o2mfr3.c(17,5): error: use of undeclared identifier 'face_t' face_t f; ^ o2mfr3.c(18,5): error: use of undeclared identifier 'real' real total_flux = 0.0; ^ o2mfr3.c(20,18): error: use of undeclared identifier 'f' begin_f_loop(f, tf) ^ o2mfr3.c(20,21): error: use of undeclared identifier 'tf' begin_f_loop(f, tf) ^ o2mfr3.c(41,5): error: use of undeclared identifier 'FILE' FILE* fp = fopen("interior_o2_flux.csv", "a"); ^ o2mfr3.c(41,11): error: use of undeclared identifier 'fp' FILE* fp = fopen("interior_o2_flux.csv", "a"); ^ o2mfr3.c(41,16): warning: declaration of built-in function 'fopen' requires inclusion of the header <stdio.h> [-Wbuiltin-requires-header] FILE* fp = fopen("interior_o2_flux.csv", "a"); ^ o2mfr3.c(42,9): error: use of undeclared identifier 'fp' if (fp) { ^ o2mfr3.c(43,9): warning: declaration of built-in function 'fprintf' requires inclusion of the header <stdio.h> [-Wbuiltin-requires-header] fprintf(fp, "%.6f, %.12e\n", CURRENT_TIME, total_flux); ^ o2mfr3.c(43,17): error: use of undeclared identifier 'fp' fprintf(fp, "%.6f, %.12e\n", CURRENT_TIME, total_flux); ^ fatal error: too many errors emitted, stopping now [-ferror-limit=] 3 warnings and 20 errors generated. scons: *** [o2mfr3.obj] Error 1 Done. 提示这

filetype

/*! * ************************************************************************************************* * @copyright(c) 2008-2025 Shenzhen TP-LINK Technologies Co.Ltd. * * @file http.c * @brief * * @author huyushuai * @version 1.0.0 * @date 2025-08-08 * ************************************************************************************************* */ /**************************************************************************************************/ /* INCLUDE_FILES */ /**************************************************************************************************/ #include <stdio.h> #include <stdbool.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <ctype.h> #include <strings.h> #include <string.h> #include <sys/stat.h> #include <pthread.h> #include <sys/wait.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <stdatomic.h> /**************************************************************************************************/ /* DEFINES */ /**************************************************************************************************/ #define ISspace(x) isspace((int)(x)) #define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n" #define MAX_THREADS 100 #define DOCUMENT_ROOT "htdocs" /**************************************************************************************************/ /* TYPES */ /**************************************************************************************************/ typedef struct { pthread_t *threads; int count; atomic_int active_count; pthread_mutex_t mutex; pthread_cond_t cond; } ThreadPool; typedef struct { int client_sock; struct sockaddr_in client_addr; } RequestContext; ThreadPool *thread_pool = NULL; /**************************************************************************************************/ /* LOCAL_PROTOTYPES */ /**************************************************************************************************/ void *worker_thread(void *arg); void accept_request(RequestContext *ctx); void bad_request(int client); void cat(int, FILE *); void cannot_execute(int); void error_die(const char *); void execute_cgi(int, const char *, const char *, const char *); int get_line(int, char *, int); void headers(int, const char *); void not_found(int); void serve_file(int, const char *); int startup(u_short *); void unimplemented(int); void normalize_path(char *path); const char *get_content_type(const char *path); void handle_sigpipe(int sig); ThreadPool* create_thread_pool(int size); void destroy_thread_pool(ThreadPool *pool); void submit_request(ThreadPool *pool, RequestContext *ctx); /**************************************************************************************************/ /* LOCAL_FUNCTIONS */ /**************************************************************************************************/ /*! * @brief 路径规范函数 * * @param[in] path 文件路径 */ void normalize_path(char *path) { char *p = path; char *q = path; while (*p) { if (strncmp(p, "/../", 4) == 0) { p += 3; while (q > path && *--q != '/') { ; } } else if (strncmp(p, "/./", 3) == 0) { p += 2; } else if (strncmp(p, "//", 2) == 0) { p += 1; } else { *q++ = *p++; } } *q = '\0'; } /*! * @brief 忽略SIGPIPE信号处理 * * @param[in] sig SIGPIPE信号 */ void handle_sigpipe(int sig) { } /*! * @brief 创建线程池 * * @param[in] size 大小 * @return ThreadPool* */ ThreadPool* create_thread_pool(int size) { ThreadPool *pool = malloc(sizeof(ThreadPool)); if (!pool) { return NULL; } pool->threads = malloc(sizeof(pthread_t) * size); if (!pool->threads) { free(pool); return NULL; } pool->count = size; pool->active_count = 0; pthread_mutex_init(&pool->mutex, NULL); pthread_cond_init(&pool->cond, NULL); for (int i = 0; i < size; i++) { if (pthread_create(&pool->threads[i], NULL, worker_thread, pool) != 0) { perror("Failed to create thread"); destroy_thread_pool(pool); return NULL; } } return pool; } // 销毁线程池 void destroy_thread_pool(ThreadPool *pool) { if (!pool) { return; } // 通知所有线程退出 for (int i = 0; i < pool->count; i++) { pthread_cancel(pool->threads[i]); } // 等待所有线程结束 for (int i = 0; i < pool->count; i++) { pthread_join(pool->threads[i], NULL); } free(pool->threads); pthread_mutex_destroy(&pool->mutex); pthread_cond_destroy(&pool->cond); free(pool); } /*! * @brief 提交请求到线程池 * * @param[in] pool 线程池 * @param[in] ctx 请求 */ void submit_request(ThreadPool *pool, RequestContext *ctx) { pthread_mutex_lock(&pool->mutex); while (pool->active_count >= pool->count) { pthread_cond_wait(&pool->cond, &pool->mutex); } atomic_fetch_add(&pool->active_count, 1); pthread_mutex_unlock(&pool->mutex); pthread_t thread; if (pthread_create(&thread, NULL, (void *(*)(void *))accept_request, ctx) != 0) { perror("Failed to create request thread"); close(ctx->client_sock); free(ctx); atomic_fetch_sub(&pool->active_count, 1); pthread_cond_signal(&pool->cond); } else { pthread_detach(thread); } } // 工作线程函数 void *worker_thread(void *arg) { ThreadPool *pool = (ThreadPool *)arg; while (1) { // 线程池管理逻辑 sleep(1); // 简化实现,实际应有任务队列 } return NULL; } /**********************************************************************/ /* A request has caused a call to accept() on the server port to * return. Process the request appropriately. * Parameters: the socket connected to the client */ /**********************************************************************/ void accept_request(RequestContext *ctx) { int client = ctx->client_sock; char buf[1024]; char method[255]; char url[255]; char path[512]; struct stat st; int cgi = 0; char *query_string = NULL; int numchars = get_line(client, buf, sizeof(buf)); size_t i = 0; size_t j = 0; while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { method[i] = buf[j]; i++; j++; } method[i] = '\0'; // 只支持GET和POST方法 if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { unimplemented(client); close(client); free(ctx); return; } // POST请求启用CGI if (strcasecmp(method, "POST") == 0) { cgi = 1; } i = 0; while (ISspace(buf[j]) && (j < sizeof(buf))) { j++; } while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) { url[i] = buf[j]; i++; j++; } url[i] = '\0'; // 处理GET查询参数 if (strcasecmp(method, "GET") == 0) { query_string = url; while ((*query_string != '?') && (*query_string != '\0')) query_string++; if (*query_string == '?') { cgi = 1; *query_string = '\0'; query_string++; } } // 构建安全路径 if (strlen(url) == 0 || strstr(url, "..") != NULL) { bad_request(client); close(client); free(ctx); return; } snprintf(path, sizeof(path), "%s%s", DOCUMENT_ROOT, url); normalize_path(path); // 确保路径在文档根目录内 if (strncmp(path, DOCUMENT_ROOT, strlen(DOCUMENT_ROOT)) != 0) { not_found(client); close(client); free(ctx); return; } // 默认索引文件 if (path[strlen(path) - 1] == '/') { strncat(path, "index.html", sizeof(path) - strlen(path) - 1); } // 检查文件状态 if (stat(path, &st) == -1) { // 丢弃剩余头部 while ((numchars > 0) && strcmp("\n", buf) != 0) { numchars = get_line(client, buf, sizeof(buf)); } not_found(client); } else { if (S_ISDIR(st.st_mode)) { strncat(path, "/index.html", sizeof(path) - strlen(path) - 1); stat(path, &st); // 重新获取状态 } if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)) { cgi = 1; } if (!cgi) { serve_file(client, path); } else { execute_cgi(client, path, method, query_string); } } /* 清理资源 */ close(client); free(ctx); /* 更新线程池状态 */ if (thread_pool) { atomic_fetch_sub(&thread_pool->active_count, 1); pthread_cond_signal(&thread_pool->cond); } } /**********************************************************************/ /* Inform the client that a request it has made has a problem. * Parameters: client socket */ /**********************************************************************/ void bad_request(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "

Your browser sent a bad request, "); send(client, buf, sizeof(buf), 0); sprintf(buf, "such as a POST without a Content-Length.\r\n"); send(client, buf, sizeof(buf), 0); } /*! * @brief 将文件内容发送到网络客户端 * * @param[in] client 客户端套接字描述符 * @param[in] resource 文件指针(指向要发送的文件) */ void cat(int client, FILE *resource) { char buf[1024]; size_t bytes_read; while ((bytes_read = fread(buf, 1, sizeof(buf), resource)) > 0) { if (send(client, buf, bytes_read, 0) < bytes_read) { perror("send"); break; } } } /**********************************************************************/ /* Inform the client that a CGI script could not be executed. * Parameter: the client socket descriptor. */ /**********************************************************************/ void cannot_execute(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "

Error prohibited CGI execution.\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* Print out an error message with perror() (for system errors; based * on value of errno, which indicates system call errors) and exit the * program indicating an error. */ /**********************************************************************/ void error_die(const char *sc) { perror(sc); exit(1); } /**********************************************************************/ /* Execute a CGI script. Will need to set environment variables as * appropriate. * Parameters: client socket descriptor * path to the CGI script */ /**********************************************************************/ void execute_cgi(int client, const char *path, const char *method, const char *query_string) { // 缓冲区 char buf[1024]; int cgi_output[2]; int cgi_input[2]; pid_t pid; int status; int i = 0; char c; int numchars = 1; int content_length = -1; // 默认字符 buf[0] = 'A'; buf[1] = '\0'; // 忽略大小写比较字符串 if (strcasecmp(method, "GET") == 0) { while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ { numchars = get_line(client, buf, sizeof(buf)); } } else /* POST */ { numchars = get_line(client, buf, sizeof(buf)); while ((numchars > 0) && strcmp("\n", buf)) { buf[15] = '\0'; if (strcasecmp(buf, "Content-Length:") == 0) { content_length = atoi(&(buf[16])); } numchars = get_line(client, buf, sizeof(buf)); } if (content_length == -1) { bad_request(client); return; } } sprintf(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); /* 建立output管道 */ if (pipe(cgi_output) < 0) { cannot_execute(client); return; } /* 建立input管道 */ if (pipe(cgi_input) < 0) { cannot_execute(client); return; } if ((pid = fork()) < 0) { cannot_execute(client); return; } if (pid == 0) /* child: CGI script */ { char meth_env[255]; char query_env[255]; char length_env[255]; // 子进程输出重定向到output管道的1端 dup2(cgi_output[1], 1); // 子进程输入重定向到input管道的0端 dup2(cgi_input[0], 0); // 关闭无用管道口 close(cgi_output[0]); close(cgi_input[1]); // CGI环境变量 sprintf(meth_env, "REQUEST_METHOD=%s", method); putenv(meth_env); if (strcasecmp(method, "GET") == 0) { sprintf(query_env, "QUERY_STRING=%s", query_string); putenv(query_env); } else { /* POST */ sprintf(length_env, "CONTENT_LENGTH=%d", content_length); putenv(length_env); } // 替换执行path execl(path, path, NULL); // int m = execl(path, path, NULL); // 如果path有问题,例如将html网页改成可执行的,但是执行后m为-1 // 退出子进程,管道被破坏,但是父进程还在往里面写东西,触发Program received signal SIGPIPE, Broken pipe. exit(0); } else { /* parent */ close(cgi_output[1]); close(cgi_input[0]); if (strcasecmp(method, "POST") == 0) { for (i = 0; i < content_length; i++) { recv(client, &c, 1, 0); ssize_t bytes_written = write(cgi_input[1], &c, 1); if (bytes_written <= 0) { perror("Failed to write to CGI"); close(cgi_input[1]); close(cgi_input[0]); break; } } } while (read(cgi_output[0], &c, 1) > 0) { send(client, &c, 1, 0); } close(cgi_output[0]); close(cgi_input[1]); waitpid(pid, &status, 0); } } /**********************************************************************/ /* Get a line from a socket, whether the line ends in a newline, * carriage return, or a CRLF combination. Terminates the string read * with a null character. If no newline indicator is found before the * end of the buffer, the string is terminated with a null. If any of * the above three line terminators is read, the last character of the * string will be a linefeed and the string will be terminated with a * null character. * Parameters: the socket descriptor * the buffer to save the data in * the size of the buffer * Returns: the number of bytes stored (excluding null) */ /**********************************************************************/ // 得到一行数据,只要发现c为\n,就认为是一行结束,如果读到\r,再用MSG_PEEK的方式读入一个字符,如果是\n,从socket用读出 // 如果是下个字符则不处理,将c置为\n,结束。如果读到的数据为0中断,或者小于0,也视为结束,c置为\n /*! * @brief 从socket中获取一行数据 * * @param[in] sock socket * @param[in] buf 用于保存数据的缓冲区 * @param[in] size 缓冲区的大小 * @return int 存储的字节数 */ int get_line(int sock, char *buf, int size) { int i = 0; char c = '\0'; int n; while ((i < size - 1) && (c != '\n')) { n = recv(sock, &c, 1, 0); if (n > 0) { if (c == '\r') { n = recv(sock, &c, 1, MSG_PEEK); if ((n > 0) && (c == '\n')) { recv(sock, &c, 1, 0); } else { c = '\n'; } } buf[i] = c; i++; } else c = '\n'; } buf[i] = '\0'; return (i); } /*! * @brief 根据文件路径的扩展名返回对应的 MIME 类型 * * @param[in] path 路径字符串 * @return const char* */ const char *get_content_type(const char *path) { const char *ext = strrchr(path, '.'); if (ext) { if (strcasecmp(ext, ".css") == 0) { return "text/css"; } if (strcasecmp(ext, ".js") == 0) { return "application/javascript"; } if (strcasecmp(ext, ".png") == 0) { return "image/png"; } if (strcasecmp(ext, ".jpg") == 0 || strcasecmp(ext, ".jpeg") == 0) { return "image/jpeg"; } if (strcasecmp(ext, ".gif") == 0) { return "image/gif"; } if (strcasecmp(ext, ".ico") == 0) { return "image/x-icon"; } if (strcasecmp(ext, ".svg") == 0) { return "image/svg+xml"; } if (strcasecmp(ext, ".woff") == 0) { return "font/woff"; } if (strcasecmp(ext, ".woff2") == 0) { return "font/woff2"; } } return "text/html"; } /*! * @brief 发送HTTP响应头 * * @param[in] client 客户端 * @param[in] filename 文件名 */ void headers(int client, const char *filename) { char buf[1024]; const char *content_type = get_content_type(filename); sprintf(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: %s\r\n", content_type); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); } /*! * @brief 404 * * @param[in] client 客户端 */ void not_found(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY>

The server could not fulfill\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "your request because the resource specified\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "is unavailable or nonexistent.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0); } /*! * @brief 如果不是CGI文件,直接读取文件返回给请求的http客户端 * * @param[in] client 客户端 * @param[in] filename 文件名 */ void serve_file(int client, const char *filename) { FILE *resource = NULL; int numchars = 1; char buf[1024]; buf[0] = 'A'; buf[1] = '\0'; while ((numchars > 0) && strcmp("\n", buf)) { numchars = get_line(client, buf, sizeof(buf)); } resource = fopen(filename, "rb"); if (resource == NULL) { not_found(client); } else { headers(client, filename); cat(client, resource); } fclose(resource); } /**********************************************************************/ /* This function starts the process of listening for web connections * on a specified port. If the port is 0, then dynamically allocate a * port and modify the original port variable to reflect the actual * port. * Parameters: pointer to variable containing the port to connect on * Returns: the socket */ /**********************************************************************/ int startup(u_short *port) { int httpd = 0; struct sockaddr_in name; httpd = socket(PF_INET, SOCK_STREAM, 0); if (httpd == -1) { error_die("socket"); } memset(&name, 0, sizeof(name)); name.sin_family = AF_INET; name.sin_port = htons(*port); name.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) { error_die("bind"); } if (*port == 0) { socklen_t namelen = sizeof(name); if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) { error_die("getsockname"); } *port = ntohs(name.sin_port); } if (listen(httpd, 5) < 0) { error_die("listen"); } return (httpd); } /*! * @brief 没有实现请求 * * @param[in] client 客户端 */ void unimplemented(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</TITLE></HEAD>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY>

HTTP request method not supported.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0); } int main(void) { int server_sock = -1; u_short port = 80; // 使用8080端口避免权限问题 struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); // 设置信号处理 signal(SIGPIPE, handle_sigpipe); // 创建线程池 thread_pool = create_thread_pool(MAX_THREADS); if (!thread_pool) { fprintf(stderr, "Failed to create thread pool\n"); exit(EXIT_FAILURE); } server_sock = startup(&port); printf("HTTP server running on port %d\n", port); while (1) { RequestContext *ctx = malloc(sizeof(RequestContext)); if (!ctx) { perror("malloc failed"); continue; } ctx->client_sock = accept(server_sock, (struct sockaddr *)&ctx->client_addr, &client_name_len); if (ctx->client_sock == -1) { perror("accept failed"); free(ctx); continue; } submit_request(thread_pool, ctx); } close(server_sock); destroy_thread_pool(thread_pool); return 0; } 阅读上面代码,提炼出多线程实现web_server的核心部分,(关键点在于和多进程,io多路复用的对比实现,要列出可能不同的地方)

冀北老许
  • 粉丝: 30
上传资源 快速赚钱