当前位置: 首页 > article >正文

【Linux】浅谈冯诺依曼和进程

一、冯诺依曼体系结构

在这里插入图片描述
冯诺依曼由 输入设备、输出设备、运算器、控制器、存储器 五部分组成。

冯诺依曼的设计特点

  1. 二进制表示
    所有数据(包括程序指令)均以二进制形式存储和运算,简化了硬件逻辑设计,提高了可靠性。

  2. 存储程序原理 程序与数据共同存储于同一存储器中,且程序可像数据一样被修改。

  3. 顺序执行机制 指令按线性顺序逐条执行,由程序计数器(PC)控制执行流程。

  4. ​指令结构 每条指令由操作码​(定义操作类型)和地址码​(指定操作数位置)组成。

冯诺依曼体系结构的作用

引言: 外设CPU 读取速度完全不一样,如果 外设CPU 直接进行数据交互,就会导致 CPU 读取速度非常缓慢。但 冯诺依曼体系结构 就解决了这种问题,那它是怎么解决的呢?

  1. 内存作为缓冲区
    冯·诺依曼体系结构将内存作为CPU和外设之间的缓冲区。外设将数据写入内存,CPU再从内存读取数据进行处理。这种设计解决了CPU与外设之间速度不匹配的问题。例如,外设(如硬盘)的读写速度远低于CPU,通过内存作为中间存储,可以避免CPU长时间等待外设完成操作。
  2. 中断机制
    冯·诺依曼体系结构引入了中断机制,以优化CPU与外设之间的交互。外设在完成数据写入内存后,会发送中断信号通知CPU。CPU在收到中断信号后,再读取内存中的数据进行处理。这种方式避免了CPU轮询外设状态的低效操作,使得CPU可以将更多时间用于执行其他任务。
  3. 数据传输的统一性
    在冯·诺依曼体系结构中,所有数据(包括程序指令和外设数据)都通过内存进行交互。这种统一的数据流动模式简化了硬件设计,使得CPU只需要支持对内存的读写操作,而无需直接处理多种外设协议。
  4. 存储分级
    冯·诺依曼体系结构通过存储分级(如寄存器、缓存、内存、外存)来优化数据访问速度。内存作为中间层,其访问速度远高于外设,但低于CPU缓存和寄存器。通过这种分级设计,数据可以按需在不同存储层级之间流动,从而提高整体效率。
  5. 操作系统与驱动程序的支持
    操作系统通过驱动程序将外设的复杂操作抽象成统一的接口,进一步优化了CPU与外设之间的数据交互。驱动程序可以动态更新,以支持新功能,而无需修改硬件。

二、操作系统(Operator System)

概念

操作系统(Operating System,简称OS) 是管理计算机硬件与软件资源的系统软件。它提供用户接口和资源分配,是计算机系统中最基本的系统软件,任何计算机系统都包含一个基本的程序集合,统称为 操作系统(OS)
简单来讲, 操作系统 包含 :

  1. 内核(进程管理、内存管理、文件管理、驱动管理)
  2. 其他程序 (函数库、shell程序等)

设计OS的目的

  1. 对下,与硬件交互,管理所有软硬件资源
  2. 对上,为用户程序提供一个良好的运行环境
    在这里插入图片描述

OS如何管理软、硬件

OS 是怎么管理软、硬件的呢?,简单6个字就可以总结:先描述,再组织

  1. 先描述 :描述起来,用 struct 结构体来描述
  2. 再组织 :通过数据结构报 struct 结构体组织起来,用链表或其他数据结构(队列、哈希等)组织起来

进程

上面讲了 OS 是怎么将软硬件资源管理起来的,那么对于进程也是再这样吗?答案是肯定的

概念

  1. 进程信息被放在一个叫做 进程控制块 的数据结构中,可以理解为描述进程属性的结构体
  2. 课本上称之为 PCBprocess control block), Linux 操作系统下的PCB叫做 task_struct,是一种结构体

task_struct (描述进程)

task_struct 中的进程属性很多,我在这里描述其中的一部分:
3. 标识符:描述本进程的唯一标识符,用来区别其他进程
4. 状态:任务状态,退出代码,退出信号等
5. 优先级:用来描述进程被调度的先后的数字,数字小的优先级高,数字大的优先级低,和成绩排名有异曲同工之妙
6. 程序技术器(PC):表示程序中即将执行的下一条指令的地址
7. 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享内存块的指针
8. 上下文数据:进程执行时处理器的寄存器中的数据,主要包括:
CPU寄存器的值:包括程序计数器(PC)、指令寄存器、堆栈指针等。
内存映射信息:进程的虚拟内存空间布局。
进程状态:如运行状态、阻塞状态、就绪状态等。
I/O状态:与进程相关的输入输出设备的状态。
其他系统资源的状态:如文件描述符、信号状态等。
9. I / O状态信息:包含显示的 I / O 请求,分配给进程的 I / O 设备和被进程使用的文件链表
10. 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号的等。

组织进程

PCB 在内核中使用双链表进程链接起来的
在这里插入图片描述

查看进程

  1. 通过 /proc 系统文件夹来查看
    在这里插入图片描述

  2. 通过 topps 这些用户工具来获取进程信息
    top: top -p [pid]
    在这里插入图片描述
    ps: ps axj

在这里插入图片描述

通过系统调用获取进程标识符

  • 获取进程id (PID):getpid()
  • 父进程id (PID): getppid()
  • 通过手册来查看相关系统调用:man 2 getpid
    在这里插入图片描述

通过系统调用fork创建子进程

在这里插入图片描述
通过 fork 创建的子进程和父进程共享同一份代码,但数据是各自私有的(写时拷贝)(其中 pid_t 的类型其实是一个有符号整数类型,取了个别名而已,当 fork() 返回值为 0 时就表示子进程,当 fork() 返回值大于0时,表示的就为父进程。)
至于为什么数据各自私一份,有我们通过一个程序就可以看出来

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

int main()
{
    int val = 0;
    pid_t id = fork();
    if(id == 0)//表示子进程
    {
        while(1)
        {
            printf("我是子进程,pid: %d ,ppid: %d, val = %d\n",getpid(),getppid(),val++);
            sleep(1);
        }
    }
    else//父进程
    {
        while(1)
        {
            printf("我是父进程,pid: %d ,ppid: %d, val = %d\n",getpid(),getppid(),val);
            sleep(1);
        }
    }
    return 0;
}

程序运行结果:
在这里插入图片描述
可以看到父进程和子进程的 val 的值并不是一样的,我们就可以得出父子进程并不共享同一份数据的结论

进程状态

进程状态有很多种:

  • R运行状态:并不意味着进程一定在运行中,它表明进程要么在运行中,要么在运行队列中(运行队列用于管理处于“就绪状态”(Ready State)的进程。这些进程已经准备好运行,但正在等待 CPU 时间片分配。当调度器选择一个进程运行时,它会从运行队列中选取一个进程,并将其状态从“就绪”变为“运行”。
  • S睡眠状态:以为着进程在等待事件完成(也叫做可中断睡眠)
    注意: 以下有个场景可能让人误会为是 R 状态 ,但其实是 S 状态

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

int main()
{
    int cnt = 0;
    while(1)
    {
        printf("pid: %d,cnt = %d\n",getpid(),cnt++);
    }
    return 0;
}

~  

这里可能很多认为进程的状态是R状态,其实不然,这里的进程状态其实大部分都是S+(+表示是前台进程)状态,为什么呢?因为进程这里大部分时间都在做IO交互,IO的时间是很慢的,所有看不到R状态
在这里插入图片描述
表示每隔1秒查看对应名为code进程的信息:while :;do ps axj | head -1 && ps axj | grep code | grep -v grep;sleep 1 ; done
在这里插入图片描述
在这里插入图片描述

  • D磁盘休眠状态:有时候叫做不可中断状态(不会响应信号),在这个状态等待IO的结束

  • T停止状态:可以通过发送 SIGSTOP 信号来给进程停止 (T)进程。这个被暂停的进程可以通过发送 SIGCOUT 信号让进程继续执行
    在这里插入图片描述

  • X死亡状态:这是一个返回状态,你不会在任务列表中看到这个状态,因为这是一个瞬时状态,一下就消失了

  • Z僵死状态:是一个比较特殊的状态。当进程退出并且父进程没有读到子进程返回的退出码时,子进程就会除以一个僵死状态。僵死状态会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码(通过 kill -9 pid 杀死子进程就可以看到子进程处于僵死状态)
    在这里插入图片描述

僵死状态危害

没创建一个子进程就需要一个 PCB ,如果父进程创建了很多子进程,而没有去回收就会造成内存资源的浪费

孤儿进程

父进程如果提前退出,子进程就会变成孤儿进程,孤儿进程就会被1号 init 也就是操作系统启动后第一个运行的用户空间程序
在这里插入图片描述

进程优先级

基本概念

  • cpu资源分配的先后顺序,就是进程的优先权。

  • 优先级高的进程有优先执行权利。
    在这里插入图片描述

  • UID : 代表执⾏者的⾝份

  • PID : 代表这个进程的代号

  • PPID :代表这个进程是由哪个进程发展衍⽣⽽来的,亦即⽗进程的代号

  • PRI :代表这个进程可被执⾏的优先级,其值越⼩越早被执⾏

  • NI :代表这个进程的nice值(修正优先级数值的

其中 PRI(new) = PRI(采用默认值80) + nice

更改进程nice值

通过更改进程 nice 来改变进程优先级
top 命令来改变进程 nice 值:

  1. 进入top后按 “r” -> 输入进程PID -> 输入nice值

进程竞争、独立、并行、并发

  1. 竞争:本质是僧多粥少,系统进程数目众多,而CPU资源只有少量,所以进程之间具有竞争属性,为了更高效的完成任务,更合理竞争相关资源,于是就有了优先级
  2. 独立:多进程在运行时,需要独享各种资源,进程之间互不干扰,就算一个进程挂掉了,也不影响另一个进程,而如果线程挂掉了,不仅会影响其他进程,可能还会导致进程挂掉。
  3. 并行:多个进程在多个CPU下同时进行运行
  4. 并发:多个进程在同一个CPU资源下采用进程切换的方式,在同一段时间之内,让多个进程都得以推进

进程切换

在多任务操作系统中,多个进程会共享有限的 CPU 资源。为了实现高效的资源利用和良好的用户体验,操作系统需要在这些进程之间切换 CPU 的控制权。这种切换通常发生在以下几种情况:

  1. 时间片用完:在时间片轮转(Round Robin)调度算法中,当一个进程的时间片用完时,操作系统会将 CPU 切换到另一个进程。
    进程阻塞:当一个进程因为等待 I/O 操作(如磁盘读写、网络通信)而阻塞时,操作系统会将 CPU 切换到另一个就绪的进程。
    更高优先级的进程就绪:当一个更高优先级的进程进入就绪队列时,操作系统可能会抢占当前运行的进程,将 CPU 切换到优先级更高的进程。
    系统调用或中断:当进程执行系统调用或发生中断时,操作系统可能会触发进程切换。
  2. 进程切换的过程
    进程切换是一个复杂的过程,涉及到多个步骤,主要包括以下内容:
    2.1 保存当前进程的上下文
    当操作系统决定切换进程时,首先需要保存当前运行进程的上下文信息。上下文信息包括:
    CPU 寄存器的值:如程序计数器(PC)、指令寄存器(IR)、堆栈指针(SP)等。
    进程状态:如运行状态、就绪状态或阻塞状态。
    内存映射信息:如页表或段表。
    I/O 状态:如打开的文件描述符、设备状态等。
    这些上下文信息通常被保存在进程控制块(PCB,Process Control Block)中,或者在内核栈中。
    2.2 更新进程状态
    操作系统会更新当前进程的状态,将其从“运行状态”(Running State)改为“就绪状态”(Ready State)或“阻塞状态”(Blocked State),具体取决于进程为何被切换出去。
    2.3 选择下一个要运行的进程
    操作系统通过调度器(Scheduler)选择下一个要运行的进程。调度器会根据调度算法(如先来先服务、时间片轮转、优先级调度等)从就绪队列中选择一个进程。
    2.4 恢复新进程的上下文
    操作系统从新进程的 PCB 或内核栈中恢复其上下文信息,包括:
    将寄存器的值恢复到 CPU 寄存器中。
    更新内存映射信息。
    恢复 I/O 状态。
    2.5 将 CPU 控制权交给新进程
    操作系统将 CPU 的控制权交给新进程,新进程从上次被中断的地方继续执行。
  3. 进程切换的开销
    进程切换是一个相对昂贵的操作,因为它涉及到大量的上下文保存和恢复工作。每次切换都会消耗一定的 CPU 时间,具体开销包括:
    寄存器保存和恢复:保存和恢复 CPU 寄存器的值。
    内存映射切换:更新页表或段表。
    调度器开销:选择下一个进程的开销。
    缓存失效:切换进程可能导致 CPU 缓存失效,新进程需要重新加载数据到缓存中。
    因此,操作系统会尽量减少不必要的进程切换,以提高系统性能。
  4. 示例:进程切换的场景
    假设系统中有两个进程 A 和 B,它们共享 CPU 资源。以下是进程切换的一个简单示例:
    进程 A 运行:
    进程 A 正在 CPU 上运行,执行某些任务。
    时间片用完,操作系统决定切换进程。
    保存进程 A 的上下文:
    操作系统保存进程 A 的寄存器值、内存映射信息等,并将其状态从“运行状态”改为“就绪状态”。
    选择进程 B:
    调度器从就绪队列中选择进程 B。
    恢复进程 B 的上下文:
    操作系统恢复进程 B 的寄存器值、内存映射信息等。
    进程 B 运行:
    进程 B 从上次被中断的地方继续执行。

在这里插入图片描述

Linux2.6内核进程O(1)调度队列

LInux源码查看网站
Linux源码官方网站

Linux2.6内核调度队列

在这里插入图片描述

活动队列(array[0])
  • 时间片还没有结束的所有的进程都会按照优先级放在该队列
  • nr_active:总共有多少个运行的进程
  • queue[140]:一个元素就是一个进程队列,相同优先级的进程按照 FIFO(先进先出)的规则排队调度,所以数组下标就是优先级
  • bitmap[5]:一共140个优先级,用位图就可以提高非空队列的效率,一个整形占32个比特位,数组大小为5,就有5 * 32 = 160 个bit位来表示队列是否为空,大大提高了查找效率
    在这里插入图片描述
过期队列
  • 过期队列上的进程都是时间片耗尽之后的进程
  • 活动队列上的进程都被处理完毕以后,就会对过期队列的进程进行时间片重新计算
active 和 expired 指针
  • active指针永远指向活动队列
  • expired指针永远指向过期队列
  • 活动队列中的进程进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期就会进入过期队列,但在合适的时间交换 active 指针和 expired 指针的内容,即 swap(&active,&expired) ,活动队列就又有了一批新的活动进程

http://www.kler.cn/a/583992.html

相关文章:

  • unity基础——3D画线
  • 【MySQL】MySQL服务器——mysqld
  • C语言实现十六进制转十进制
  • 分布式事务中XA 事务 和 两阶段提交(2PC)应该如何理解?
  • NineData 社区版:从 MySQL 到 TiDB 数据复制新选择
  • 网络安全反渗透 网络安全攻防渗透
  • 【javaEE】文件操作--io
  • 使用mybatis-plus自定义分页实现一对多的分页功能
  • Unity引擎架构介绍及代码示例
  • Nature最新报道:分析四大主流AI工具、性能测评、推荐使用场景
  • Vim忍者速成秘卷:让你的键盘冒出残影の奥义
  • 如何通过ibd文件恢复MySql数据
  • 鸿蒙编译框架插件HvigorPlugin接口的用法介绍
  • 蓝桥杯备考:数据结构堆之 除2!
  • STM32Cubemx-H7-9-串口接受不定长度数据并识别
  • 解决 VSCode SSH 连接报错:“REMOTE HOST IDENTIFICATION HAS CHANGED” 的问题
  • Nginx 多协议代理功能(Nginx Multi Protocol Proxy Function)
  • windows11 LTSC 24h2 访问NAS问题的安全高效解决
  • C语言:计算并输出三个整数的最大值 并对三个数排序
  • 图解AUTOSAR_CP_ServiceDiscovery