西安互联网网站建设/推广方案怎么做
1. 引言
在现代操作系统中,信号是一种进程间通信机制,它允许操作系统或其他进程向一个进程发送消息。信号可以用来通知进程发生了一些重要事件,如用户请求终止进程、硬件异常、定时器超时等。掌握信号处理技术对于开发健壮、高效的系统程序至关重要。本文将带你深入了解信号的基础知识,并通过一系列示例演示如何在C语言程序中实现信号处理。
2. 信号概述
信号是由操作系统产生的软件中断,用于通知接收进程发生了某些类型的事件。信号可以分为两大类:
- 不可忽略的信号:如SIGKILL和SIGSTOP,它们总是会被操作系统强制执行。
- 可忽略的信号:如SIGINT和SIGTERM,接收进程可以选择忽略或者自定义处理。
常见的信号及其用途如下表所示:
信号 | 编号 | 描述 |
---|---|---|
SIGINT | 2 | 终端中断信号,通常由用户按下Ctrl+C触发。 |
SIGTERM | 15 | 终止信号,通常用于请求程序优雅地停止运行。 |
SIGKILL | 9 | 强制终止信号,无法被捕捉或忽略。 |
SIGALRM | 14 | 定时信号,由alarm()函数设置的时间间隔到期时产生。 |
SIGHUP | 1 | 挂断信号,当控制终端挂起或登录会话结束时产生。 |
SIGPIPE | 13 | 管道破裂信号,当写入一个已经断开连接的管道时产生。 |
SIGUSR1 | 10 | 用户定义信号1,用于进程间的通讯。 |
SIGUSR2 | 12 | 用户定义信号2,用于进程间的通讯。 |
3. 信号处理基础
在C语言中,信号处理主要依赖于signal()
函数。该函数允许用户注册一个信号处理函数,当指定的信号到达时,就会调用这个函数。然而,signal()
函数存在一些限制,如不能传递额外参数给信号处理函数,且不是线程安全的。因此,在多线程程序中,更推荐使用sigaction()
函数来替代。
3.1 使用signal()
函数
#include <signal.h>
#include <stdio.h>void signal_handler(int signum) {printf("Received signal %d\n", signum);exit(signum);
}int main() {signal(SIGINT, signal_handler); // 注册信号处理函数while (1) {printf("Hello World!\n");sleep(1);}return 0;
}
上述代码注册了一个SIGINT信号处理函数signal_handler
,当用户按下Ctrl+C时,程序将打印一条消息并退出。
3.2 使用sigaction()
函数
sigaction()
函数提供了更多的灵活性和安全性,可以设置信号掩码、指定信号处理方式(忽略、默认处理或自定义处理函数)等。
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void sigint_handler(int signum, siginfo_t *info, void *context) {printf("Caught signal %d\n", signum);exit(signum);
}int main() {struct sigaction sa;sa.sa_sigaction = sigint_handler;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_SIGINFO;if (sigaction(SIGINT, &sa, NULL) == -1) {perror("sigaction");return 1;}while (1) {printf("Hello World!\n");sleep(1);}return 0;
}
在这个版本中,我们使用sigaction()
函数注册了一个信号处理程序,并且启用了SA_SIGINFO
标志,这允许我们的信号处理函数接受额外的参数。
4. 信号与线程
在多线程程序中,信号的处理需要特别注意。默认情况下,信号是针对整个进程而不是特定线程的。这意味着,如果一个线程捕获到了信号,所有线程都会受到影响。为了避免这种情况,可以使用pthread_sigmask()
函数来设置线程的信号掩码,从而控制哪些信号可以被线程捕获。
4.1 设置线程信号掩码
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>void *thread_func(void *arg) {int *num = (int *)arg;*num += 1;printf("Thread: %d\n", *num);pthread_exit(NULL);
}int main() {pthread_t thread_id;int num = 0;if (pthread_create(&thread_id, NULL, thread_func, &num) != 0) {perror("Failed to create thread");exit(EXIT_FAILURE);}sigset_t mask;sigfillset(&mask); // 设置信号掩码sigdelset(&mask, SIGINT); // 允许SIGINT信号if (pthread_sigmask(SIG_BLOCK, &mask, NULL) == -1) {perror("Failed to set signal mask");exit(EXIT_FAILURE);}while (1) {printf("Main thread: %d\n", num);sleep(1);}return 0;
}
在此示例中,我们创建了一个线程,并设置了信号掩码,使得只有SIGINT信号可以被线程捕获。这样即使在主线程中按下Ctrl+C,也不会影响到正在运行的线程。
5. 定时信号:alarm()
与sigtimedwait()
除了处理外部信号外,我们还可以通过alarm()
函数来设置定时信号。当指定的时间过去之后,SIGALRM信号就会被发送给进程。此外,sigtimedwait()
函数提供了一种等待信号的方式,并且可以指定一个超时时间。
5.1 使用alarm()
函数
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void alarm_handler(int signum) {printf("Alarm signal received.\n");
}int main() {signal(SIGALRM, alarm_handler);alarm(5); // 设置5秒后发送SIGALRM信号while (1) {printf("Waiting...\n");sleep(1);}return 0;
}
此程序将在启动后五秒发出报警信号。
5.2 使用sigtimedwait()
函数
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>int main() {sigset_t pending;sigemptyset(&pending);sigaddset(&pending, SIGALRM);alarm(5); // 设置5秒后发送SIGALRM信号struct timespec timeout = {1, 0}; // 超时时间为1秒siginfo_t info;while (1) {printf("Waiting for a signal...\n");if (sigtimedwait(&pending, &info, &timeout) != -1) {printf("Signal caught: %d\n", info.si_signo);}sleep(1);}return 0;
}
在这个例子中,我们使用sigtimedwait()
函数来等待信号,如果在一秒钟内没有信号到来,则会继续循环。
6. 高级主题:信号队列与实时信号
6.1 信号队列
当一个信号被发送给一个进程时,如果该信号正在被处理或被阻止,则信号会被放入进程的信号队列中。每个进程都有一个信号队列,最多可以存储一个每个类型的信号。当信号队列已满时,再来的相同类型的信号将被丢弃。
信号队列的管理通常是由操作系统完成的,但作为程序员,我们需要知道信号队列的存在,并且在设计程序时考虑到这一点。例如,如果程序频繁地忽略或阻止某个信号,可能导致信号丢失,从而引发不可预期的行为。
6.2 实时信号
实时信号是一组特殊的信号,它们具有更高的优先级,并且可以携带额外的数据。使用sigqueue()
函数可以发送实时信号,并附带一个用户定义的值。
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void real_time_signal_handler(int signum, siginfo_t *info, void *context) {printf("Real-time signal %d with value %d\n", signum, info->si_value.sival_int);
}int main() {struct sigaction sa;sa.sa_sigaction = real_time_signal_handler;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_SIGINFO;if (sigaction(SIGRTMIN, &sa, NULL) == -1) {perror("sigaction");return 1;}// 发送带有整数值的实时信号union sigval value;value.sival_int = 1234;if (sigqueue(0, SIGRTMIN, value) == -1) {perror("sigqueue");return 1;}while (1) {printf("Waiting for a real-time signal...\n");sleep(1);}return 0;
}
这段代码演示了如何发送一个带有整数值的实时信号,并在信号处理函数中读取这个值。
7. 实战案例:实现一个简单的守护进程
守护进程(Daemon)是一种在后台运行的服务程序,它不与任何终端关联,并且通常会在系统启动时自动运行。下面我们将展示如何使用信号处理技术来创建一个简单的守护进程。
7.1 创建守护进程
创建守护进程的一般步骤如下:
- 第一次fork:创建一个子进程,然后让父进程退出。这是为了防止后续操作受到shell的影响。
- 成为会话领导者:通过调用
setsid()
函数,使进程脱离原来的会话和终端。 - 第二次fork:再次创建一个子进程,并让父进程退出。这是因为
setsid()
只能在一个没有控制终端的进程中调用,否则会失败。 - 更改工作目录:将当前工作目录改为根目录,防止进程删除其当前目录而导致进程无法正常工作。
- 关闭文件描述符:关闭标准输入、输出和错误文件描述符,防止守护进程占用不必要的资源。
- 设置信号处理程序:忽略某些信号,使守护进程更加稳定。
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>void daemonize() {pid_t pid = fork();if (pid < 0) {perror("Fork failed");exit(EXIT_FAILURE);} else if (pid > 0) {exit(EXIT_SUCCESS); // 父进程退出}// 成为会话领导者if (setsid() < 0) {perror("Setsid failed");exit(EXIT_FAILURE);}// 第二次forkpid = fork();if (pid < 0) {perror("Fork failed");exit(EXIT_FAILURE);} else if (pid > 0) {exit(EXIT_SUCCESS); // 父进程退出}// 更改工作目录if (chdir("/") < 0) {perror("Chdir failed");exit(EXIT_FAILURE);}// 关闭文件描述符umask(0);close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 设置信号处理程序signal(SIGHUP, SIG_IGN);signal(SIGINT, SIG_IGN);signal(SIGQUIT, SIG_IGN);signal(SIGTERM, exit);
}int main() {daemonize();while (1) {printf("Daemon running...\n");sleep(10);}return 0;
}
这个简单的守护进程忽略了大多数信号,只对SIGTERM信号作出响应,即当接收到终止信号时退出。
8. 高级实战案例:守护进程与信号处理
让我们进一步扩展之前的守护进程示例,使其成为一个更加实用的服务程序。我们将添加日志记录功能,并且允许守护进程通过信号进行重启、停止等操作。
8.1 日志记录
在守护进程中添加日志记录功能可以帮助我们跟踪程序的状态和错误。我们可以将日志输出到一个文件中,这样即使程序崩溃,我们也能够查看到它最后的状态。
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>#define LOG_FILE "/var/log/mydaemon.log"void log(const char *message) {int fd = open(LOG_FILE, O_WRONLY | O_APPEND | O_CREAT, 0644);if (fd == -1) {perror("Open log file failed");return;}fprintf(fd, "%s\n", message);close(fd);
}void daemonize() {pid_t pid = fork();if (pid < 0) {perror("Fork failed");exit(EXIT_FAILURE);} else if (pid > 0) {exit(EXIT_SUCCESS); // 父进程退出}if (setsid() < 0) {perror("Setsid failed");exit(EXIT_FAILURE);}pid = fork();if (pid < 0) {perror("Fork failed");exit(EXIT_FAILURE);} else if (pid > 0) {exit(EXIT_SUCCESS); // 父进程退出}if (chdir("/") < 0) {perror("Chdir failed");exit(EXIT_FAILURE);}umask(0);close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);signal(SIGHUP, SIG_IGN);signal(SIGINT, SIG_IGN);signal(SIGQUIT, SIG_IGN);signal(SIGTERM, exit);
}void handle_signals(int signum) {switch (signum) {case SIGHUP:log("SIGHUP received, reloading configuration...");break;case SIGTERM:log("SIGTERM received, shutting down...");exit(EXIT_SUCCESS);default:log("Unknown signal received.");break;}
}int main() {daemonize();// 设置信号处理函数signal(SIGHUP, handle_signals);signal(SIGTERM, handle_signals);while (1) {log("Daemon running...");sleep(10);}return 0;
}
在这个版本中,我们添加了一个log()
函数,用于将消息输出到日志文件中。同时,我们修改了信号处理函数handle_signals()
,使其能够根据不同类型的信号采取不同的行动。
9. 总结与展望
通过本文,你不仅了解了信号的基本概念和用途,还学会了如何在C语言程序中使用信号处理技术。从简单的信号处理到复杂的守护进程创建,每一步都充满了挑战与乐趣。希望这些知识能够帮助你在未来的开发过程中更好地利用信号机制来提升程序的健壮性和可用性。
相关文章:

使用C语言进行信号处理:从理论到实践的全面指南
1. 引言 在现代操作系统中,信号是一种进程间通信机制,它允许操作系统或其他进程向一个进程发送消息。信号可以用来通知进程发生了一些重要事件,如用户请求终止进程、硬件异常、定时器超时等。掌握信号处理技术对于开发健壮、高效的系统程序至…...

什么是工单管理系统?全面认识指南
在现代企业中,客户服务和支持是业务成功的关键因素之一。为了有效地管理客户请求和问题,许多公司采用了工单管理系统。本文将深入探讨工单管理系统的定义、功能、优势。 一、工单管理系统的定义 工单管理系统是一种软件工具,旨在帮助企业管…...

集群化消息服务解决方案
目录 集群化消息服务解决方案项目概述架构图使用说明服务端通过API接口推送消息给客户端调用方式 请求参数返回参数 客户端推送消息连接websocket或发送消息 接收消息项目地址作者信息 集群化消息服务解决方案 项目概述 集群化消息服务解决方案是一种用于处理大量消息的高可用…...

python数据结构操作与可视化的应用
Python具有丰富的数据结构操作和可视化库,可以进行各种数据结构的创建、编辑和分析,并将结果可视化。以下是几个常见的Python数据结构操作和可视化的应用示例: 1. 列表(List)操作和可视化: - 创建列表&a…...

【基于轻量型架构的WEB开发】课程 作业4 AOP
一. 单选题(共7题,38.5分) 1 (单选题)下列选项中,用于通知/增强处理的是( )。 A. Joinpoint B. Pointcut C. Aspect D. Advice 正确答案:D 答案解析:在面向切面编程ÿ…...

跨境独立站新手,如何用DuoPlus云手机破局海外社媒引流?
独立站作为电商领域的一个重要组成部分,其发展在最近几年里确实令人瞩目,对于想要进入跨境赛道的新手卖家来说,手上握着有优势的货源,建立小型的DTC独立站确实会比入驻第三方平台具有更大的灵活性。本文将给跨境卖家们总结独立站和…...

【Android、IOS、Flutter、鸿蒙、ReactNative 】标题栏
Android 标题栏 参考 Android Studio版本 配置gradle镜像 阿里云 Android使用 android:theme 显示标题栏 添加依赖 dependencies {implementation("androidx.appcompat:appcompat:1.6.1")implementation("com.google.android.material:material:1.9.0")…...

信息安全工程师(83)Windows操作系统安全分析与防护
一、Windows操作系统安全分析 系统漏洞: Windows操作系统由于其复杂性和广泛使用,可能存在一些已知或未知的漏洞。这些漏洞可能会被黑客利用,进行恶意攻击。微软会定期发布系统更新和补丁,以修复这些漏洞,提高系统的安…...

QT Unknown module(s) in QT 以及maintenance tool的更详细用法(qt6.6.0)
不小心接了同事的委托,帮改一个qt的工程代码。然后出事了,那个proj是qt5.9版本的吧,搞到6.6版本的环境中各种问题。至少有3个是这样的: :-1: error: Unknown module(s) in QT: multimedia 直接百度,好像很简单&#x…...

如何在vscode中安装git详细新手教程
一、安装git后点击vscode中的设置 今天教大家如何在VScode中编写代码后提交到git仓库,如果我们不想切换到git的命令行窗口,可以在VScode中配置git,然后就可以很方便快捷的把代码提交到仓库中。 二、在输入框中输入 git.path ,再点…...

JVM垃圾回收详解二(重点)
死亡对象判断方法 堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。 引用计数法 给对象中添加一个引用计数器: 每当有一个地方引用它,计数器就加 1…...

VLAN 高级技术实验
目录 一、实验背景 二、实验任务 三、实验步骤 四、实验总结 一、实验背景 假如你是公司的网络管理员,为了节省内网的IP地址空间,你决定在内网部署VLAN聚合,同时为了限制不同业务之间的访问,决定同时部署MUX VLAN。 二、实验…...

windowsC#-创建和引发异常
异常用于指示在运行程序时发生了错误。 此时将创建一个描述错误的异常对象,然后使用 throw 语句或表达式引发。 然后,运行时搜索最兼容的异常处理程序。 当存在下列一种或多种情况时,程序员应引发异常: 1. 方法无法完成其定义的…...

python爬虫案例——请求的网页源码被加密,解密方法全过程(19)
文章目录 1、任务目标2、网页分析3、代码编写1、任务目标 目标网站:https://jzsc.mohurd.gov.cn/data/company,该网站的网页源码被加密了,用于本文测验 要求:解密该网站的网页源码,请求网站并返回解密后的明文数据,网页内容如下: 2、网页分析 进入网站,打开开发者模式,…...

详解广告联盟
某种程度上,动荡的程度甚于以往。产业链中快速挤进了众多不曾有过的角色,产业逻辑被完全颠覆。巨大的变化在几年间迅速产生,源头是快速发展的互联网和科技。 这个行业走到了十字路口,身处其中的大多数人感到乐观,但同…...

Getting accurate time estimates from your tea(从您的团队获得准确的时间估计)
Hi again. 嗨了。 Ready to get back into it? 准备好重新开始了吗? Let’s go. Time estimation, 我们走吧。时间估计, effort estimation, 努力估计, and capacity planning are all helpful techniques for creating your project schedule. 容量规划都是创建项…...

攻防世界35-easyupload-CTFWeb
攻防世界35-easyupload-CTFWeb 通过各种上传发现,过滤了php后缀和内容中有php的文件 有这几种方式上传一句话木马 <script language"php">eval($_POST[1]);</script> <?php eval($_POST[cmd]);?> <? eval($_POST[cmd]);?>…...

在Mysql中,如何定位慢查询
参考回答:之前我们有个项目做压测的时候有的接口非常的慢,接口的响应时间超过了2秒以上,因为在MySOL中也提供了慢日志查询的功能,可以在MySOL的系统配置文件中开启这个慢日志的功能,并且也可以设置SOL执行超过多少时间…...

CSS教程(三)- CSS 三大特性
1. 层叠性 介绍 多组CSS样式共同作用于一个元素,就会出现 覆盖(层叠) 另一个冲突的样式。 层叠原则 样式冲突:遵循就近原则(哪个样式离结构近,就执行哪个样式) 样式不冲突,就不会重…...

如何保证Redis与MySQL双写一致性
什么是双写一致性问题? 双写一致性主要指在一个数据同时存在于缓存(如Redis)和持久化存储(如MySQL)的情况下,任何一方的数据更新都必须确保另一方数据的同步更新,以保持双方数据的一致状态。这一…...

【IC每日一题:IC验证面试--UVM验证-2】
IC每日一题:IC验证面试--UVM验证-2 2.9 get_next_iterm()和try_next_item()的区别?2.10 一个典型的UVM验证平台,谈一下UVM验证环境结构,各个组件之间的关系?2.11 uvm组件之间通信的方式? analysis_port和其…...

SPIRE: Semantic Prompt-Driven Image Restoration 论文阅读笔记
这是一篇港科大学生在google research 实习期间发在ECCV2024的语义引导生成式修复的文章,港科大陈启峰也挂了名字。从首页图看效果确实很惊艳,尤其是第三行能用文本调控修复结果牌上的字。不过看起来更倾向于生成,对原图内容并不是很复原&…...

#揭秘万维网:从静态页面到智能互联网
揭秘万维网:从静态页面到智能互联网 今天刚上了学校开设的课程,于是便有了下文的思考内容。 在当今数字化时代,Web(万维网)扮演着重要的角色,成为人们获取信息、沟通交流和进行商业活动的主要平台。 1. …...

【计算机基础——数据结构——红黑树】
1. 红黑树(RBTree) 为什么HashMap不直接使用AVL树,而是选择了红黑树呢? 由于AVL树必须保证左右子树平衡,Max(最大树高-最小树高) < 1,所以在插入的时候很容易出现不平衡的情况,一旦这样&…...

Sentinel — 微服务保护
微服务架构将大型应用程序拆分为多个小而独立的服务,每个服务可以独立部署和扩展。然而,微服务系统需要面对的挑战也随之增加,例如服务之间的依赖、分布式环境下的故障传播和安全问题。因此,微服务保护措施是确保系统在高并发、资…...

Cynet:全方位一体化安全防护工具
前言 1999年,布鲁斯施奈尔曾说过:“复杂性是安全最大的敌人。”彼时还是19年前,而现在,网络安全已然变得更加繁杂。 近日我在网上冲浪过程中发现了这么一个平台性质的软件,看似具有相当强的防护能力。 根据Cynet的描…...

python中常见的8种数据结构之一数组的应用
在Python中,数组是一种常见的数据结构,用于存储一系列相同类型的元素。在实际应用中,数组可以用于解决各种问题。 以下是数组在Python中的一些常见应用: 1. 存储和访问数据:数组可以用于存储和访问一组数据。可以通过…...

安装多个低版本谷歌Chrome浏览器用于测试,适配Vue3+vite项目
安装多个低版本谷歌Chrome浏览器用于测试,适配Vue3vite项目 问题:使用vue3tsvite搭建了一个项目,在chrome新版本浏览器上无问题,但是部署到现场页面直接空白,且控制台报错: Uncaugnt SyntaxError: Unexpe…...

UI组件---如何设置el-pagination分页组件的背景色
1. 要替换 el-pagination 组件的背景色,您可以通过自定义CSS来实现。 具体的CSS规则取决于您想要更改的是哪个部分的背景色,例如普通页码、活跃页码、上下导航箭头等。以下是一些示例CSS规则,您可以根据自己的需求进行调整: /* …...

LabVIEW编程过程中为什么会出现bug?
在LabVIEW编程过程中,Bug的产生往往源自多方面原因。以下从具体的案例角度分析一些常见的Bug成因和调试方法,以便更好地理解和预防这些问题。 1. 数据流错误 案例:在一个LabVIEW程序中,多个计算节点依赖相同的输入数据&#…...