向已暂停的父进程发送信号并保证该信号将在父进程暂停后到达

向已暂停的父进程发送信号并保证该信号将在父进程暂停后到达

我有这种伪代码,我想知道我必须更改什么才能保证发送给父级的信号确实会在父级暂停后到达。

int main()
{
    int pid;
    
    for(int i = 0; i < 5; i++)
    {
        if((pid = fork()) == 0)
        {   
            mapp(i);
        }
    }

    for(int i = 0; i < 5; i++)
    {
        /*Parent opens fifo i for reading*/
        pause(); /*Parent must stop it's execution until child i writes to fifo i*/
        /*Parent reads from the fifo number i => SUCCESS*/ 
    }
    
    
}

void mapp(int i)
{
    /*Children opens fifo i for writing*/
    /*Children writes to fifo i*/
    kill(getppid(), SIGCONT); /*Children notifies it's father so that he's now be able to read*/
}

看起来逻辑上没有什么问题吧?但遗憾的是,程序并不总是按预期运行:有时执行会挂起,因为有时(并非总是)信号在父进程暂停之前发送,因此当父进程暂停时,程序将挂起,因为它不会接收到信号任何其他类型的信号。

PD:sleep()不是一个选项,我不能延长程序的执行时间。

谢谢!

答案1

你是对的,你的程序受到竞争条件的影响。

这里的解决方案是在父进程中创建通信管道,而不是让每个进程创建并连接它们。您提到他们使用 fifo,但 fifo 文件就像管道一样,并且由于它们是家庭,因此您可能不需要 fifo,管道就足够了。

非常粗略地:

#define CHILDREN 5
int main()
{
    int pid;
    int pipes[CHILDREN][2];
    
    for(int i = 0; i < CHILDREN; i++)
    {
        if (pipe(pipes[i]) == -1) {
           // Error
        ]
        
        pid = fork();
        if(pid == -1) {
          // Error
        } else if (pid == 0) {
            // Child   
            close(pipes[i][0]);
            mapp(i, pipes[i][1]);
        } else {
            // Parent
            close(pipes[i][1]);
        }
    }

    for(int i = 0; i < CHILDREN; i++)
    {
        /*Parent reads from the pipe number i => SUCCESS*/ 
        read(pipes[i][0], ...); // Will block until children writes
    }
    
    
}

void mapp(int i, int fd)
{
    /*Children writes to its pipe */
    write(fd, "hello Dad!\n", 11);
}

父进程将阻塞,直到它可以从管道中读取数据,因此它是完全高效的。如果你想阻塞直到它可以读取任何对于管道,你也可以使用 select、epoll 等来做到这一点。

对管道的写入是原子的,假设它们小于 PIPE_BUF(在 Linux 上,即 4096 字节)。在最近的 Linux 上,您可以使用 O_DIRECT 并拥有“数据包”模式。尽管每个孩子都有一支烟斗,但这似乎没有必要。

相关内容