volatile 和 sig_atomic_t

本文介绍了volatile关键字的作用,即防止编译器对特定变量进行优化,并解释了何时及为何需要使用volatile。此外,还详细阐述了sig_atomic_t类型的特点及其应用场景,确保变量操作的原子性。

1 volatile—影响编译器编译的结果

volatile 变量是随时可能发生变化的,每次使用时都需要去内存里重新读取它的值,与volatile变量有关的运算,不要进行编译优化,以免出错(VC++ 在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化)。

例如:

   volatile int i=10;

   int j = i;

   ...

   int k = i;

volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。

而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样一来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。

建议使用volatile变量的场所:

   (1) 并行设备的硬件寄存器

   (2) 一个中断服务子程序中会访问到的非自动变量(全局变量)

   (3) 多线程应用中被几个任务共享的变量

2 sig_atomic_t:

当把变量声明为该类型是,则会保证该变量在使用或赋值时,无论是在32位还是64位的机器上都能保证操作是原子的,它会根据机器的类型自动适应。

这个类型是定义在signal.h文件中,下面来说说这个类型。

在处理信号(signal)的时候,有时对于一些变量的访问希望不会被中断,无论是硬件中断还是软件中断,这就要求访问或改变这些变量需要在计算机的一条指令内完成

通常情况下,int类型的变量通常是原子访问的,也可以认为sig_atomic_t就是int类型的数据,因为对这些变量要求一条指令完成,所以sig_atomic_t不可能是结构体,只会是数字类型。

   在linux里这样定义: typedef int __sig_atomic_t;

另外gnu c的文档也说比int短的类型通常也是具有原子性的,例如short类型。同时,指针(地址)类型也一定是原子性的。该类型在所有gnu c库支持的系统和支持posix的系统中都有定义。

代码高并发卡死原因分析根本原因:信号处理与计数同步问题信号丢失风险SIGCHLD信号是非排队的,当多个子进程同时退出时,可能只触发一次信号处理高并发场景下(5000并发),大量子进程同时退出,信号处理函数无法及时处理所有僵尸进程计数更新延迟 Check and Limit Children 检查并限制子进程 当num_children=500时进入等待在sleep期间,即使有子进程退出,主进程仍在sleep状态无法立即accept新连接信号处理函数更新num_children,但主进程不会重新检查计数(直接continue)竞争条件 High Concurrency Timing Issue 高并发时序问题 具体问题表现连接队列溢出默认监听队列长度(SOMAXCONN)通常为1285000并发远超队列容量,新连接被丢弃输出中反复出现Reached max children表明系统持续过载进程创建瓶颈Windows下fork()模拟效率较低(通过WSL/Cygwin)500进程上限 + 高频创建/销毁导致调度开销剧增输出显示的证据 log Child 83948 exited with status 0 # 子进程退出 New child process 83949 (total: 500) # 立即创建新进程 Reached max children (500), waiting... # 立刻再次达到上限 表明计数更新连接接受存在时间差 请基于以上问题,考虑到我的代码运行在Linux虚拟机中,对我的代码进一步优化, #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/wait.h> #include <sys/types.h> #include <errno.h> #include <fcntl.h> #include <netinet/in.h> #include <signal.h> #include "http.h" #include "multiProcess.h" #define MAX_CHILDREN 500 // 最大并发子进程数 #define MAX_WAIT_TIME 5 // 等待子进程退出的最长时间(秒) static volatile sig_atomic_t num_children = 0; // 信号处理函数:回收僵尸进程 void sigchld_handler(int sig) { int saved_errno = errno; pid_t pid; int status; // 非阻塞方式回收所有已退出的子进程 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { printf("Child %d exited with status %d\n", pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("Child %d killed by signal %d\n", pid, WTERMSIG(status)); } num_children--; } errno = saved_errno; } // 信号处理函数:处理SIGTERM/SIGINT void sigterm_handler(int sig) { printf("Received signal %d, shutting down...\n", sig); // 这里可以添加清理代码 exit(EXIT_SUCCESS); } int multiProcessEntry() { int server_sock = -1; int port = 80; struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); // 设置信号处理 struct sigaction sa; sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(EXIT_FAILURE); } // 设置终止信号处理 signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE server_sock = startup(&port); printf("HTTP server running on port %d\n", port); // 设置监听套接字为非阻塞 int flags = fcntl(server_sock, F_GETFL, 0); fcntl(server_sock, F_SETFL, flags | O_NONBLOCK); while (1) { // 检查进程数限制 if (num_children >= MAX_CHILDREN) { // 等待部分子进程退出 printf("Reached max children (%d), waiting...\n", MAX_CHILDREN); sleep(1); continue; } int client_sock = accept(server_sock, (struct sockaddr *)&client_name, &client_name_len); if (client_sock == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // 没有新连接,短暂休眠后重试 usleep(10000); // 10ms continue; } perror("accept failed"); continue; } pid_t pid = fork(); if (pid < 0) { perror("fork failed"); close(client_sock); } else if (0 == pid) { // 子进程 // 关闭不需要的文件描述符 close(server_sock); // 处理请求 accept_request(client_sock); // 关闭客户端socket close(client_sock); // 子进程退出 exit(EXIT_SUCCESS); } else { // 父进程 // 关闭客户端socket(父进程不需要) close(client_sock); // 增加子进程计数 num_children++; printf("New child process %d (total: %d)\n", pid, num_children); } } // 等待所有子进程退出 while (num_children > 0) { printf("Waiting for %d children to exit...\n", num_children); sleep(1); } close(server_sock); return EXIT_SUCCESS; }
最新发布
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值