? 由软件条件生成信号
SIGPIPE 是由软件条件生成的一种信号,已经在“管道”中介绍过。本节主要介绍SIGALRM信号。
alarm函数可以设置一个闹钟,即告诉内核在指定的秒数之后向当前进程发送SIGALRM信号。该信号的默认处理动作是终止当前进程。
这个函数的返回值是0或以前设置的闹钟时间剩余的秒数。例如,某人小睡时设置闹钟在30分钟后响,20分钟后被吵醒并重新设置闹钟为15分钟后响,“以前设置的闹钟时间剩余的时间”就是10分钟。如果seconds值为0,表示取消以前设置的闹钟,函数的返回值仍然是以前设置的闹钟时间剩余的秒数。
? 基本alarm验证 - 体会IO效率问题
程序的作用是在1秒内不断计数,到达1秒时被SIGALRM信号终止。必要时,可以对SIGALRM信号进行捕捉。
IO多:由于频繁地往显示器文件进行写入操作,导致效率低下。
// IO多 #include#include #include int main() { int count = 0; alarm(1); while (true) { std::cout << count++ << std::endl; } return 0; }

IO少:每秒收到alarm信号后,通过特定的处理函数进行一次写入显示器文件。
// IO少include
include
include
int count = 0;
void handler(int signumber) { std::cout << count << std::endl; count = 0; alarm(1); }
int main() { signal(SIGALRM, handler); alarm(1); while (true) { count++; } return 0; }
由23万次的计数,提升到5亿次,可见频繁进行IO会显著降低效率。

结论:闹钟会响一次,默认终止进程,IO效率低。
? 设置重复闹钟
可以通过在处理函数中再次设置alarm,形成发送取消中断信号,重新运行:
#includeinclude
include
include
include
include
include
include
using func_t = std::function
; int gcount = 0; std::vector
gfuncs; // 把信号更换成为硬件中断 void handler(int signo) { for (auto& f : gfuncs) { f(); } std::cout << "Alarm went off " << gcount++ << " times" << std::endl; alarm(1); // 再次设置闹钟 }
int main() { signal(SIGALRM, handler); alarm(1); while (true) { pause(); // 暂停等待信号 } return 0; }
程序运行一次后暂停,当一个进程调用pause函数时,它会使该进程进入睡眠状态(阻塞状态),并暂停执行后续代码,直到该进程接收到一个信号并从该信号的处理程序中返回。也就是说,pause函数实际上是在等待一个信号来中断当前的暂停状态,使进程能够继续执行。

再次运行,会进行死循环操作流程:main函数设置alarm一秒,然后通过signal设置alarm的处理方式从终止改为handler,然后进入while(true)执行pause()暂停循环;当一秒到来,执行handler函数,等到handler里的alarm函数再次设置启动,pause()收到取消睡眠状态信号,再次运行这个1秒。接下来就是重复的动作了。

闹钟设置一次,起效一次。重复设置的方法是alarm(0):如果seconds值为0,表示取消以前设置的闹钟,函数的返回值仍然是以前设置的闹钟时间剩余的秒数。
? 如何理解软件条件
在操作系统中,信号的软件条件指的是由软件内部状态或特定软件操作触发的信号生成机制。这些条件包括但不限于定时器超时(如alarm函数设定的时间到达)、软件异常(如向已关闭的管道写数据生成的SIGPIPE信号)等。当这些软件条件满足时,操作系统会向相关进程发送相应的信号,以通知进程进行相应的处理。简而言之,软件条件是由操作系统内部或外部软件操作触发的信号生成。
? 如何简单快速理解系统闹钟
系统闹钟的本质是操作系统本身具有定时功能,并允许用户设置这种定时功能。现代Linux提供了定时功能,定时器也需要被管理:先描述,再组织。内核中的定时器数据结构是:
struct timer_list {
struct list_head entry;
unsigned long expires;
void (function)(unsigned long);
unsigned long data;
struct tvec_t_base_s base;
};我们不在这部分进行深究,为了理解它,我们可以看到:定时器超时时间expires和处理方法function。
操作系统管理定时器,采用的是时间轮的做法,但为了简单理解,可以将其组织成“堆结构”(小堆排序,处理时间短的节点)。
? 总结











