Linux 进程等待

LinuxBeginner
立即练习

介绍

Linux 进程是操作系统的基本组成部分,用于执行程序和完成任务。在开发 shell 脚本时,通常需要同时运行多个进程,并确保它们完成后再继续后续操作。

在这个实验中,你将学习 Linux shell 脚本中的 wait 命令。这个强大的工具允许父进程暂停执行,直到其后台子进程完成。通过掌握进程等待技术,你可以创建更高效的脚本,以正确协调多个并发操作。

理解进程等待对于系统管理、自动化和开发健壮的 shell 脚本至关重要。你将学习如何启动后台进程、等待它们完成,并处理它们的退出状态,以确保可靠的执行流程。

这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 初级 级别的实验,完成率为 92%。获得了学习者 97% 的好评率。

理解 Linux 进程和后台执行

在这一步中,你将了解 Linux 进程,以及如何使用 & 运算符在后台运行命令。

什么是 Linux 进程?

Linux 中的进程是正在运行的程序的实例。每个进程都有一个唯一的进程 ID (PID),并且独立于其他进程运行。当你在终端中运行命令时,就会启动一个进程。

在后台运行进程

通常,当你在终端中运行命令时,需要等待它完成后才能运行另一个命令。但是,你可以通过在命令末尾添加一个 & 符号来在后台运行命令。

让我们来试试看:

  1. 导航到你的项目目录:

    cd ~/project
  2. 创建一个模拟长时间运行任务的简单脚本:

    nano long_task.sh
  3. 向脚本中添加以下内容:

    #!/bin/bash
    echo "Starting long task with PID $$"
    sleep 5
    echo "Long task completed"
  4. Ctrl+O 保存文件,然后按 Enter,再按 Ctrl+X 退出。

  5. 使脚本可执行:

    chmod +x long_task.sh
  6. 正常运行脚本:

    ./long_task.sh

    你会看到类似如下的输出:

    Starting long task with PID 1234
    Long task completed

    注意,你必须等待脚本完成后才能再次获得命令提示符。

  7. 现在在后台运行脚本:

    ./long_task.sh &

    你会看到类似如下的输出:

    [1] 1235
    Starting long task with PID 1235

    [1] 是作业编号,1235 是 PID。注意,你会立即获得命令提示符。

  8. 大约 5 秒后,你会看到:

    Long task completed
    [1]+  Done                    ./long_task.sh

    这表明后台进程已完成。

当你在后台运行命令时,shell 不会等待它完成就允许你输入更多命令。这对于同时运行多个任务很有用。

使用 wait 命令同步进程

在这一步中,你将学习如何使用 wait 命令来同步进程,使父进程等待后台进程完成。

什么是 wait 命令?

wait 命令用于 shell 脚本中,它会暂停脚本的执行,直到一个或多个后台进程完成。当你需要确保某些任务完成后再继续后续操作时,这个命令特别有用。

不带参数使用 wait

当不带参数使用 wait 命令时,它会等待所有后台进程完成。

让我们创建一个脚本来演示这一点:

  1. 导航到你的项目目录:

    cd ~/project
  2. 创建一个新脚本:

    nano wait_demo.sh
  3. 向脚本中添加以下内容:

    #!/bin/bash
    
    echo "Starting background tasks..."
    
    ## Start two background tasks
    ./long_task.sh &
    ./long_task.sh &
    
    echo "Waiting for all background tasks to complete..."
    wait
    echo "All background tasks have completed!"
  4. Ctrl+O 保存文件,然后按 Enter,最后按 Ctrl+X 退出编辑器。

  5. 使脚本可执行:

    chmod +x wait_demo.sh
  6. 运行脚本:

    ./wait_demo.sh

    你会看到类似如下的输出:

    Starting background tasks...
    Starting long task with PID 1236
    Starting long task with PID 1237
    Waiting for all background tasks to complete...
    Long task completed
    Long task completed
    All background tasks have completed!

注意,“All background tasks have completed!”消息只有在两个长时间运行的任务都完成后才会出现。这展示了 wait 命令如何暂停脚本,直到所有后台进程完成。

带特定 PID 使用 wait

你也可以通过提供特定进程的 PID 来使用 wait 命令等待该进程:

  1. 创建另一个脚本:

    nano wait_pid_demo.sh
  2. 添加以下内容:

    #!/bin/bash
    
    echo "Starting background tasks..."
    
    ## Start two background tasks and capture their PIDs
    ./long_task.sh &
    pid1=$!
    
    ./long_task.sh &
    pid2=$!
    
    echo "First process PID: $pid1"
    echo "Second process PID: $pid2"
    
    echo "Waiting for the first task to complete..."
    wait $pid1
    echo "First task has completed!"
    
    echo "Waiting for the second task to complete..."
    wait $pid2
    echo "Second task has completed!"
  3. Ctrl+O 保存文件,然后按 Enter,最后按 Ctrl+X 退出编辑器。

  4. 使脚本可执行:

    chmod +x wait_pid_demo.sh
  5. 运行脚本:

    ./wait_pid_demo.sh

    输出将显示脚本会分别等待每个进程完成。

$! 变量包含最近执行的后台进程的 PID。这使你能够捕获并在后续使用该 PID 与 wait 命令配合使用。

处理 wait 命令的返回状态

在这一步中,你将学习如何捕获和处理 wait 命令的返回状态,该状态反映了后台进程的退出状态。

理解退出状态和返回状态

在 Linux 中,每个命令完成时都会返回一个退出状态。退出状态为 0 通常表示成功,而非零值则表示出现错误或某种形式的失败。

wait 命令返回所等待命令的退出状态。如果等待多个进程,它将返回最后一个终止进程的退出状态。

让我们创建脚本来演示这一点:

  1. 导航到你的项目目录:

    cd ~/project
  2. 创建一个执行成功的脚本:

    nano success_task.sh
  3. 添加以下内容:

    #!/bin/bash
    echo "Starting success task"
    sleep 2
    echo "Success task completed successfully"
    exit 0 ## Exit with success status
  4. Ctrl+O 保存,然后按 Enter,最后按 Ctrl+X 退出。接着使其可执行:

    chmod +x success_task.sh
  5. 创建一个执行失败的脚本:

    nano fail_task.sh
  6. 添加以下内容:

    #!/bin/bash
    echo "Starting fail task"
    sleep 3
    echo "Fail task encountered an error"
    exit 1 ## Exit with error status
  7. Ctrl+O 保存,然后按 Enter,最后按 Ctrl+X 退出。接着使其可执行:

    chmod +x fail_task.sh
  8. 现在创建一个捕获 wait 状态的脚本:

    nano wait_status_demo.sh
  9. 添加以下内容:

    #!/bin/bash
    
    echo "Running a successful background task..."
    ./success_task.sh &
    wait
    
    wait_status=$?
    echo "Wait returned with status: $wait_status"
    
    if [ $wait_status -eq 0 ]; then
      echo "The background task succeeded!"
    else
      echo "The background task failed with status: $wait_status"
    fi
    
    echo ""
    echo "Running a failing background task..."
    ./fail_task.sh &
    wait
    
    wait_status=$?
    echo "Wait returned with status: $wait_status"
    
    if [ $wait_status -eq 0 ]; then
      echo "The background task succeeded!"
    else
      echo "The background task failed with status: $wait_status"
    fi
  10. Ctrl+O 保存,然后按 Enter,最后按 Ctrl+X 退出。接着使其可执行:

    chmod +x wait_status_demo.sh
  11. 运行脚本:

    ./wait_status_demo.sh

    你会看到类似如下的输出:

    Running a successful background task...
    Starting success task
    Success task completed successfully
    Wait returned with status: 0
    The background task succeeded!
    
    Running a failing background task...
    Starting fail task
    Fail task encountered an error
    Wait returned with status: 1
    The background task failed with status: 1

这展示了你如何使用 wait 命令的返回状态来确定后台进程是否成功完成,这对于 shell 脚本中的错误处理至关重要。

完成一个实际应用

在这最后一步中,你将运用所学知识创建一个更复杂的脚本,该脚本模拟一个系统准备过程。此脚本将协调多个后台任务,并确保它们都成功完成后再继续执行后续操作。

创建准备脚本

首先,我们将创建两个模拟不同准备任务的脚本:

  1. 导航到你的项目目录:

    cd ~/project
  2. 创建第一个准备脚本:

    nano decorate_hall.sh
  3. 添加以下内容:

    #!/bin/bash
    echo "Decorating the hall..."
    sleep 3
    echo "Hanging decorations..."
    sleep 2
    echo "Decoration complete."
    exit 0
  4. Ctrl+O 保存,然后按 Enter,最后按 Ctrl+X 退出。接着使其可执行:

    chmod +x decorate_hall.sh
  5. 创建第二个准备脚本:

    nano cook_feast.sh
  6. 添加以下内容:

    #!/bin/bash
    echo "Preparing ingredients..."
    sleep 2
    echo "Cooking main dishes..."
    sleep 3
    echo "Preparing desserts..."
    sleep 1
    echo "Cooking complete."
    exit 0
  7. Ctrl+O 保存,然后按 Enter,最后按 Ctrl+X 退出。接着使其可执行:

    chmod +x cook_feast.sh

创建主协调脚本

现在,让我们创建一个主脚本,用于协调这些准备任务:

  1. 创建主脚本:

    nano prepare_feast.sh
  2. 添加以下内容:

    #!/bin/bash
    
    echo "=== Preparation Ceremony Started ==="
    echo "Starting all preparation tasks..."
    
    ## Start the preparations in the background
    ./decorate_hall.sh &
    decoration_pid=$!
    
    ./cook_feast.sh &
    cooking_pid=$!
    
    echo "All tasks started. Waiting for completion..."
    
    ## Wait for decoration to finish
    wait $decoration_pid
    decoration_status=$?
    
    if [ $decoration_status -eq 0 ]; then
      echo "Decoration completed successfully."
    else
      echo "Error: Decoration failed with status $decoration_status"
      exit 1
    fi
    
    ## Wait for cooking to finish
    wait $cooking_pid
    cooking_status=$?
    
    if [ $cooking_status -eq 0 ]; then
      echo "Cooking completed successfully."
    else
      echo "Error: Cooking failed with status $cooking_status"
      exit 1
    fi
    
    ## All preparations completed successfully
    echo "=== All preparations completed successfully! ==="
    echo "The ceremony can now begin."
    
    ## Create a verification file to indicate successful completion
    echo "decoration_status=$decoration_status" > preparation_results.txt
    echo "cooking_status=$cooking_status" >> preparation_results.txt
    echo "overall_status=0" >> preparation_results.txt
    
    exit 0
  3. Ctrl+O 保存,然后按 Enter,最后按 Ctrl+X 退出。接着使其可执行:

    chmod +x prepare_feast.sh
  4. 运行主脚本:

    ./prepare_feast.sh

    你将看到所有准备任务并发运行的输出,并且主脚本会等待每个任务完成后才宣布整体成功。

理解脚本

让我们来分析主脚本的关键部分:

  • 我们使用 & 让每个准备任务在后台启动
  • 我们使用 $! 捕获每个任务的 PID
  • 我们使用 wait $pid 等待每个任务完成
  • 我们检查每个任务的退出状态
  • 我们创建一个包含结果的验证文件

这种方法使我们能够:

  1. 并发运行多个任务
  2. 监控每个任务的成功/失败情况
  3. 如果任何单个任务失败,则中止整个过程
  4. 创建结果记录

这是系统管理和自动化脚本中的常见模式,在这些场景中,需要协调和监控多个进程。

总结

在这个实验中,你学习了如何使用 wait 命令在 Linux 中有效地管理和同步进程。这项重要技能使你能够创建更复杂的 shell 脚本,以协调多个并发操作。

你掌握的关键概念包括:

  • 使用 & 运算符在后台运行进程
  • 使用 wait 命令暂停执行,直到后台进程完成
  • 通过进程 ID(PID)等待特定进程
  • 捕获和处理 wait 命令的返回状态
  • 为后台进程实现错误处理
  • 在实际场景中协调多个并发任务

这些技能是 Linux 环境中系统管理、自动化和 shell 脚本编写的基础。通过正确管理进程同步,你可以创建更高效的脚本,在利用并发的同时,仍能保持依赖操作的正确顺序。

无论你是在开发构建系统、部署脚本还是系统管理工具,协调多个进程的能力都是一项必备技能,它能让你创建更强大、更高效的解决方案。