0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

文盘Rust--把程序作为守护进程启动

jf_wN0SrCdH 来源:Rust语言中文社区 作者:Rust语言中文社区 2022-11-07 10:22 次阅读

当我们写完一个服务端程序,需要上线部署的时候,或多或少都会和操作系统的守护进程打交道,毕竟谁也不希望shell关闭既停服。今天我们就来聊聊这个事儿。

最早大家部署应用的通常操作是 “nohup xxxx &”,别说像weblogic 或者其他java 容器有启动脚本,里面其实也差不多;很喜欢 nginx的 -d 参数,或者像redis 配置文件里可以指定是否以守护进程启动。看起来很优雅。

那么,使用rust 写一个服务端程序能不能优雅的使用一个参数指定应用 daemon 模式启动,同时使用stop 方式优雅的停机呢?我们通过一个例子来说说基本的实现方式。

实例代码依然集成在[interactcli-rs](https://github.com/jiashiwen/interactcli-rs)工程中。

首先来模拟一个启动的服务进程 /src/server/server.rs

```rust
pub fn start(prefix: String) {
    for i in 0..1000 {
        println!("{}", prefix.clone() + &i.to_string());
        thread::sleep(Duration::from_secs(1));
    }
}
```
程序每秒输出一个字符串,持续999秒,这个时间足够验证实验结果了。

后台启动有两个实现,分别是利用[fork](github.com/immortal/fork) 或 [daemonize](github.com/knsd/daemonize),这两个crate 实现原理类似,但在使用上稍有不同。

/src/cmd/cmdserver.rs,构建了两个启动子命令,分别来调用 fork 和 daemonize的守护进程启动实现。

```rust
pub fn new_server_cmd() -> Command {
    clap::new("server")
        .about("server")
        .subcommand(server_start_byfork())
        .subcommand(server_start_bydaemonize())
}


pub fn server_start_byfork() -> Command {
    clap::new("byfork")
        .about("start daemon by fork crate")
        .arg(
            Arg::new("daemon")
                .short('d')
                .long("daemon")
                .action(ArgAction::SetTrue)
                .help("start as daemon")
                .required(false),
        )
}
pub fn server_start_bydaemonize() -> Command {
    clap::new("bydaemonize")
        .about("start daemon by daemonize crate")
        .arg(
            Arg::new("daemon")
                .short('d')
                .long("daemon")
                .action(ArgAction::SetTrue)
                .help("start as daemon")
                .required(false),
        )
}
```
server 的子命令 byfork 启动 通过 fork 实现的功能,bydaemonize 则调用通过 daemonize 的功能实现。

命令解析的代码在 /src/cmd/rootcmd.rs 文件中。

先来看看基于 fork 的实现:

```rust
if let Some(startbyfork) = server.subcommand_matches("byfork") {
    println!("start by fork");
    if startbyfork.get_flag("daemon") {
        let args: Vec = env::args().collect();
        if let Ok(Fork::Child) = daemon(true, false) {
            // 启动子进程
            let mut cmd = Command::new(&args[0])
            for idx in 1..args.len() {
                let arg = args.get(idx).expect("get cmd arg error!");
                // 去除后台启动参数,避免重复启动
                if arg.eq("-d") || arg.eq("-daemon") {
                    continue;
                }
                cmd.arg(arg);
            
            let child = cmd.spawn().expect("Child process failed to start.");
            fs::write("pid", child.id().to_string()).unwrap();
            println!("process id is:{}", std::id());
            println!("child id is:{}", child.id());
        }
        println!("{}", "daemon mod");
        process::exit(0);
    }
    start("by_fork:".to_string());
}


```

首先,通过 Fork::daemon 函数派生出一个子进程;然后解析一下当前命令,去掉 -d 参数,构建一个启动命令,子命令启动,退出父进程。这基本符合操作系统创建守护进程的过程 -- 两次 fork。

再来看看基于 daemonize 的实现:

```rust
if let Some(startbydaemonize) = server.subcommand_matches("bydaemonize") {
            println!("start by daemonize");
            let base_dir = env::current_dir().unwrap();
            if startbydaemonize.get_flag("daemon") {
                let stdout = File::create("/tmp/daemon.out").unwrap();
                let stderr = File::create("/tmp/daemon.err").unwrap();


                println!("{:?}", base_dir);


                let daemonize = Daemonize::new()
                    .pid_file("/tmp/test.pid") // Every method except `new` and `start`
                    .chown_pid_file(true) // is optional, see `Daemonize` documentation
                    .working_directory(base_dir.as_path()) // for default behaviour.          
                    .umask(0o777) // Set umask, `0o027` by default.
                    .stdout(stdout) // Redirect stdout to `/tmp/daemon.out`.
                    .stderr(stderr) // Redirect stderr to `/tmp/daemon.err`.
                    .privileged_action(|| "Executed before drop privileges");


                match daemonize.start() {
                    Ok(_) => {
                        println!("Success, daemonized");
                    }
                    Err(e) => eprintln!("Error, {}", e),
                }
            }
            println!("pid is:{}", std::id());
            fs::write("pid", process::id().to_string()).unwrap();
            start("by_daemonize:".to_string());
        }
```

首先获取当前的工作目录,默认情况下 daemonize 会将工作目录设置为 "/",为了避免权限问题,我们获取当前目录作为守护进程的工作目录。不知道是什么原因,在配置了pid_file 后,启动守护进程时并没在文件中有记录 pid。不过也没关系,我们可以在外部获取并记录守护进程的pid。

两种方式启动的守护进程均可在关闭shell的情况下维持进程运行。

从实现上来讲,不论是 fork 还是 daemonize 都是 通过unsafe 方式调用了 libc api,类 unix 系统大多跑起来没问题,windows 系统作者没有验证。

本期关于守护进程的话题就聊到这儿。

咱们下期见。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 程序
    +关注

    关注

    115

    文章

    3719

    浏览量

    80350
  • Rust
    +关注

    关注

    1

    文章

    226

    浏览量

    6494

原文标题:文盘Rust -- 把程序作为守护进程启动

文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    深度无多配置一些经验心得

    的情况下,在安装守护驱动前,个人一些建议,首先我们要确保intel 和amd的机器都能正常无启动。大家都知道,网吧做母盘时,遇到在intel机器上做完的母盘拿到amd机器上蓝屏的情况,这个本身是intel
    发表于 07-19 09:22

    Linux守护进程

    )是不能卸载的,这对以后的使用会造成诸多的麻烦(如系统由于某种原因要进入单用户模式)。因此,通常的做法是让“/”作为守护进程的当前工作目录,这样就可以避免上述问题。当然,如有特殊需要,也可以
    发表于 08-22 09:17

    【Linux学习杂谈】之守护进程以及简单创建

    首先我们需要了解一下什么叫做守护进程,以及我们为什么需要这样的进程。我们知道当我们写一个简单的程序的时候我们知道,这个程序比如说printf
    发表于 09-27 13:28

    升级程序作为一个单独进程的方法

    步骤:升级程序作为一个单独的进程1. 定时请求2. 对比版本号(使用正则匹配或字符串提取主版本号、次版本号、末版本号)3. 当前版本号较小时下载升级资源包4. 备份当前版本程序5. 解
    发表于 12-17 07:33

    Xtensa调试器守护进程在Linux中不工作的原因?怎么解决?

    试器守护进程” 中,我从路径 /opt/xtensa/XtDevTools/downloads/RI-2021.8/tools/ 安装 xt-ocd-14.08-linux64-installer。安装
    发表于 03-21 08:39

    iny Linux有没有办法设置ssh或telnet守护进程可以在启动后自动执行?

    Linux 有没有办法设置ssh 或telnet 守护进程可以在启动后自动执行? 我们想在不通过控制台的情况下使用 ssh 或 telnet 连接到微型 Linux。
    发表于 04-23 06:16

    守护进程的初级教程

    守护进程的初级教程,浅显易懂,适合初学者
    发表于 06-17 16:16 0次下载

    Linux守护进程详解

    较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导载入时启动,在系统关闭时终止。Linux有很多系统服务,大多数服务都是通过
    发表于 10-18 14:24 0次下载
    Linux<b class='flag-5'>守护</b><b class='flag-5'>进程</b>详解

    linux守护进程实例

      今天完成一个守护进程实验。  1 熟悉守护进程编写和调试(系统日志)  2 编写多进程
    发表于 04-02 14:42 356次阅读

    U启动盘制作工具应用程序免费下载

    本文档的主要内容详细介绍的是U启动盘制作工具应用程序免费下载。
    发表于 04-24 08:00 5次下载
    U<b class='flag-5'>盘</b><b class='flag-5'>启动盘</b>制作工具应用<b class='flag-5'>程序</b>免费下载

    Linux 安全模块:守护进程和套接字

    守护进程通常是在后台观察操作以等待状态、服务于特定子系统并确定整个系统的操作规则的实用程序。例如,一个守护进程被配置为监控打印服务的状态。
    发表于 08-26 10:01 594次阅读

    Rust -- rust连接oss

    我们以 [S3 sdk](https://github.com/awslabs/aws-sdk-rust)为例来说说基本的连接与操作,作者验证过aws、京东云、阿里云。主要的增删改查功能没有什么差别。
    的头像 发表于 05-12 16:18 489次阅读

    Rust -- tokio绑定cpu实践

    )。core_affinity_rs是一个用于管理CPU亲和力的Rust crate。目前支持Linux、Mac OSX和Windows。官方宣称支持多平台,本人只做了linux 操作系统的测试。
    的头像 发表于 06-11 15:32 473次阅读
    <b class='flag-5'>文</b><b class='flag-5'>盘</b><b class='flag-5'>Rust</b> -- tokio绑定cpu实践

    程序进程和线程的区别

    什么是进程 1、进程和线程的区别 进程是指正在运行的程序,它拥有独立的内存空间和系统资源,不同进程之间的数据不共享。
    的头像 发表于 06-22 11:39 525次阅读
    <b class='flag-5'>程序</b>中<b class='flag-5'>进程</b>和线程的区别

    Linux中如何编写守护进程程序

    的一种进程,它们一般在系统启动时开始运行,除非强行终止,否则直到系统关机都会保持运行。与守护进程相比,普通进程都是在用户登录或运行
    的头像 发表于 10-07 17:12 529次阅读
    Linux中如何编写<b class='flag-5'>守护</b><b class='flag-5'>进程</b><b class='flag-5'>程序</b>