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

时间:2021-06-27 19:46:35

标签: c signals fork exec

我有 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){}
}

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

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

1 个答案:

答案 0 :(得分:2)

一些事情......

当子进程向父进程发送信号时,父进程在 sleep 调用中。这会提前终止(例如 EINTR)。

因此,在第二轮中,父母将[可能]在孩子准备接收信号之前杀死孩子。

为了添加调试、waitpid 等,我进行了一些重大修改。

主要问题是[要么]第二个孩子没有从父母那里得到SIGUSR1。或者,孩子没有将信号发送给父母或父母被阻止。从下面的第二个日志文件来看,第二个孩子似乎从未从父母那里得到 SIGUSR1

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

在我让父母和孩子使用不同信号之前,我无法让事情发挥作用。也就是说,parent 将 SIGUSR1 发送给 child,child 将 SIGUSR2 发送给 parent。

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

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

编辑:使代码完全运行。有关详细信息,请参阅下面的更新部分。


这是“坏”日志。父母会在孩子进行设置之前发送第二个信号:

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 选项添加到构建中,则 kill 从父到子的 SIGUSR1 调用将产生 ESRCH 错误(无此过程) .


如果我们有父级的 sleep 调用循环并等待 2 秒(例如它计算睡眠的剩余时间),我们会得到一个不同的序列。父母只会在孩子有时间设置后发送 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 调用——这可能更重要)。

即使信号编号相同,这也有效

我已经更新了下面的代码,但我保留了上一篇博文中的 [above] 日志文件。


这是源代码。我添加了 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)