多次被杀死的进程没有收到信号 SIGUSR1

process killed more than one time does't receive signal SIGUSR1

我有 2 个 .c 脚本。一个是 father.c:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

pid_t childPid;
void createChild(){
    fprintf(stderr,"creating a new child\n");
    childPid=fork();
    if(childPid==0){
        execlp("./child","child",NULL);
        perror("err");
    }
    fprintf(stderr,"created child pid is %d\n",childPid);
}
void signalHandler(int signal){
    if(signal==SIGUSR1){
        kill(childPid,SIGINT);
        createChild();
    }
}

int main(int argc,char ** argv){
    signal(SIGUSR1,signalHandler);
    signal(SIGCHLD,SIG_IGN);    
    createChild();/*create first child*/    
    while(1){
        sleep(2);
        int ret=kill(childPid,SIGUSR1);
        if(ret==-1){
            perror("exit err ");
        }
    }
}


另一个是child.c:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void signalHandler(int signal){
    if(signal==SIGUSR1){
        fprintf(stderr,"child process received signal\n");
        kill(getppid(),SIGUSR1);
    }
}

int main(int argc,char ** argv){
    fprintf(stderr,"i'm the new child with pid %d\n",getpid());
    if(signal(SIGUSR1,signalHandler)==SIG_ERR){
        fprintf(stderr,"error");
    }
    while(1){}
}

当 father 启动时,每 2 秒它应该终止 child,分叉自身并启动一个新的 child。 child 通过向他发送 SIGUSR1 信号被杀死,该信号被处理并转发给父亲(使用另一个 SIGUSR1 信号),父亲用 SIGINT 杀死 child。 我面临的问题是第二次创建 child 时,它不再接收 SIGUSR1 信号。 有人可以帮忙吗?

编辑:感谢@CraigEstey(请参阅下面的回答),他发现从 child 向 parent 进程发送不同的信号确实可以完成工作。 按照这个建议,我上面发布的代码应该像这样更改(为了让它工作):在 father.c 中将 if(signal==SIGUSR1) 替换为 if(signal==SIGUSR2) 并将 signal(SIGUSR1,signalHandler); 替换为 signal(SIGUSR2,signalHandler);。 在 child.c 中将 kill(getppid(),SIGUSR1); 替换为 kill(getppid(),SIGUSR2);。 如果这不是您想要的,我建议您阅读@CraigEstey 的回答,他在其中详细解释了所有内容,并为 2 个进程提供了使用相同信号编号的工作代码。

几件事...

当 child 向 parent 发出信号时,parent 在 sleep 调用中。这会提前终止(例如 EINTR)。

所以,在第二回合,parent会在之前杀死child[可能] child 已准备好接收信号。

我做了一些主要的黑客攻击来添加调试,waitpid,等等

主要问题是 [要么] 第二个 child 没有从 parent 得到 SIGUSR1。或者,child 没有向 parent 发送信号或 parent 被阻塞。从下面的第二个日志文件来看,似乎第二个 child 从未从 parent.

中获取 SIGUSR1

我添加了额外的 signal 调用来重新装备处理程序 [可能 不需要 。我添加了 waitpid 个电话

在 parent 和 child 使用 不同的 信号之前,我无法正常工作。即parent发送SIGUSR1到child,child发送SIGUSR2到parent。

这可能不是你想要的,我[还]不明白为什么信号数量差异很重要。

查看 /proc/pid/status 中的信号掩码可能有助于辨别可能发生的情况。

编辑: 让代码完全正常工作。有关详细信息,请参阅下面的更新部分。


这是“错误”日志。 parent 将在 child 可以进行设置之前发送第二个信号:

SIGUSR1 is 10
SIGUSR2 is 12
SIGTERM is 15
SIGINT is 2
SIGDN is 10
SIGUP is 10
SIGFIX is 0
KILLERR is 0
QSLEEP is 0

creating a new child
created child pid is 738524
i'm the new child with pid 738524
while forever
killing child with SIGDN 738524
child got signal 10
child killing parent 738523
parent received signal 10
killing child with SIGINT 738524
waitpid on 738524

creating a new child
created child pid is 738525
killing child with SIGDN 738525
i'm the new child with pid 738525
while forever
killing child with SIGDN 738525
killing child with SIGDN 738525
killing child with SIGDN 738525
killing child with SIGDN 738525

请注意,如果我们将 KILLERR 选项添加到构建中,SIGUSR1 从 parent 到 child 的 kill 调用将产生一个 ESRCH错误(没有这样的过程)。


如果我们有 parent 的 sleep 调用循环并等待 2 秒(例如,它计算剩余的睡眠时间),我们会得到 不同的序列。 parent只会在child有时间设置后发送SIGUSR1:

SIGUSR1 is 10
SIGUSR2 is 12
SIGTERM is 15
SIGINT is 2
SIGDN is 10
SIGUP is 10
SIGFIX is 0
KILLERR is 0
QSLEEP is 1

creating a new child
created child pid is 739105
i'm the new child with pid 739105
while forever
killing child with SIGDN 739105
child got signal 10
child killing parent 739104
parent received signal 10
killing child with SIGINT 739105
waitpid on 739105

creating a new child
created child pid is 739106
i'm the new child with pid 739106
while forever
killing child with SIGDN 739106
killing child with SIGDN 739106
killing child with SIGDN 739106
killing child with SIGDN 739106

这是工作日志:

SIGUSR1 is 10
SIGUSR2 is 12
SIGTERM is 15
SIGINT is 2
SIGDN is 10
SIGUP is 12
SIGFIX is 1
KILLERR is 0
QSLEEP is 1

creating a new child
created child pid is 740214
i'm the new child with pid 740214
while forever
killing child with SIGDN 740214
child got signal 10
child killing parent 740213
parent received signal 12
killing child with SIGINT 740214
waitpid on 740214

creating a new child
created child pid is 740215
i'm the new child with pid 740215
while forever
killing child with SIGDN 740215
child got signal 10
child killing parent 740213
parent received signal 12
killing child with SIGINT 740215
waitpid on 740215

creating a new child
created child pid is 740216
i'm the new child with pid 740216
while forever
killing child with SIGDN 740216
child got signal 10
child killing parent 740213
parent received signal 12
killing child with SIGINT 740216
waitpid on 740216

creating a new child
created child pid is 740218
i'm the new child with pid 740218
while forever
killing child with SIGDN 740218
child got signal 10
child killing parent 740213
parent received signal 12
killing child with SIGINT 740218
waitpid on 740218

creating a new child
created child pid is 740219
i'm the new child with pid 740219
while forever

更新:

最初,我听从了您使用 sigaction 的评论,所以我没有尝试。

但是,我添加了 sigaction 作为一个选项(以及 sigprocmask 调用以解锁信号——这可能更重要)。

有效 即使信号编号相同。

我已经更新了下面的代码,但是我已经从之前的 post.

中保留了 [上面] 的日志文件

这是源代码。我添加了 common.cMakefile:

==> child.c <==
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

#include "common.c"

pid_t ppid;

void
signalHandler(int signo)
{
    xsignal2(SIGDN, signalHandler);

    msg2("child got signal",signo);

    if (signo == SIGDN) {
        msg2("child killing parent",ppid);
        qkill(ppid, SIGUP);
    }
}

int
main(int argc, char **argv)
{

    ppid = getppid();

    xsignal(SIGDN, signalHandler);
    msg2("i'm the new child with pid",getpid());

    msg("while forever\n");
    while (1) {
    }
}

==> common.c <==
#include <time.h>
#include <stdlib.h>
#include <errno.h>

typedef long long tsc_t;
#define NSEC        1000000000

#ifndef SIGFIX
#define SIGFIX      0
#endif

#ifndef SIGACT
#define SIGACT      0
#endif

#ifndef KILLERR
#define KILLERR     0
#endif

#if SIGFIX
#define SIGDN       SIGUSR1
#define SIGUP       SIGUSR2
#else
#define SIGDN       SIGUSR1
#define SIGUP       SIGUSR1
#endif

#ifndef QSLEEP
#define QSLEEP      0
#endif

void
xsignal(int signo,void (*fnc)(int))
{

#if SIGACT
    struct sigaction act;
    sigset_t set;

    memset(&act,0,sizeof(act));
    act.sa_handler = (void *) fnc;
    sigaction(signo,&act,NULL);

    sigemptyset(&set);
    sigaddset(&set,signo);
    sigprocmask(SIG_UNBLOCK,&set,NULL);
#else
    signal(signo,fnc);
#endif
}

void
xsignal2(int signo,void (*fnc)(int))
{

#if ! SIGACT
    xsignal(signo,fnc);
#endif
}

tsc_t
tscget(void)
{
    struct timespec ts;
    tsc_t tsc;

    clock_gettime(CLOCK_MONOTONIC,&ts);

    tsc = ts.tv_sec;
    tsc *= NSEC;
    tsc += ts.tv_nsec;

    return tsc;
}

void
msg(const char *str)
{
    size_t len = strlen(str);
    write(2,str,len);
}

void
num(tsc_t val)
{
    char *rhs;
    char *lhs;
    char buf[100];

    lhs = buf;
    rhs = buf;

    while (val > 0) {
        *rhs++ = (val % 10) + '0';
        val /= 10;
    }

    if (rhs <= buf)
        *rhs++ = '0';

    *rhs-- = 0;

    for (;  lhs < rhs;  ++lhs, --rhs) {
        int tmp = *lhs;
        *lhs = *rhs;
        *rhs = tmp;
    }

    msg(buf);
}

void
msg2(const char *str,tsc_t val)
{

    msg(str);
    msg(" ");
    num(val);
    msg("\n");
}

void
qkill(pid_t pid,int signo)
{
    int err;

    err = kill(pid,signo);
    if (err < 0) {
        err = errno;
#if KILLERR
        msg2("qkill: failed -- err is",err);
        exit(1);
#endif
    }
}

void
qsleep(int sec)
{
    tsc_t nsec;
    tsc_t beg;
    tsc_t now;
    struct timespec ts;

    nsec = sec;
    nsec *= NSEC;

    while (nsec > 0) {
        beg = tscget();

        ts.tv_nsec = nsec % NSEC;
        ts.tv_sec = nsec / NSEC;
        nanosleep(&ts,NULL);

        now = tscget();
        now -= beg;

        nsec -= now;
    }
}

==> parent.c <==
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <syscall.h>

#include "common.c"

pid_t childPid;

void
createChild()
{

    msg("\n");
    msg("creating a new child\n");

#if 0
    childPid = fork();
#else
    childPid = syscall(SYS_fork);
#endif
    if (childPid == 0) {
        execlp("./child", "child", NULL);
        perror("err");
        exit(1);
    }

    msg("created child pid is ");
    num(childPid);
    msg("\n");
}

void
signalHandler(int signo)
{

    msg2("parent received signal",signo);

    if (signo == SIGUP) {
        xsignal2(SIGUP, signalHandler);

        msg2("killing child with SIGINT",childPid);
#if 0
        qkill(childPid, SIGTERM);
#else
        qkill(childPid, SIGKILL);
#endif

        msg2("waitpid on",childPid);
        waitpid(childPid,NULL,0);

        createChild();
    }
}

int
main(int argc, char **argv)
{

    msg2("SIGUSR1 is",SIGUSR1);
    msg2("SIGUSR2 is",SIGUSR2);
    msg2("SIGTERM is",SIGTERM);
    msg2("SIGINT is",SIGINT);
    msg2("SIGDN is",SIGDN);
    msg2("SIGUP is",SIGUP);

    msg2("SIGFIX is",SIGFIX);
    msg2("SIGACT is",SIGACT);
    msg2("KILLERR is",KILLERR);
    msg2("QSLEEP is",QSLEEP);

    xsignal(SIGUP, signalHandler);
    signal(SIGCHLD, SIG_IGN);

    createChild();                      /* create first child */

    while (1) {
#if QSLEEP
        qsleep(2);
#else
        sleep(2);
#endif

        msg2("killing child with SIGDN",childPid);
        int ret = kill(childPid, SIGDN);

        if (ret == -1) {
            perror("exit err ");
        }

        //msg2("waitpid on",childPid);
        //waitpid(childPid,NULL,0);
    }
}

==> Makefile <==
PGM += child
PGM += parent

all: $(PGM)

$(PGM):
    cc -o $@ $@.c $(CFLAGS) -Wall

clean:
    rm -f $(PGM)