#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <string.h> pid_t child1, child2; char* child_message; void parent_handler(int sig) { kill(child1, SIGUSR1); // 向子进程1发送信号 kill(child2, SIGUSR1); // 向子进程2发送信号 } void child_handler(int sig) { write(STDOUT_FILENO, child_message, strlen(child_message)); exit(0); } int main() { // 创建第一个子进程 child1 = fork(); if (child1 == 0) { child_message = "我是分身一号!\n"; signal(SIGUSR1, child_handler); pause(); // 等待信号 exit(0); } else { // 创建第二个子进程 child2 = fork(); if (child2 == 0) { child_message = "我是分身二号!\n"; signal(SIGUSR1, child_handler); pause(); exit(0); } else { // 父进程注册SIGINT处理函数 signal(SIGINT, parent_handler); // 等待子进程终止 waitpid(child1, NULL, 0); waitpid(child2, NULL, 0);
时间: 2025-08-23 19:15:22 浏览: 6
<think>好的,用户想了解如何使用SIGUSR1进行父子进程通信的C语言示例代码解析。首先,我需要回忆信号处理的基本机制。在Unix系统中,信号是进程间通信的一种方式,SIGUSR1和SIGUSR2是预留给用户自定义用途的信号。父子进程之间可以通过发送这些信号来触发特定的处理函数。
接下来,用户可能需要一个具体的代码示例,所以应该先构建一个简单的父子进程模型。父进程创建子进程后,两者需要设置信号处理函数。通常,父进程发送信号给子进程,或者反过来。例如,父进程发送SIGUSR1给子进程,子进程收到后执行某个操作,或者发送回复信号。
然后,代码结构方面,需要使用fork()创建子进程。在父进程和子进程中分别注册信号处理函数,使用signal()或sigaction()。考虑到sigaction更灵活,可以设置SA_RESTART等选项,但为了简单起见,可能先用signal函数。
要注意的是,在发送信号前,子进程可能需要先注册处理函数,否则可能无法正确捕获信号。比如,子进程在fork之后立即设置信号处理,而父进程稍等片刻再发送信号,确保子进程已经准备好。
此外,信号处理函数中需要注意可重入性,避免使用不可重入函数,如printf。但为了示例,可能还是需要使用printf来输出信息,不过在实际应用中可能需要更安全的处理方式。
用户的问题可能还涉及如何阻塞其他信号,或者处理多个信号的情况,但当前需求是基础示例,所以保持简单。示例中可以展示父进程发送信号,子进程接收后打印消息,或者子进程发送信号给父进程。
另外,需要考虑进程间的同步问题。例如,父进程在发送信号前,可能需要等待子进程完成处理函数的注册。可以用sleep()函数,但这并不是可靠的方法。更可靠的方式可能使用某种同步机制,但在简单示例中,sleep可能足够。
最后,代码中需要包含必要的头文件,如stdio.h, unistd.h, signal.h, sys/types.h等。编译和运行步骤也需要说明,帮助用户测试代码。
现在,综合这些考虑,构造一个父进程发送SIGUSR1给子进程,子进程处理并回复的例子。同时解析代码的关键部分,比如fork、signal注册、kill函数的使用,以及处理函数的结构。可能还需要提到僵尸进程的处理,但在这个示例中可能不涉及,因为主要关注通信。
还需要注意错误处理,比如检查fork的返回值,处理可能的错误情况。不过示例代码可能简化这部分,以突出重点。
总结步骤:
1. 父进程创建子进程。
2. 子进程注册SIGUSR1处理函数,并进入等待。
3. 父进程等待子进程准备,发送SIGUSR1。
4. 子进程处理信号,可能发送回复信号。
5. 处理函数中安全地输出信息。
6. 确保进程正确终止,避免僵尸进程。
在解析代码时,需要逐段解释,比如fork的分支,信号处理函数的注册,kill的使用,以及处理函数的行为。同时,结合用户提供的引用,比如引用[3]中提到SIGCHLD的处理,但这里可能不涉及,但需要确保子进程正确回收,如果有必要的话,但本例可能没有子进程终止,只是通信。
可能的问题包括信号丢失,或者处理函数未能正确注册。在示例中应确保正确的顺序,比如子进程先注册处理函数,父进程稍后发送信号。
现在,根据这些思路编写示例代码和解析。</think>以下是一个使用$SIGUSR1$进行父子进程通信的C语言示例代码解析:
```c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
volatile sig_atomic_t flag = 0; // 原子类型保证信号处理中的安全访问
// 子进程信号处理函数
void child_handler(int sig) {
if (sig == SIGUSR1) {
printf("[子进程%d] 收到SIGUSR1信号\n", getpid());
flag = 1; // 设置信号接收标志
}
}
// 父进程信号处理函数
void parent_handler(int sig) {
if (sig == SIGUSR1) {
printf("[父进程%d] 收到SIGUSR1信号\n", getpid());
}
}
int main() {
pid_t pid = fork();
if (pid == -1) {
perror("fork失败");
return 1;
} else if (pid == 0) { // 子进程
signal(SIGUSR1, child_handler);
printf("子进程%d已启动\n", getpid());
while(!flag) { // 等待信号到来
pause(); // 挂起进程直到信号到达
}
printf("子进程%d正在退出\n", getpid());
} else { // 父进程
signal(SIGUSR1, parent_handler);
printf("父进程%d已创建子进程%d\n", getpid(), pid);
sleep(1); // 确保子进程完成信号处理注册
kill(pid, SIGUSR1); // 向子进程发送信号
sleep(1); // 等待信号处理完成
printf("父进程%d正在退出\n", getpid());
}
return 0;
}
```
### 代码解析
1. **进程创建**
- 通过$fork()$系统调用创建子进程
- 父子进程获得不同的$pid$值实现分支判断
2. **信号处理注册**
- 子进程注册$child_handler$处理$SIGUSR1$
```c
signal(SIGUSR1, child_handler);
```
- 父进程注册$parent_handler$处理$SIGUSR1$
3. **信号发送机制**
- 父进程使用$kill(pid, SIGUSR1)$发送信号
- 使用$sleep(1)$保证子进程完成初始化
4. **原子标志位**
```c
volatile sig_atomic_t flag = 0;
```
保证信号处理函数与主程序之间的可见性
5. **信号处理函数设计**
- 使用标准格式:$void handler(int sig)$
- 避免在信号处理函数中调用非异步安全函数[^3]
### 执行流程
$$父进程 \xrightarrow{fork()} 子进程$$
$$父进程 \xrightarrow{kill()} 发送SIGUSR1$$
$$子进程 \xrightarrow{child_handler} 设置flag退出循环$$
阅读全文
相关推荐



















