第41集:进程概念与状态
章节标题
进程概念与状态
核心知识点讲解
进程的基本概念
进程是操作系统中正在运行的程序的实例。它包含了程序代码、数据、堆栈、寄存器状态以及其他资源。在Linux系统中,每个进程都有自己独立的地址空间和系统资源。
进程与程序的区别
| 特性 | 程序 | 进程 |
|---|---|---|
| 定义 | 静态的可执行文件 | 动态的执行过程 |
| 存在形式 | 存储在磁盘上的文件 | 存在于内存中 |
| 生命周期 | 永久存在 | 有创建、执行、终止的生命周期 |
| 资源占用 | 不占用系统资源 | 占用CPU、内存等系统资源 |
| 独立性 | 无 | 独立的地址空间和资源 |
进程标识符
进程ID (PID)
每个进程都有一个唯一的进程ID (Process ID),用于在系统中标识和管理进程。PID是一个非负整数,通常从1开始递增。
# 查看当前进程的PID
echo $$
# 查看特定进程的PID
pgrep 进程名父进程ID (PPID)
每个进程都有一个父进程,父进程ID (Parent Process ID) 标识了创建当前进程的进程。
# 查看当前进程的PPID
echo $PPID进程组ID (PGID)
进程组是一个或多个进程的集合,它们共享同一个进程组ID (Process Group ID)。进程组通常与作业控制相关联。
# 查看当前进程的PGID
echo $PGID会话ID (SID)
会话是一个或多个进程组的集合,它们共享同一个会话ID (Session ID)。会话通常与终端相关联。
# 查看当前进程的SID
ps -p $$ -o sid=进程状态
Linux系统中的进程有以下几种基本状态:
运行状态 (R - Running/Runnable)
进程正在CPU上运行,或者在就绪队列中等待CPU时间片。
睡眠状态
可中断睡眠 (S - Interruptible Sleep)
进程等待某个事件的发生(如IO操作完成),可以被信号中断。
不可中断睡眠 (D - Uninterruptible Sleep)
进程等待某个事件的发生,不能被信号中断,通常与磁盘IO相关。
停止状态 (T - Stopped)
进程被暂停,通常是由于收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU信号。
僵尸状态 (Z - Zombie)
进程已经终止,但父进程还没有回收其资源(如进程描述符)。
死亡状态 (X - Dead)
进程已经完全终止,即将被系统移除。
进程状态转换
进程状态之间的转换是一个动态的过程,主要包括以下几种转换:
- 运行 → 可中断睡眠:进程需要等待某个事件的发生,主动放弃CPU。
- 可中断睡眠 → 运行:进程等待的事件发生,被唤醒。
- 运行 → 不可中断睡眠:进程进行某些特殊的IO操作,需要等待完成。
- 不可中断睡眠 → 运行:特殊IO操作完成,进程被唤醒。
- 运行 → 停止:进程收到停止信号(如SIGSTOP)。
- 停止 → 运行:进程收到继续信号(如SIGCONT)。
- 运行 → 僵尸:进程执行完毕,终止运行。
- 僵尸 → 死亡:父进程回收子进程资源。
进程的生命周期
进程的生命周期包括以下几个阶段:
- 创建:通过
fork()、vfork()或clone()系统调用创建新进程。 - 初始化:分配资源、设置环境、加载程序代码。
- 执行:在CPU上运行,可能在不同状态之间转换。
- 等待:等待子进程结束(父进程)。
- 终止:完成执行或被信号终止。
- 回收:父进程回收子进程资源。
进程的创建方式
fork()系统调用
fork()系统调用创建一个与父进程几乎完全相同的子进程。
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid;
pid = fork();
if (pid < 0) {
// fork失败
fprintf(stderr, "Fork failed\n");
return 1;
} else if (pid == 0) {
// 子进程
printf("Child process: PID = %d, PPID = %d\n", getpid(), getppid());
} else {
// 父进程
printf("Parent process: PID = %d, Child PID = %d\n", getpid(), pid);
wait(NULL); // 等待子进程结束
}
return 0;
}exec()系列系统调用
exec()系列系统调用用于在当前进程中加载并执行新的程序,替换当前进程的代码和数据。
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Before exec\n");
// 执行ls命令
execl("/bin/ls", "ls", "-la", NULL);
// 如果exec成功,下面的代码不会执行
printf("After exec (should not be printed)\n");
return 0;
}system()函数
system()函数在一个新的子进程中执行指定的命令。
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("Executing ls command\n");
system("ls -la");
printf("Command executed\n");
return 0;
}实用案例分析
案例1:查看进程状态
# 查看所有进程的状态
ps aux
# 查看特定进程的状态
ps -p PID -o pid,ppid,state,command
# 查看进程树(包含状态)
ps axjf案例2:进程状态转换示例
# 1. 创建一个后台进程(运行状态)
sleep 60 &
# 2. 查看进程状态(应该是S状态,可中断睡眠)
ps -p $! -o pid,state,command
# 3. 向进程发送停止信号(变为T状态,停止)
kill -STOP $!
ps -p $! -o pid,state,command
# 4. 向进程发送继续信号(变为S状态,可中断睡眠)
kill -CONT $!
ps -p $! -o pid,state,command
# 5. 等待进程结束(变为Z状态,僵尸),然后被父进程回收
wait $!
ps -p $! -o pid,state,command # 应该找不到进程案例3:僵尸进程的产生与处理
# 1. 创建一个会产生僵尸进程的脚本
cat > zombie.sh << 'EOF'
#!/bin/bash
# 创建子进程
/bin/sleep 1 &
# 父进程睡眠10秒,期间子进程会结束并变为僵尸
sleep 10
# 父进程结束后,僵尸进程会被init进程回收
EOF
chmod +x zombie.sh
# 2. 运行脚本
./zombie.sh &
# 3. 在脚本运行期间查看僵尸进程
ps aux | grep Z代码示例
示例1:进程创建与状态查看
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
pid_t pid;
int status;
printf("Parent process started: PID = %d\n", getpid());
// 创建子进程
pid = fork();
if (pid < 0) {
// fork失败
fprintf(stderr, "Fork failed\n");
return 1;
} else if (pid == 0) {
// 子进程
printf("Child process created: PID = %d, PPID = %d\n", getpid(), getppid());
// 子进程睡眠5秒
printf("Child process sleeping for 5 seconds...\n");
sleep(5);
printf("Child process exiting\n");
return 0;
} else {
// 父进程
printf("Parent process: Child PID = %d\n", pid);
// 父进程睡眠2秒,然后查看子进程状态
sleep(2);
printf("Parent process: Checking child process status...\n");
// 查看子进程状态
char command[100];
sprintf(command, "ps -p %d -o pid,ppid,state,command", pid);
system(command);
// 等待子进程结束
printf("Parent process: Waiting for child to exit...\n");
wait(&status);
printf("Parent process: Child exited with status %d\n", WEXITSTATUS(status));
printf("Parent process exiting\n");
}
return 0;
}示例2:进程状态监控脚本
#!/bin/bash
# 进程状态监控脚本
# 显示帮助信息
show_help() {
echo "进程状态监控脚本"
echo "用法: $0 [选项]"
echo "选项:"
echo " -p, --pid <pid> 监控指定PID的进程"
echo " -n, --name <name> 监控指定名称的进程"
echo " -i, --interval <sec> 监控间隔(秒),默认1秒"
echo " -c, --count <num> 监控次数,默认无限"
echo " -h, --help 显示帮助信息"
}
# 解析命令行参数
PID=""
NAME=""
INTERVAL=1
COUNT=-1
while [[ $# -gt 0 ]]; do
case $1 in
-p|--pid)
PID="$2"
shift 2
;;
-n|--name)
NAME="$2"
shift 2
;;
-i|--interval)
INTERVAL="$2"
shift 2
;;
-c|--count)
COUNT="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
*)
echo "未知选项: $1"
show_help
exit 1
;;
esac
done
# 检查参数
if [[ -z "$PID" && -z "$NAME" ]]; then
echo "错误: 必须指定PID或进程名称"
show_help
exit 1
fi
# 如果指定了进程名称,获取其PID
if [[ -n "$NAME" ]]; then
PID=$(pgrep -f "$NAME" | head -1)
if [[ -z "$PID" ]]; then
echo "错误: 找不到名称为 '$NAME' 的进程"
exit 1
fi
echo "找到进程 '$NAME',PID: $PID"
fi
# 监控进程状态
iteration=0
while true; do
# 检查进程是否存在
if ! ps -p $PID > /dev/null 2>&1; then
echo "进程 $PID 不存在或已结束"
break
fi
# 显示进程状态
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 进程 $PID 状态:"
ps -p $PID -o pid,ppid,pgid,sid,state,command
echo ""
# 增加迭代计数
iteration=$((iteration + 1))
# 检查是否达到指定的监控次数
if [[ $COUNT -gt 0 && $iteration -ge $COUNT ]]; then
break
fi
# 等待指定的间隔
sleep $INTERVAL
done
echo "监控结束"示例3:进程生命周期演示
#!/bin/bash
# 进程生命周期演示脚本
echo "=== 进程生命周期演示 ==="
echo "当前脚本PID: $$"
echo "父进程PID: $PPID"
echo ""
# 阶段1: 创建子进程
echo "阶段1: 创建子进程"
child_pid=$(bash -c 'echo $$; sleep 5') &
sleep 1 # 等待子进程启动
echo "创建的子进程PID: $!"
echo ""
# 阶段2: 查看进程状态
echo "阶段2: 查看进程状态"
ps -p $$ -o pid,state,command
ps -p $! -o pid,state,command
echo ""
# 阶段3: 向子进程发送信号
echo "阶段3: 向子进程发送信号"
echo "向子进程发送停止信号..."
kill -STOP $!
sleep 1
ps -p $! -o pid,state,command
echo "向子进程发送继续信号..."
kill -CONT $!
sleep 1
ps -p $! -o pid,state,command
echo ""
# 阶段4: 等待子进程结束
echo "阶段4: 等待子进程结束"
echo "等待子进程结束..."
wait $!
echo "子进程已结束"
echo ""
# 阶段5: 查看子进程状态(应该不存在)
echo "阶段5: 查看子进程状态"
if ps -p $! > /dev/null 2>&1; then
ps -p $! -o pid,state,command
else
echo "子进程已不存在,资源已被回收"
fi
echo ""
echo "=== 演示结束 ==="总结
本集介绍了 Linux 中的进程概念与状态,包括:
- 进程的基本概念和与程序的区别
- 进程标识符,包括PID、PPID、PGID和SID
- 进程的各种状态,如运行(R)、可中断睡眠(S)、不可中断睡眠(D)、停止(T)和僵尸(Z)状态
- 进程状态之间的转换关系
- 进程的生命周期,包括创建、初始化、执行、等待、终止和回收
- 进程的创建方式,如fork()、exec()系列系统调用和system()函数
- 实用案例分析,包括查看进程状态、进程状态转换示例和僵尸进程的产生与处理
- 代码示例,展示了进程创建与状态查看、进程状态监控脚本和进程生命周期演示
通过理解进程的概念、状态和生命周期,用户可以更好地管理和监控系统中的进程,提高系统的稳定性和性能。在后续的章节中,我们将学习如何查看、监控和控制进程,以及如何管理进程的优先级和资源限制。