【Linux应用编程】深入理解Linux多任务机制:进程原理、状态转换与进程控制实战

1. 多任务与并发概述

在单核或多核处理器上,让操作系统具备同时处理多个任务 的能力称为多任务(Multitasking)。

●并发(Concurrency) :微观上单核 CPU 采用时间片轮询算法在多个任务之间快速切换,宏观上表现为多个任务"同时"执行。

●并行(Parallelism):在多核 CPU 或多处理器系统中,不同的任务同时在不同的物理核心上真实地独立运行。

实现多任务的常见方式包括:多进程 与 多线程。

2. 深入理解 Linux 进程

2.1 进程与程序的区别

|--------------|---------------------|---------------------|
| 对比维度 | 程序(Program) | 进程(Process) |
| 概念定义 | 静态的数据指令集合 | 正在执行的程序 |
| 存储位置 | 外存/硬盘(如 a.out) | 内存 + CPU 寄存器 |
| 生命周期 | 永久存在 | 动态的,从启动到消亡 |
| 资源消耗 | 仅占用磁盘空间 | 消耗内存空间与 CPU 计算资源 |

核心结论 :进程是操作系统资源分配的最小单位。

2.2 进程的 0-4G 内存布局

当一个进程被创建时,操作系统会为其分配一个 4G(32 位系统) 的虚拟地址空间,分为:

●0 - 3G:用户空间(各个进程相互独立)

●3G - 4G:内核空间(所有进程共享)

3. 进程的调度与状态转换

3.1 CPU 调度算法

操作系统内核通过调度器(Scheduler)分配 CPU 资源,常见的调度策略包括:

1.时间片轮询算法(Round-Robin)

2.先来先服务(FCFS) / 短作业优先(SJF)

3.高优先级优先算法(Priority Scheduling)

3.2 Linux 进程状态(七态模型)

在 Linux 中,进程在生命周期内会经历如下状态变迁:

|--------------|-------------------------------|--------------------------------|
| 状态代码 | 状态名称 | 详细说明 |
| R | Running / Runnable | 运行态/就绪态:正在 CPU 上运行或等待 CPU 调度 |
| S | Interruptible Sleep | 可中断睡眠态:阻塞等待资源(如等待键盘输入),可被信号唤醒 |
| D | Uninterruptible Sleep | 不可中断睡眠态:进程不响应信号打断(通常发生在驱动 I/O) |
| T | Stopped | 暂停态:收到 SIGSTOP 信号后暂停执行 |
| Z | Zombie | 僵尸态:进程已终止,但其资源尚未被父进程回收 |
| X | Dead | 消亡态:进程资源被彻底释放回收 |

4. 进程管理常用命令

在 Linux 终端中,可以使用以下命令查看与控制进程:

1.ps -aux :查看系统中所有进程的详细参数(PID、CPU/内存占用率、进程状态等)。

ps -aux | grep a.out

2.ps -ef:查看进程的树形父子关系(PID 与 PPID)。

3.top:动态实时查看进程状态与系统 CPU、内存占用率。

4.pstree -sp <PID>:查看指定 PID 进程的族谱/创建关系。

5.kill:给指定进程发送控制信号。

    • kill -l:查看系统支持的所有信号。
    • kill -9 <PID>:强制结束指定 PID 的进程。

6.jobs / fg:查看后台进程与将后台进程切换至前台。

5. 进程控制 API 实战

5.1 创建子进程 fork()

操作系统通过 fork() 创建新进程。fork() 调用完成后,系统会将父进程 0-3G 的用户空间完整拷贝一份给子进程(写时复制 Copy-On-Write 机制)。

#include <sys/types.h>

#include <unistd.h>

pid_t fork(void);

●返回值:

○在父进程中 :返回创建成功的 子进程 PID(pid > 0)。

○在子进程中 :固定返回 0(pid == 0)。

○失败:返回 -1。

注意:

1.父子进程共享 3G-4G 内核空间 ,但各自 0-3G 用户空间相互独立,不能直接通过共享全局变量进行通信。

2.获取进程 PID API:getpid() 获取当前进程 PID,getppid() 获取父进程 PID。

C语言代码实战:fork() 使用示例

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

int main(void) {

int num = 100;

printf("Before fork, PID = %d\n", getpid());

pid_t pid = fork();

if (pid < 0) {

perror("fork error");

return -1;

}

else if (pid == 0) {

// 子进程逻辑

num += 50;

printf("Child PID: %d, PPID: %d, num = %d\n", getpid(), getppid(), num);

}

else {

// 父进程逻辑

sleep(1); // 让子进程先运行

printf("Parent PID: %d, Child PID: %d, num = %d\n", getpid(), pid, num);

}

return 0;

}

5.2 进程的退出 exit()

进程退出的常见方式:

  1. main 函数中使用 return 退出。
  2. 调用标准 C 库函数 exit() 或系统调用 _exit() / _Exit()。
  3. 外部发送信号强行终止(如 kill -9 PID)。

#include <stdlib.h>

void exit(int status);

●参数 status:0 表示正常退出;非 0 表示因异常或特定错误退出。

5.3 孤儿进程与僵尸进程处理

(1) 孤儿进程(Orphan Process)

●定义 :父进程先退出,子进程仍在运行。子进程成为孤儿进程。

●处理机制 :孤儿进程会被系统顶级进程(init 或 systemd,PID=1)收养,子进程结束后由系统回收,不会造成系统资源泄露。

(2) 僵尸进程(Zombie Process)

●定义:子进程已经退出,但父进程没有调用 wait() / waitpid() 回收其内核空间资源(PCB)。

●危害:过多僵尸进程会导致 PID 号被耗尽,系统无法创建新进程。

(3) 解决僵尸进程:wait() 与 waitpid()

父进程可以调用 wait() 或 waitpid() 阻塞/非阻塞等待子进程结束并回收其资源:

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int *wstatus);

C语言代码实战:wait() 回收子进程

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/wait.h>

int main(void) {

pid_t pid = fork();

if (pid < 0) {

perror("fork error");

return -1;

}

else if (pid == 0) {

// 子进程逻辑

printf("Child process running... PID: %d\n", getpid());

sleep(2);

exit(10); // 子进程带状态码 10 退出

}

else {

// 父进程逻辑

int status = 0;

printf("Parent process waiting for child...\n");

pid_t z_pid = wait(&status); // 阻塞等待并回收子进程

if (WIFEXITED(status)) {

printf("Child (PID: %d) exited with code: %d\n",

z_pid, WEXITSTATUS(status));

}

}

return 0;

}

6. 总结

1.进程 是 Linux 系统分配资源的基本单位,拥有独立 0-3G 用户空间,安全稳定。

2.使用 fork() 可以轻松创建多进程,父子进程通过返回值区分逻辑。

3.必须注意避免产生 僵尸进程 ,通过 wait() / waitpid() 显式回收子进程资源。

4.由于进程间用户内存空间隔离,实现多进程协同需进一步学习 IPC(进程间通信:管道、信号、共享内存、消息队列、信号量)。

相关推荐
罗湖老棍子10 小时前
Biorhythms(信息学奥赛一本通- P1639)
算法·数论·裴蜀定理·扩展欧几里得·扩展中国剩余定理
“AI国潮设计-小江”15 小时前
《Python+SDXL实战:用ControlNet精准控制“英歌舞翻糖吐司”构图,附批量生成脚本》
开发语言·人工智能·python·prompt·aigc
程序喵大人15 小时前
【C++入门】编译链接模型 - 01 一个 C++ 程序是怎样变成可执行文件的
开发语言·c++·编译链接模型
奋发向前wcx15 小时前
CSP-J复赛模拟赛4 王晨旭补题 2026.10.5
算法
Felven16 小时前
stress-ng 与 fio 性能测试工具使用指南
linux·测试工具·fio·stress-ng
sunshine22 girl16 小时前
Java学习七 Java 项目实战1-项目创建和主页面搭建
java·开发语言·学习
菜鸟~noob23317 小时前
【电子战】第三季预告—波武器与电子系统强力毁伤
开发语言·数据库·matlab·电子战
huainingning17 小时前
RJ SW Console口忘记密码处理方法
linux·运维·服务器
布裘17 小时前
【银河麒麟】桌面系统循环登录,无法进入桌面?
linux·运维·服务器
慧都小项17 小时前
程序到了Linux才出错?用CLion把调试接到目标环境
linux·运维·服务器