【问题标题】:Parent process killing child process in infinite loop父进程无限循环杀死子进程
【发布时间】:2014-01-14 16:13:00
【问题描述】:

为了解决我的问题,我设置 prctl(PR_SET_PDEATHSIG, SIGHUP);stackoverflow answer 在我打电话给 exec* 之前一样,并取出了我们管道 PID 的部分。有用!!!!!哇……

但是,stackoverflow 不会让我说我已经回答了自己的问题...

所以我尝试编写一个程序,我想运行一个程序,如果它没有完成,则在 cpl 秒后终止该程序。爸爸分叉了一个孩子,孩子分叉了另一个婴儿,孩子将婴儿的 PID 通过管道传送给爸爸,然后等待一秒钟,如果他们还没有结束他们的生意,就会杀死他们两个(这是一个可怕的场景)。但它不起作用,DADDY 停留在 S+ 状态,并且作为 Baby 的无限循环一直持续到我 ctr+c 为止。从好的方面来说,这段代码融合了我在堆栈溢出方面学到的所有知识。我们开始吧。

#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

static int read_from_pipe(int file)
{
    int c;
    FILE *stream = fdopen(file, "r");

    if (fscanf(stream, "%d", &c) != 1)
    {
        fprintf(stderr, "Failed to read integer from pipe\n");
        exit(1);
    }
    fclose(stream);
    return c;
}

static void write_to_pipe(int file, int pidRacket)
{
    FILE *stream = fdopen(file, "w");
    fprintf(stream, "%d", pidRacket);
    fclose(stream);
}

static int spawnpipe(char *fileName, int *fd)
{
    int pid;
    int pipe_fds[2];
    char *command[] = {"racket", fileName, NULL};
    if (pipe(pipe_fds) < 0)
    {
        fprintf(stderr, "FE: pipe\n");
        exit(1);
    }

    switch ((pid = fork()))
    {
    case -1:
        printf("syserr");
        exit(1);
    case 0:

        close(1);
        close(2);
        dup(pipe_fds[1]);
        close(pipe_fds[0]);
        close(pipe_fds[1]);
        execvp(*command, command);
        perror("execv");
        exit(EXIT_FAILURE);

    default:
        *fd = pipe_fds[0];
        close(pipe_fds[1]);
        return pid;
    }
}

static int spawnfp(char *fileName, FILE **fpp)
{
    int fd, pid;
    pid = spawnpipe(fileName, &fd);
    *fpp = fdopen(fd, "r");
    return pid;
}

int main(int argc, char *argv[])
{
    pid_t pid;
    int mypipe[2];
    if (pipe(mypipe))
    {
        fprintf(stderr, "Pipe failed.\n");
        return EXIT_FAILURE;
    }

    pid = fork();
    if (pid < (pid_t) 0)
    {
        fprintf(stderr, "Fork failed.\n");
        return EXIT_FAILURE;
    }
    else if (pid != (pid_t) 0)
    {
        double diff = 0;
        clock_t launch = clock();

        close(mypipe[1]);
        int pidRacket = read_from_pipe(mypipe[0]);
        while (diff < 1.3)
        {
            clock_t done = clock();
            diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
        }
        kill(pidRacket, SIGKILL);
        kill(pid, SIGKILL);
        return EXIT_SUCCESS;
    }
    else if (pid == (pid_t) 0)
    {
        close(mypipe[0]);
        char buf[100];
        FILE *fp;
        char *fileName = argv[1];
        int pidRacket = spawnfp(fileName, &fp);
        write_to_pipe(mypipe[1], pidRacket);

        if (argc == 1)
        {
            printf("Not enough arguments!");
            _exit(EXIT_FAILURE);
        }
        else if (argc == 2)
        {
        }
        sleep(1);
        while (fgets(buf, sizeof buf, fp))
        {
            printf("%s\n", buf);
        }
        fclose(fp);
        kill(pid, SIGKILL);
        return 0;
    }
}

感谢 quinsley 和 vijay!

【问题讨论】:

  • 注意:我在读取第二代PID后尝试关闭管道,无济于事

标签: c linux fork pipe kill


【解决方案1】:

我看代码的各种cmets:

  1. 用换行符结束消息;您现在使用的是 Linux,而不是 Windows。 Windows 系统似乎鼓励人们在留言时不使用换行符,但在一般的 Unix 上,尤其是在 Linux 上,它不能很好地工作。

  2. 如果您希望出现错误消息,尤其是不以换行符结尾的错误消息,请不要使用 _exit()

  3. 不要在标准输出上报告错误信息;报告标准错误(这就是它的用途!)。

  4. 如果后面有else 子句,那么写else if (argc == 2) { }(大括号中没有任何内容)有点奇怪,但如果没有else 子句,则毫无意义。您应该测试argc != 2,因为这是正确数量的参数(或者更准确地说,任何超出argc == 2 的参数都将被忽略)。

  5. 如果您想休眠一段涉及亚秒级计时的时间(例如 1.3 秒),请使用适当的亚秒级休眠命令之一。在这种情况下,nanosleep() 可能是要使用的函数。

  6. 除非在紧急情况下,否则不要使用 SIGKILL。用 SIGKILL 发出信号的进程没有机会清理或任何东西;它立即被杀死(当然,假设你的进程被允许向另一个进程发送信号)。

  7. case -1: printf("syserr"); 后面没有break; 表示出错时,控制流进入以下case 0: 代码,这不是必需的。 break;exit(1); 可能是合适的。 (第 3 条也适用。)

  8. 不要关闭标准错误。代码:

    close(1);
    close(2);
    dup(pipe_fds[1]);
    close(pipe_fds[0]);
    close(pipe_fds[1]);
    execvp(*command, command);
    perror("execv");
    _exit(EXIT_FAILURE);
    

    永远不会报告错误;你关闭了标准错误。请记住,程序有权拥有标准错误通道。 C 标准保证它,但你必须配合并确保你没有关闭标准错误。

  9. 部分演员表:

    diff = ((double)((uintmax_t)(clock_t)done) - (double)((uintmax_t)(clock_t)launch)) / (double)CLOCKS_PER_SEC;
    

    是不必要的。因为donelaunch 都属于clock_t 类型,所以不需要转换为clock_t。对uintmax_t 的中间转换也不是必需的。你可以简单地写:

    diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
    

    即使那样,三个演员中的两个在理论上是多余的(三个中的任何两个都可以删除)。

  10. read_from_pipe() 中的代码很奇怪且容易出错。由于您有一个文件流,因此只需使用fscanf() 从其中读取一个整数,而不是使用双精度算术和小数值然后在最后相乘的奇怪构造。这是特别合适的,因为write_to_pipe() 代码使用printf("%d", ...); 来写入数据。由于c 已经是int,所以return (int)c; 中的演员表是多余的。

  11. 理论上,检查fdopen()返回的流以确保操作没有失败是个好主意。

  12. 如果 pipe() 函数失败,您会在标准输出上报告错误,然后继续,因为没有出现任何问题。

  13. 尚不清楚racket 命令的实际作用。它在我的机器上不存在。

  14. spawnfp() 中的argv 未使用。

  15. pid = fork(); if (pidDos &lt; (pid_t) 0) 生成警告(准确)pidDos 可能未初始化使用。条件大概应该是使用pid,而不是pidDos。然后,您向pidDos 随机标识的 PID 发送一个 SIGKILL 信号,这不太可能带来幸福。

当我将cat 复制到racket 并以mk /etc/passwd 的形式调用以下代码(作为从mk.c 构建的程序mk)时,我会看到双倍行距的密码文件(以及消息来自shell关于Killed: 9

#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

static int read_from_pipe(int file)
{
    int c;
    FILE *stream = fdopen(file, "r");

    if (fscanf(stream, "%d", &c) != 1)
    {
        fprintf(stderr, "Failed to read integer from pipe\n");
        exit(1);
    }
    fclose(stream);
    return c;
}

static void write_to_pipe(int file, int pidRacket)
{
    FILE *stream = fdopen(file, "w");
    fprintf(stream, "%d", pidRacket);
    fclose(stream);
}

static int spawnpipe(char *fileName, int *fd)
{
    int pid;
    int pipe_fds[2];
    char *command[] = {"racket", fileName, NULL};
    if (pipe(pipe_fds) < 0)
    {
        fprintf(stderr, "FE: pipe\n");
        exit(1);
    }

    switch ((pid = fork()))
    {
    case -1:
        printf("syserr");
        exit(1);
    case 0:

        close(1);
        close(2);
        dup(pipe_fds[1]);
        close(pipe_fds[0]);
        close(pipe_fds[1]);
        execvp(*command, command);
        perror("execv");
        exit(EXIT_FAILURE);

    default:
        *fd = pipe_fds[0];
        close(pipe_fds[1]);
        return pid;
    }
}

static int spawnfp(char *fileName, FILE **fpp)
{
    int fd, pid;
    pid = spawnpipe(fileName, &fd);
    *fpp = fdopen(fd, "r");
    return pid;
}

int main(int argc, char *argv[])
{
    pid_t pid;
    int mypipe[2];
    if (pipe(mypipe))
    {
        fprintf(stderr, "Pipe failed.\n");
        return EXIT_FAILURE;
    }

    pid = fork();
    if (pid < (pid_t) 0)
    {
        fprintf(stderr, "Fork failed.\n");
        return EXIT_FAILURE;
    }
    else if (pid != (pid_t) 0)
    {
        double diff = 0;
        clock_t launch = clock();

        close(mypipe[1]);
        int pidRacket = read_from_pipe(mypipe[0]);
        while (diff < 1.3)
        {
            clock_t done = clock();
            diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
        }
        kill(pidRacket, SIGKILL);
        kill(pid, SIGKILL);
        return EXIT_SUCCESS;
    }
    else if (pid == (pid_t) 0)
    {
        close(mypipe[0]);
        char buf[100];
        FILE *fp;
        char *fileName = argv[1];
        int pidRacket = spawnfp(fileName, &fp);
        write_to_pipe(mypipe[1], pidRacket);

        if (argc == 1)
        {
            printf("Not enough arguments!");
            _exit(EXIT_FAILURE);
        }
        else if (argc == 2)
        {
        }
        sleep(1);
        while (fgets(buf, sizeof buf, fp))
        {
            printf("%s\n", buf);
        }
        fclose(fp);
        kill(pid, SIGKILL);
        return 0;
    }
}

我修复了此代码修订版中发现的一些问题,但绝不是全部。


哦,第 16 项:管道的读取端直到第三个进程终止才关闭。您需要将mypipe[1] 传递给spawnfp(),这需要将其中继到spawnpipe(),并且在那里创建的子进程需要在执行“球拍”之前关闭管道描述符。 fscanf() 在从管道读取的 PID 末尾寻找 EOF 或非数字,这使情况更加复杂。您可以在最后提供换行符或其他内容,这也将释放父进程以在其计时循环中旋转。既然你说racket 没有终止,这就是为什么你看不到任何东西。

再次粘贴整个程序比显示差异更容易:

#include <assert.h>
#include <math.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

static int read_from_pipe(int file)
{
    int c;
    FILE *stream = fdopen(file, "r");
    assert(stream != 0);

    if (fscanf(stream, "%d", &c) != 1)
    {
        fprintf(stderr, "Failed to read integer from pipe\n");
        exit(1);
    }
    fclose(stream);
    return c;
}

static void write_to_pipe(int file, int pidRacket)
{
    FILE *stream = fdopen(file, "w");
    assert(stream != 0);
    fprintf(stderr, "%d: pidRacket = %d\n", (int)getpid(), pidRacket);
    fprintf(stream, "%d", pidRacket);
    fclose(stream);
}

static int spawnpipe(char *fileName, int *fd, int pfd)
{
    int pid;
    int pipe_fds[2];
    char *command[] = {"racket", fileName, NULL};
    if (pipe(pipe_fds) < 0)
    {
        fprintf(stderr, "FE: pipe\n");
        exit(1);
    }

    switch ((pid = fork()))
    {
    case -1:
        printf("syserr");
        exit(1);
    case 0:

        close(pfd);
        close(1);
        //close(2);
        dup(pipe_fds[1]);
        close(pipe_fds[0]);
        close(pipe_fds[1]);
        execvp(*command, command);
        perror("execv");
        exit(EXIT_FAILURE);

    default:

        fprintf(stderr, "%d: pid = %d\n", (int)getpid(), pid);
        *fd = pipe_fds[0];
        close(pipe_fds[1]);
        return pid;
    }
}

static int spawnfp(char *fileName, FILE **fpp, int pfd)
{
    int fd, pid;
    pid = spawnpipe(fileName, &fd, pfd);
    *fpp = fdopen(fd, "r");
    assert(*fpp != 0);
    return pid;
}

int main(int argc, char *argv[])
{
    pid_t pid;
    int mypipe[2];
    if (pipe(mypipe))
    {
        fprintf(stderr, "Pipe failed.\n");
        return EXIT_FAILURE;
    }

    pid = fork();
    if (pid < (pid_t) 0)
    {
        fprintf(stderr, "Fork failed.\n");
        return EXIT_FAILURE;
    }
    else if (pid != (pid_t) 0)
    {
        double diff = 0.0;
        clock_t launch = clock();

        close(mypipe[1]);
        fprintf(stderr, "%d: Reading from pipe:\n", (int)getpid());
        int pidRacket = read_from_pipe(mypipe[0]);
        fprintf(stderr, "%d: Read PID %d from pipe\n", (int)getpid(), pidRacket);
        while (diff < 1.3)
        {
            clock_t done = clock();
            diff = ((double)done - (double)launch) / (double)CLOCKS_PER_SEC;
            printf("%f\n", diff);
        }
        kill(pidRacket, SIGKILL);
        kill(pid, SIGKILL);
        return EXIT_SUCCESS;
    }
    else if (pid == (pid_t) 0)
    {
        close(mypipe[0]);
        char buf[100];
        FILE *fp;
        char *fileName = argv[1];
        int pidRacket = spawnfp(fileName, &fp, mypipe[1]);
        fprintf(stderr, "%d: Writing PID %d to pipe\n", (int)getpid(), pidRacket);
        write_to_pipe(mypipe[1], pidRacket);
        fprintf(stderr, "%d: Written PID to pipe\n", (int)getpid());

        if (argc == 1)
        {
            printf("Not enough arguments!");
            _exit(EXIT_FAILURE);
        }
        else if (argc == 2)
        {
        }
        sleep(1);
        while (fgets(buf, sizeof buf, fp))
        {
            printf("%s\n", buf);
        }
        fclose(fp);
        fprintf(stderr, "%d: Finished reading from pipe\n", (int)getpid());
        kill(pid, SIGKILL);
        return 0;
    }
}

【讨论】:

  • 啊!我不是故意让你做这一切的。你真是太慷慨了!我粘贴的代码是从其他人的代码中复制和粘贴的杂乱无章的工作!我边走边学。 'racket' 是方案的解释器。我想确保这个程序在解析无限循环时会杀死一个“球拍”进程 - 但这个杀死不起作用,因为最顶层的进程挂在管道中并最终等待编译器完成打印(它永远不会完成)到中间层进程。我正在尝试新事物。
【解决方案2】:

我前段时间做这个是为了好玩,它会占用你的大部分 CPU 来运行,但我相信你可以修改它以在某个点中断或满足你的需要。

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

int main(int argc, char*argv[])
{
    int childpid;
    int pids[100];
    int count1 = 0, count2 = 0;
    int count3 = 0;

    L1:
    childpid = fork();

    if(childpid == 0)
    {       

    }   
    else
    {

        if(childpid != 0 && childpid != -1)
        {
            if(count3 < 100)
            {
                pids[count3] = childpid;
                printf("Pid:%d\n",pids[count3]);                
                count3++;
                goto L1;
            }
            else
            {
                count3--;
                goto L2;
            }   
        }

        L2:
            while(count3 > 0)
            {
                if(pids[count3] != -1 || pids[count3] != 1)
                {
                    printf("Killing pid:%d\n",pids[count3]);
                    kill(pids[count3],SIGKILL);
                }       
                count3--;
            }

            if(count3 == 0)
            {
                goto L1;
            }   
    }

    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-14
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多