文盘Rust--启动程序作为守护进程停止运行。今天我们就来说说这个问题。大家部署应用最早的常用操作就是“nohupxxxx&”,何况weblogic或者其他java容器都有启动脚本,其实都差不多;我喜欢nginx的-d参数,也可以指定是否守护进程启动。它看起来很优雅。那么,用rust编写的服务器程序是否可以通过一个参数指定应用守护进程方式优雅启动,同时使用stop方法优雅停止呢?下面通过一个例子来谈谈基本的实现。示例代码仍然集成在interactcli-rs项目中。首先模拟一个启动的服务进程/src/server/server.rspubfnstart(prefix:String){foriin0..1000{println!("{}",prefix.clone()+&i.to_string());thread::sleep(Duration::from_secs(1));}}程序每秒输出一个字符串,持续999秒,足够验证实验结果的时间。后台启动有两种实现方式,分别使用fork或者daemonize。这两个crate的实现原理相似,但在使用上略有不同。/src/cmd/cmdserver.rs,构建两个启动子命令,分别调用fork和daemonize的daemon进程启动实现。pubfnnew_server_cmd()->命令{clap::Command::new("server").about("server").subcommand(server_start_byfork()).subcommand(server_start_bydaemonize())}pubfnserver_start_byfork()->Command{clap::Command::new("byfork").about("通过forkcrate启动守护进程").arg(Arg::new("daemon").short('d').long("daemon").action(ArgAction::SetTrue).help("startasdaemon").required(false),)}pubfnserver_start_bydaemonize()->Command{clap::Command::new("bydaemonize").about("通过daemonizecrate启动守护进程").arg(Arg::new("daemon").short('d').long("daemon").action(ArgAction::SetTrue).help("startasdaemon").required(false),)}server的子命令byfork启动通过fork实现的功能,bydaemonize通过/src/cmd/rootcmd.rs文件中的daemonize功能调用实现命令解析的代码。我们先看看基于fork的实现ifstartbyfork.get_flag("daemon"){letargs:Vec=env::args().collect();ifletOk(Fork::Child)=daemon(true,false){//启动子进程letmutcmd=Command::new(&args[0])foridxin1..args.len(){letarg=args.get(idx).expect("获取cmdarg错误!");//去除后台启动参数,避免重复启动ifarg.eq("-d")||arg.eq("-daemon"){继续;}cmd.arg(arg);letchild=cmd.spawn().expect("子进程启动失败。");fs::write("pid",child.id().to_string()).unwrap();println!("进程ID为:{}",std::process::id());println!("孩子id是:{}",child。ID());}println!("{}","daemonmod");过程::退出(0);}start("by_fork:".to_string());}首先,通过Fork::daemon函数产生一个子进程;然后解析当前命令,去掉-d参数,构建启动命令,启动子命令,退出父进程。这基本符合操作系统中创建守护进程的过程——两次fork。下面看一下基于daemonize的实现。ifletSome(startbydaemonize)=server.subcommand_matches("bydaemonize"){println!("startbydaemonize");让base_dir=env::current_dir().unwrap();如果startbydaemonize.get_flag("daemon"){让stdout=File::create("/tmp/daemon.out").unwrap();让stderr=File::create("/tmp/daemon.err").unwrap();println!("{:?}",base_dir);letdaemonize=Daemonize::new().pid_file("/tmp/test.pid")//除`new`和`start`之外的每个方法.chown_pid_file(true)//都是可选的,请参阅`Daemonize`文档.working_directory(base_dir.as_path())//用于默认行为。.umask(0o777)//设置umask,默认为`0o027`。.stdout(stdout)//将stdout重定向到`/tmp/daemon.out`。.stderr(stderr)//将stderr重定向到`/tmp/daemon.err`。.privileged_action(||"在删除权限之前执行");matchdaemonize.start(){Ok(_)=>{println!("成功,守护进程");}Err(e)=>eprintln!("Error,{}",e),}}println!("pidis:{}",std::process::id());fs::write("pid",process::id().to_string()).unwrap();开始(“by_daemonize:”。to_string());}首先获取当前工作目录,默认情况下daemonize会将工作目录设置为“/”。为了避免权限问题,我们获取当前目录作为守护进程的工作目录。我不知道为什么。配置pid_file后,守护进程启动时,pid不会记录在文件中。不过没关系,我们可以在外部获取并记录守护进程的pid。两种方式启动的守护进程即使在shell关闭的情况下也能保持进程运行。在实现方面,fork和daemonize都以不安全的方式调用libcapi。大多数unix-like系统运行没有问题,windows系统的作者没有验证过。本期守护进程的话题到此结束。下次见。作者:贾世文