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); // 跳转回 setjmp 位置
}

int main() {
signal(SIGALRM, timeout_handler);
alarm(3); // 设置 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; // 设置 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); // 等待 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}, // 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 和事件循环。
  • 可控循环任务 → 手动检查时间戳。

重要提示:被中断的函数可能遗留资源锁、内存泄漏等问题,设计时需确保安全终止逻辑。