Общение с дочерним процессом через Tokio UnixStream - PullRequest
0 голосов
/ 14 марта 2020

Я пытаюсь заставить родительский процесс и дочерний процесс общаться друг с другом, используя Tokio UnixStream . Проблема в том, что по какой-то причине ребенок не может прочитать то, что родитель пишет в сокет (и, вероятно, наоборот).

Функция, которую я имею, похожа на следующую

    pub async fn run() -> Result<(), Error> {
        let mut socks = UnixStream::pair()?;

        match fork() {
            Ok(ForkResult::Parent { .. }) => {
                socks.0.write_u32(31337).await?;
                Ok(())
            }

            Ok(ForkResult::Child) => {
                eprintln!("Reading from master");
                let msg = socks.1.read_u32().await?;
                eprintln!("Read from master {}", msg);
                Ok(())
            }
            Err(_) => Err(Error),
        }
    }

Сокет не закрывается, в противном случае я получу немедленную ошибку при попытке прочитать из socks.1. Если я переместу чтение в родительский процесс, оно будет работать как положено. Первая строка Reading from master будет напечатана, но вторая строка никогда не вызывается.

Я не могу изменить парадигму связи, так как я буду использовать execve для запуска другого двоичного файла, который ожидает общения с socketpair.

Любой Понимаете, что я здесь не так делаю? Это как-то связано с машиной состояний async / await, сгенерированной из функции async?

1 Ответ

1 голос
/ 15 марта 2020

Когда вы вызываете системный вызов fork () :

Дочерний процесс создается с одним потоком - тот, который вызвал fork ().

Исполнителем по умолчанию в Tokio является исполнитель пула потоков. Дочерний процесс получит только один из потоков в пуле, поэтому он не будет работать должным образом.

Я обнаружил, что смог заставить вашу программу работать, настроив пул потоков, содержащий только один поток, как это:

use tokio::prelude::*;
use tokio::net::UnixStream;
use nix::unistd::{fork, ForkResult};
use nix::sys::wait;
use std::io::Error;
use std::io::ErrorKind;
use wait::wait;

// Limit to 1 thread
#[tokio::main(core_threads = 1)]
async fn main() -> Result<(), Error> {
    let mut socks = UnixStream::pair()?;

    match fork() {
        Ok(ForkResult::Parent { .. }) => {
            eprintln!("Writing!");
            socks.0.write_u32(31337).await?;
            eprintln!("Written!");
            wait().unwrap();
            Ok(())
        }

        Ok(ForkResult::Child) => {
            eprintln!("Reading from master");
            let msg = socks.1.read_u32().await?;
            eprintln!("Read from master {}", msg);
            Ok(())
        }

        Err(_) => Err(Error::new(ErrorKind::Other, "oh no!")),
    }
}

Еще одно изменение, которое я должен был сделать, - заставить родителя ждать завершения работы ребенка, вызвав wait() - то, что вы, вероятно, не хотите делать в реальном asyn c program.

Большинство прочитанных мною советов, что если вам нужно разветвиться из многопоточной программы, сделайте это перед созданием каких-либо потоков или вызовите exec_ve() в потомке сразу после разветвления ( что ты планируешь делать в любом случае).

...