将 C 代码从 POSIX 移植到 Windows(siginfo 问题)
Porting a C code to Windows from POSIX (problem with siginfo)
我正在尝试在 Windows 下本地编译程序“MRCC”(为 Linux 开发)。该程序主要是用 Fortran 语言编写的,而据我所知,与系统的接口是用 C 语言编写的。除了一个导致问题的 C 源文件之外,所有源文件都可以使用 mingw64-gnu 编译器成功编译。问题出在 mingw64 中未实现的类型“siginfo_t”。
源文件(signal.c)是:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef INT64
void mrccend_(long*);
void dmrccend_(long*);
long ERROR_SIGTERM = -1;
long ERROR_SIGSEGV = -2;
#else
void mrccend_(int*);
void dmrccend_(int*);
int ERROR_SIGTERM = -1;
int ERROR_SIGSEGV = -2;
#endif
void parent_sigterm_(int, siginfo_t *, void *);
void child_sigterm_(int, siginfo_t *, void *);
void child_sigsegv_(int, siginfo_t *, void *);
void sendSignalToChildren(int);
static struct sigaction old_act;
void signalinit_() {
// initialise dmrcc's responses to signals
struct sigaction act_term;
memset(&act_term, '[=12=]', sizeof(act_term));
act_term.sa_sigaction = &parent_sigterm_;
act_term.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &act_term, NULL);
}
void parent_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise response to signal SIGTERM
pid_t pid_parent;
char pidchar[10];
char command[40];
pid_parent = getpid();
sprintf(pidchar, "%d", pid_parent);
printf("\n Program dmrcc recieved SIGTERM\n"); fflush(stdout);
sendSignalToChildren(SIGTERM);
sleep(5);
sendSignalToChildren(SIGKILL);
printf("\n Program dmrcc terminating\n"); fflush(stdout);
dmrccend_(&ERROR_SIGTERM);
}
void sendSignalToChildren(int sig) {
int ownPid = getpid();
int pid, numPids = 0;
int *pids;
FILE *pidfile = fopen("pids", "r");
if (pidfile == NULL) {
printf("Error: Could not open pids file\n");
return;
}
// number of running processes other than the current process
while (fscanf(pidfile, "%d", &pid) != EOF) {
if (pid != ownPid) {
numPids++;
}
}
rewind(pidfile);
// read other process' PIDs
pids = (int *)malloc(numPids * sizeof(int));
int i = -1;
while (fscanf(pidfile, "%d", &pid) != EOF) {
if (pid != ownPid) {
pids[++i] = pid;
}
}
// send signal sig to processes
printf("\n Sending signal %2d to child processes\n", sig); fflush(stdout);
for (i = 0; i < numPids; i++) {
kill((pid_t)pids[i], sig);
}
fclose(pidfile);
free(pids);
}
void signalinitchild_() {
// initialise child's responses to signals
struct sigaction act_term;
memset (&act_term, '[=12=]', sizeof(act_term));
act_term.sa_sigaction = &child_sigterm_;
act_term.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &act_term, NULL);
struct sigaction act_segv;
memset (&act_segv, '[=12=]', sizeof(act_segv));
act_segv.sa_sigaction = &child_sigsegv_;
act_segv.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act_segv, &old_act);
}
void child_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGTERM
mrccend_(&ERROR_SIGTERM);
}
void child_sigsegv_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGSEGV
mrccend_(&ERROR_SIGSEGV);
if(old_act.sa_flags & SA_SIGINFO)
{
(*old_act.sa_sigaction)(signum, siginfo, context);
}
else
{
(*old_act.sa_handler)(signum);
}
}
#ifdef __cplusplus
}
#endif
Windows 原生支持 SIGTERM 和 SIGSEGV。但是,SIGINFO 不是,因此未定义 siginfo_t 类型。 Ther 编译器还会在 sigaction 处抛出错误。我知道我需要更改代码,以便它使用 Windows API 而不是 POSIX API。但是,我不确定该怎么做。我可以将 siginfo_t 类型替换为 void 类型吗?关于 sigaction 我能做些什么?
[我用的mingw64的signal.h header是粘贴的here]
您可以将 Gnulib 添加到您的项目中。
“Gnulib 修复的可移植性问题:
某些平台上缺少 struct sigaction 和 siginfo_t:mingw、MSVC 14。”
https://www.gnu.org/software/gnulib/manual/gnulib.html#signal_002eh
如果您在使用 gnulib 编译 mingw 项目时遇到问题,这些链接可能会有所帮助
https://courses.p2pu.org/en/groups/cross-linux-from-scratch/content/set-up-your-build-environment/
另一种选择是尝试 cygwin,它带来更多 POSIX 兼容性。
重写它以在 Windows 上使用 signal
。
(在幕后,这就是 Gnulib 为 mingw 平台所做的事情:根据 signal
重新实现 sigaction
,使用已定义但有效的虚拟 siginfo_t
结构来解决过去的编译问题.)
虽然标准做法是 use sigaction
in preference to signal
——事实上,我想说现在使用 signal
几乎是疏忽了——你必须利用你所拥有的,而你没有有一个符合 SA_SIGINFO 的实现。您不必担心重新安装处理程序,因为您打算终止您处理的信号。
因此,当您完成后,它将看起来像这样:
static void (*old_segv_handler)(int) = NULL;
void parent_sigterm_(int s) { ... }
void child_sigterm_(int s) { ... }
void child_segv_(int s) {
mrccend_(...);
old_segv_handler(s); // FIXME: handle SIG_IGN, SIG_DFL, SIG_ERR
}
void signalinit_() {
signal(SIGTERM, parent_sigterm_);
}
void signalinitchild_() {
signal(SIGTERM, child_sigterm_);
old_segv_handler = signal(SIGSEGV, child_segv_);
}
请在您的父 SIGTERM 处理程序中解决上面的 FIXME 和 remove those unsafe stdio calls。
我正在尝试在 Windows 下本地编译程序“MRCC”(为 Linux 开发)。该程序主要是用 Fortran 语言编写的,而据我所知,与系统的接口是用 C 语言编写的。除了一个导致问题的 C 源文件之外,所有源文件都可以使用 mingw64-gnu 编译器成功编译。问题出在 mingw64 中未实现的类型“siginfo_t”。
源文件(signal.c)是:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef INT64
void mrccend_(long*);
void dmrccend_(long*);
long ERROR_SIGTERM = -1;
long ERROR_SIGSEGV = -2;
#else
void mrccend_(int*);
void dmrccend_(int*);
int ERROR_SIGTERM = -1;
int ERROR_SIGSEGV = -2;
#endif
void parent_sigterm_(int, siginfo_t *, void *);
void child_sigterm_(int, siginfo_t *, void *);
void child_sigsegv_(int, siginfo_t *, void *);
void sendSignalToChildren(int);
static struct sigaction old_act;
void signalinit_() {
// initialise dmrcc's responses to signals
struct sigaction act_term;
memset(&act_term, '[=12=]', sizeof(act_term));
act_term.sa_sigaction = &parent_sigterm_;
act_term.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &act_term, NULL);
}
void parent_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise response to signal SIGTERM
pid_t pid_parent;
char pidchar[10];
char command[40];
pid_parent = getpid();
sprintf(pidchar, "%d", pid_parent);
printf("\n Program dmrcc recieved SIGTERM\n"); fflush(stdout);
sendSignalToChildren(SIGTERM);
sleep(5);
sendSignalToChildren(SIGKILL);
printf("\n Program dmrcc terminating\n"); fflush(stdout);
dmrccend_(&ERROR_SIGTERM);
}
void sendSignalToChildren(int sig) {
int ownPid = getpid();
int pid, numPids = 0;
int *pids;
FILE *pidfile = fopen("pids", "r");
if (pidfile == NULL) {
printf("Error: Could not open pids file\n");
return;
}
// number of running processes other than the current process
while (fscanf(pidfile, "%d", &pid) != EOF) {
if (pid != ownPid) {
numPids++;
}
}
rewind(pidfile);
// read other process' PIDs
pids = (int *)malloc(numPids * sizeof(int));
int i = -1;
while (fscanf(pidfile, "%d", &pid) != EOF) {
if (pid != ownPid) {
pids[++i] = pid;
}
}
// send signal sig to processes
printf("\n Sending signal %2d to child processes\n", sig); fflush(stdout);
for (i = 0; i < numPids; i++) {
kill((pid_t)pids[i], sig);
}
fclose(pidfile);
free(pids);
}
void signalinitchild_() {
// initialise child's responses to signals
struct sigaction act_term;
memset (&act_term, '[=12=]', sizeof(act_term));
act_term.sa_sigaction = &child_sigterm_;
act_term.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &act_term, NULL);
struct sigaction act_segv;
memset (&act_segv, '[=12=]', sizeof(act_segv));
act_segv.sa_sigaction = &child_sigsegv_;
act_segv.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act_segv, &old_act);
}
void child_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGTERM
mrccend_(&ERROR_SIGTERM);
}
void child_sigsegv_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGSEGV
mrccend_(&ERROR_SIGSEGV);
if(old_act.sa_flags & SA_SIGINFO)
{
(*old_act.sa_sigaction)(signum, siginfo, context);
}
else
{
(*old_act.sa_handler)(signum);
}
}
#ifdef __cplusplus
}
#endif
Windows 原生支持 SIGTERM 和 SIGSEGV。但是,SIGINFO 不是,因此未定义 siginfo_t 类型。 Ther 编译器还会在 sigaction 处抛出错误。我知道我需要更改代码,以便它使用 Windows API 而不是 POSIX API。但是,我不确定该怎么做。我可以将 siginfo_t 类型替换为 void 类型吗?关于 sigaction 我能做些什么?
[我用的mingw64的signal.h header是粘贴的here]
您可以将 Gnulib 添加到您的项目中。
“Gnulib 修复的可移植性问题: 某些平台上缺少 struct sigaction 和 siginfo_t:mingw、MSVC 14。” https://www.gnu.org/software/gnulib/manual/gnulib.html#signal_002eh
如果您在使用 gnulib 编译 mingw 项目时遇到问题,这些链接可能会有所帮助
https://courses.p2pu.org/en/groups/cross-linux-from-scratch/content/set-up-your-build-environment/
另一种选择是尝试 cygwin,它带来更多 POSIX 兼容性。
重写它以在 Windows 上使用 signal
。
(在幕后,这就是 Gnulib 为 mingw 平台所做的事情:根据 signal
重新实现 sigaction
,使用已定义但有效的虚拟 siginfo_t
结构来解决过去的编译问题.)
虽然标准做法是 use sigaction
in preference to signal
——事实上,我想说现在使用 signal
几乎是疏忽了——你必须利用你所拥有的,而你没有有一个符合 SA_SIGINFO 的实现。您不必担心重新安装处理程序,因为您打算终止您处理的信号。
因此,当您完成后,它将看起来像这样:
static void (*old_segv_handler)(int) = NULL;
void parent_sigterm_(int s) { ... }
void child_sigterm_(int s) { ... }
void child_segv_(int s) {
mrccend_(...);
old_segv_handler(s); // FIXME: handle SIG_IGN, SIG_DFL, SIG_ERR
}
void signalinit_() {
signal(SIGTERM, parent_sigterm_);
}
void signalinitchild_() {
signal(SIGTERM, child_sigterm_);
old_segv_handler = signal(SIGSEGV, child_segv_);
}
请在您的父 SIGTERM 处理程序中解决上面的 FIXME 和 remove those unsafe stdio calls。