我正在尝试编写一个信号处理程序,该处理程序需要知道发送信号的进程的pid。我无法从siginfo_t
中获得任何有用的东西,这些东西都通过Xcode 10传递给了macOS 10.14上的处理程序。
我已将代码简化为以下最小示例,以演示该问题。在此示例中,我生成了一个子进程来发送要测试的信号,该信号默认为SIGTERM
,但是我尝试过的其他信号都无法正常工作。
假设您要在Mac上进行构建和测试,您可能想告诉lldb在收到信号时不要停止。您可以使用以下lldb命令:pro hand -p true -s false SIGTERM
。
我也正在使用C ++进行编译,但是我相信我已经将所有内容都删除了,示例代码现在应该是纯C了。
请注意,信号是否来自子进程,终端或其他进程都无关紧要,结果始终是si_pid
始终为0(以及{{1}以外的所有内容) }和si_signo
)。我发送信号的次数无关紧要,所以这似乎不只是一种竞争条件。
如何获取在macOS 10.14上发送信号的进程的pid?我不记得以前在10.12上遇到过此问题。
这只是说明问题的一个示例,因此请忽略实际上没有引起问题的任何内容。
如果代码看起来像我期望的那样工作,那么我将有兴趣查看关于它也可以工作的系统的评论。
si_addr
答案 0 :(得分:4)
我当前正在使用macOS Mojave 10.14.1。
如何获取在macOS上发送信号的进程的pid 10.14?我不记得以前在10.12上遇到过这个问题。
以下代码简单地满足了您的愿望。如果您发送SIGTERM
,则可以看到发件人进程的pid。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
static void hdl (int sig, siginfo_t *siginfo, void *context)
{
printf ("Sending PID: %ld, UID: %ld\n",
(long)siginfo->si_pid, (long)siginfo->si_uid);
}
int main (int argc, char *argv[])
{
struct sigaction act;
fprintf(stderr, "%i pp %i\n",getpid(), getppid());
memset (&act, '\0', sizeof(act));
/* Use the sa_sigaction field because the handles has two additional parameters */
act.sa_sigaction = &hdl;
/* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGTERM, &act, NULL) < 0) {
perror ("sigaction");
return 1;
}
while (1)
sleep (10);
return 0;
}
对于您的代码,
经验法则:即使您确定子进程在父进程之前结束,也不要忘记执行埋葬程序。通过调用
wait(...)
,您告诉操作系统我已经为孩子完成了工作,因此现在您可以清除分配的字段等。
我希望在派生之前初始化信号实用程序,如果父进程没有机会注册信号动作该怎么办?而且,我不明白您为什么要在0
中处理1
和switch
的情况。本质上来说,这种情况没有发生,所以总是省略。
此外,您没有在break
内的if
条件下使用main()
。过一会儿它不会进入if
中,但是以下情况是无法预料的,那就是该程序永远停留在while()
循环中。我更愿意将signaled
置于while()
循环的条件下。
最后但并非最不重要的是,由于在子进程中进行了sleep()
的调用,直到signaled
被证明0
为止,SIGTERM
才被成功捕获了几次。当发出0
信号时,循环停止。
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <memory.h>
#include <sys/wait.h>
volatile sig_atomic_t histogram[3] = {0,0,0};
volatile sig_atomic_t signaled = 0;
const int testsig = SIGTERM;
void sigaction_handler(int sig, siginfo_t* info, void* context)
{
switch (info->si_pid) {
case 0:
case 1:
histogram[info->si_pid]++;
break;
default:
fprintf(stderr, "sender pid -> %i\n", info->si_pid);
histogram[2]++;
break;
}
signaled = 1;
}
int main(int argc, const char * argv[]) {
struct sigaction sigAction;
memset( &sigAction, 0, sizeof( sigAction ) );
sigAction.sa_sigaction = sigaction_handler;
sigemptyset (&sigAction.sa_mask);
sigAction.sa_flags = SA_SIGINFO;
sigaction(testsig, &sigAction, NULL);
pid_t mainpid = getpid();
pid_t pid = fork();
if (pid == 0) {
fprintf(stderr, "my pid -> %i parent's pid-> %i\n", getpid(), getppid());
if (kill(mainpid, 0) == 0) { // signals are not queued not need loop
sleep(1);
kill(mainpid, testsig);
}
_exit(0);
} else {
wait(NULL); // play with this line to see what the difference is
while ( signaled ) {
printf("pid 0: %d, pid 1: %d, others: %d\n", histogram[0], histogram[1], histogram[2]);
signaled = 0;
sleep(1);
}
// wait(NULL); // play with this line to see what the difference is
}
}
答案 1 :(得分:2)
事实证明,通过 Xcode LLDB进行调试是罪魁祸首。如果我正常构建并运行该程序,则可以正常运行。如果我知道为什么我将更新此答案。
我已经在问题中指出了在lldb中为SIGTERM设置了“ PASS”,所以似乎Xcode 10.0附带的lldb版本中存在一个错误,并且通过创建一个“传递”信号新的结构并设置信号号,而不是通常会收到的结构。正如我之前所说,在macOS 10.12随附的任何lldb版本中,它的确可以正常工作
如果有人有更好的解释,请发布答案,我将接受并奖励赏金。