为什么用 SIGINT 可能比 SIGALRM 好的几个原因
1. Flexibility
SIGINT 不需要将时间间隔写进程式
可以透过系统的壳层脚本触发
随情况调整触发的频率跟是否要触发
这有助于程式的弹性和降低重新编译的次数
甚至可能之后
想要对写好的程式进行 profile
去计算 cache miss rate 等等
就可以免去 SIGALRM 开销带来的副作用
因此 SIGINT 可以说是方便很多
在很多时候会有意想不到的好处
2. Stability
A. Timer Inheritance 计时器继承
fork() / exec()-s:
POSIX 标准规定使用 alarm() 触发计时器时
在往后调用 fork() 的时候会清除
在往后调用 exec() 系列函数的时候
保留剩余的时间到下一个行程映像持续计算
Alarms created by alarm are preserved across execve
and are not inherited by children created via fork.
然而标准对 signal disposition 却有不同的逻辑
在 fork() 的时候被继承
在 exec() 的时候却会被重设成默认的 handler
A child created via fork inherits a copy of its
parent's signal dispositions. During an execve,
the dispositions of handled signals are reset to
the default; the dispositions of ignored signals
are left unchanged.
不一致的设计潜在隐患
可能导致非常难以除错或是异常的行为
B. Timer Anomaly 计时器异常
除此之外
Linux 对 setitimer() 的异常行为有以下的描述:
The generation and delivery of a signal are distinct,
and only one instance of each of the signals listed
above may be pending for a process. Under very heavy
loading, an ITIMER_REAL timer may expire before the
signal from a previous expiration has been delivered.
The second signal in such an event will be lost.
ITIMER_REAL 触发的是 SIGALRM
如果要避免上述的问题
可以采用 ITIMER_VIRTUAL 计时器
而它相对应的信号是 SIGVTALRM
但是它的时间不再是 wall time (real time)
而是 execution time
C. Unspecified Interactions 未定义行为
sleep(), usleep(), ualarm(), setitimer():
根据 POSIX 的标准
所有的计时器都是 per-process basis
而且不会 stack 起来
所以设定新的计时器会覆蓋旧有的计时器
因此每次检查 alarm 的返回值至关重要
不然可能导致不再有 SIGALRM 被触发
3. Conclusions
计时器的设定有很多琐碎的细节
甚至可能有上述我没想到的更隐晦的
都可能在一个不经意的情况
让程式运作的不如预期
除非真的很需要计时器这种对时间很要求的情况
才有使用的必要
如果只是想要看看目前执行的进度
定期写记录到硬盘或 SIGINT 不失为一个好方法
※ 引述《BreathWay (息尉)》之铭言:
: 标题: [问题] 如何设定时间上限使程式自动输出?
: 时间: Sat Dec 30 11:04:55 2017
:
: 问题(Question):
: 我写了一个以暴力算法求最佳解的程式,
: 主要是透过不断更新所找到的更好的解来达成。
: 但是我希望能设定一个时间上限,
: 如果程式还没跑完就直接输出目前找到的最好的解。
: 请问有办法在 C 里面实作这个功能吗?
:
: 推 Hazukashiine: POSIX Thread 或是 Signal Handler 12/30 14:32
: → Hazukashiine: 像是可以 SIGINT 时印出 ex. ping 12/30 14:34
: → Hazukashiine: 事实上你可以搭配 Linux 的 cron (job scheduler) 12/30 22:35
: → Hazukashiine: 来达成定时发送 SIGINT 的任务 不一定要人亲自去按 12/30 22:35
: → galic: Ctrl+C只是其中一种Signal(SIGINT)好吗 然后推文为何一直叫 12/30 23:01
: → galic: 人家用SIGINT 明明就有timer用的signal 12/30 23:01
: → galic: 你也只有polling counter或是透过timer两种作法 12/30 23:03
: → galic: 但照po后来的回复 这种需求就是要走timer阿 12/30 23:06
: → galic: 那就看一看timer的文件 https://tinyurl.com/lo9e3w3 12/30 23:08
: → galic: https://tinyurl.com/d5fw2rh 12/30 23:09
: → Hazukashiine: 用 SIGINT 是因为方便 弹性比较高 可以搭配脚本使用 12/30 23:40
: → Hazukashiine: 如果程式正在前景跑 但是手痒想在 timer expired 12/30 23:40
: → Hazukashiine: 前看偷看 按一下 ctrl + c 就可以了 如果想要静静放 12/30 23:40
: → Hazukashiine: 后景跑 就指令后面加 & 如果直接用楼上的方法写死在 12/30 23:40
: → Hazukashiine: 程式里编译好的话 就没有这种好处了 更何况楼主可能 12/30 23:40
: → Hazukashiine: 只是想偶尔偷看一下进度而已 我觉得用 SIGINT 合适 12/30 23:40
: → galic: 不用帮别人脑补 12/30 23:49