操作系统进程管理笔记

1. 进程的基本概念

1.1 进程的定义

进程就是运行中的程序。程序本身是没有生命周期的,它只是存在磁盘上面的一些指令(也可能是一些静态数据)。是操作系统让这些字节运行起来,让程序发挥作用。

1.2 CPU的时分共享

操作系统通过让一个进程只运行一个时间片,然后切换到其他进程,提供了存在多个虚拟CPU的假象。这就是时分共享(time sharing)CPU技术,允许用户如愿运行多个并发进程。潜在的开销就是性能损失,因为如果CPU必须共享,每个进程的运行就会慢一点。

1.3 进程的机器状态

进程的机器状态包括:

  • 内存:进程可以访问的内存(称为地址空间,address space)
    • 指令存在内存中
    • 正在运行的程序读取和写入的数据也在内存中
  • 寄存器
    • 程序计数器(Program Counter,PC):告诉我们程序当前正在执行哪个指令
    • 栈指针(stack pointer)和帧指针(frame pointer):用于管理函数参数栈、局部变量和返回地址

寄存器结构图

2. 程序与进程的区别

  • 程序(Program):一个静态实体,通常是以可执行文件形式存储在磁盘上的指令和数据的集合
  • 进程(Process):程序的动态执行实例,运行时加载到内存中,拥有独立的地址空间、执行状态和系统资源

简单来说,程序是静态的"蓝图",而进程是程序被激活后的"活体"。

3. 操作系统启动并运行程序的过程

3.1 加载程序到内存

  1. 定位可执行文件:根据用户命令或系统调用找到磁盘上的可执行文件
  2. 读取文件头部
    • 入口点(Entry Point):程序开始执行的地址
    • 段信息:代码段、数据段等的地址和大小
  3. 从磁盘读取字节
    • 代码段:包含程序的指令,通常是只读的
    • 数据段:包含初始化的全局变量和静态变量
    • BSS段:包含未初始化的全局变量
  4. 分配虚拟地址空间:为进程分配独立的虚拟地址空间
  5. 按需加载(可选):延迟加载优化性能

3.2 分配堆栈和堆

  • 堆栈(Stack):存储函数调用信息、局部变量等
  • 堆(Heap):用于动态内存分配

3.3 设置执行环境

  • 寄存器初始化
  • 命令行参数和环境变量
  • 文件描述符

3.4 创建进程控制块(PCB)

记录进程元数据,包括:

  • 进程ID(PID)
  • 进程状态
  • 内存管理信息
  • 打开的文件描述符

3.5 调度执行

进程被加入就绪队列,等待CPU调度

4. 进程状态

进程的三种基本状态:

  • 运行(running):在处理器上运行,执行指令
  • 就绪(ready):准备好运行,但操作系统选择不在此时运行
  • 阻塞(blocked):执行了某种操作,直到发生其他事件时才会准备运行
    从就绪到运行意味着该进程已经被调度(scheduled)。从运行转移到就绪意味着该进程已经取消调度(descheduled)。一旦进程被阻塞(例如,通过发起 I/O 操作),OS 将保持进程的这种状态,直到发生某种事件(例如,I/O 完成)。此时,进程再次转入就绪状态(也可能立即再次运行,如果操作系统这样决定)。

image.png
寄存器上下文将保存其寄存器的内容。当一个进程停止时,它的寄存器将被保存到这个内存位置。==通过恢复这些寄存器(将它们的值放回实际的物理寄存器中)​,==操作系统可以恢复运行该进程。我们将在后面的章节中更多地了解这种技术,它被称为上下文切换(contextswitch)​。

5. 进程管理系统调用

5.1 fork系统调用

  • 创建一个新进程,作为调用进程的副本
  • 子进程复制父进程的地址空间、PCB等
  • 父进程返回子进程PID,子进程返回0
#include <stdio.h>      // 包含标准输入输出库,提供 printf() 等函数
#include <stdlib.h>     // 包含标准库,提供 exit() 函数等
#include <unistd.h>     // 包含 UNIX 标准函数声明,提供 fork()、getpid() 等int main(int argc, char *argv[])  // 程序入口,argc/argv 用于获取命令行参数{// 在创建子进程之前,先打印当前进程的 PID(进程标识符)printf("hello world (pid:%d)\n", (int)getpid());// 调用 fork(),创建一个新进程(子进程)int rc = fork();if (rc < 0) {            // fork 返回值小于 0,表示创建子进程失败fprintf(stderr, "fork failed\n");  // 向标准错误输出错误信息exit(1);            // 退出程序,并返回非零状态码表示异常} else if (rc == 0) {    // fork 返回值等于 0,表示当前是子进程// 子进程执行的代码路径printf("hello, I am child (pid:%d)\n", (int)getpid());} else {                 // fork 返回值大于 0,表示当前是父进程// rc 存储的是子进程的 PIDprintf("hello, I am parent of %d (pid:%d)\n",rc, (int)getpid());}return 0;  // 程序正常退出,返回值 0}

image.png
子进程并不是完全拷贝了父进程。具体来说,虽然它拥有自己的地址空间(即拥有自己的私有内存)​、寄存器、程序计数器等,但是它从fork()返回的值是不同的。父进程获得的返回值是新创建子进程的PID,而子进程获得的返回值是0。
系统显示父进程先执行,但是这是随机的,CPU调度程序(scheduler)决定了某个时刻哪个进程被执行

5.2 wait系统调用

  • 父进程阻塞直到子进程结束
  • 返回已结束子进程的PID
#include <stdio.h>      // 标准输入输出,提供 printf()
#include <stdlib.h>     // 标准库,提供 exit()
#include <unistd.h>     // POSIX API,提供 fork()、getpid()
#include <sys/wait.h>   // 等待子进程,提供 wait()int main(int argc, char *argv[])
{// 在 fork 之前,先打印当前进程(父进程)的 PIDprintf("hello world (pid:%d)\n", (int)getpid());// 创建一个新进程;父进程中 rc > 0,子进程中 rc == 0,失败时 rc < 0int rc = fork();if (rc < 0) {// fork 调用失败fprintf(stderr, "fork failed\n");exit(1);} else if (rc == 0) {// 子进程执行这里的代码printf("hello, I am child (pid:%d)\n", (int)getpid());} else {// 父进程执行这里的代码// wait(NULL) 阻塞直到任意子进程结束,返回值是已结束子进程的 PIDint wc = wait(NULL);printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",rc, wc, (int)getpid());}return 0;  // 正常退出
}

image.png

5.3 exec系统调用

  • 在当前进程中加载并执行新程序
  • 替换当前地址空间
  • 重置堆栈、堆和寄存器
#include <stdio.h>      // 标准输入输出,提供 printf()
#include <stdlib.h>     // 标准库,提供 exit()
#include <unistd.h>     // POSIX API,提供 fork()、execvp()、getpid()
#include <string.h>     // 字符串操作,提供 strdup()
#include <sys/wait.h>   // 等待子进程,提供 wait()int main(int argc, char *argv[])
{// 程序启动时打印当前进程(父进程)的 PIDprintf("hello world (pid:%d)\n", (int)getpid());// 创建子进程:父进程 rc>0,子进程 rc==0,失败时 rc<0int rc = fork();if (rc < 0) {// fork 失败,打印错误并退出fprintf(stderr, "fork failed\n");exit(1);}else if (rc == 0) {// 子进程执行此路径printf("hello, I am child (pid:%d)\n", (int)getpid());// 准备 execvp 的参数数组// myargs[0] 指定要运行的程序名 "wc"// myargs[1] 指定要处理的文件 "p3.c"// myargs[2] 置 NULL,标记参数数组结束char *myargs[3];myargs[0] = strdup("wc");      myargs[1] = strdup("p3.c");   myargs[2] = NULL;// 用 execvp 替换当前子进程映像,执行 word count 程序execvp(myargs[0], myargs);// 如果 execvp 返回,说明执行失败,才会走到这里perror("execvp failed");exit(1);}else {// 父进程执行此路径// wait(NULL) 阻塞直到任意子进程结束,返回已结束子进程的 PIDint wc = wait(NULL);// 打印父进程信息,rc 是子进程的 PID,wc 是 wait 返回的 PIDprintf("hello, I am parent of %d (wc:%d) (pid:%d)\n",rc, wc, (int)getpid());}return 0;  // 正常退出
}

要点说明

  • strdup():复制字符串并返回指向新内存的指针,用于给 execvp 准备参数。
  • execvp():用指定程序替换当前进程映像,不返回成功;如果失败,会返回 -1,此时应打印错误并退出。
  • wait(NULL):父进程阻塞直到子进程结束,避免子进程成为僵尸。

image.png

image.png

6. 安全机制:地址空间布局随机化(ASLR)

6.1 ASLR的定义

ASLR是一种安全技术,通过随机化进程的内存地址布局,防止攻击者利用已知的内存地址执行恶意代码。

6.2 工作原理

随机化内存布局的关键区域:

  • 堆栈(Stack)
  • 堆(Heap)
  • 可执行代码(Text Segment)
  • 动态链接库(Shared Libraries)

6.3 优缺点

优点

  • 提升安全性
  • 兼容性强

缺点

  • 非绝对防御
  • 轻微性能开销

7. 内存分配:堆和栈

7.1 栈(Stack)

保存内容

  • 局部变量
  • 函数参数
  • 返回地址
  • 栈帧

特点

  • 先进后出(LIFO)
  • 速度快
  • 大小有限

7.2 堆(Heap)

保存内容

  • 动态分配的对象
  • 全局数据(部分情况)

特点

  • 手动管理
  • 灵活性高
  • 速度较慢
  • 可能产生碎片

7.3 堆和栈的区别对比

特性栈(Stack)堆(Heap)
分配方式自动分配和释放手动分配和释放
存储内容局部变量、函数参数动态分配的数据
生命周期随函数调用结束而销毁在手动释放前一直存在
大小限制容量较小容量较大
速度操作更快操作较慢

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/902651.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python中random库的应用

文章目录 一、random 库常用函数二、条件语句 随机数示例1&#xff1a;随机决定程序分支示例2&#xff1a;模拟概率事件 三、循环语句 随机数示例1&#xff1a;循环直到满足随机条件示例2&#xff1a;随机次数循环 四、随机操作数据结构示例1&#xff1a;随机打乱列表顺序示例…

密码学货币混币器详解及python实现

目录 一、前言二、混币器概述2.1 混币器的工作原理2.2 关键特性三、数据生成与预处理四、系统架构与流程五、核心数学公式六、异步任务调度与 GPU 加速七、PyQt6 GUI 设计八、完整代码实现九、自查测试与总结十、展望摘要 本博客聚焦 “密码学货币混币器实现”,以 Python + P…

各种各样的bug合集

一、连不上数据库db 1.可能是密码一大包东西不对&#xff1b; 2.可能是里面某个port和数据库不一样&#xff08;针对于修改了数据库但是连不上的情况&#xff09;&#xff1b; 3.可能是git代码没拉对&#xff0c;再拉一下代码。❤ 二、没有这个包 可能是可以#注释掉。❤ …

面阵相机中M12镜头和远心镜头的区别及性能优势

以下是关于面阵相机中M12镜头和远心镜头的区别及性能优势的详细分析&#xff0c;结合知识库内容整理如下&#xff1a; 一、M12镜头与远心镜头的核心区别 1. 设计原理与光学特性 特性M12镜头远心镜头光学设计标准镜头设计&#xff0c;无特殊光学校正&#xff0c;依赖传统光路。…

从内核到应用层:深度剖析信号捕捉技术栈(含sigaction系统调用/SIGCHLD回收/volatile内存屏障)

Linux系列 文章目录 Linux系列前言一、进程对信号的捕捉1.1 内核对信号的捕捉1.2 sigaction()函数1.3 信号集的修改时机 二、可重入函数三、volatile关键字四、SIGCHLD信号 前言 Linux系统中&#xff0c;信号捕捉是指进程可以通过设置信号处理函数来响应特定信号。通过信号捕捉…

DDD领域驱动与传统CRUD

DDD 是一套 应对复杂业务系统 的设计方法论&#xff0c;核心是 让代码直接映射业务逻辑&#xff0c;避免技术实现与业务需求脱节。 关键区别&#xff1a; 传统开发&#xff1a;根据数据库表写 CRUD&#xff08;技术驱动&#xff09;。DDD&#xff1a;根据业务行为建模&#xf…

20. git diff

基本概述 git diff的作用是&#xff1a;比较代码差异 基本用法 1.工作区 VS 暂存区 git diff [file]2.暂存区 VS 最新提交 git diff --staged [file] # 或 git diff --cached [file]3.工作区 VS 最新提交 git diff HEAD [file]高级用法 1.比较两个提交间的差异 git dif…

大模型面经 | 春招、秋招算法面试常考八股文附答案(五)

大家好,我是皮先生!! 今天给大家分享一些关于大模型面试常见的面试题,希望对大家的面试有所帮助。 往期回顾: 大模型面经 | 春招、秋招算法面试常考八股文附答案(RAG专题一) 大模型面经 | 春招、秋招算法面试常考八股文附答案(RAG专题二) 大模型面经 | 春招、秋招算法…

Sql刷题日志(day5)

面试&#xff1a; 1、从数据分析角度&#xff0c;推荐模块怎么用指标衡量&#xff1f; 推荐模块主要目的是将用户进行转化&#xff0c;所以其主指标是推荐的转化率推荐模块的指标一般都通过埋点去收集用户的行为并完成相应的计算而形成相应的指标数据&#xff0c;而这里的驱动…

封装 element-ui 二次弹框

author 封装 element-ui 弹框 param text 文本内容 &#xff08;不传默认显示 确定执行此操作吗&#xff1f; &#xff09; param type 弹框类型&#xff08;不传默认warning类型&#xff09; param title 弹框标题&#xff08;不传默认显示 提示 &#xff09; export fun…

【Rust 精进之路之第12篇-生命周期·入门】为何需要与显式标注 (`‘a`):让编译器读懂引用的“有效期”

系列: Rust 精进之路:构建可靠、高效软件的底层逻辑 作者: 码觉客 发布日期: 2025-04-20 引言:悬垂引用的“幽灵”与编译器的“侦探” 在前面的章节中,我们深入学习了 Rust 的所有权系统,以及如何通过引用 (& 和 &mut) 进行借用,从而在不转移所有权的情况下安…

[密码学实战]CTF竞赛高频加密与解密技术详解

CTF竞赛高频加密与解密技术详解 一、CTF加密体系全景图 在CTF密码学挑战中&#xff0c;加解密技术主要分为四大战域&#xff1a; #mermaid-svg-lmm07BXqYAGYjymI {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lm…

docker.desktop下安装普罗米修斯prometheus、grafana并看服务器信息

目标 在docker.desktop下先安装这三种组件,然后显示当前服务的CPU等指标。各种坑已踩,用的是当前时间最新的镜像 核心关系概述 组件角色依赖关系Prometheus开源监控系统,负责 数据采集、存储、查询及告警。依赖 Node-Exporter 提供的指标数据。Node-Exporter专用的 数据采集…

《MySQL:MySQL表的内外连接》

表的连接分为内连接和外连接。 内连接 内连接实际上就是利用where子句对两种表形成的笛卡尔积进行筛选&#xff0c;之前的文章中所用的查询都是内连接&#xff0c;也是开发中使用的最多的连接查询。 select 字段 from 表1 inner join 表2 on 连接条件 and 其他条件&#xff1…

实现SpringBoot底层机制【Tomcat启动分析+Spring容器初始化+Tomcat 如何关联 Spring容器】

下载地址&#xff1a; https://download.csdn.net/download/2401_83418369/90675207 一、搭建环境 创建新项目 在pom.xml文件中导入依赖 &#xff08;一定要刷新Maven&#xff09;排除内嵌的Tomcat&#xff0c;引入自己指定的Tomcat <?xml version"1.0" enco…

从零开始构建微博爬虫:实现自动获取并保存微博内容

从零开始构建微博爬虫&#xff1a;实现自动获取并保存微博内容 前言 在信息爆炸的时代&#xff0c;社交媒体平台已经成为信息传播的重要渠道&#xff0c;其中微博作为中国最大的社交媒体平台之一&#xff0c;包含了大量有价值的信息和数据。对于研究人员、数据分析师或者只是…

Uniapp微信小程序:轻松获取用户头像和昵称

参考文献&#xff1a;Uniapp微信小程序&#xff1a;轻松获取用户头像和昵称-百度开发者中心 (baidu.com) uni.login({ provider: weixin, success: function (loginRes) { console.log(loginRes.authResult); // 打印登录凭证 // 使用登录凭证获取用户信息 uni.getUserInfo({ …

【自然语言处理与大模型】大模型(LLM)基础知识③

&#xff08;1&#xff09;大模型的“7B”是什么意思&#xff1f; "B"通常代表“Billion”&#xff0c;即十亿。因此&#xff0c;当提到7B时&#xff0c;指的是该模型拥有7 billion&#xff08;70亿&#xff09;个参数。 &#xff08;2&#xff09;模型后面标的“ins…

聊聊自动化用例的维护

自动化测试中的农药悖论&#xff1a;为何长期维护至关重要 自动化测试常被视为"一次编写&#xff0c;永久有效"的解决方案&#xff0c;但随着时间的推移&#xff0c;即使设计最精良的测试套件也会逐渐失效。这种现象被称为农药悖论&#xff08;Pesticide Paradox&am…

微帧Visionular斩获NAB Show 2025年度产品奖

在本月刚结束的NAB Show 2025展会上&#xff0c;全球领先的视频编码与AI超高清服务提供商微帧Visionular大放异彩&#xff0c;其核心产品AI-driven Video Compression&#xff08;AI视频智能编码引擎&#xff09;不仅在展会中吸引了众多行业目光&#xff0c;更凭借其卓越的编码…