网络子系统84_sock事件通知进程

本文介绍了Linux内核网络子系统中,socket初始化过程以及在状态改变、数据就绪、可写空间和错误发生时的回调函数,包括sock_def_wakeup、sock_def_readable、sock_def_write_space和sock_def_error_report的详细工作流程,这些函数用于唤醒阻塞的进程并处理IO信号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//	socket初始化
//	调用路径:inet_create->sock_init_data
1.1 void sock_init_data(struct socket *sock, struct sock *sk)
{
	...
	//sock状态改变的回调函数,当sock的状态变迁(如从established到close_wait)就会调用这个函数
	sk->sk_state_change	=	sock_def_wakeup;
	//sock有输入数据的时被调用
	sk->sk_data_ready	=	sock_def_readable;
	//sock有可写空间时被调用
	sk->sk_write_space	=	sock_def_write_space;
	//sock出错(如收到一个rst)会被调
	sk->sk_error_report	=	sock_def_error_report;
	sk->sk_destruct		=	sock_def_destruct;
	...
}


//	唤醒sock上阻塞的进程
//	步骤:
//		1.检查struct sock->sk_wq上是否有阻塞的进程
//			1.2 唤醒struct sock->sk_wq上的进程
2.1 static void sock_def_wakeup(struct sock *sk)
{
	struct socket_wq *wq;

	rcu_read_lock();
	wq = rcu_dereference(sk->sk_wq);
	if (wq_has_sleeper(wq))
		wake_up_interruptible_all(&wq->wait);
	rcu_read_unlock();
}


//	有可用输入数据
//	调用路径:raw_local_deliver->sock_def_readable
//	步骤:
//		1.检查struct sock->sk_wq上是否有阻塞的进程
//			1.1 唤醒struct sock->sk_wq上的进程
//		2.唤醒在用户空间设置了信号io的进程
2.2
原始代码在这里,请在这个基础上修改,尽量不改变原代码/* webserver.c - 支持多线程/多进程/IO复用的Web服务器 */ #include <stdio.h> /* 标准输入输出 */ #include <stdlib.h> /* 内存分配、系统命令等 */ #include <string.h> /* 字符串处理函数 */ #include <unistd.h> /* POSIX操作系统API */ #include <sys/socket.h> /* 套接字编程接口 */ #include <netinet/in.h> /* 互联网地址族 */ #include <arpa/inet.h> /* 互联网地址转换 */ #include <sys/stat.h> /* 文件状态信息 */ #include <fcntl.h> /* 文件控制选项 */ #include <pthread.h> /* POSIX线程 */ #include <sys/wait.h> /* 进程控制 */ #include <sys/select.h> /* IO多路复用 */ /* 服务器配置常量 */ #define PORT 8080 /* 默认监听端口 */ #define WEB_ROOT "./web"/* 网站根目录路径 */ #define BUFFER_SIZE 8192/* 读写缓冲区大小 */ /* 服务器运行模式枚举 */ typedef enum { MODE_THREAD, /* 多线程模式 */ MODE_PROCESS, /* 多进程模式 */ MODE_MULTIPLEX /* IO多路复用模式 */ } ServerMode; /* HTTP状态行常量 */ const char* http_200 = "HTTP/1.1 200 OK\r\n"; const char* http_404 = "HTTP/1.1 404 Not Found\r\n"; const char* http_500 = "HTTP/1.1 500 Internal Server Error\r\n"; /* * 获取文件MIME类型 * 参数: path - 文件路径 * 返回: 对应的MIME类型字符串 */ const char* get_mime_type(const char *path) { /* 提取文件扩展名 */ const char *ext = strrchr(path, '.'); if (!ext) return "text/plain"; /* 默认文本类型 */ /* 常见Web文件类型匹配 */ 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, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; if (strcmp(ext, ".png") == 0) return "image/png"; return "application/octet-stream"; /* 二进制流默认类型 */ } /* * 发送文件内容到客户端 * 参数: * client_sock - 客户端套接字 * file_path - 请求的文件路径 */ void send_file(int client_sock, const char *file_path) { /* 尝试打开文件 */ FILE *file = fopen(file_path, "rb"); if (!file) { /* 文件不存在时发送404响应 */ send(client_sock, http_404, strlen(http_404), 0); const char *msg = "Content-Type: text/html\r\n\r\n<h1>404 Not Found</h1>"; send(client_sock, msg, strlen(msg), 0); return; } /* 获取文件大小 */ fseek(file, 0, SEEK_END); long size = ftell(file); /* 获取文件字节数 */ fseek(file, 0, SEEK_SET); /* 重置文件指针 */ /* 构造HTTP响应头 */ char header[BUFFER_SIZE]; snprintf(header, sizeof(header), "%sContent-Type: %s\r\nContent-Length: %ld\r\nConnection: close\r\n\r\n", http_200, get_mime_type(file_path), size); send(client_sock, header, strlen(header), 0); /* 分块发送文件内容 */ char buffer[BUFFER_SIZE]; size_t bytes_read; while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { send(client_sock, buffer, bytes_read, 0); } fclose(file); /* 关闭文件描述符 */ } /* * 处理HTTP请求 * 参数: client_sock - 客户端连接套接字 */ void handle_request(int client_sock) { char buffer[BUFFER_SIZE]; /* 接收客户端请求数据 */ ssize_t bytes_read = recv(client_sock, buffer, sizeof(buffer) - 1, 0); if (bytes_read <= 0) return; buffer[bytes_read] = '\0'; /* 确保字符串终止 */ /* 解析HTTP请求行 */ char method[16], path[256]; sscanf(buffer, "%s %s", method, path); /* 提取方法和路径 */ /* 调试输出 - 显示收到的请求 */ printf("Request: %s %s\n", method, path); /* 安全处理:防御路径遍历攻击 */ char *pos; while ((pos = strstr(path, "/../")) != NULL) { memmove(pos, pos + 3, strlen(pos + 3) + 1); } /* 构造实际文件路径 */ char full_path[512]; if (strcmp(path, "/") == 0) { /* 根请求默认返回Index.html */ snprintf(full_path, sizeof(full_path), "%s/Index.html", WEB_ROOT); } else { /* 拼接web根目录和请求路径 */ snprintf(full_path, sizeof(full_path), "%s%s", WEB_ROOT, path); } /* 处理GET/POST方法 */ if (strcmp(method, "GET") == 0 || strcmp(method, "POST") == 0) { struct stat st; /* 检查文件是否存在且是普通文件 */ if (stat(full_path, &st) == 0 && S_ISREG(st.st_mode)) { send_file(client_sock, full_path); } else { /* 文件不存在时发送404 */ send(client_sock, http_404, strlen(http_404), 0); const char *msg = "Content-Type: text/html\r\n\r\n<h1>404 Not Found</h1>"; send(client_sock, msg, strlen(msg), 0); } } else { /* 不支持的HTTP方法 */ send(client_sock, http_500, strlen(http_500), 0); const char *msg = "Content-Type: text/html\r\n\r\n<h1>Method Not Supported</h1>"; send(client_sock, msg, strlen(msg), 0); } close(client_sock); /* 关闭客户端连接 */ } /* * 线程处理函数(多线程模式使用) * 参数: arg - 包含客户端套接字指针 */ void* thread_handler(void *arg) { int client_sock = *((int*)arg); /* 解引用套接字 */ free(arg); /* 释放动态分配的内存 */ handle_request(client_sock); /* 处理请求 */ return NULL; } /* * 多进程模式主循环 * 参数: server_sock - 服务器监听套接字 */ void run_process_mode(int server_sock) { while (1) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 接受客户端连接 */ int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len); if (client_sock < 0) continue; /* 创建子进程处理请求 */ pid_t pid = fork(); if (pid == 0) { /* 子进程 */ close(server_sock); /* 关闭不需要的监听套接字 */ handle_request(client_sock); exit(0); /* 处理完成后退出 */ } else { /* 父进程 */ close(client_sock); /* 父进程不需要客户端套接字 */ waitpid(-1, NULL, WNOHANG); /* 非阻塞回收僵尸进程 */ } } } /* * 多线程模式主循环 * 参数: server_sock - 服务器监听套接字 */ void run_thread_mode(int server_sock) { while (1) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 动态分配内存存储客户端套接字 */ int *client_sock = malloc(sizeof(int)); *client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len); if (*client_sock < 0) continue; /* 创建线程处理请求 */ pthread_t tid; pthread_create(&tid, NULL, thread_handler, client_sock); pthread_detach(tid); /* 分离线程自动回收资源 */ } } /* * IO多路复用模式主循环 * 参数: server_sock - 服务器监听套接字 */ void run_multiplex_mode(int server_sock) { fd_set read_fds; /* 读文件描述符集合 */ int max_fd = server_sock; /* 当前最大文件描述符 */ while (1) { FD_ZERO(&read_fds); /* 清空描述符集合 */ FD_SET(server_sock, &read_fds); /* 添加服务器套接字 */ /* 等待可读事件 */ int activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL); if (activity < 0) break; /* 检查服务器套接字是否有新连接 */ if (FD_ISSET(server_sock, &read_fds)) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len); if (client_sock < 0) continue; /* 直接处理请求(单线程) */ handle_request(client_sock); } } } /* * 主函数 - 程序入口 * 参数: * argc - 命令行参数个数 * argv - 命令行参数数组 */ int main(int argc, char *argv[]) { /* 检查命令行参数 */ if (argc < 2) { fprintf(stderr, "Usage: %s <thread|process|multiplex>\n", argv[0]); return 1; } /* 解析运行模式 */ ServerMode mode; if (strcmp(argv[1], "thread") == 0) mode = MODE_THREAD; else if (strcmp(argv[1], "process") == 0) mode = MODE_PROCESS; else if (strcmp(argv[1], "multiplex") == 0) mode = MODE_MULTIPLEX; else { fprintf(stderr, "Invalid mode. Use thread, process or multiplex\n"); return 1; } /* 创建TCP套接字 */ int server_sock = socket(AF_INET, SOCK_STREAM, 0); if (server_sock < 0) { perror("Socket creation failed"); return 1; } /* 配置服务器地址 */ struct sockaddr_in server_addr = { .sin_family = AF_INET, /* IPv4地址族 */ .sin_port = htons(PORT), /* 网络字节序端口 */ .sin_addr.s_addr = INADDR_ANY /* 监听所有接口 */ }; /* 设置套接字选项 - 地址重用 */ int opt = 1; setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /* 绑定套接字到地址 */ if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("Bind failed"); return 1; } /* 开始监听,最大连接队列128 */ listen(server_sock, 128); printf("Server running on port %d in %s mode\n", PORT, argv[1]); /* 根据模式运行服务器 */ switch (mode) { case MODE_PROCESS: run_process_mode(server_sock); break; case MODE_THREAD: run_thread_mode(server_sock); break; case MODE_MULTIPLEX: run_multiplex_mode(server_sock); break; } close(server_sock); /* 关闭服务器套接字 */ return 0; }
08-12
/* webserver.c - 支持多线程/多进程/IO复用的Web服务器 */ #include <stdio.h> #include <stdlib.h> /* 内存分配、系统命令 */ #include <string.h> #include <unistd.h> /* POSIX操作系统API */ #include <sys/socket.h> /* 套接字编程接口 */ #include <netinet/in.h> /* 互联网地址族 */ #include <arpa/inet.h> /* 互联网地址转换 */ #include <sys/stat.h> /* 文件状态信息 */ #include <fcntl.h> /* 文件控制选项 */ #include <pthread.h> /* POSIX线程 */ #include <sys/wait.h> /* 进程控制 */ #include <sys/select.h> /* IO多路复用 */ /* 服务器配置常量 */ #define PORT 80 /* 监听端口80,需要sudo运行 */ #define WEB_ROOT "./web"/* 网站根目录路径 */ #define BUFFER_SIZE 8192/* 读写缓冲区大小 */ /* 服务器运行模式 */ typedef enum { MODE_THREAD, /* 多线程模式 */ MODE_PROCESS, /* 多进程模式 */ MODE_MULTIPLEX /* IO多路复用模式 */ } ServerMode; /* HTTP状态行常量 */ const char* http_200 = "HTTP/1.1 200 OK\r\n"; const char* http_404 = "HTTP/1.1 404 Not Found\r\n"; const char* http_500 = "HTTP/1.1 500 Internal Server Error\r\n"; /* * 获取文件类型 * 参数: path - 文件路径 * 返回: 对应的MIME类型字符串 */ const char* get_mime_type(const char *path) { /* 提取文件扩展名 */ const char *ext = strrchr(path, '.'); if (!ext) { return "text/plain"; /* 默认文本类型 */ } /* 常见Web文件类型匹配 */ 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, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) { return "image/jpeg"; } if (strcmp(ext, ".png") == 0) { return "image/png"; } return "application/octet-stream"; /* 二进制流默认类型 */ } /* * 发送文件内容到客户端 * 参数: * client_sock - 客户端套接字 * file_path - 请求的文件路径 */ void send_file(int client_sock, const char *file_path) { /* 尝试打开文件 */ FILE *file = fopen(file_path, "rb"); if (!file) { /* 文件不存在时发送404响应 */ send(client_sock, http_404, strlen(http_404), 0); const char *msg = "Content-Type: text/html\r\n\r\n<h1>404 Not Found</h1>"; send(client_sock, msg, strlen(msg), 0); return; } /* 获取文件大小 */ fseek(file, 0, SEEK_END); long size = ftell(file); /* 获取指针当前位置,即文件字节数 */ fseek(file, 0, SEEK_SET); /* 重置文件指针 */ /* 构造HTTP响应头 */ char header[BUFFER_SIZE]; snprintf(header, sizeof(header), "%sContent-Type: %s\r\nContent-Length: %ld\r\nConnection: close\r\n\r\n", http_200, get_mime_type(file_path), size); send(client_sock, header, strlen(header), 0); /* 分块发送文件内容 */ char buffer[BUFFER_SIZE]; size_t bytes_read; while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { send(client_sock, buffer, bytes_read, 0); } fclose(file); /* 关闭文件描述符 */ } /* * 处理HTTP请求 * 参数: client_sock - 客户端连接套接字 */ void handle_request(int client_sock) { char buffer[BUFFER_SIZE]; /* 接收客户端请求数据 */ ssize_t bytes_read = recv(client_sock, buffer, sizeof(buffer) - 1, 0); if (bytes_read <= 0) { return; } buffer[bytes_read] = '\0'; /* 确保字符串终止 */ /* 解析HTTP请求行 */ char method[16], path[256]; sscanf(buffer, "%s %s", method, path); /* 提取方法和路径 */ /* 调试输出 - 显示收到的请求 */ printf("Request: %s %s\n", method, path); /* 安全处理:防御路径遍历攻击 */ char *pos; while ((pos = strstr(path, "/../")) != NULL) /* 路径规范化,阻止通过/../回溯到上级目录 */ { memmove(pos, pos + 3, strlen(pos + 3) + 1); } /* 构造实际文件路径 */ char full_path[512]; if (strcmp(path, "/") == 0) { /* 根请求默认返回Index.html */ snprintf(full_path, sizeof(full_path), "%s/Index.html", WEB_ROOT); } else { /* 拼接web根目录和请求路径 */ snprintf(full_path, sizeof(full_path), "%s%s", WEB_ROOT, path); } /* 处理GET/POST方法 */ if (strcmp(method, "GET") == 0 || strcmp(method, "POST") == 0) { struct stat st; /* 检查文件是否存在且是普通文件 */ if (stat(full_path, &st) == 0 && S_ISREG(st.st_mode)) { send_file(client_sock, full_path); } else { /* 文件不存在时发送404 */ send(client_sock, http_404, strlen(http_404), 0); const char *msg = "Content-Type: text/html\r\n\r\n<h1>404 Not Found</h1>"; send(client_sock, msg, strlen(msg), 0); } } else { /* 不支持的HTTP方法 */ send(client_sock, http_500, strlen(http_500), 0); const char *msg = "Content-Type: text/html\r\n\r\n<h1>Method Not Supported</h1>"; send(client_sock, msg, strlen(msg), 0); } close(client_sock); /* 关闭客户端连接 */ } /* * 线程处理函数(多线程模式使用) * 参数: arg - 包含客户端套接字指针 */ void* thread_handler(void *arg) { int client_sock = *((int*)arg); /* 解引用套接字 */ free(arg); /* 释放动态分配的内存 */ handle_request(client_sock); /* 处理请求 */ return NULL; } /* * 多进程模式主循环 * 参数: server_sock - 服务器监听套接字 */ void run_process_mode(int server_sock) { while (1) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 接受客户端连接 */ int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len); if (client_sock < 0) continue; /* 创建子进程处理请求 */ pid_t pid = fork(); if (pid == 0) { /* 子进程 */ close(server_sock); /* 关闭不需要的监听套接字 */ handle_request(client_sock); exit(0); /* 处理完成后退出 */ } else { /* 父进程 */ close(client_sock); /* 父进程不需要客户端套接字 */ waitpid(-1, NULL, WNOHANG); /* 非阻塞回收僵尸进程 */ } } } /* * 多线程模式主循环 * 参数: server_sock - 服务器监听套接字 */ void run_thread_mode(int server_sock) { while (1) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 动态分配内存存储客户端套接字 */ int *client_sock = malloc(sizeof(int)); *client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len); if (*client_sock < 0) continue; /* 创建线程处理请求 */ pthread_t tid; pthread_create(&tid, NULL, thread_handler, client_sock); pthread_detach(tid); /* 分离线程自动回收资源 */ } } /* * IO多路复用模式主循环 * 参数: server_sock - 服务器监听套接字 */ void run_multiplex_mode(int server_sock) { fd_set read_fds; /* 读文件描述符集合 */ int max_fd = server_sock; /* 当前最大文件描述符 */ while (1) { FD_ZERO(&read_fds); /* 清空描述符集合 */ FD_SET(server_sock, &read_fds); /* 添加服务器套接字 */ /* 等待可读事件 */ int activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL); if (activity < 0) break; /* 检查服务器套接字是否有新连接 */ if (FD_ISSET(server_sock, &read_fds)) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len); if (client_sock < 0) continue; /* 直接处理请求(单线程) */ handle_request(client_sock); } } } /* * 主函数 - 程序入口 * 参数: * argc - 命令行参数个数 * argv - 命令行参数数组 */ int main(int argc, char *argv[]) { /* 检查命令行参数 */ if (argc < 2) { fprintf(stderr, "Usage: %s <thread|process|multiplex>\n", argv[0]); return 1; } /* 解析运行模式 */ ServerMode mode; if (strcmp(argv[1], "thread") == 0) mode = MODE_THREAD; else if (strcmp(argv[1], "process") == 0) mode = MODE_PROCESS; else if (strcmp(argv[1], "multiplex") == 0) mode = MODE_MULTIPLEX; else { fprintf(stderr, "Invalid mode. Use thread, process or multiplex\n"); return 1; } /* 创建TCP套接字 */ int server_sock = socket(AF_INET, SOCK_STREAM, 0); if (server_sock < 0) { perror("Socket creation failed"); return 1; } /* 配置服务器地址 */ struct sockaddr_in server_addr = { .sin_family = AF_INET, /* IPv4地址族 */ .sin_port = htons(PORT), /* 网络字节序端口 */ .sin_addr.s_addr = INADDR_ANY /* 监听所有接口 */ }; /* 设置套接字选项 - 地址重用 */ int opt = 1; setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /* 绑定套接字到地址 */ if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("Bind failed"); return 1; } /* 开始监听,最大连接队列128 */ listen(server_sock, 128); printf("Server running on port %d in %s mode\n", PORT, argv[1]); /* 根据模式运行服务器 */ switch (mode) { case MODE_PROCESS: run_process_mode(server_sock); break; case MODE_THREAD: run_thread_mode(server_sock); break; case MODE_MULTIPLEX: run_multiplex_mode(server_sock); break; } close(server_sock); /* 关闭服务器套接字 */ return 0; } 这是一份多模式的Web服务器实现代码,请在这份代码的基础上将三种模式升级为支持多个线程/进程同时并发处理的代码,并且添加进程/线程生成监控
最新发布
08-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值