无法使用execl()、、fork()、kill()和信号检索信号

ssm49v7z  于 2022-12-03  发布在  其他
关注(0)|答案(2)|浏览(118)

你好我有问题,在我的第二个程序获得信号。我有这样的代码:
程序1:

int main() {
    int id = fork();
    
    if (id == 0) {
        execl("program2", "program2", NULL);
        kill(id, SIGUSR1);
    }
    return 0;
}

方案二:

void signal_handler1(int sig) {
    printf("test\n");
}

int main(int argc, char* argv[]) {
    signal(SIGUSR1, signal_handler1);
    printf("Hello world\n");

    return 0;
}

为什么我得到

Hello World

代替

test
Hello World
ryevplcw

ryevplcw1#

execl()仅在发生错误时返回,因此如果execl()成功,则kill()不会在program1中执行。它应该在父进程中发送信号。但信号可能在program2设置信号处理程序之前发送,因此信号可能被忽略。让它等待一段时间后再发送信号:

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

int main(void)
{
    pid_t id = fork();
    if (id == -1) {
        perror("fork");
        return 1;
    }

    if (id == 0)
    {   /* Child process */
        /* Execute program from current directory */
        execl("program2", "program2", NULL);
        perror("execl");
    } else
    {   /* Parent process */
        /* Wait while child setup signal handler */
        sleep(1);

        if (kill(id, SIGUSR1) == -1)
        {
            perror("kill");
        }
    }
    return 0;
}

如果你想让program2捕捉信号,让它等待信号:

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

static sig_atomic_t sigusr1_received = 0;

static void signal_handler(int)
{
    sigusr1_received = 1;
}

int main(void)
{
    signal(SIGUSR1, signal_handler);

    printf("Hello World!\n");

    while (1)
    {
        /* Check flag first to avoid race condition */
        if (sigusr1_received)
        {
            printf("test\n");
            /* Exit after receiving signal */
            break;
        }
        pause();        /* Wait for any signal */
    }

    return 0;
}
qgelzfjb

qgelzfjb2#

查看您的父代码:

int main()
{
    int id = fork();
    
    if (id == 0) {
  • 您不测试id是否为负(表示fork()中存在错误),但由于此错误很少见,因此在此处不构成问题)
  • 您已经测试了id是否为零(就在上面),所以请三思,您是否正在终止进程id为0的进程???此终止不应该在代码的其他部分吗?
  • execl()只有在确实失败时才会传回。您应该只打印一些关于execl()失败原因的错误,以及exit(),并以传回码指出失败的原因。
execl("program2", "program2", NULL);
        fprintf(stderr, "EXECL: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
        /* kill(id, SIGUSR1); <-- this code will never execute */
    }
    return 0;
}

Exec函数永远不会返回,因为执行它们的程序会被你试图执行的程序覆盖。所以,如果你调用execl(2),不要在它下面放任何东西,只放错误代码。这不像调用子例程,它会返回到调用程序。它永远不会返回(正如前面所说,只有当函数失败时)。
如果要向子进程发送信号,则kill()调用应该在if语句之后(其中确实是要执行的父代码)。您应该首先检查-1,因为这意味着fork()失败。
因此,更好的示例代码应该是:(我已经放了一个完整的例子,准备在你的网站上构建和执行,请阅读How to create a minimal, reproducible example,并遵循那里的指导方针来发布可测试的代码。(你缺少头文件和许多东西来测试这个代码)

program1.c

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>

#include "log.h"  /* see log.h file, below */

char *p1;
int   pid;

int main(int argc, char **argv)
{
    int res;
    p1       = argv[0];
    char *p2 = argc > 1 ? argv[1] : p1;
    pid      = getpid();

    LOG(p1, pid, "STARTING\n");
    LOG(p1, pid, "IGNORING SIGUSR1(%d)\n", SIGUSR1);
    signal(SIGUSR1, SIG_IGN);

    int  id  = fork();
    if (id < 0) { /* error */
        ERR(p1, pid, "FORK: %s\n", strerror(errno));
    }

    if (id == 0) { /* child process */
        pid  = getpid(); /* a new pid as we are children */

        if (argc <= 1) {
            ERR(p1, pid, "nothing to execute\n");
        }

        /* shift the parameters to get the next command name
         * and parameter list */
        argv++;
        argc--;

        LOG(p1, pid, "about to EXECV(");
        char *sep  = "";
        for (int i = 0; i < argc; i++) {
            LOG_TAIL("%s\"%s\"", sep, argv[i]);
            sep    = ", ";
        }
        LOG_TAIL(").\n");

        /* we use execv(2) instead of execl(2) because
         * we are using the parameters as derived from
         * our own parameters, so we change (we did
         * above) the local parameters to main to get
         * the parameter list to execv(). */
        execv(p2, argv);
        ERR(p1, pid, "EXEC: %s\n", strerror(errno));
    }
    /* id > 0 */
    LOG(p1, pid, "FORKed a new child (pid=%d)\n", id);

    {   /* randomize, as all children start at the same time
         * calling srandom() with a time based value will not
         * be a good chance, so I selected to read the seed
         * value from /dev/random */
        static const char rand_dev[] = "/dev/random";
        int fd = open(rand_dev, O_RDONLY);
        int val;
        if (fd >= 0) {
            read(fd, &val, sizeof val);
            srandom(val);
            close(fd);
        } else {
            ERR(p1, pid, "%s: %s\n", rand_dev, strerror(errno));
        }
    }

    int secs = random() % 10 + 1; /* 1..10 s. delay */
    LOG(p1, pid, "Waiting for %ds to next step\n", secs);
    sleep(secs); /* wait between 1 and 10s. */

    LOG(p1, pid, "Sending SIGUSR1(%d) to %s[pid=%d]\n",
            SIGUSR1, p2, id);
    res = kill(id, SIGUSR1);
    if (res < 0) {
        LOG(p1, pid, "KILL(%d, SIGUSR1): %s\n", id, strerror(errno));
    }
    LOG(p1, pid, "Waiting for %s[pid=%d] to finalize\n", p2, id);
    int status;
    res = wait(&status); /* wait for child to end */
    if (res < 0) {
        /* error in wait(2) */
        ERR(p1, pid, "WAIT: %s\n",
                strerror(errno));
    } else if (WIFEXITED(status)) {
        /* child called exit(2) */
        LOG(p1, pid, "WAIT: %s[pid=%d] terminated with exit code %d.\n",
                p2, id, WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        /* child was aborted by a signal */
        LOG(p1, pid, "WAIT: %s[pid=%d] terminated by signal %d%s.\n",
                p2, id, WTERMSIG(status),
                WCOREDUMP(status)
                    ? ", and dumped a core file"
                    : "");
    }
    LOG(p1, pid, "finalizing\n");
    exit(EXIT_SUCCESS);
} /* main */

program2.c

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#include "log.h"

char *pn;
int pid;

void signal_handler1(int sig) {
    LOG(pn, pid, "RECEIVED signal %d\n", sig);
}

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

    pn = argv[0];
    pid = getpid();

    LOG(pn, pid, "STARTING\n");

    LOG(pn, pid, "Installing signal handler for SIGUSR1(%d)\n",
            SIGUSR1);
    signal(SIGUSR1, signal_handler1);

    LOG(pn, pid, "Program params: ");
    char *sep = "";
    for (int i = 1; i < argc; i++) {
        LOG_TAIL("%s[%s]", sep, argv[i]);
        sep = ", ";
    }
    LOG_TAIL("\n");

    if (argc > 1) { /* at least one parameter, so pause */
        LOG(pn, pid, "Waiting to receive a signal\n");
        int res = pause();
        if (res < 0) {
            LOG(pn, pid, "PAUSE: %s\n", strerror(errno));
        }
    }

    /* and print final message */
    LOG(pn, pid, "Hello world, program terminating\n");
    return 0;
}

这两个计划都需要:

log.h

#ifndef _LOG_H
#define _LOG_H

#define F(_prog, _pid, _fmt) "%s:%s:%d:pid=\033[1;%dm%d\033[m:%s: "\
    _fmt, _prog, __FILE__, __LINE__, (_pid % 7 + 31), (_pid), __func__

#define LOG_TAIL(_fmt, ...) \
        printf(_fmt, ##__VA_ARGS__)

#define LOG(_prog, _pid, _fmt, ...)     \
        printf(F(_prog, _pid, " "_fmt), \
                ##__VA_ARGS__)

#define ERR(_prog, _pid, _fmt, ...) do {      \
        printf(F(_prog, _pid, "ERROR: "_fmt), \
                ##__VA_ARGS__);               \
        exit(EXIT_FAILURE);                   \
    } while (0)

#endif /* _LOG_H */

您可以使用以下内容进行构建:

Makefile

targets = p1 p2
toclean = $(targets)

p1_objs = program1.o
p2_objs = program2.o
toclean += $(p1_objs) $(p2_objs)

all: $(targets)
clean:
    rm -f $(toclean)

p1: $(p1_objs)
    $(CC) $(CFLAGS) $(LDFLAGS) $($@_objs) -o $@

p2: $(p2_objs)
    $(CC) $(CFLAGS) $(LDFLAGS) $($@_objs) -o $@

program1.o program2.o: log.h

对于运行为:

$ p1 p1 p2 
p1:program1.c:24:pid=11089:main:  STARTING
p1:program1.c:25:pid=11089:main:  IGNORING SIGUSR1(10)
p1:program1.c:62:pid=11089:main:  FORKed a new child (pid=11090)
p1:program1.c:83:pid=11089:main:  Waiting for 1s to next step
p1:program1.c:45:pid=11090:main:  about to EXECV("p1", "p2").
p1:program1.c:24:pid=11090:main:  STARTING
p1:program1.c:25:pid=11090:main:  IGNORING SIGUSR1(10)
p1:program1.c:62:pid=11090:main:  FORKed a new child (pid=11091)
p1:program1.c:83:pid=11090:main:  Waiting for 6s to next step
p1:program1.c:45:pid=11091:main:  about to EXECV("p2").
p2:program2.c:23:pid=11091:main:  STARTING
p2:program2.c:25:pid=11091:main:  Installing signal handler for SIGUSR1(10)
p2:program2.c:29:pid=11091:main:  Program params: 
p2:program2.c:46:pid=11091:main:  Hello world, program terminating
p1:program1.c:86:pid=11089:main:  Sending SIGUSR1(10) to p1[pid=11090]
p1:program1.c:92:pid=11089:main:  Waiting for p1[pid=11090] to finalize
p1:program1.c:86:pid=11090:main:  Sending SIGUSR1(10) to p2[pid=11091]
p1:program1.c:92:pid=11090:main:  Waiting for p2[pid=11091] to finalize
p1:program1.c:101:pid=11090:main:  WAIT: p2[pid=11091] terminated with exit code 0.
p1:program1.c:111:pid=11090:main:  finalizing
p1:program1.c:101:pid=11089:main:  WAIT: p1[pid=11090] terminated with exit code 0.
p1:program1.c:111:pid=11089:main:  finalizing
$ _

相关问题