一、前言

前面结束过使用fork()创建多个进程,使用sem_open(), sem_wait(), sem_post()保护临界区资源。 但是遗漏了一个重要的事情,如果调用fork()之后,父进程先结束了。本来要在父进程里面做些清理工作。这时候就尴尬了 爸爸process没了,儿子process还在。

二、演示父进程先结束

1.Cargo.toml 配置

1
2
[dependencies]
nix = "0.16.1"

2. 父进程结束,子进程不结束–最小化example

下面的代码会先输出

Continuing execution in parent process, new child has pid
bye bye main!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//use nix::sys::wait;
use nix::unistd::{fork, ForkResult};
use std::{thread, time};

fn main() {
    match fork() {
        // 父进程
        Ok(ForkResult::Parent { child, .. }) => {
            println!(
                "Continuing execution in parent process, new child has pid: {}",
                child
            );
        }
        // 子进程
        Ok(ForkResult::Child) => {
            let sec = time::Duration::from_secs(10);
            thread::sleep(sec);
            println!("I'm a new child process")
        }
        Err(err) => println!("Fork failed:{}", err),
    }

    //match wait::wait() {
    //    Ok(status) => println!("Child exited with status {:?}", status),
    //    Err(err) => panic!("waitpid() failed: {}", err),
    //}
    println!("bye bye main !")
}

三、父进程等待子进程先结束

要变成父进程等待子进程结束然后退出。只要把上面的注释去掉就行。这里揭晓下谜底wait() system call

man 说明文档

The wait() system call suspends execution of the calling process until one of its children terminates. The call wait(&wstatus) is equivalent to:

waitpid(-1, &wstatus, 0);

四、在rust实现erlang的哲学

已经有fork和wait,我们也许可以实现下erlang的哲学理念.

1.回顾下erlang的哲学

  1. 让其它进程来修复错误。

  2. 工作者不成功,便成仁。(if you can’t do what you want to do, die.)

  3. 任它崩溃。

  4. 杜绝防御式编程。

2.解析erlang哲学

第一点可以理解为,如果监控到work进程崩溃,fork新的work 进程继续干活。2,3,4只是一个态度,好办.

3.Cargo.toml

1
2
[dependencies]
nix = "0.16.1"

4.任他奔溃,工作者不成功,便成仁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
use nix::sys::wait;
use nix::unistd::{fork, ForkResult};
use std::{thread, time};

fn proc_new() {
    match fork() {
        // 父进程
        Ok(ForkResult::Parent { child, .. }) => {
            println!(
                "Continuing execution in parent process, new child has pid: {}",
                child
            );
        }
        // 子进程
        Ok(ForkResult::Child) => {
            let sec = time::Duration::from_secs(3);
            thread::sleep(sec);
            println!("I'm a new child process");

            // 模拟coredump 或者段错误
            panic!("child process dead");
        }
        Err(err) => println!("Fork failed:{}", err),
    }
}

fn main() {
    proc_new();

    // 监控子进程状态
    loop {
        match wait::wait() {
            Ok(status) => {
                println!("Child exited with status {:?}", status);
                proc_new();
            }

            Err(err) => panic!("waitpid() failed: {}", err),
        }
    }
}

5. ps命令测试

撸码的最后一个环境是测试。这里使用``ps```测试以上代码是否都保持在2个进程

1
2
3
4
s aux |grep use_
# 输出
# guo      12288  0.0  0.0  13304  1012 pts/3    S+   14:44   0:00 ./target/debug/use_fork_wait
# guo      12297  0.0  0.0  13304   136 pts/3    S+   14:45   0:00 ./target/debug/use_fork_wait

6. 还可以优化的地方

使用wait() 监控子进程状态,然后fork进程,这里根据错误数设计滑动窗口,削峰填谷。解决系统的抖动。

五、参考资料

https://iximiuz.com/en/posts/dealing-with-processes-termination-in-Linux/