0%

子进程

子进程

什么是进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

查看下当前系统中的进程

1
2
3
4
5
6
7
8
9
10
[root@vm-101 ~]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 4月23 ? 00:00:04 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root 2 0 0 4月23 ? 00:00:00 [kthreadd]
root 4 2 0 4月23 ? 00:00:00 [kworker/0:0H]
root 6 2 0 4月23 ? 00:00:06 [ksoftirqd/0]
root 7 2 0 4月23 ? 00:00:00 [migration/0]
root 8 2 0 4月23 ? 00:00:00 [rcu_bh]
root 9 2 0 4月23 ? 00:00:26 [rcu_sched]
。。。。。。

什么是子进程

子进程就是在父进程中创建的进程。它会fork一份几乎完全一样的空间,来运行它的代码。

PID就是该程序的进程ID,PPID就是该程序的父进程ID。

父进程和子进程是两个不同的进程。进程里面的变量,函数,等不能直接访问。

什么时候会产生子进程

在bash中,主要有四中情况会产生子进程。需要特别注意,不然以后写脚本的时候,可能遇到一些奇奇怪怪的问题,无从下手。

  1. 创建一个新的bash

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [root@vm-101 ~]# echo $BASHPID		# 显示当前shell的进程ID
    4840
    [root@vm-101 ~]# bash # 创建一个新的shell
    [root@vm-101 ~]# echo $BASHPID # BASHPID和上面不一样了。
    5100
    [root@vm-101 ~]# ps -ef | grep bash
    root 3700 3698 0 04:24 pts/1 00:00:00 -bash
    root 4739 3700 0 05:25 pts/1 00:00:00 man bash
    root 4840 4838 0 05:28 pts/0 00:00:00 -bash
    root 5100 4840 0 05:42 pts/0 00:00:00 bash # 新创建的子shell的父进程是4840
    root 5123 5100 0 05:43 pts/0 00:00:00 grep --color=auto bash
  2. 管道: | |&

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    [root@vm-101 ~]# cat fork.sh
    #!/bin/bash

    # 注释说明,好习惯

    echo "当前BASHPID:$BASHPID"

    let a=$BASHPID

    echo "内部命令BASHPID:$BASHPID"

    echo | echo "管道后面的BASHPID:$BASHPID"
    echo "当前BASHPID:$BASHPID"

    exit 0
    [root@vm-101 ~]# bash fork.sh
    当前BASHPID:5247
    内部命令BASHPID:5247 # 内部命令不会创建新的进程,会在当前进程会执行。bash一共有61个内部命令
    管道后面的BASHPID:5249 # 虽然是内部命令,但是用到了管道操作,所以BASHPID不一样了。
    当前BASHPID:5247

    ########## 扩展一下内部命令
    [root@vm-101 ~]# compgen -b # 全部61个bash内部命令。可以看看。了解了解
    .
    :
    [
    alias
    bg
    bind
    break
    builtin
    caller
    cd
    command
    compgen
    complete
    compopt
    continue
    declare
    dirs
    disown
    echo
    enable
    eval
    exec
    exit
    export
    false
    fc
    fg
    getopts
    hash
    help
    history
    jobs
    kill
    let
    local
    logout
    mapfile
    popd
    printf
    pushd
    pwd
    read
    readarray
    readonly
    return
    set
    shift
    shopt
    source
    suspend
    test
    times
    trap
    true
    type
    typeset
    ulimit
    umask
    unalias
    unset
    wait
  3. 后台: &

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [root@vm-101 ~]# cat fork.sh
    #!/bin/bash

    # 注释说明,好习惯

    echo "当前BASHPID:$BASHPID"
    echo "后台的BASHPID:$BASHPID" &
    echo "当前BASHPID:$BASHPID"

    exit 0


    [root@vm-101 ~]# bash fork.sh
    当前BASHPID:5286
    当前BASHPID:5286 # 注意这个执行顺序。因为是后台创建一个新的进程,有性能开销,但是不影响前台的任务。所以这里后发先至,先打印出来了。
    后台的BASHPID:5287 # 后台创建的子进程BASHPID不一样了。
  4. 分组操作符:( )

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    [root@vm-101 ~]# cat fork.sh
    #!/bin/bash

    # 注释说明,好习惯

    echo "当前BASHPID:$BASHPID"
    ( echo "分组命令的BASHPID:$BASHPID" ) # ( )是命令分组操作符
    echo "当前BASHPID:$BASHPID"

    exit 0



    [root@vm-101 ~]# bash fork.sh
    当前BASHPID:5323
    分组命令的BASHPID:5324 # BASHPID也不一样了
    当前BASHPID:5323
  5. 调用外部命令

    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
    [root@vm-101 ~]# cat fork.sh
    #!/bin/bash

    # 注释说明,好习惯

    echo "当前BASHPID:$BASHPID"
    ping -c 5 www.baidu.com # ping不是内部命令。在5秒内,快速在另外一个终端,执行ps -ef看看新创建的子进程
    echo "当前BASHPID:$BASHPID"

    exit 0


    [root@vm-101 ~]# bash fork.sh
    当前BASHPID:5355
    PING www.a.shifen.com (39.156.66.14) 56(84) bytes of data.
    64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=1 ttl=63 time=6.41 ms
    64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=2 ttl=63 time=7.03 ms
    64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=3 ttl=63 time=6.27 ms
    64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=4 ttl=63 time=7.58 ms
    64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=5 ttl=63 time=7.10 ms

    --- www.a.shifen.com ping statistics ---
    5 packets transmitted, 5 received, 0% packet loss, time 4002ms
    rtt min/avg/max/mdev = 6.270/6.882/7.583/0.482 ms
    当前BASHPID:5355

    [root@vm-101 ~]# ps -ef | grep ping # 另外一个终端执行的
    root 5356 5355 0 06:00 pts/1 00:00:00 ping -c 5 www.baidu.com # ping进程的父进程是5355
    root 5358 5100 0 06:00 pts/0 00:00:00 grep --color=auto ping
  6. 命令替换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [root@vm-101 ~]# cat fork.sh
    #!/bin/bash

    # 注释说明,好习惯

    echo "当前BASHPID:$BASHPID"
    echo "命令替换时BASHPID:$(echo $BASHPID)" # 新知识点,命令替换
    echo "当前BASHPID:$BASHPID"

    exit 0


    [root@vm-101 ~]# bash fork.sh
    当前BASHPID:5407
    命令替换时BASHPID:5408
    当前BASHPID:5407
  7. 进程替换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [root@vm-101 ~]# cat fork.sh
    #!/bin/bash

    # 注释说明,好习惯

    echo "当前BASHPID:$BASHPID"
    echo "进程替换时BASHPID: `cat <(echo $BASHPID)`"
    echo "当前BASHPID:$BASHPID"

    exit 0


    [root@vm-101 ~]# bash fork.sh
    当前BASHPID:5549
    进程替换时BASHPID: 5551
    当前BASHPID:5549

以上这些情况要知道比较好。shell脚本不难,这个地方可能会是一个坑,有可能会在以后遇到。遇到了,没有这些思路,很容易抓瞎。

exec

说到子进程,就改提一提exec这个命令了。

一般我们执行一个外部命令都会生成一个子进程来运行这个外部命令。但是有的时候,我们不希望这样。性能是一方面,更重要的是子进程的运行环境完全不一样了。

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
[root@vm-101 ~]# cat fork.sh
#!/bin/bash

# 注释说明,好习惯

echo "当前BASHPID:$BASHPID"
exec ping -c 5 www.baidu.com # 快速切换到另外一个终端观察
echo "当前BASHPID:$BASHPID"

exit 0

[root@vm-101 ~]# bash fork.sh
当前BASHPID:6159
PING www.a.shifen.com (39.156.66.14) 56(84) bytes of data.
64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=1 ttl=63 time=7.24 ms
64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=2 ttl=63 time=6.05 ms
64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=3 ttl=63 time=6.12 ms
64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=4 ttl=63 time=7.06 ms
64 bytes from 39.156.66.14 (39.156.66.14): icmp_seq=5 ttl=63 time=6.63 ms

--- www.a.shifen.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4013ms
rtt min/avg/max/mdev = 6.053/6.624/7.246/0.486 ms
# 主要到这里没有? 第二个echo没有打印出来。因为ping程序就是当前shell,直接退出了。


[root@vm-101 ~]# ps -ef | grep ping # 这是在另外一个终端
root 6159 5100 0 06:48 pts/0 00:00:00 ping -c 5 www.baidu.com # 6159,就是脚本的BASHPID。对比上面的`调用外部命令`,就会发现不一样。
root 6161 3700 0 06:48 pts/1 00:00:00 grep --color=auto ping