当前位置: 首页 > news >正文

网站的建设宗旨网站开发公司源码

网站的建设宗旨,网站开发公司源码,服务网站备案,wordpress 产品展示插件序言 在本篇内容中#xff0c;将为大家介绍在操作系统中的一个重要的机制 — 信号。大家可能感到疑惑#xff0c;好像我在使用 Linux 的过程中并没有接触过信号#xff0c;这是啥呀#xff1f;其实我们经常遇到过#xff0c;当我们运行的进程当进程尝试访问非法内存地址时…序言 在本篇内容中将为大家介绍在操作系统中的一个重要的机制 — 信号。大家可能感到疑惑好像我在使用 Linux 的过程中并没有接触过信号这是啥呀其实我们经常遇到过当我们运行的进程当进程尝试访问非法内存地址时我们的进程会被中断这是因为操作系统向该进程发送了中断信号。  Linux 操作系统离不开信号机制在这篇文章中让我们走进信号了解信号的从哪里来又到哪里去。 1. 信号的概念 1.1 定义 信号是操作系统向进程发送的一种通知表示某个特定事件已经发生。在Unix、类Unix 以及其他系统中信号被广泛使用。 1.2 特点 信号具有如下的特点 异步性信号的产生对进程来说是异步的即 进程无法预知信号何时到来。通知机制信号是一种 软件中断(由软件程序触发的中断方式)用于中断进程的正常执行流程使其处理特定事件。进程间通信虽然信号主要用于异常处理和系统调试但也可以用于进程间的基本通信。 1.3 种类 在 Linux 系统下使用指令kill -l 即可查看所有的信号 信号是使用宏定义的每个信号前面的数字就是该信号宏对应的值。 前 31 个信号为常规信号其余为实时信号。在本篇文章中我们主要讨论前 31 个常规信号。 你也可以使用指令: man 7 signal 查看每一个详细信息 补充知识点Core Term 在描述信号的字段中有一个叫做 Action 的特征他的值大多都是 Core Term 这是什么呢 Term term 是 terminate 的缩写表示默认动作是终止进程。当进程接收到一个默认动作为 term 的信号时进程会被立即终止。 Core core 表示 默认动作是终止进程并生成一个核心转储core dump文件。核心转储是一个包含进程在终止时的内存映像的文件它对于调试程序非常有用因为它 提供了进程终止时的状态信息。  咦就比如SIGSEGV 段错误 当我的程序非法访问被终结时被没有产生传说中的核心转储文件呀这是因为你的服务器默认关闭了该功能使用指令 ulimit -a 查看 现在我们使用指令 ulimit -c 4096 开启该功能 现在我们运行下一段程序 8 int main()9 {10 11 int *ptr NULL;12 *ptr 1;13 14 return 0;15 }程序不负众望地报错并退出了产生了一个文件 这个文件可以干嘛呢当我们的程序出现异常时相当该文件保存了案发现场具体的用法是 首先使用 gdb 调试你的程序 之后输入指令 core-file your_core 可以看到直接就复原了事故现场。 区别 进程终止term 信号会终止进程但 不生成核心转储文件。term 信号通常是用于请求进程正常终止的情况。调试信息core 信号不仅会终止进程还会 生成核心转储文件这包含了进程的内存映像、寄存器状态、堆栈跟踪等信息用于调试目的。 2. 信号的产生 信号是从哪里产生的呢虽然最后都是操作系统来执行对一个进程发送信号但是是谁告诉操作系统这样做的呢 2.1 用户操作 — kill 指令 当我们运行一个程序时可以通过指令 kill 来让操作系统对该进程发送相应的信号就比如我们可以手动发送 SIGKILL 9号 信号将该进程终结这里有一个程序 1 TestSig1.cc X 1 #include iostream2 #include unistd.h3 4 5 int main()6 {7 while(true)8 {9 std::cout I am Running, my pid is getpid() std::endl;10 sleep(1);11 }12 return 0;13 }该程序会每秒打印相应的内容现在我们可以使用相关的指令 kill -9 [pid] 来杀掉该进程 可以看到该进程被杀掉了 2.2 用户操作 — 按键操作 我们也可通过按键来让操作系统发送相关的信号就比如我们平时终止一个进程的方式更多的是通过键盘按键就比如 ctrl c 其实这个按键对应的就是 3 号信号 SIGQUIT。 2.3 用户操作 — 系统调用 操作系统提供一个系统调用 int kill(pid_t pid, int sig); 该函数你可以想指定进程发送信号 pid: 表示要发送信号的进程 IDsig: 表示要发送的信号返回值成功返回 0 失败返回 -1 错误码被设置 还有一个函数是 int raise(int sig);该函数是向当前进程发送指定信号简单来说相当于简化的 kill kill(getpid(), int sig); 2.4 触发软件条件 在之前管道的学习中我们了解到如果 读端被关闭了写端一直再写那么操作系统就会认为这是一个坏掉的管道就会发送 13 号信号 SIGPIPE 终止该进程这就是触发了某种软件条件。  现在在这里先向大家介绍几个非常重要的函数 signal 函数 该函数允许程序员定义当特定信号发生时程序应该如何响应, 简单说这个函数用于捕获特定信号然后执行指定操作的。sighandler_t signal(int signum, sighandler_t handler);: signum指定要处理的信号类型。注意SIGKILL 和 SIGSTOP 这两个信号不能被捕获、阻塞或忽略。handler指定信号的处理方式。它可以是一个函数指针指向一个用户定义的信号处理函数也可以是 SIG_IGN表示忽略该信号或者是 SIG_DFL表示采用信号的默认处理方式。返回值成功时signal 函数返回之前为该信号设置的信号处理函数的指针。如果之前没有为该信号设置过处理函数则返回 SIG_DFL。失败时返回 SIG_ERR并设置 errno 以指示错误原因。 看着描述这么多其实用起来不复杂比如现在我要捕获 2 号信号 SIGINT他的默认操作是退出现在我不想要推出想要执行我的逻辑 1 TestSig1.cc X 1 #include iostream2 3 #include unistd.h4 #include sys/types.h5 #include signal.h6 7 void signal_handle(int signum)8 {9 std::cout I got you signal: signum std::endl;10 }11 12 int main()13 {14 15 // 2号信号的捕获16 signal(2, signal_handle);17 18 while(true)19 {20 std::cout I am Running, my pid is getpid() std::endl;21 sleep(1);22 }23 return 0;24 }现在我们使用 ctrl c 已经不能终止该进程了 你也可将 signal(2, signal_handle); 中的函数换成 SIG_IGN 这样就会忽略该信号。 现在我有个想法就是将全部信号都捕获在写个死循环是不是就没有人把我停下来了我们能想到的人家肯定也想到了规定 9 号信号 SIGKILL 和 19 号信号 SIGSTOP 这两个信号不能被捕获、阻塞或忽略。保证系统的稳定性和管理员的控制权。 alarm 函数 大家为了早起都设置过闹钟吧闹钟的作用就是时间一到就提醒我们执行某件任务。在 Linux 中的闹钟 alarm 也是一样的我们设置一个定时当时间一到执行某项任务unsigned int alarm(unsigned int seconds); seconds定时器应该等待的秒数。如果 seconds 是 0则任何当前设置的定时器都会被取消你可以同时设置多个闹钟但不会发送 SIGALRM 信号。返回值如果之前已经设置了定时器alarm 函数 返回之前设置的剩余时间秒直到定时器到期。如果之前没有设置定时器则返回 0。 当 alarm 定时器到期时会向进程发送 SIGALRM 信号终止进程 12 int main()13 {14 15 // 设置一个闹钟执行默认操作16 alarm(2);17 18 while(true)19 {20 std::cout I am Running, my pid is getpid() std::endl;21 sleep(1);22 }23 return 0;24 }两秒之后进程自动终止 但更多情况下我们想要闹钟解释后执行我们的逻辑而不是终止进程那咋办呢 捕获该信号自定义处理信号这就需要我们上面说的 signal 函数了 7 void signal_handle(int signum)8 {9 std::cout Your alarm clock is ringing. std::endl;10 }11 12 int main()13 {14 15 // 设置一个闹钟执行默认操作16 alarm(2);17 // 捕获闹钟信号18 signal(SIGALRM, signal_handle);19 20 while(true)21 {22 std::cout I am Running, my pid is getpid() std::endl;23 sleep(1);24 }25 return 0;26 }现在闹钟时间到了就不会终止进程啦但是我还有一个疑问你这个闹钟只能执行一次呀之后就失效了我想要一个一直生效的定时任务怎么做到呢当捕获并执行自定义函数时再设置一个闹钟不就好啦 7 void signal_handle(int signum)8 {9 std::cout Your alarm clock is ringing. std::endl;10 alarm(2);11 }这样就得到一个持续的定时任务啦 在这里的闹钟就是一个触发了软件条件倒计时从而产生信号发送给进程 2.4 硬件异常 段错误 在我们的程序中很可能涉及到 段错误非法内存访问具体触发错误的细节如下 现代计算机使用内存管理单元MMU来管理内存。MMU 负责 将虚拟地址程序使用的地址映射到物理地址实际内存地址。当我们尝试访问一个地址时MMU 尝试将虚拟地址翻译为物理地址并检查该虚拟地址对应的页表项以确定是否有权限访问该地址以及地址是否有效当 CPU 发现该块地址是 无效的或者是不具有写权限的或者是无权限访问的将触发异常操作系统向该进程发送 SIGSEG 的信号 这就是简单的硬件异常触发流程。 3. 信号的保存 现在我们已经基本了解了信号是从哪里来的那么信号被一个进程接受过后是以什么形式存在于进程当中呢 在介绍信号的保存之前希望大家记住这几个概念 实际执行信号的处理动作称为信号递达(Delivery)信号从产生到递达之间的状态,称为信号未决(Pending)。 信号的信息被保存在一个进程的 task_struct 中 我们来好好的介绍这 3 个结构 3.1 block 位图 Block 位图用于指示哪些信号当前被进程 阻塞。如果一个信号在 Block 位图中对应的位被设置为 1 那么即使该信号已经到达它也不会被立即处理而是会 保持在未决状态直到进程解除对该信号的阻塞。 3.2 pending 位图 Pending 位图通常不是直接暴露给用户的而是作为进程控制块 task_struct 的一部分用于 跟踪哪些信号已经到达进程但尚未被处理。每个位代表一个信号如果该位被设置通常为 1 则表示对应的信号已经到达且处于 未决状态。 3.3 handler 函数指针数组 用户可以通过系统调用来设置特定信号的处理函数。当用户为某个信号注册了一个自定义的处理函数时操作系统就会将该函数的地址存储在 handler 表中对应信号编号的位置。如果用户没有为某个信号设置自定义处理函数那么当该信号发生时操作系统就会 调用默认的处理函数。 所以我们总结一下当一个信号传递给进程时操作系统会将 pending 表中该信号对应的值置 1如果 block表中的对应的值 也是 1代表该信号被阻塞不会被立即处理直至解除阻塞当解除阻塞或者一开始就不是阻塞状态的话就会执行 handle 表中该信号对应的函数操作。 3.4 验证结论 现在我们准备验证我们的想法我们先阻塞一个信号然后发送该信号查看是否执行相关操作在解除对该信号的阻塞再次观察现象 在这里会涉及到对信号集的操作大家可以简单理解为 对信号集进行的对某个信号的阻塞操作最终会保存到阻塞表中 在这里就不具体说明操作了感兴趣的小伙伴我找了一篇比较好的文章 信号集操作指南。 6 // 自定义函数7 void signal_handler(int signum)8 {9 std::cout Recived signal SIGINT!!! std::endl;10 }11 12 int main()13 {14 // 捕获信号15 signal(SIGINT, signal_handler);16 17 sigset_t sigset;18 // 初始化信号集19 sigemptyset(sigset);20 // 添加指定信号到信号集21 sigaddset(sigset, SIGINT);22 23 // 阻塞该信号24 if(sigprocmask(SIG_BLOCK, sigset, NULL) -1)25 {26 perror(sigprocmask);27 }28 29 std::cout SIGINT is blocked. Try pressing CtrlC after 5s!!!\n std::endl;30 sleep(5);31 32 // 解除阻塞33 if(sigprocmask(SIG_UNBLOCK, sigset, NULL) -1)34 {35 perror(sigprocmask);36 }37 38 std::cout SIGINT is unblocked. Try pressing CtrlC!!!\n std::endl;39 40 while(true)41 {42 sleep(1);43 }44 }第一次我们按 ctrl c 没什么反应过了 5s 后函数自动被执行可以看出我们的结论是正确的。 4. 信号的处理 现在我们知道信号哪里来的了也知道保存在哪里了现在我们来看看信号的处理方式。 4.1 执行默认方式 对于没有为其注册信号处理函数的信号进程会执行该信号的默认操作。就比如SIGTERM 信号的默认操作是请求进程终止而 SIGSEGV段错误信号的默认操作是生成core文件并终止进程。 4.2 调用信号处理函数 如果进程为某个信号注册了信号处理函数也称为信号处理器上面代码中的 signal_handler 函数那么当该信号到达时内核会暂停进程的正常执行流程转而调用该处理函数。 4.3 忽略信号 进程可以选择忽略某些信号。这意味着当这些信号到达时进程不会执行任何特别的操作而是继续执行其当前的代码路径。然而需要注意的是并非所有信号都可以被忽略。例如SIGKILL和SIGSTOP等信号是不能被忽略的。 4.4 阻塞信号 进程可以选择屏蔽某些信号以 避免在关键操作期间接收到这些信号。通过调用sigprocmask 等系统调用进程可以设置其信号屏蔽字以决定哪些信号能够传递到进程中。被屏蔽的信号将保持在未决状态直到屏蔽被解除后才会被处理。 4.5 阻塞和忽略的区别 这两个概念相当容易混淆从定义上来说 阻塞是指 进程选择性地阻止某些信号的传递。当这些被阻塞的信号发生时它们会被内核记录下来处于未决状态但不会立即执行信号的处理函数或执行默认操作。忽略是指进程对收到的某些信号 不执行任何操作即不调用处理函数也不执行默认操作而是简单地丢弃这些信号。 大家可以这样理解信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。 前者是处于未决的状态后者是被递达后选择了忽略不做其他处理。 总结 在这篇文章中介绍了信号的概念也介绍了信号从哪里来到哪里去被接受处理的过程希望大家有所收获。
http://www.w-s-a.com/news/551904/

相关文章:

  • 江苏省建设安全协会网站天津网站建设哪家公司好
  • 资源类网站怎么做的网站上线准备工作
  • 长沙专业网站建设怎么做企业建站公司服务
  • 肇庆市有限公司网站建设手机直接看的网站有哪些
  • 织梦修改网站后备份英语作文模板高中
  • 个人网站域名用什么好上海公司拍沪牌需要什么条件
  • 网站建设 保密做网站赚钱交税
  • 食品建设网站前的市场分析进出口网站贸易平台有哪些
  • php商城网站建设个人网站用什么服务器
  • 如何做好品牌网站建设方案网站开发的学习
  • 网站开发 管理方案wordpress怎么搭建微博
  • 有哪些ui的设计网站网上商城网站建设设计方案
  • iis中怎样配置网站绑定运城可以做网站的公司
  • 品牌网站建设开发价格dedecms电影网站模板
  • 网站设计外包合同帝国网站后台认证码错误
  • 网站设计公司深圳怎么免费做公司网站
  • 90设计网站几次是什么意思swipe类网站
  • 安康微网站建设网站域名使用费用
  • 网站建设执招标评分表微信代理网站模板
  • ps做网站分辨率自适应地方网站盈利
  • 免费自助小型网站专业网站建设组织
  • 猎聘网网站建设目标查看别人wordpress主题
  • 免费建设网站入驻网站备案不能更新吗
  • 个人网站制作代码西安建筑类公司
  • 网站备案要营业执照吗网站建设如何记账
  • 新手学做网站难吗外包服务商
  • 公司网站建设的项目工作分解结构wordpress插件后端页面
  • 四川省建设人才网站2018南京专业建站
  • ppt制作网站推荐seo教程百度网盘
  • 网站建设多少钱一平米网上商城网站开发报告