给Linux C/C++程序设定执行时限
时间管理,是每个现代人的必修课。给一个任务固定的执行时间,是控制整体进度的一种常用方法。
回到程序中: 如何让一个运行时间可能比较长的Linux C/C++程序能够有一个执行时限呢?这就是本文想解答的一个问题。
方法一
最直接的做法就是,起一个timer,设定该timer到期发送 SIGTERM 信号。如果定时器超时后需要做善后工作,那么就给 SIGTERM 信号写一个信号处理函数;若不需要善后工作,则不用写。
代码如下:
// g++ timer.cpp -lrt
#include <ctime>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <signal.h>
#include <unistd.h>
void CreateAndStartTimer(unsigned int wait_seconds)
{
if (wait_seconds == 0) return;
timer_t timerid;
struct sigevent sev;
struct itimerspec its;
// Create the timer
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGTERM;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) == -1) {
fprintf(stderr, "Failed to create timer, will continue running.\n");
return;
}
// Start the timer
its.it_value.tv_sec = wait_seconds;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
fprintf(stderr, "Failed to set timer, will continue running.\n");
}
}
int main(int argc, char *argv[])
{
unsigned int wait_seconds = 0;
if (argc != 2) {
fprintf(stderr, "Usage: %s <time-out-seconds> \n", argv[0]);
exit(EXIT_FAILURE);
}
wait_seconds = atoi(argv[1]);
if (wait_seconds > 0) CreateAndStartTimer(wait_seconds);
while (1) {
sleep(1);
}
return 0;
}
以上是最简洁的做法。因为要做的工作就是时间到了杀死本进程,因而也没有什么好多做处理的。基本上,本文到此就可以结束了。要注意的主要是,timer_create(), timer_settime(),以及 sigevent 的用法。man一下基本上就比较清楚了。
方法二
不过,在有些情况下,可能需要捕获实时信号;而在设定实时信号的处理函数时,有可能希望暂时屏蔽实时信号,待设置完再进行处理。这些都是比较常见的需求。
这里以另一个程序来演示实时信号以及屏蔽和去屏蔽信号的做法。
// g++ timer.cpp -lrt
#include <ctime>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <signal.h>
#include <unistd.h>
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)
#define SIG SIGRTMIN
typedef void (*signal_handler_t) (int, siginfo_t *, void *);
void signal_handler(int signo, siginfo_t * info, void * context)
{
pid_t myself = getpid();
kill(myself, SIGTERM);
}
void set_signal_handler(int signo, signal_handler_t handler)
{
struct sigaction sigaction_info;
memset(&sigaction_info, 0, sizeof(sigaction_info));
sigset_t sigset_info;
sigemptyset(&sigset_info);
sigaction_info.sa_handler = NULL;
sigaction_info.sa_sigaction = handler;
sigaction_info.sa_mask = sigset_info;
sigaction_info.sa_flags = SA_SIGINFO;
}
void CreateAndStartTimer(unsigned int wait_seconds)
{
if (wait_seconds == 0) return;
timer_t timerid;
struct sigevent sev;
struct itimerspec its;
sigset_t mask;
set_signal_handler(SIG, signal_handler);
// Block timer signal temporarily
sigemptyset(&mask);
sigaddset(&mask, SIG);
if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
errExit("sigprocmask");
}
// Create the timer
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIG;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
fprintf(stderr, "Failed to create timer, will continue running.\n");
return;
}
// Start the timer
its.it_value.tv_sec = wait_seconds;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 1;
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
fprintf(stderr, "Failed to set timer, will continue running.\n");
}
// Unblock timer signal
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) {
errExit("sigprocmask");
}
}
int main(int argc, char *argv[])
{
unsigned int wait_seconds = 0;
if (argc != 2) {
fprintf(stderr, "Usage: %s <time-out-seconds> \n", argv[0]);
exit(EXIT_FAILURE);
}
wait_seconds = atoi(argv[1]);
if (wait_seconds > 0) CreateAndStartTimer(wait_seconds);
while (1) {
sleep(10);
}
exit(EXIT_SUCCESS);
}
方法三
最后,还有一种土办法,可以不用timer. 那就是,再开一个线程,在其中写一个 while (true) 的循环,循环体中 sleep(1). 每次醒来的时候,检查一下时间过去了多久。如果时间已经超过了设定的时限,那么就获得本进程的pid,并且发送 SIGTERM 信号给本进程。
这个代码就不列在这里了,可以参见: ThreadTimeKiller
(完)