【问题标题】:How to keep sending signals to children from parent process repeatedly?如何不断地从父进程向子进程发送信号?
【发布时间】:2019-12-28 10:06:41
【问题描述】:

我试图让一个程序创建 n 个子进程来读取文件,并且父进程不断循环它们并尝试发送信号以继续读取文件,如果该进程是空闲的。否则,如果该进程被占用,它会移动到下一个进程。这是我使用信号的方法,但输出是意外的。如果我使用 printf 在父级的 while 循环中调试代码,我只会得到 printf 语句本身。为什么会这样?

据我了解,预期的输出应该是几乎同时从文件中读取的所有 n 个子进程(文件很小),然后间隔 1 秒再发生这种情况。这是正确的,还是由于竞争条件而不确定?

我知道使用管道可能有更好的方法,但是使用信号可以做到这一点吗?提前致谢。

  • 我知道 signal() 的使用不如 sigaction() 多,如手册页中所述,但这是否可以解决?

  • 会不会是printf本身对IPC的干扰造成的printf输出异常?

编辑:用于测试的 Pastebin 链接:https://pastebin.com/ZWFnkJDA

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

int counter = 0;
int fd;
const char* filename;
int j = 0;
int no_of_children;
int* cpid;

//User define signal handler
static void sig_usr1(int);
static void sig_usr2(int signo, siginfo_t* info, void* context);

static void sig_usr1(int signo)
{
    //Now the child process waits for reading the Filename

    //Block SIGUSR1 until it's complete
    signal(SIGUSR1, SIG_IGN);
    printf("Blocked now.\n");
    printf("Child no %d is reading now.\n\n",getpid());
    fd = open(filename, O_RDONLY | O_CREAT);

    char buf = 'a';
    int k=0;
    char* op = (char*) malloc (255*sizeof(char));

    while(read (fd, &buf, 1))
    {
        if (buf == '\n')
        {
            op[k] = '\0';
            break;
        }

        else
        {
            op[k++] = buf;
        }
    }

    //Now wait for a second and then send a signal
    sleep(1);
    //Print the contents of the buffer via op
    printf("Output: %s\n\n", op);


    //Now unblock the signal
    kill(getppid(), SIGUSR2);
    signal(SIGUSR1, sig_usr1);
    printf("Unblocked now\n");
}

static void sig_usr2(int signo, siginfo_t* info, void* context)
{
    if (signo == SIGUSR2)
    {
        child_pid = info->si_pid;
        printf("Parent Received SIGUSR2. Child Process with PID %d is now free\n\n", child_pid);
    }
}

int main(int argc, char* argv[])
{
    //Filename is the first argument
    filename = argv[1];

    //Number of Child Processes to be spawned
    no_of_children = atoi(argv[2]);

    cpid = (int*) malloc (no_of_children*sizeof(int));

    //Create a sigaction() handler for SIGUSR2
    struct sigaction sa;
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = sig_usr2;
    sigaction(SIGUSR2, &sa, NULL);

    //Create no_of_children children
    for(int i=0; i<no_of_children; i++)
    {
        cpid[i] = fork();
        if (cpid[i] == 0)
        {
            //Inside a child
            printf("Created %dth child process", i);
            printf(" with Process ID = %d\n", getpid());            

            signal(SIGUSR1, sig_usr1);

            while(1)
            {
                pause();
            }

            //Every child process must exit so control goes back to the parent
            exit(0);
        }
    }

    //Returns to the parent process
    while(1)
    {
        int fpid = cpid[j];
        //Send the signal to the free child process
        //printf("Sending to PID %d\n", fpid); //----> Uncommenting this line only prints this statement. Why does this happen?
        kill(fpid, SIGUSR1);
        j = (j < no_of_children - 1) ? j + 1 : 0; //----->Does not work as expected if i uncomment this line. I want to switch between PIDs and send signals to all of them
    }

    return 0;
}
  • 样本输出:
$ ./a.out sample.txt 4
Created 0th child process with Process ID = 15734
Created 1th child process with Process ID = 15735
PID: 15737 -> PID: 15736 -> PID: 15735 -> PID: 15734
Created 2th child process with Process ID = 15736
Blocked now.
Child no 15734 is reading now.

Created 3th child process with Process ID = 15737
Output: This is a sample file

Unblocked now
Parent Received SIGUSR2. Child Process with PID 15734 is now free
Blocked now.
Child no 15734 is reading now.

Blocked now.
Child no 15735 is reading now.

Output: This is a sample file

Unblocked now
Output: This is a sample file

Parent Received SIGUSR2. Child Process with PID 15735 is now free
Blocked now.
Child no 15734 is reading now.

Unblocked now
Parent Received SIGUSR2. Child Process with PID 15736 is now free
Blocked now.
Child no 15735 is reading now.

Blocked now.
Blocked now.
Child no 15736 is reading now.

Child no 15737 is reading now.

^\% 

【问题讨论】:

  • 请提供minimal reproducible example 并将编译器的警告级别提高到最大值。尝试编译您的示例会产生太多错误。
  • 我已经编辑了我的帖子以生成一个工作示例。你现在可以检查一下吗?谢谢
  • 抱歉,现在不行,因为我在工作,必须使用 Windows。为了增加其他人加入的机会,您还可以显示您期望的输出以及您得到的输出。
  • 是的。我也添加了输出。我期望的是“输出”行每 1 秒一次显示 4 次,因为此时所有 4 个进程都读取文件
  • pintf 是信号不安全的。永远不要在信号处理程序中这样做。

标签: c signals fork parent-child ipc


【解决方案1】:

注意:这不是一个答案,只是一些经过编辑的代码和可能导致解决方案的结论。

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

int counter = 0;
int fd;
const char* filename;
int j = 0;
int no_of_children;
int* cpid;
int my_pid;

//User define signal handler
static void sig_usr1(int);
static void sig_usr2(int);

static void sig_usr1(int signo)
{
    //Now the child process waits for reading the Filename

    //Block SIGUSR1 until it's complete
    signal(SIGUSR1, SIG_IGN);
    printf("%d\tBlocked now.\n", my_pid);
    printf("%d\tChild no %d is reading now.\n",my_pid, getpid());
    fd = open(filename, O_RDONLY | O_CREAT);

    char buf = 'a';
    int k=0;
    char* op = (char*) malloc (255*sizeof(char));

    while(read (fd, &buf, 1))
    {
        if (buf == '\n')
        {
            op[k] = '\0';
            break;
        }

        else
        {
            op[k++] = buf;
        }
    }

    //Now wait for a second and then send a signal
    sleep(1);
    //Print the contents of the buffer via op
    printf("%d\tOutput: %s\n", my_pid, op);


    //Now unblock the signal
    kill(getppid(), SIGUSR2);
    signal(SIGUSR1, sig_usr1);
    printf("%d\tUnblocked now\n", my_pid);
}

static void sig_usr2(int signo)
{
    if (signo == SIGUSR2)
    {
        printf("%d\tParent Received SIGUSR2. Child Process with PID %d is now free\n", my_pid, cpid[j]);
        kill (cpid[j++], SIGUSR1);
        if (j == no_of_children)
        {
            j = 0;
        }

    }
}

int main(int argc, char* argv[])
{
    my_pid = getpid();
    //Filename is the first argument
    filename = argv[1];

    //Number of Child Processes to be spawned
    no_of_children = atoi(argv[2]);

    cpid = (int*) malloc (no_of_children*sizeof(int));

    signal(SIGUSR2, sig_usr2);
    //Create no_of_children children
    for(int i=0; i<no_of_children; i++)
    {
        cpid[i] = fork();
        if (cpid[i] == 0)
        {
            //Inside a child
            my_pid = getpid();
            printf("%d\tCreated %dth child process", my_pid, i); 
            printf(" with Process ID = %d\n", getpid());            

            signal(SIGUSR1, sig_usr1);

            while(1)
            {
                pause();
            }

            //Every child process must exit so control goes back to the parent
            exit(0);
        }
        printf("%d\tforked %dth child -> %d\n", my_pid, i, cpid[i]);
    }

    //Returns to the parent process
    while(1)
    {
        int fpid = cpid[j];
        //Send the signal to the free child process
        printf("%d\tSending to PID %d\n", my_pid, fpid);
        kill(fpid, SIGUSR1);
        //j = (j < no_of_children - 1) ? j + 1 : 0;
        pause();
    }

    return 0;
}

好吧,我做了什么?

  1. 在每个printf()的开头添加当前进程的PID。为此,添加了变量my_pid 并将其设置在方便的位置。是的,我知道一些输出已经提供了这些信息。我不想过多更改 OP 的源代码。
  2. 在课程中删除了双 \n。他们没有帮助。
  3. 要查看fork() 的成功,请打印其结果。
  4. 最后在主进程的无限循环中添加printf。我想看看哪个进程以哪个速率发出了多少次信号。哇,好多啊!

嗯,会不会是主进程向子进程充斥着太多信号,以至于它甚至无法调用signal() 来暂时阻止它?

  1. 最后在主进程的无限循环中添加pause()

现在事情开始起作用了,但很可能不是 OP 想要的方式。无论如何,所有子进程都被创建并接收它们的信号、读取文件、发送回它们的信号等等。

系统中的信号似乎太多了。必须考虑这样的算法。所以这是我的建议:

  • 让它慢一点。您可以稍后加快速度。
  • 在代码中添加尽可能多的观察点(此处:printf())以跟踪所有事件。
  • 特别是对于多处理考虑一种可视化结果的方法。我在编辑器中复制了 shell 输出并缩进如下:
$ ./signal_repetition signal_repetition.c 4
1901                    forked 0th child -> 1902
    1902                Created 0th child process with Process ID = 1902
1901                    forked 1th child -> 1903
        1903            Created 1th child process with Process ID = 1903
1901                    forked 2th child -> 1904
            1904        Created 2th child process with Process ID = 1904
1901                    forked 3th child -> 1905
1901                    Sending to PID 1902
    1902                Blocked now.
    1902                Child no 1902 is reading now.
                1905    Created 3th child process with Process ID = 1905
    1902                Output: #include<stdio.h>
    1902                Unblocked now
1901                    Parent Received SIGUSR2. Child Process with PID 1902 is now free
1901                    Sending to PID 1903
    1902                Blocked now.
    1902                Child no 1902 is reading now.
1903                    Blocked now.
1903                    Child no 1903 is reading now.
    1902                Output: #include<stdio.h>
    1902                Unblocked now
1901                    Parent Received SIGUSR2. Child Process with PID 1903 is now free
1901                    Sending to PID 1904
            1904        Blocked now.
        1903            Output: #include<stdio.h>
            1904        Child no 1904 is reading now.
        1903            Unblocked now
        1903            Blocked now.
        1903            Child no 1903 is reading now.
1901                    Parent Received SIGUSR2. Child Process with PID 1904 is now free
1901                    Sending to PID 1905
                1905    Blocked now.
                1905    Child no 1905 is reading now.
        1903            Output: #include<stdio.h>
            1904        Output: #include<stdio.h>
        1903            Unblocked now
            1904        Unblocked now
            1904        Blocked now.
            1904        Child no 1904 is reading now.
1901                    Parent Received SIGUSR2. Child Process with PID 1905 is now free
  • 您可以在每个进程自己的日志文件中创建一个日志。添加至少毫秒分辨率的时间戳,以便稍后同步所有日志。这样您就可以避免在 stdout 上出现混淆输出的问题。

还有足够多的其他事情需要解决。玩得很开心!祝你好运!

【讨论】:

  • 感谢您的回答。你的一些观点真的很有帮助。我同意你的看法;父母向孩子发送太多信号很可能是这里的错误。我确实想过将pause 放在父级的while 循环中,但没有,因为它不是我期望的输出。但我同意你的观点,你的更改包含了更好的编程实践供我遵循。通过这个问题学到了一些教训。再次感谢您的意见!
  • 我将此标记为答案,因为这为我提供了足够的见解来完成我的任务
猜你喜欢
  • 1970-01-01
  • 2019-04-11
  • 1970-01-01
  • 2019-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多