第4章 现代操作系统中的进程管理与生命周期实现

第4章 现代操作系统中的进程管理与生命周期实现

操作系统的核心功能之一是管理计算机系统上运行的程序。通过将程序转变为进程,操作系统可以更有效地控制和分配系统资源,实现多任务处理。本章将深入探讨进程的本质、状态变化以及控制机制,帮助读者全面理解现代操作系统中进程管理的理论与实践。

4.1 进程的基本概念

4.1.1 进程的历史发展

进程概念的出现解决了早期计算机系统中程序执行的重大问题。在单任务操作系统时代,计算机一次只能运行一个程序,资源利用率低下。随着多道程序设计的发展,操作系统需要一种机制来管理并发执行的多个程序,这促使了进程概念的诞生。

早期的计算机系统直接在CPU上运行程序,没有抽象层,导致程序之间相互干扰。进程抽象的引入使得每个程序都运行在自己的虚拟空间中,互不干扰,显著提高了系统的稳定性和安全性。

c

// 早期的简单程序执行方式(无进程概念)
void early_computer_execution() {
    load_program_to_memory();  // 将程序加载到内存
    set_program_counter();     // 设置程序计数器
    
    // 直接执行,直到程序结束
    while (!program_finished) {
        execute_next_instruction();
    }
    
    // 加载下一个程序
    load_next_program();
}
4.1.2 进程定义与进程控制块结构

进程是一个执行中的程序实例,包含程序代码(文本段)、当前活动(程序计数器、处理器寄存器)、堆栈(临时数据)、数据段(全局变量)以及堆(动态分配的内存)。

进程控制块(PCB)是操作系统用来表示进程的数据结构,包含了进程的所有信息。PCB通常包括以下信息:

  • 进程标识符(Process ID)
  • 进程状态(运行、就绪、阻塞等)
  • 程序计数器(下一条将执行的指令地址)
  • CPU寄存器
  • CPU调度信息(优先级、调度队列指针等)
  • 内存管理信息(内存边界、页表等)
  • 资源占用信息(打开的文件等)
  • 账户信息(CPU使用时间等)

c

// 进程控制块的C语言表示
typedef struct {
    int pid;                  // 进程ID
    ProcessState state;       // 进程状态(枚举类型)
    int program_counter;      // 程序计数器
    int cpu_registers[16];    // CPU寄存器集合
    int priority;             // 进程优先级
    
    // 内存管理信息
    void* text_segment;       // 代码段
    void* data_segment;       // 数据段
    void* stack;              // 栈
    void* heap;               // 堆
    
    // 资源占用信息
    int open_files[MAX_FILES];// 打开的文件描述符
    
    // 记账信息
    time_t creation_time;     // 创建时间
    int cpu_time_used;        // 已使用的CPU时间
    
    // 调度信息
    struct pcb* next;         // 链表中的下一个PCB
} ProcessControlBlock;

通过PCB,操作系统能够追踪每个进程的状态和资源使用情况,实现对进程的有效管理和控制。当进程切换时,操作系统保存当前进程的状态到其PCB中,然后加载下一个进程的PCB来恢复其执行环境。

c

// 进程上下文切换
void context_switch(ProcessControlBlock* current, ProcessControlBlock* next) {
    // 保存当前进程状态
    save_cpu_state(current);
    current->program_counter = get_current_pc();
    
    // 加载下一个进程状态
    load_cpu_state(next);
    set_program_counter(next->program_counter);
    
    // 更新进程状态
    current->state = READY;
    next->state = RUNNING;
    
    printf("进程切换: 从PID %d 切换到 PID %d\n", current->pid, next->pid);
}

4.2 进程的状态转换

进程在其生命周期中会经历不同的状态,操作系统通过管理这些状态转换来协调多个进程的执行。

4.2.1 双状态进程模型分析

最简单的进程模型只包含两个状态:运行(Running)和非运行(Not Running)。在这个模型中,进程要么正在执行,要么在等待被调度执行。

这种简单模型的优点是易于理解和实现,但它不能精确描述进程的各种等待情况,如等待I/O完成或等待某个事件发生。

c

// 双状态模型中的进程调度
void simple_scheduler() {
    ProcessControlBlock* current_process;
    
    while (1) {
        // 选择下一个要运行的进程
        current_process = select_next_process();
        
        if (current_process == NULL) {
            // 没有可运行的进程,系统空闲
            idle();
            continue;
        }
        
        // 运行选中的进程
        current_process->state = RUNNING;
        run_process(current_process);
        
        // 进程运行结束或时间片用尽
        current_process->state = NOT_RUNNING;
        
        // 将进程放回队列(如果未结束)
        if (!process_terminated(current_process)) {
            enqueue_process(current_process);
        }
    }
}
4.2.2 进程的创建与终止机制

进程创建

操作系统中的进程可以通过多种方式创建:

  1. 系统初始化时创建
  2. 用户请求创建新进程
  3. 现有进程执行创建进程的系统调用
  4. 批处理作业初始化

在UNIX/Linux系统中,使用fork()系统调用创建新进程。fork()创建调用进程的副本,两个进程从fork()返回点继续执行。在子进程中,fork()返回0;在父进程中,返回子进程ID。

c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
    pid_t pid;
    
    // 创建新进程
    pid = fork();
    
    if (pid < 0) {
        // fork失败
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (pid == 0) {
        // 子进程代码
        printf("子进程: 我的PID是 %d,父进程PID是 %d\n", 
               getpid(), getppid());
    } else {
        // 父进程代码
        printf("父进程: 我的PID是 %d,子进程PID是 %d\n", 
               getpid(), pid);
    }
    
    return 0;
}

在Windows系统中,使用CreateProcess()函数创建新进程,该函数直接创建一个运行指定程序的新进程。

进程终止

进程可以通过以下方式终止:

  1. 正常退出(主动)
  2. 错误退出(主动)
  3. 严重错误(被动)
  4. 被其他进程终止(被动)

在UNIX/Linux系统中,进程通过exit()系统调用终止自己,或通过kill()系统调用终止其他进程。

c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    pid_t pid;
    int status;
    
    pid = fork();
    
    if (pid < 0) {
        fprintf(stderr, "Fork failed\n");
        return 1;
    } else if (pid == 0) {
        // 子进程代码
        printf("子进程(%d)开始执行\n", getpid());
        sleep(2);
        printf("子进程正常退出\n");
        exit(0);  // 正常终止
    } else {
        // 父进程代码
        printf("父进程创建了子进程(%d)\n", pid);
        
        // 等待子进程结束
        waitpid(pid, &status, 0);
        
        if (WIFEXITED(status)) {
            printf("子进程正常终止,退出状态: %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("子进程被信号 %d 终止\n", WTERMSIG(status));
        }
    }
    
    return 0;
}
4.2.3 五状态进程模型详解

更复杂且现实的进程模型包含五个状态:

  1. 新建(New): 进程正在被创建,尚未就绪
  2. 就绪(Ready): 进程已准备好运行,等待CPU分配
  3. 运行(Running): 进程正在执行
  4. 阻塞/等待(Blocked/Waiting): 进程等待某个事件发生
  5. 终止(Terminated): 进程已结束执行

进程状态转换图展示了这些状态之间的转换关系:

  • 新建 → 就绪:进程创建完成后转为就绪状态
  • 就绪 → 运行:调度器选择进程执行
  • 运行 → 就绪:时间片用尽或更高优先级进程到达
  • 运行 → 阻塞:进程等待I/O或其他事件
  • 阻塞 → 就绪:等待的事件发生
  • 运行 → 终止:进程执行完成或被终止

c

// 五状态模型的进程状态定义
typedef enum {
    NEW,
    READY,
    RUNNING,
    BLOCKED,
    TERMINATED
} ProcessState;

// 进程状态转换函数
void change_process_state(ProcessControlBlock* process, ProcessState new_state) {
    ProcessState old_state = process->state;
    process->state = new_state;
    
    printf("进程 %d 状态从 %s 变为 %s\n", 
           process->pid, 
           state_to_string(old_state), 
           state_to_string(new_state));
    
    // 根据新状态执行相应操作
    switch (new_state) {
        case READY:
            // 如果进程变为就绪态,将其加入就绪队列
            if (old_state != RUNNING) { // 避免时间片到期的重复入队
                add_to_ready_queue(process);
            }
            break;
            
        case RUNNING:
            // 进程开始运行,从就绪队列中移除
            remove_from_ready_queue(process);
            break;
            
        case BLOCKED:
            // 进程阻塞,可能需要加入等待队列
            add_to_wait_queue(process);
            break;
            
        case TERMINATED:
            // 进程终止,释放资源
            release_resources(process);
            break;
            
        default:
            break;
    }
}

// 状态枚举转字符串
const char* state_to_string(ProcessState state) {
    switch (state) {
        case NEW: return "新建";
        case READY: return "就绪";
        case RUNNING: return "运行";
        case BLOCKED: return "阻塞";
        case TERMINATED: return "终止";
        default: return "未知";
    }
}
4.2.4 被挂起进程的处理

在虚拟内存系统中,进程可能会被挂起(Suspended),即暂时从内存中交换到磁盘上,以释放内存空间给其他进程使用。这引入了两个附加状态:

  1. 就绪挂起(Ready-Suspended): 进程在磁盘上,但一旦回到内存就可以运行
  2. 阻塞挂起(Blocked-Suspended): 进程在磁盘上且在等待某个事件

这样,进程状态模型扩展为七状态模型:新建、就绪、运行、阻塞、就绪挂起、阻塞挂起和终止。

c

// 七状态模型
typedef enum {
    NEW,
    READY,
    RUNNING,
    BLOCKED,
    READY_SUSPENDED,
    BLOCKED_SUSPENDED,
    TERMINATED
} ExtendedProcessState;

// 挂起进程
void suspend_process(ProcessControlBlock* process) {
    if (process->state == READY) {
        // 就绪进程挂起
        change_extended_state(process, READY_SUSPENDED);
        
        // 将进程映像写入磁盘
        swap_out_process(process);
        
        printf("就绪进程 %d 被挂起到磁盘\n", process->pid);
    } 
    else if (process->state == BLOCKED) {
        // 阻塞进程挂起
        change_extended_state(process, BLOCKED_SUSPENDED);
        
        // 将进程映像写入磁盘
        swap_out_process(process);
        
        printf("阻塞进程 %d 被挂起到磁盘\n", process->pid);
    }
}

// 恢复挂起进程
void resume_process(Proc
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小宝哥Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值