Linux 理解进程信号
目录一、共享内存通信机制中的临界资源访问与同步控制1、概念2、生活角度理解信号机制3、信号量的操作二、信号1、生活角度的信号2、技术应用角度的信号3、操作系统角度的信号信号如何产生理解组合键变为信号理解信号如何被进程保存时钟中断了解4、信号定义、kill -l查看信号列表5、信号处理的常见方式一、共享内存通信机制中的临界资源访问与同步控制1、概念基于对共享内存通信机制的理解我们认识到要实现不同进程间的数据共享关键是确保这些进程能够访问到同一份物理内存资源。在此之前我们探讨的所有进程间通信手段其核心目标都是确保进程间可以共享和访问同一份数据资源。当这份资源被多个进程同时访问时我们称其为“临界资源”因为它的访问必须受到严格的控制以避免竞争条件和数据不一致性问题。在每个进程中涉及访问临界资源的那段代码区域被称为“临界区”。临界区内的操作要求互斥执行即在任意时刻至多只有一个进程能够进入并执行这段代码。为了保证进程间对临界资源的访问满足互斥性我们需要采用适当的同步机制确保在任何时候只有一个进程可以进入临界区进行操作。#include iostream // 假设这是我们的临界资源一个全局变量它模拟一个打印机 int shared_resource 0; // 全局计数器一次只能有一个线程访问 // 这是线程函数其中访问和修改临界资源的代码段就是临界区 void thread_function(const char* name) { while (true) { // 简化版的“临界区入口”在这里我们假设某种机制能够确保一次只有一个线程进入 // 注真实环境中这将由互斥锁等同步原语来实现 // 开始临界区 std::cout name entered the critical section.\n; // 访问和修改临界资源 shared_resource; // 假设这是打印一页纸的操作 std::cout Page printed by name . Total pages: shared_resource \n; // 简化的“临界区出口” std::cout name left the critical section.\n; // 模拟其他非临界区的工作 // ... } } int main() { // 假设有两个线程分别模拟两个任务 std::thread t1(thread_function, Thread A); std::thread t2(thread_function, Thread B); // 等待线程结束在此处省略了join操作 return 0; }在这个例子中临界资源:shared_resource是一个全局变量代表着只能由一个线程同时访问和修改的资源比如打印机的物理打印操作。临界区: 在thread_function函数中打印输出entered the critical section.到left the critical section.之间的代码段就是临界区。在这个区域内线程会访问和修改临界资源shared_resource。原子性则是对这类操作性质的一种描述它强调的是一个操作要么全部完成要么完全不执行不存在中间状态。在多执行流环境下如果不加保护地同时访问临界资源可能会导致数据竞争和不一致的情况发生。而在非临界区的代码执行则不受这种互斥性的约束多个执行流可以并行执行且彼此互不影响。简言之正是由于对临界资源的不当访问才引发了进程间通信中的同步问题而通过实现互斥和保证原子性我们可以有效地避免这些问题的发生。2、生活角度理解信号机制在生活中我们可以把信号机制比作去电影院观影时的座位预订系统。当你打算看电影时并非径直走进影院就能随意落座。首先需要经历购票环节这张电影票就像是一份承诺代表着你有权在特定时间段内使用一个座位即便此刻你并未实际坐在那里座位已经被视为你的预定资源。对应到计算机这一现象与进程试图进入临界区即仅允许单个进程访问的资源区域的过程十分相似。就如同电影院不会放任观众自行进入影厅抢占座位操作系统也不会让进程未经许可就擅自访问临界资源。在此情境下进程必须先通过某种方式取得“许可”——这个许可就是所谓的“信号量”。信号量的核心作用犹如现实中的电影票当进程获取到一个有效的信号量时这就意味着操作系统已经为该进程安排好对临界资源的使用权。只有持有信号量的进程才能安全地进入并操作临界资源从而避免多个进程同时访问导致的数据冲突或不一致问题。3、信号量的操作信号量的操作基本可概括为以下几个步骤申请信号量这是一个尝试进入临界区的过程。进程通过减少信号量的计数如果信号量大于零来标记对资源的请求。这等同于购买电影票确认座位的预订。访问临界资源一旦信号量成功申请进程就可以进入临界区执行其操作。这相当于拿着电影票进入放映厅坐在自己预订的座位上。释放信号量进程完成其在临界区的操作后会释放信号量通常是通过增加信号量的计数来实现。这表明资源现在可以被其他进程预订就如同观众观看完电影离开座位座位可供下一位观众使用。为了确保对临界资源的安全访问我们需要使用信号量计数器来实现互斥和同步。申请信号量会导致计数器递减释放信号量则会使计数器递增。P操作申请信号量和V操作释放信号量必须是原子的以防止中间状态问题。二、信号1、生活角度的信号在生活中接收快递的过程与操作系统中处理信号的概念有异曲同工之处。想象一下你在网购平台上购买了多个商品就如同在系统中启动了多项任务等待各自的“信号”——快递送达的通知。当快递员抵达楼下并向你发送送达通知时这就相当于操作系统接收到一个“信号”。在这个场景中你可以做如下类比默认动作当接收到“快递已到”的信号时如果不做特别处理默认的动作就像是立刻停下手头的事如玩游戏然后下楼签收包裹之后拆开包裹并开始使用商品如同进程接收到信号后执行默认操作如进程终止或继续执行。自定义动作如果对某个特殊商品比如一盒零食设置了特殊的“信号处理函数”那么在接到通知后你可能会选择暂时不立即取件而是等一会儿准备把它当作礼物送给女朋友这就类似编程中设置自定义信号处理器函数以便在接收到特定信号时执行定制化操作。忽略快递在收到快递到达的消息时若你选择继续沉浸在游戏中而不去理会这就类似于进程选择忽略某个信号暂时不对该事件做出反应。从接收到快递送达通知到实际下楼取件的过程构成了一个类似操作系统中的“信号处理延时窗口”。在这个窗口期内尽管你知道快递已到但并不急于立即采取行动而是根据自己的优先级和安排来调整处理时机。整个过程体现了生活中的“异步处理”理念因为快递送达的具体时间点具有不确定性正如操作系统中的信号也可能随时到来一样。而如何应对这些“信号”则完全取决于个人或进程事先设定的策略及实时情境判断。2、技术应用角度的信号[localhost code_test]$ cat sig.c #include stdio.h int main() { while (1) { printf(I am a process, I am waiting signal!\n); sleep(1); } } [localhost code_test]$ . / sig I am a process, I am waiting signal! I am a process, I am waiting signal! I am a process, I am waiting signal! ^ C [localhost code_test]$在技术应用场景中假设你网购下单购买了多件商品正处于热切等待各商品快递送达的状态。就像上述C语言代码中的进程在持续循环等待信号一样你清楚自己应该如何应对每一份即将到来的包裹。在实际生活中快递员抵达你所填写的住址时你会通过某种方式得知快递已到达的通知。在计算机程序中sig.c中的进程不断循环并输出“我正在等待信号”这象征着你在网购后持续关注物流动态的行为。sleep(1)则代表你间隔一定时间查询一次订单状态。当按下Ctrl-C时就如同快递员送达包裹并触发了一次“中断通知”硬件中断操作系统接收到这个中断后将其解释为一个信号如SIGINT并将其发送给正在运行的前台进程即等待信号的进程。在现实生活中当你收到快递员到达的通知后可以选择立刻下楼取件或安排稍后处理。同样地在计算机程序中进程在接收到SIGINT信号后默认会立即停止执行退出进程这就好比你收到快递通知后立即停止手头的事去收取包裹。不过如果你希望在接收到信号后延时处理就需要在程序中添加自定义的信号处理函数来模拟“稍后再取件”的逻辑。在示例代码中按下Ctrl-C后进程直接结束对应的就是立刻响应并结束等待信号的状态。需要注意在计算机操作系统的环境中当你在终端中直接启动一个命令或程序时默认情况下它将以前台进程的身份运行。这意味着如果你按下了Ctrl-C产生的 SIGINT 信号将直接发送给当前处于前台的进程。若要在启动命令后使其在后台运行只需在命令末尾添加一个 符号这样一来Shell 不必等待此进程结束即可接收新命令并启动其他进程。Shell 具有灵活的并发处理能力它能同时管理一个前台进程及任意数量的后台进程。然而值得注意的是只有当前台进程才具备捕获由诸如Ctrl-C这类控制键触发产生的信号的能力。换句话说后台进程无法直接响应来自终端的这类交互式信号。对于前台进程中运行的用户空间代码而言由于用户在进程执行的任何时刻都可能按下Ctrl-C发送 SIGINT 信号因此进程在执行流程中的任何一个点都可能遭遇此信号并随之终止。这种不受特定执行阶段限制、随时可能出现的信号特性使得信号对于进程的控制流程来说具有显著的异步性。这就意味着进程必须能够适时地响应这些非预期的外部事件以保证其正常且稳定地运行。3、操作系统角度的信号信号如何产生[localhost code_test]$ cat sig.c #include stdio.h int main() { while (1) { printf(I am a process, I am waiting signal!\n); sleep(1); } } [localhost code_test]$ . / sig I am a process, I am waiting signal! I am a process, I am waiting signal! I am a process, I am waiting signal! ^ C [localhost code_test]$当用户在终端中按下CtrlC组合键时实际上触发了一个硬件中断。操作系统OS通过键盘设备驱动程序识别到这个特定的中断请求并将其解释为向目标前台进程发送一个特定的信号——通常是编号为2的 SIGINT 信号。操作系统接收到组合键信号后会遍历进程列表定位到当前在终端前台运行的进程。一旦确定目标进程OS就会在该进程的进程控制块PCB内部的数据结构中记录或更新相应的信号位图信息。信号位图通常是一个二进制的表示形式例如一个二进制位对应一个信号类型当某位置1时就表示该进程已经收到了相应类型的信号。理解组合键变为信号键盘通过中断机制与CPU通信其中包含了对组合键如CtrlC的识别功能。当这样的组合键被按下时硬件会触发一个中断请求操作系统接收到这个中断后经过一系列解析和处理步骤将组合键的含义映射为特定的信号类型。理解信号如何被进程保存每个进程在内核中都有一个进程控制块PCB这是一个核心数据结构用于存储和维护进程的状态信息其中包括信号位图字段。当一个信号被发送给进程时本质上是操作系统将这个信号的标记写入到该进程的PCB中的信号位图中。“信号发送”的实质在于操作系统不是通过某种通信机制发送信号而是直接修改目标进程的PCB结构改变其中信号位的状态以此告知进程它已收到一个新的信号。进程随后可以通过检查其自身的PCB来得知收到了何种信号并根据预先设定的策略进行相应的处理如执行默认操作如终止进程、忽略信号或执行自定义的动作如注册的信号处理器函数。[hbrVM-16-9-centos signal]$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN1 36) SIGRTMIN2 37) SIGRTMIN3 38) SIGRTMIN4 39) SIGRTMIN5 40) SIGRTMIN6 41) SIGRTMIN7 42) SIGRTMIN8 43) SIGRTMIN9 44) SIGRTMIN10 45) SIGRTMIN11 46) SIGRTMIN12 47) SIGRTMIN13 48) SIGRTMIN14 49) SIGRTMIN15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX时钟中断了解时钟中断是计算机硬件体系结构中的一个重要特性它为操作系统提供了一个周期性的触发点使得操作系统能够在规定的时间间隔基础上进行一系列关键的系统管理操作。以下是对时钟中断的详细解释发生原理时钟中断通常由计算机系统内部的一个定时器硬件设备产生。这个定时器有时也称为滴答定时器或心跳定时器基于硬件计数器工作每当计数器递增达到预设的阈值时会产生一个电信号向中央处理器CPU发出中断请求。操作系统通过配置定时器的计数速率可以设定时钟中断的频率这个频率通常以毫秒级别进行设置。核心作用任务调度时钟中断是操作系统进行进程调度的主要依据之一。每当发生时钟中断操作系统会暂停当前正在执行的进程并保存其现场上下文然后根据调度策略选择下一个高优先级或等待时间片耗尽的进程继续执行。这种机制实现了多任务的并发执行尽管在单核CPU上实际上是轮流执行使各进程能够共享CPU资源提高了系统的并发性能和响应能力。时间管理时钟中断用于维护系统的全局时间包括但不限于系统时钟、进程运行时间统计、定时器超时检测等。操作系统通过累计时钟中断次数来更新系统时间确保了系统时间的准确性和实时性。实时性支持在实时操作系统RTOS中时钟中断更加重要因为它为系统提供了严格的时间基线确保了关键任务能够按照预定的时间间隔得到及时执行满足实时性要求。硬件同步与I/O轮询时钟中断也可能用于硬件同步操作例如与网络设备的时间同步或周期性检查I/O设备的状态确保CPU在设备准备就绪时迅速响应。总结时钟中断是操作系统的心跳它赋予了系统时间感知能力和任务切换机制。通过周期性地打断CPU的连续执行操作系统得以执行后台管理和调度任务确保了系统资源的合理分配和高效使用。4、信号定义、kill -l查看信号列表信号是一种操作系统机制用于在进程间实现事件的异步通信和通知其本质上属于软件层面的中断处理方式。当一个进程接收到信号时它会暂停当前任务的执行并转而去响应预先定义好的信号处理程序从而实现进程间的非同步交互。简而言之信号作为一种软中断手段能够在进程之间高效、灵活地传播事件信息和触发相应动作。信号列表kill -l命令在Unix/Linux系统中用于列出系统内建的所有信号及其对应的数字编号。信号是操作系统用于进程间通信或控制进程行为的一种机制例如通知进程结束运行、报告错误状态或者要求进程执行某种特定动作。每个信号都有一个独一无二的整数编号并且在C/C编程中通过包含signal.h头文件可以访问到这些信号对应的宏定义名称。例如宏定义SIGINT通常代表的是中断信号CtrlC产生的信号其对应的信号编号是2。[hbrVM-16-9-centos signal]$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN1 36) SIGRTMIN2 37) SIGRTMIN3 38) SIGRTMIN4 39) SIGRTMIN5 40) SIGRTMIN6 41) SIGRTMIN7 42) SIGRTMIN8 43) SIGRTMIN9 44) SIGRTMIN10 45) SIGRTMIN11 46) SIGRTMIN12 47) SIGRTMIN13 48) SIGRTMIN14 49) SIGRTMIN15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX对于标准信号即编号小于等于33的那些信号它们是内建于大多数Unix-like系统的基本信号集(普通信号)。例如SIGHUP1终端挂断或会话首进程退出时发出。SIGINT2用户按下中断键通常是CtrlC时产生。SIGQUIT3用户按下退出键通常是Ctrl\时产生。SIGKILL9无条件终止进程信号不可被捕获或忽略。SIGTERM15请求进程正常终止的信号可被捕获并自定义处理。而编号34及以上的信号称为实时信号它们是为了满足特定需求而设计的额外信号主要用于实现更高优先级或更精确的进程间同步与通信且通常具有更高的优先级。在Unix/Linux系统中通过查阅man 7 signal手册页可以获得更加详细的信号列表包括每种信号何时由操作系统产生触发条件、它们默认的行为比如是否会导致进程终止、暂停或继续执行以及如何更改这些信号的默认处理方式等内容。5、信号处理的常见方式在计算机系统中对于接收到的信号常见的处理方式包括但不限于以下三种策略忽略信号这种处理方式允许进程选择不响应特定的信号即当接收到该信号时不进行任何操作任由信号自然消逝。例如进程可以选择忽略某些不影响其正常运行的非关键性信号。执行默认处理动作每个信号都有一个系统预设的默认处理动作。当接收到信号时进程可以选择按照系统的默认方式进行响应。比如SIGINT中断信号的默认处理通常是终止进程而SIGSEGV段错误信号的默认处理通常是结束进程并生成core dump文件。捕捉信号Signal Catching这是一种更为精细的信号处理方式允许进程为特定信号注册一个自定义的信号处理函数。当指定的信号发生时操作系统会暂停当前进程并切换到用户态执行预先设定好的处理函数。这样进程可以根据自身需求实现更灵活、个性化的信号响应逻辑。通过这种方式进程能够捕捉并针对性地处理各种可能出现的异常情况或进行特定的程序控制流转。