一、啥是Shell脚本里的子进程

咱先说说啥是子进程。在Shell脚本这个世界里,就好像大树会分叉一样,一个脚本可能会分出其他小的进程来执行额外的任务,这些小进程就是子进程。打个比方,你有个脚本是用来下载文件的,在下载过程中,它还分出一个小进程去检查磁盘空间够不够,这个检查磁盘空间的就是子进程。

为啥要分离子进程呢?就好比一个人不能同时又做饭又洗衣服,Shell脚本把不同的任务分给子进程,就可以同时做好几件事,提高效率。比如说,你有个大脚本要处理很多文件,一边处理文件内容,一边统计文件数量,如果都在主进程里做,那就只能一件事一件事来,分离子进程后,两个任务就能同时进行了。

二、怎么创建子进程

在Shell脚本里创建子进程挺简单的,一般用&符号就可以。下面给大家看个例子:

# 技术栈:Shell
#!/bin/bash

# 定义一个函数作为子进程要执行的任务
function child_task {
    sleep 5  # 让子进程休眠5秒
    echo "子进程任务完成"
}

# 创建子进程,在函数调用后面加上 & 符号
child_task &
echo "主进程继续执行其他任务"

在这个例子里,child_task函数就是子进程要执行的任务。当我们在调用child_task后面加上&符号时,这个任务就会在后台作为子进程运行,主进程可以继续执行下面的代码,输出主进程继续执行其他任务,而不用等子进程的任务完成。

还有一种方式,使用( )来创建子进程。看下面这个例子:

# 技术栈:Shell
#!/bin/bash

# 创建子进程,用括号把命令括起来
( 
    sleep 3  # 子进程休眠3秒
    echo "括号创建的子进程任务完成"
) &
echo "主进程正常工作"

这里用( )把要执行的命令括起来,再加上&,就创建了一个子进程。主进程不会等待( )里的命令执行完,而是继续执行下面的echo "主进程正常工作"

三、怎样监控子进程

监控子进程很重要,就像你养了个小宠物,得知道它过得咋样。我们可以用脚本的返回状态来监控子进程。看下面这个例子:

# 技术栈:Shell
#!/bin/bash

# 定义一个函数作为子进程任务
function child_task {
    sleep 2
    return 0  # 正常返回
}

child_task &  # 创建子进程
child_pid=$!  # 获取子进程的进程ID

wait $child_pid  # 等待子进程结束
exit_status=$?  # 获取子进程的退出状态

if [ $exit_status -eq 0 ]; then
    echo "子进程正常结束"
else
    echo "子进程异常结束,退出状态:$exit_status"
fi

在这个例子里,$!能获取到刚创建的子进程的进程ID。wait命令会让主进程等待指定的子进程结束,然后用$?获取子进程的退出状态。如果退出状态是0,就说明子进程正常结束;要是别的值,就代表子进程可能出问题了。

我们还可以用ps命令来监控子进程。看这个例子:

# 技术栈:Shell
#!/bin/bash

function child_task {
    sleep 10
}

child_task &
child_pid=$!

echo "子进程ID: $child_pid"

# 每隔2秒检查一次子进程是否还在运行
while ps -p $child_pid > /dev/null; do
    echo "子进程还在运行..."
    sleep 2
done

echo "子进程已经结束"

这里用ps -p命令检查指定进程ID的子进程是否还在运行。如果还在运行,while循环就会继续,每隔2秒输出一次信息,直到子进程结束。

四、如何控制子进程

有时候,我们可能需要停止或者暂停子进程。先用kill命令来停止子进程,看例子:

# 技术栈:Shell
#!/bin/bash

function child_task {
    while true; do
        echo "子进程正在运行..."
        sleep 1
    done
}

child_task &
child_pid=$!

echo "子进程ID: $child_pid"

# 等待5秒后停止子进程
sleep 5
kill $child_pid  # 发送终止信号给子进程

# 检查子进程是否已经停止
wait $child_pid 2>/dev/null
if [ $? -ne 0 ]; then
    echo "子进程已经停止"
else
    echo "子进程还在运行"
fi

在这个例子里,子进程是一个无限循环,每隔1秒输出一次信息。主进程等待5秒后,用kill命令给子进程发送一个终止信号,然后用wait命令检查子进程是否已经停止。

要是想暂停子进程,可以用kill -STOP命令;想让暂停的子进程继续,可以用kill -CONT命令。看下面这个例子:

# 技术栈:Shell
#!/bin/bash

function child_task {
    while true; do
        echo "子进程正在运行..."
        sleep 1
    done
}

child_task &
child_pid=$!

echo "子进程ID: $child_pid"

# 等待3秒后暂停子进程
sleep 3
kill -STOP $child_pid
echo "子进程已暂停"

# 等待3秒后让子进程继续
sleep 3
kill -CONT $child_pid
echo "子进程继续运行"

这里先让子进程运行3秒,然后用kill -STOP暂停它,再等待3秒,用kill -CONT让它继续运行。

五、应用场景

批量任务处理

假如你有一堆文件要处理,比如给图片加水印。你可以写个Shell脚本,把处理每张图片的任务分给不同的子进程,这样就能同时处理多张图片,大大提高效率。

# 技术栈:Shell
#!/bin/bash

# 定义图片处理函数
function process_image {
    image=$1
    convert $image -gravity southeast -pointsize 30 -fill white -annotate +10+10 'Watermark' watermarked_$image
    echo "图片 $image 处理完成"
}

# 遍历图片文件
for image in *.jpg; do
    process_image $image &  # 创建子进程处理图片
done

wait  # 等待所有子进程结束
echo "所有图片处理完成"

服务监控

你有个Web服务在运行,你可以写个Shell脚本来监控它。脚本可以创建子进程,每隔一段时间去检查服务的状态,如果服务挂了就自动重启。

# 技术栈:Shell
#!/bin/bash

function check_service {
    while true; do
        if ! pgrep -x "nginx" > /dev/null; then
            echo "Nginx服务未运行,正在重启..."
            systemctl restart nginx
        fi
        sleep 60
    done
}

check_service &
echo "服务监控子进程已启动"

六、技术优缺点

优点

  • 提高效率:像前面说的,子进程能让脚本同时处理多个任务,就像一个团队分工合作,比一个人干多个活快多了。
  • 资源隔离:子进程有自己独立的运行环境,一个子进程出问题,一般不会影响其他子进程和主进程,就像不同的房间,一个房间着火了,不会马上烧到其他房间。
  • 灵活性高:可以根据不同的需求创建不同的子进程,完成各种各样的任务,就像搭积木一样,想搭成啥样都行。

缺点

  • 资源消耗:每个子进程都要占用一定的系统资源,要是创建太多子进程,可能会让系统变得很慢,就像一个房间里挤了太多人,大家都施展不开。
  • 管理复杂:子进程多了,管理起来就麻烦了,比如监控子进程的状态、协调子进程之间的关系等,就像一个班级学生太多,老师管理起来就费劲。
  • 错误处理难:子进程出问题后,有时候很难找到问题出在哪,因为子进程是在后台运行的,就像一个黑盒子,不知道里面发生了啥。

七、注意事项

进程ID的管理

在脚本里要妥善管理子进程的进程ID,不然可能会误杀其他进程。每次创建子进程后,要及时记录它的进程ID,用的时候要确保是正确的。

资源释放

子进程结束后,要确保释放它占用的资源,比如文件句柄、网络连接等。不然时间长了,系统资源会被耗尽,就像一个房间里堆满了垃圾,再也放不下新东西了。

信号处理

要处理好子进程接收到的信号,比如SIGTERMSIGINT等。不同的信号有不同的含义,要根据信号的含义做出正确的处理,不然子进程可能会异常退出。

八、文章总结

在Shell脚本里,子进程是个很有用的工具,能让脚本同时完成多个任务,提高效率。我们可以用&( )来创建子进程,用返回状态和ps命令来监控子进程,用kill命令来控制子进程。子进程在批量任务处理、服务监控等场景有很大的作用,但也有资源消耗大、管理复杂等缺点。在使用子进程时,要注意进程ID的管理、资源释放和信号处理等问题。掌握好子进程的创建、监控和控制,能让你的Shell脚本更强大、更灵活。