Linux中限定函数执行时间的接口
在 Linux 中,有多种方法可以限定函数的执行时间。以下是几种常用接口和技术:
1. 信号机制(SIGALRM)
使用 alarm()
或 setitimer()
设置定时器,超时后发送 SIGALRM
信号中断执行。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <unistd.h> #include <signal.h> #include <setjmp.h> #include <stdio.h>
static jmp_buf env;
void timeout_handler(int sig) { longjmp(env, 1); }
int main() { signal(SIGALRM, timeout_handler); alarm(3);
if (setjmp(env) == 0) { sleep(10); printf("Function completed\n"); } else { printf("Function timed out\n"); }
alarm(0); return 0; }
|
缺点:信号处理函数中只能使用异步安全函数,且 longjmp
可能导致资源泄漏。
2. 多线程(pthread)
在子线程中执行函数,主线程通过 pthread_timedjoin_np()
等待超时。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <pthread.h> #include <time.h> #include <stdio.h>
void* task(void* arg) { sleep(10); return NULL; }
int main() { pthread_t thread; pthread_create(&thread, NULL, task, NULL);
struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 3;
int rc = pthread_timedjoin_np(thread, NULL, &ts); if (rc == ETIMEDOUT) { printf("Function timed out\n"); pthread_cancel(thread); } else { printf("Function completed\n"); }
return 0; }
|
注意:
- 需要链接
-pthread
编译选项。
pthread_timedjoin_np()
是 GNU 扩展,非 POSIX 标准。
3. 进程超时(fork + waitpid)
在子进程中执行函数,父进程监控超时。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <sys/wait.h> #include <unistd.h> #include <signal.h> #include <stdio.h>
int main() { pid_t pid = fork(); if (pid == 0) { sleep(10); _exit(0); } else { sleep(3); int status; if (waitpid(pid, &status, WNOHANG) == 0) { printf("Function timed out\n"); kill(pid, SIGKILL); waitpid(pid, &status, 0); } else { printf("Function completed\n"); } } return 0; }
|
优点:隔离性好,资源清理简单。
缺点:进程创建开销较大。
4. 定时器文件描述符(timerfd)
结合 epoll
/select
实现高精度超时控制(适合事件循环)。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <sys/timerfd.h> #include <sys/epoll.h> #include <unistd.h> #include <stdio.h>
int main() { int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); struct itimerspec its = { .it_value = {.tv_sec = 3}, .it_interval = {0} }; timerfd_settime(timer_fd, 0, &its, NULL);
int epoll_fd = epoll_create1(0); struct epoll_event ev = {.events = EPOLLIN}; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev);
struct epoll_event events[1]; int n = epoll_wait(epoll_fd, events, 1, -1); if (n > 0 && events[0].data.fd == timer_fd) { printf("Function timed out\n"); }
close(timer_fd); close(epoll_fd); return 0; }
|
适用场景:异步 I/O 框架(如 libevent、libuv)。
5. 用户态超时(循环检查时间戳)
适用于可拆分任务,在循环中检查已用时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <time.h> #include <stdio.h>
void long_task() { time_t start = time(NULL); while (1) { if (time(NULL) - start > 3) { printf("Task aborted\n"); return; } } }
|
选择建议:
- 简单场景 → 使用
SIGALRM
+ setjmp
(注意信号安全)。
- 多线程环境 → 优先用
pthread_timedjoin_np()
。
- 资源隔离需求 → 使用
fork()
+ 超时监控。
- 高性能 I/O → 结合
timerfd
和事件循环。
- 可控循环任务 → 手动检查时间戳。
重要提示:被中断的函数可能遗留资源锁、内存泄漏等问题,设计时需确保安全终止逻辑。