1. 函数介绍
pause
通常用于那些需要无限期等待某个外部事件(通过信号来通知)的程序中。它提供了一种简单、高效(不占用 CPU)的等待机制。
你可以把它想象成一个人在等待电话。他什么也不做,只是静静地坐着(睡眠),直到电话铃响(收到信号),他才会起身去接电话(pause
返回)。
2. 函数原型
#include <unistd.h> // 必需
int pause(void);
3. 功能
- 进入睡眠: 调用
pause
的进程会立即放弃 CPU,并被放入内核的等待队列中。 - 等待信号: 进程进入睡眠状态,直到有任何信号递达(delivered)到该进程。
- 被信号中断: 当信号被递达时(并且该信号没有被忽略或导致进程终止),进程会从
pause
调用中返回。
4. 参数
void
:pause
函数不接受任何参数。
5. 返回值
- 总是返回 -1:
pause
调用永远不会成功返回一个非负值。 - 总是设置
errno
: 当pause
因接收到信号而返回时,它会将errno
设置为EINTR
(Interrupted system call)。
重要: pause
的返回唯一原因就是被信号中断。因此,检查返回值和 errno
通常是确认 pause
是因信号返回的标准做法。
6. 相似函数,或关联函数
sleep
,nanosleep
: 这些函数使进程睡眠指定的时间。pause
是无限期睡眠,直到信号。sigsuspend
: 这是一个更高级、更安全的用于等待信号的函数。它允许在等待信号的原子性操作中临时替换进程的信号掩码(blocked signals set)。这可以避免在设置信号掩码和调用pause
之间收到信号的竞态条件(race condition)。- 信号处理函数 (
signal
,sigaction
): 用于设置当进程收到特定信号时应执行的操作。 sigprocmask
: 用于检查或修改进程的信号掩码(哪些信号被阻塞)。wait
,waitpid
: 使进程等待子进程状态改变(结束、停止等),这也是一种阻塞等待。
7. 示例代码
示例 1:基本的 pause
使用和信号处理
这个例子演示了如何使用 pause
使进程等待信号,并通过信号处理函数来响应信号。
#include <unistd.h> // pause
#include <stdio.h> // printf, perror
#include <stdlib.h> // exit
#include <signal.h> // signal, SIGINT, SIGTERM
#include <errno.h> // errno
#include <string.h> // memset
// 全局标志,用于在信号处理函数和主循环间通信
volatile sig_atomic_t signal_received = 0;
volatile int last_signal = 0;
// 信号处理函数
void signal_handler(int sig) {
printf("\nSignal handler called for signal %d\n", sig);
signal_received = 1;
last_signal = sig;
// 注意:在信号处理函数中,应只调用异步信号安全的函数
// printf 通常被认为是安全的,但严格来说不是 100% 可靠
// 更安全的做法是只设置标志位,然后在主循环中检查
}
int main() {
printf("Process PID: %d\n", getpid());
printf("Try sending signals using 'kill %d' or pressing Ctrl+C\n", getpid());
printf("Send SIGTERM (kill %d) or SIGINT (Ctrl+C) to exit.\n", getpid());
// 1. 设置信号处理函数
if (signal(SIGINT, signal_handler) == SIG_ERR) {
perror("signal SIGINT");
exit(EXIT_FAILURE);
}
if (signal(SIGTERM, signal_handler) == SIG_ERR) {
perror("signal SIGTERM");
exit(EXIT_FAILURE);
}
// 忽略 SIGUSR1,但它仍然会中断 pause
if (signal(SIGUSR1, SIG_IGN) == SIG_ERR) {
perror("signal SIGUSR1");
exit(EXIT_FAILURE);
}
printf("Entering main loop with pause()...\n");
// 2. 主循环
while (1) {
// 3. 调用 pause 进入睡眠
printf("Going to sleep... (waiting for a signal)\n");
int result = pause(); // 进程在此处挂起
// 4. pause 返回(唯一原因是被信号中断)
if (result == -1 && errno == EINTR) {
printf("pause() was interrupted by a signal (errno=EINTR).\n");
// 5. 检查是哪个信号
if (signal_received) {
printf("Handled signal %d in signal handler.\n", last_signal);
if (last_signal == SIGINT || last_signal == SIGTERM) {
printf("Received exit signal. Cleaning up and exiting.\n");
break; // 退出主循环
}
// 为下一次循环重置标志
signal_received = 0;
}
} else {
// 这理论上不应该发生,因为 pause 总是返回 -1 和 EINTR
printf("Unexpected return from pause(): result=%d, errno=%d (%s)\n",
result, errno, strerror(errno));
}
}
printf("Main loop exited. Performing cleanup...\n");
// 这里可以执行一些清理工作
printf("Program exiting normally.\n");
return 0;
}
代码解释:
编译和运行:
gcc -o pause_example pause_example.c
./pause_example
# 在另一个终端:
# kill -USR1 <PID> # 发送 SIGUSR1 (会被忽略,但会中断 pause)
# kill <PID> # 发送 SIGTERM (默认信号,会退出)
# kill -INT <PID> # 发送 SIGINT (等同于 Ctrl+C)
示例 2:使用 pause
等待子进程结束 (不推荐,仅作演示)
虽然 wait
/waitpid
是等待子进程结束的标准方法,但这个例子演示了如何(不推荐地)使用 pause
和 SIGCHLD
信号来实现类似功能。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
volatile sig_atomic_t child_done = 0;
void sigchld_handler(int sig) {
// 在信号处理函数中,通常只应设置标志位
// 实际的 wait 操作应在主循环中进行,以避免特定的竞争条件
// 这里简化处理
printf("SIGCHLD received.\n");
child_done = 1;
}
int main() {
pid_t pid;
// 1. 设置 SIGCHLD 信号处理函数
// SIGCHLD 在子进程状态改变时发送给父进程
if (signal(SIGCHLD, sigchld_handler) == SIG_ERR) {
perror("signal SIGCHLD");
exit(EXIT_FAILURE);
}
// 2. 创建子进程
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// --- 子进程 ---
printf("Child process (PID %d) started.\n", getpid());
sleep(5); // 模拟工作
printf("Child process (PID %d) finished.\n", getpid());
_exit(EXIT_SUCCESS);
} else {
// --- 父进程 ---
printf("Parent process (PID %d) created child (PID %d).\n", getpid(), pid);
// 3. 等待子进程结束
printf("Parent entering loop with pause() to wait for child...\n");
while (!child_done) {
printf("Parent is waiting (paused)...\n");
pause(); // 等待信号 (期望是 SIGCHLD)
printf("Parent woke up from pause().\n");
if (child_done) {
printf("Parent detected child is done via signal flag.\n");
// 清理僵尸进程
int status;
pid_t waited_pid = wait(&status);
if (waited_pid == -1) {
perror("wait");
} else {
printf("Parent reaped child PID %d with status %d.\n", waited_pid, status);
}
}
}
printf("Parent process finished.\n");
}
return 0;
}
代码解释:
- 定义了一个全局标志
child_done
。 - 定义了
SIGCHLD
信号的处理函数sigchld_handler
,当子进程结束时,内核会向父进程发送SIGCHLD
信号,该处理函数会设置child_done
标志。 - 在
main
函数中,为SIGCHLD
注册处理函数。 - 使用
fork
创建子进程。 - 子进程: 睡眠 5 秒后退出。
- 父进程:
- 进入一个循环,循环条件是
child_done
为假。 - 在循环中调用
pause()
,使父进程睡眠。 - 当子进程结束,内核发送
SIGCHLD
信号,sigchld_handler
被调用,设置child_done
为真。 pause()
返回,循环检查child_done
,发现为真,于是调用wait()
清理子进程(收割僵尸进程)并退出循环。
- 进入一个循环,循环条件是
重要提示与注意事项:
总结: