下面是我的 main.go
package main
/*
#include <stdio.h>;
#include <signal.h>;
#include <string.h>;
#include <stdlib.h>;
#include <unistd.h>;
// 用于存储旧操作的全局变量
struct sigaction old_action;
// 信号处理函数
void handler(int signum, siginfo_t *info, void *context) {
printf("Received signal: %d\n", signum);
printf("Sent by PID: %d\n", info->si_pid);
printf("Signal code: %d\n", info->si_code);
printf("User ID: %d\n", info->si_uid);
if (info->si_pid != 0) {
char proc_status_path[256];
snprintf(proc_status_path, sizeof(proc_status_path), "/proc/%d/status", info->si_pid);
FILE *fp = fopen(proc_status_path, "r");
if (fp == NULL) {
perror("fopen");
返回;
}
char buffer[256];
while (fgets(buffer, sizeof(buffer), fp)){
printf("%s", buffer);
}
fclose(fp);
}
}
// 设置信号处理的函数
void setup_signal_handler() {
struct sigaction action;
// 初始化动作结构
memset(&action, 0, sizeof(action));
sigfillset(&action.sa_mask); // 在处理程序执行期间阻断所有信号
action.sa_sigaction = handler; // 设置信号处理函数
action.sa_flags = SA_SIGINFO | SA_ONSTACK; // 设置标志
// 设置 SIGTERM 信号处理函数
if (sigaction(SIGTERM, &action, &old_action) < 0) {
perror("sigaction");
exit(EXIT_FAILURE);
}
}
*/
import "C";
import "fmt";
func main() {
C.setup_signal_handler()
为 {
fmt.Println("Sleeping..., waiting for signal")
C.sleep(10)
}
}
我正试图使用 C 语言来帮助我处理信号并打印出信号发送者的 pid。最初的 Go 软件包(syscall、os......等)并不提供此类信息。然而,当我运行上面的代码时,有两个进程启动了。一个是
go run .
,另一个是
signal
,也就是我的 go 软件包的名称。当我向
go run .
发送信号时,什么也不会发生,而向
signal
则会弹出信息。
我的理解是 implicity C 包会分叉另一个进程来运行这些 C 代码。我无法在我的
go
进程中应用 C 语言中的信号处理器。
我的目标是打印出发送到我的
go
进程的任何类型信号的信息。相关信息至少应包括
pid
,
ppid
.也许还有其他更好的解决方案。
有关于 cgo 的文档吗?我找不到描述 cgo 软件包流程控制机制的文档。
[更新]
感谢 @kostix 和 @eik 的回答!我尝试了
go build
并在稍后运行二进制文件。看起来 C 信号处理器阻止了所有信号发送到 go 处理器。是否有可能同时允许 C 和 go 处理程序接收信号?
我尝试在 C 语言中再次启动,但 C 语言和 go 语言共享同一个 pid。仍在尝试下一个解决方案....
理解 cgo 与信号处理
你的理解是正确的,使用 cgo 调用 C 代码时,C 代码会运行在一个独立的线程中。这意味着 C 代码中的信号处理器会绑定到 C 线程,而不是 Go 主线程。这就是为什么你向
go run .
发送信号没有反应,而向
signal
发送信号却有反应的原因。
解决方案:使用管道在 C 和 Go 之间传递信息
由于 C 信号处理器无法直接与 Go 主线程交互,一种解决方案是使用管道在它们之间传递信息。
步骤:
- 在 Go 代码中创建一个管道:
reader, writer, err := os.Pipe()
if err != nil {
// 处理错误
}
- 将管道写入端的文件描述符传递给 C 代码:
// ... 其他 C 代码
void setup_signal_handler(int pipeFd) {
// ...
}
func main() {
// ...
C.setup_signal_handler(C.int(writer.Fd()))
// ...
}
- 在 C 信号处理器中,将信号信息写入管道:
void handler(int signum, siginfo_t *info, void *context) {
// ... 获取信号信息 ...
// 将信息写入管道
char message[256];
snprintf(message, sizeof(message), "Signal: %d, PID: %d\n", signum, info->si_pid);
write(pipeFd, message, strlen(message));
}
- 在 Go 代码中,从管道读取信息:
go func() {
defer reader.Close()
buf := make([]byte, 256)
for {
n, err := reader.Read(buf)
if err != nil {
// 处理错误
}
fmt.Printf("Received message: %s", string(buf[:n]))
}
}()
注意事项:
- 在 C 代码中,需要小心处理管道写入错误,确保不会阻塞信号处理。
- 使用管道可能会引入额外的开销,需要根据实际情况进行权衡。
关于 cgo 文档
cgo 并没有独立的软件包,它更像是 Go 语言的一个特性。你可以在 Go 官方文档中找到关于 cgo 的详细介绍:
其他解决方案
- 使用 Go 语言实现信号处理: 如果你只需要获取基本的信号信息,可以考虑完全使用 Go 语言实现信号处理。
-
使用第三方库:
一些第三方库提供了更高级的信号处理功能,例如
github.com/sevoo/go-signal
。
希望这些信息能够帮助你解决问题!
标签:go,signals,cgo From: 78569393