linux-进程详解

一、 进程的基本概念

在 Linux 系统中,进程是程序的一次执行过程。它是操作系统进行资源分配和调度的基本单位。当一个程序被加载到内存中并开始运行时,它就变成了一个进程。

  • 程序 :指的是存储在磁盘上的可执行文件(例如 /bin/ls),它是静态的指令集合。
  • 进程:指的是程序正在运行的一个实例,它拥有独立的内存空间、寄存器状态、程序计数器以及堆栈等。它是动态的执行实体。

例如,你可以多次运行同一个程序(如 ls),每次运行都会创建一个新的、独立的进程。

二、 进程的结构

一个 Linux 进程通常包含以下几个核心部分:

  1. 代码段:存放程序的可执行机器指令,存放CPU执行的机器指令,可共享、只读,常量数据在编译时在代码段中分配空间,局部数据将在运行时在栈区分配空间,编译时不分配空间。
  2. 数据段:存放已初始化的全局变量和静态变量。
  3. :存放动态分配的内存(如 malloc 分配的空间),由程序员管理其生命周期。
  4. :存放函数的局部变量、函数参数、返回地址等信息,随着函数的调用和返回动态变化。
  5. 进程控制块 :操作系统内核中维护的一个数据结构(通常称为 task_struct),用于记录进程的状态信息(如进程ID、父进程ID、优先级、状态、打开的文件描述符、内存映射等)。
举例:一个简单的 C 程序结构
c 复制代码
#include <stdio.h>
#include <stdlib.h>

int global_var = 10; // 存储在数据段

int main() {
    int stack_var; // 存储在栈
    int *heap_var = (int *)malloc(sizeof(int)); // 指向堆上分配的空间
    *heap_var = 20;

    stack_var = 30;

    printf("Global: %d, Heap: %d, Stack: %d\n", global_var, *heap_var, stack_var);

    free(heap_var); // 释放堆空间
    return 0;
}

当这个程序运行时,操作系统会为它创建一个进程,分配独立的内存空间包含代码、数据、堆和栈区域。

三、 进程的状态

Linux 进程在其生命周期中会经历不同的状态:

  1. 就绪:进程已准备好运行,等待 CPU 时间片。
  2. 运行:进程正在 CPU 上执行指令。
  3. 阻塞:进程因等待某个事件(如 I/O 操作完成、信号量)而暂停执行。
  4. 僵尸:进程已终止,但其退出状态信息还未被父进程读取。
  5. 停止 :进程被暂停执行(通常由信号 SIGSTOP, SIGTSTP 引起)。
举例:进程状态转换
  • 一个进程调用 sleep(5) 会进入阻塞状态,等待 5 秒后变为就绪状态。
  • 一个进程等待用户键盘输入 (scanf) 时,会进入阻塞状态,直到用户输入数据。
  • 子进程结束运行后,如果父进程没有调用 wait()waitpid() 读取其退出状态,子进程会变成僵尸进程。
  • 按下 Ctrl+Z 会向当前前台进程发送 SIGTSTP 信号,使其进入停止状态。可以使用 fgbg 命令使其继续运行。

四、 进程的创建与控制

在 Linux 中,创建新进程的主要方式是 fork() 系统调用。

  • fork():创建一个当前进程的副本(子进程)。子进程拥有父进程代码、数据和环境的拷贝(写时复制优化)。fork() 在父进程中返回子进程的 PID,在子进程中返回 0。
  • exec() 系列函数:加载一个新的程序到当前进程的内存空间,替换掉原有的代码和数据,从新程序的 main 函数开始执行。fork() 通常与 exec() 配合使用来运行新程序。
举例:创建子进程并执行新程序
c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork(); // 创建子进程

    if (pid < 0) {
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        // 子进程
        printf("Child process (PID: %d)\n", getpid());
        // 使用 exec 加载 /bin/ls 程序
        execl("/bin/ls", "ls", "-l", NULL); // 如果成功,从这里开始执行 ls -l
        perror("execl failed"); // 如果 exec 失败才会执行到这里
        return 1;
    } else {
        // 父进程
        printf("Parent process (PID: %d), Child PID: %d\n", getpid(), pid);
        wait(NULL); // 等待子进程结束
        printf("Child process finished.\n");
    }
    return 0;
}
举例:避免僵尸进程
c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

int main() {
    pid_t pid = fork();

    if (pid == 0) {
        printf("Child is running...\n");
        sleep(2); // 模拟子进程工作
        printf("Child exiting.\n");
        exit(0); // 子进程正常退出
    } else {
        sleep(1); // 父进程稍等片刻
        int status;
        pid_t child_pid = wait(&status); // 主动等待子进程结束,回收资源
        if (WIFEXITED(status)) {
            printf("Parent: Child %d exited with status %d.\n", child_pid, WEXITSTATUS(status));
        }
    }
    return 0;
}

五、 进程调度

Linux 内核使用复杂的调度算法来决定哪个就绪进程获得 CPU 时间。调度策略的目标是平衡效率、响应时间和公平性。

  • 普通进程 :采用完全公平调度算法,优先级由 nice 值调整(范围通常为 -20 到 19,数值越低优先级越高)。使用命令 nicerenice 调整。
  • 实时进程 :具有更高优先级,分为:
    • SCHED_FIFO:先进先出,一直运行直到阻塞、结束或被更高优先级抢占。
    • SCHED_RR:轮转调度,在相同优先级的实时进程间分配时间片。
    • 使用命令 chrt 设置实时策略和优先级。
举例:调整进程优先级
bash 复制代码
# 以较高的优先级(较低的 nice 值)运行一个命令
nice -n -10 ./cpu_intensive_task

# 查看进程的 nice 值
ps -l -p $(pgrep cpu_intensive_task)

# 改变一个已运行进程的 nice 值(需要权限)
renice -n -5 -p $(pgrep cpu_intensive_task)

# 设置一个进程为 SCHED_FIFO 实时策略,优先级 99 (需要 root)
sudo chrt -f 99 ./critical_task

六、 进程间通信

独立的进程拥有各自独立的地址空间。为了实现协作,进程间需要通信机制:

  1. 信号 :一种异步通知机制,用于通知进程发生了某个事件。如 SIGINT (Ctrl+C), SIGKILL (强制终止)。
  2. 管道 :单向数据流,用于具有亲缘关系的进程(父子进程)。| 操作符就是管道。
  3. 命名管道:存在于文件系统中的管道,无亲缘关系的进程也可通信。
  4. 消息队列:内核维护的链表,进程可通过标识符发送/接收消息。
  5. 共享内存:多个进程访问同一块物理内存区域,速度最快,需要配合信号量等同步机制。
  6. 信号量:用于进程间的同步与互斥,控制对共享资源的访问。
  7. 套接字:可用于网络通信或同一主机上的进程间通信。
举例:使用管道通信
bash 复制代码
# 父 shell 进程创建管道,将 ls 的输出传递给 grep 的输入
ls -l /etc | grep "passwd"
举例:使用信号 (发送 SIGUSR1)
bash 复制代码
# 假设进程 PID 为 1234
kill -SIGUSR1 1234 # 向 PID 1234 发送 SIGUSR1 信号

总结

进程是 Linux 操作系统执行任务的核心单元。理解进程的概念、生命周期、创建方式、调度机制以及通信方法对于进行系统编程、性能分析和故障排查都至关重要。通过 ps, top, htop, pstree, strace, gdb 等工具可以观察和分析进程的行为。

相关推荐
應呈2 小时前
Bootloader与OTA学习记录
linux·运维·服务器
勤自省2 小时前
在Ubuntu20.04上安装ROS
linux·ros
楼田莉子2 小时前
同步/异步日志系统:工具类以及日志的简单模块
linux·服务器·数据结构·c++
corpse20102 小时前
VirtualBox 安装ubuntu-25 ,配置SSH工具登录
linux·ubuntu·ssh
skywalk81632 小时前
使用官方提供的 bump-pydantic 工具 来自动化部分迁移pydantic代码
运维·自动化
杜子不疼.2 小时前
浏览器秒连服务器!WebSSH 实战体验,远程运维再也不折腾
运维·服务器·人工智能
她说彩礼65万2 小时前
C语言 整形提升及算数转换
linux·服务器·c语言
loockluo2 小时前
NFS网络存储部署与性能优化实战:家用服务器的学习与实践
服务器·网络·性能优化
RenPenry2 小时前
2026 在Linux上搭建CS2插件服务器
linux·运维·服务器·cs2·debian13