代码之家  ›  专栏  ›  技术社区  ›  Denys Séguret

启动另一个程序,然后退出

  •  6
  • Denys Séguret  · 技术社区  · 7 年前

    从一个用rust编写的程序A中,我想启动一个程序B,有一个结束,并且让B正常运行,就像在A终止后从同一个shell手动启动一样。

    我当前的计划:

    use std::process::Command;
    
    pub fn execute(exe: &str, args: &[&str]) {
        Command::new(exe)
            .args(args)
            .spawn()
            .expect("failed to start external executable");
    }
    
    fn main() {
        execute("/usr/bin/nvim", &["/home/dys/todo.txt"]);
    }
    

    这失败了。 NVIM 作为子级启动,一旦调用程序停止,则不工作。

    我怎样才能写作 execute 所以调用程序立即停止并让 NVIM (或其他程序)正确运行(即使没有任何窗口系统)?

    2 回复  |  直到 7 年前
        1
  •  2
  •   Sven Marnach    7 年前

    经过进一步的讨论,我们发现了实际的问题:您要启动的程序应该保持在前台,这样它就可以从终端读取数据(后台进程在Unix上是做不到的)。

    实现这一点有两种方法。第一个也是最简单的方法是在父进程退出之前等待子进程:

    use std::process::{Command, ExitStatus};
    use std::io::Result;
    
    pub fn execute(exe: &str, args: &[&str]) -> Result<ExitStatus> {
        Command::new(exe).args(args).spawn()?.wait()
    }
    

    这样可以确保进程(父进程和子进程)保持在前台,因为shell正在等待父进程,因此子进程可以从终端读取。

    如果由于某种原因,在子进程运行时,您负担不起父进程逗留的费用,那么您需要依赖于平台的代码。在Unix上,可以使用 exec() 将父进程的图像替换为子进程的图像:

    use std::process::Command;
    use std::os::unix::process::CommandExt;
    use std::io::Error;
    
    pub fn execute(exe: &str, args: &[&str]) -> Error {
        Command::new(exe).args(args).exec()
    }
    

    该函数仅在出现错误时返回。否则,进程图像将替换为新图像。从shell的角度来看,它仍然是相同的过程,因此shell将等待您启动的命令完成。

    第二种方法的优势似乎微乎其微。它不能在Windows上工作,因为Windows不支持 执行() 和朋友们。在运行该命令时,您将拥有一个较少的进程,但实际上,该进程的资源使用量应该很小,它不使用任何CPU,并且可以在必要时交换内存页。

    原始答案

    从一个用rust编写的程序A中,我想启动一个程序B,有一个结束,并且让B正常运行,就像在A终止后从同一个shell手动启动一样。

    这或多或少是您的代码已经在做的事情。与直接从UNIX系统上的shell启动的进程有一些不同,不过:

    • 新进程将不包含在shell的作业列表中,因此不能使用shell的作业控制命令,例如 bg 和 fg .
    • 新的进程将在后台运行,在生锈程序退出后,shell将立即显示提示。

    失败的原因是 NVIM 是以儿童身份启动的,一旦调用程序停止,就会被杀死。

    这不是真的,也不是为了 Unix 也不 Windows .

    我怎样才能写作 执行 所以调用程序立即停止并让 NVIM (或其他程序)正确运行(即使没有任何窗口系统)?

    这应该就是您的Rust代码所做的(以及在我的Linux机器上运行时所做的)。另一方面,您答案中的代码还可以执行其他操作:它使用 execv() 到 代替 使用NVIM进行生锈处理。实际上,这个过程 不 立即停止,外壳重新绑定被阻止,直到NVIM退出。

        2
  •  1
  •   Denys Séguret    7 年前

    下面是Linux上的一个工作解决方案,使用包装 execv 功能:

    
    use nix::unistd;
    use std::ffi::CString;
    
    pub fn executev(args: &[&str]) {
        let mut args: Vec<CString> = args.iter()
            .map(|t| CString::new(*t).expect("not a proper CString"))
            .collect();
        unistd::execv(
            &args[0],
            &args,
        ).expect("failed");
    }
    
    fn main() {
        executev(&["/usr/bin/nvim", "/home/dys/todo.txt"]);
    }
    

    注: 这个 做 启动另一个程序并退出,但要注意替换当前进程意味着正确关闭了打开的资源。如果你能接受让你的程序继续运行,你可能想 wait 如Sven Marnach建议。

    推荐文章