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

操作系统-锁/内存/中断/IO

文章目录

          • 自旋锁
          • 互斥锁
          • 悲观锁和乐观锁
        • 内存管理
          • 物理/虚拟内存
          • 页表
          • 段表
          • 虚拟内存布局
          • 写时复制copy on write
          • brk,mmap
          • 页面置换算法
        • 中断
          • 中断分类
          • 中断流程
        • 网络I/O
          • I/O模型
          • 服务器处理并发请求

自旋锁

自旋锁是一种基于忙等待(Busy-Waiting)的同步机制。

通过 CPU 提供的 CAS 函数),完成加锁解锁操作:

第一步:查看锁的状态,为空,则执行第二步

第二步:将锁设置为当前线程持有

这两步是原子指令,要么一次性执行完,要么都不执行。

当线程尝试获取锁失败时,它会循环检查锁的状态(“自旋”),直到它拿到锁。

等待时间较短的情况下效率较高,因为避免了线程上下文切换的开销。但长时间等待会导致CPU资源的浪费。

适用于多核系统,且临界区代码执行时间非常短的场景。

互斥锁

在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁,确保同一时间只有一个线程访问共享资源。

自旋锁和互斥锁的区别

工作机制:自旋锁在获取不到锁时会一直循环检查,而互斥锁会让线程进入睡眠(挂起)状态,调度其他线程运行。

适用场景:自旋锁适合保护执行时间极的代码段,自旋的代价可能小于线程切换的代价。互斥锁适合保护执行时间较的代码段

性能影响:自旋锁无上下文切换开销,但一直占用CPU,互斥锁挂起唤醒线程产生下上文切换开销,但等待期间CPU执行其他任务,资源利用率更高.

实现复杂度:自旋锁依赖CAS原子操作,不需要操作系统调度,而互斥锁复杂度高,依赖操作系统调度

悲观锁和乐观锁

悲观锁先加锁,再操作

认为并发操作一定会发生冲突,因此每次访问数据时都会加锁,比如synchronized和ReentrantLock

适用写多的场景。

举个例子:出门时锁门(默认有小偷)

乐观锁先操作,提交时再检查冲突

认为并发操作很少发生冲突,只在提交操作时检查是否冲突,比如CAS操作,数据库的乐观锁和Java中的Atomic类。

适用读多写少。

举个例子:

1.购物车结算时才检查库存(默认没人抢购)

2.或者在网上订票,系统显示还有1个座位,你点击预订,系统会先让你填写信息,然后提交的时候检查是否还有座位。如果有,预订成功;如果没有,提示你重新选择

内存管理

计算机系统的核心功能之一,其目标是高效、安全地管理物理内存和虚拟内存资源,确保多个进程能共享内存且互不干扰

物理/虚拟内存
  • 物理内存

    计算机硬件中的实际内存芯片,容量固定,由硬件决定,直接由CPU通过物理地址访问,是程序运行的真实存储空间

  • 虚拟内存

    操作系统为每个进程提供的逻辑地址空间,独立于物理内存,通过通过页表、MMU(内存管理单元,负责管理内存访问和地址转换)、缺页中断等机制管理,将虚拟地址映射到物理地址或磁盘空间。

    • 虚拟内存允许程序使用比物理内存更大的地址空间

    • 每个进程拥有独立的虚拟地址空间,彼此无法直接访问对方内存,避免恶意或错误操作。

      在这里插入图片描述

页表

在这里插入图片描述

操作系统与硬件(如MMU)协作实现虚拟内存的核心数据结构,负责记录虚拟地址到物理地址的映射关系。

核心作用

  1. 地址映射
    将进程的虚拟页号 映射到物理内存的物理页号
  2. 权限控制
    通过页表项的权限位(读/写/执行)限制内存访问。
  3. 状态标记
    记录页是否在物理内存中、是否被修改过等状态信息。

工作流程

  1. 把虚拟内存地址,切分成页号和页内偏移量
  2. 根据页号,从页表里面,查询对应的物理页号
  3. 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址。

如果在页表中没有相应页号,触发缺页中断。此时进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。

段表

在这里插入图片描述

虚拟地址也可以通过段表与物理地址进行映射的

  1. 程序的内存空间被划分为多个逻辑段,每个段代表一个逻辑单元
  2. 每个段有独立的基址(起始地址)和界限(长度)
  3. 段的大小可变,与程序逻辑直接对应(例如代码段大小取决于代码量)
  4. 物理地址 = 段基址 + 段内偏移
虚拟内存布局

在这里插入图片描述

1. 代码段

  • 位置:低地址起始(如0x400000)。
  • 内容:编译后的机器指令(可执行代码)。
  • 权限:只读+可执行(防止代码被篡改)。
  • 示例:main函数、库函数的指令。

2. 数据段

  • 位置:紧接代码段。
  • 内容:已初始化的全局变量和静态变量
  • 权限:可读写,不可执行。
  • 示例:int global_var = 42;

3. BSS段

  • 位置:紧接数据段。
  • 内容:未初始化的全局变量和静态变量
  • 权限:可读写
  • 示例:int uninitialized_var;

4. 堆

  • 位置:BSS段之上,向高地址增长。
  • 管理:通过mallocfree动态分配内存。
  • 特点:碎片化问题常见,需手动管理(或依赖垃圾回收)。
  • 示例:int *arr = malloc(100 * sizeof(int));

5. 文件映射区域

  • 位置:堆与栈之间。
  • 内容:
    • 共享库(如libc.solibm.so)。
    • 内存映射文件(通过mmap映射的文件)。
    • 匿名映射(用于大块内存分配,如malloc可能使用mmap)。
  • 权限:按需设置(如可读写、可执行)。

6. 栈

  • 位置:高地址区域(如0x7FFFFFFFFFFFF000),向下增长
  • 内容:函数调用栈帧(局部变量、返回地址、函数参数)。
  • 管理:自动分配/释放内存,由编译器控制。
  • 限制:栈大小固定(默认几MB,可通过ulimit调整)。
  • 示例:int local_var = 10;

7. 内核空间

  • 位置:虚拟地址空间的高位(如64位Linux中高128TB)。
  • 权限:仅内核态可访问,用户进程无权直接读写。
  • 内容:内核代码、数据结构、设备内存映射等。
写时复制copy on write

COW,一种内存管理优化技术,延迟数据的物理拷贝,直到真正需要修改数据时才进行复制。

在这里插入图片描述

  • 问题场景
    假设父进程和子进程共享同一物理内存页(未复制),且子进程修改了某个变量。如果此时父进程读取该变量,会发现值被意外改变。

    • 示例
      父进程定义变量 int x = 42,子进程修改 x = 100。若未复制,父进程的 x 也会变为 100,导致逻辑错误。
  • COW 的解决方案
    当子进程尝试写入 x 时,触发复制,子进程获得独立的物理页副本。

    • 父进程的 x 保持 42,子进程的 x 变为 100,两者互不影响。

具体步骤:

  • 步骤1:共享内存页
    调用 fork() 时,子进程与父进程共享所有物理内存页,页表项标记为只读
  • 步骤2:触发复制
    • 当父进程或子进程尝试写入共享页时,触发缺页中断
    • 操作系统捕获中断,检查触发原因是 COW,执行以下操作:
      1. 分配新的物理页,复制原页内容到新页。
      2. 更新触发写入的进程的页表项,指向新物理页,并标记为可写
      3. 另一进程仍指向原物理页(保持只读,直到其写入时触发复制)。
  • 步骤3:后续操作
    • 修改后的页独立于原页,后续写入不再触发复制。

COW有什么好?

fork()的时候,子进程不需要复制父进程的物理内存,只需要复制父进程的页表,避免了不必要的内存复制开销,这时候父子进程的页表指向的都是共享的物理内存。

只有当父子进程任何有一方对这片共享的物理内存发生了修改操作,这时候才会复制发生修改操作的物理内存。

brk,mmap

int *arr = malloc(100 * sizeof(int))

这里的动态分配内存malloc():

1.如果请求分配的内存<128KB,则通过brk()申请

2.如果请求分配的内存>128KB,则通过mmap()申请

  • brk:修改指针,向高地址移动,获得新内存空间

    在这里插入图片描述

  • mmap:从文件映射区申请一块内存

    在这里插入图片描述

如果物理内存不足

当使用malloc()申请虚拟内存时,如果虚拟内存还没有映射到物理内存,CPU产生缺页中断,缺页中断函数查看是否有空闲物理内存,有则建立虚拟与物理内存的映射,如果没有,则开始回收内存(页面置换/丢弃/终止)

回收内存,包括:

  1. 页面置换
  • 将部分物理内存页换出到磁盘(Swap空间),释放物理内存供其他进程使用。
  1. 释放可丢弃的干净页
  • 直接丢弃未被修改的页(如代码段、文件缓存页),无需写回磁盘。
  1. 终止进程(OOM Killer)
  • 强制终止占用内存过多的进程,释放其所有内存(极端情况下的最后手段)。

1.后台内存回收:唤醒 kswapd 内核线程来回收内存(异步,不阻塞进程执行)

2.直接内存回收:如果异步回收速度跟不上申请内存速度,则直接回收(同步,阻塞进程执行)

哪些内存可以被回收?

  • 文件页

内核缓存的磁盘数据(Buffer)和内核缓存的文件数据(Cache)都叫作文件页。需要读时,重新读取磁盘即可。而如果修改过但还没写入磁盘的数据(脏页)需要先写进磁盘,才进行回收。

  • 匿名页

无实际载体,所以不能直接释放内存。会把不常访问的内存先写到磁盘,再释放这些内存。

如果经过上面的步骤物理内存仍不满足,则触发Out-Of-Memory Killer

  • OOM根据算法杀死一个占用物理内存较高的进程,释放其内存资源
  • 如果依然不满足,继续杀死,直至释放足够物理内存
页面置换算法

页面置换,内存回收的核心手段之一。

选择一个物理页面换出到磁盘,然后把需要访问的页面换入到物理页。

  • 最佳页面OPT

    思想:置换出在未来长时间不会使用的页面

    在这里插入图片描述

  • 先进先出FIFO

    思想:换出在内存中占用时间最长的页面

    在这里插入图片描述

  • 最近最久未使用LRU

    思想:换出前面长时间没有访问过的页面

    在这里插入图片描述

  • 时钟页面LOCK

    思想:将所有页面存在环形链表,每页需要一个访问位(0:未访问1:已访问)

    初始:所有页访问位为0

    缺页时,指针移动,

    ​ 访问位为1:重置为0,指针后移

    ​ 访问位为0:选择,换出内存

    在这里插入图片描述

  • 最不常用LFU

    思想:对每个页面增加一个访问计数器,被访问一次,计数器+1,发生缺页中断时,换出访问次数最少的页面,

中断

计算机系统响应外部或内部事件的机制,允许 CPU 暂停当前任务,转而处理紧急或异步事件(如键盘输入)。处理完后回来继续执行刚才的任务。

中断机制是操作系统实现多任务、设备管理、错误处理等功能的基础

中断分类

外部中断(硬件中断)

  • 触发源:外部设备(如键盘、磁盘、定时器)。
  • 类型:
    • 可屏蔽中断:可通过中断屏蔽位(如 IF 标志)暂时关闭,可随时处理,甚至不处理。
    • 不可屏蔽中断:必须立即处理(如硬件故障、内存校验错误)。

内部中断(软件中断)

  • 触发方式:由程序执行特定指令或发生异常。
    • 异常:同步触发,由 CPU 执行指令时检测到错误(如缺页、除零)交由故障处理程序处理。
    • 陷阱:一般是在编写程序时故意设下的陷阱指令,而后执行到陷阱指令后,CPU将会调用特定程序进行相应的处理,处理结束后返回到陷阱指令的下一条指令。
    • 终止:发生致命错误,不可修复,程序无法继续运行,直接终止。
中断流程
  • 发生中断:设备或程序触发中断,发送信号到 CPU 的中断控制器

  • 中断响应:保存上下文,CPU 暂停当前任务,将程序计数器(PC)、寄存器等压入内核栈,保存当前执行现场,以便处理完中断后恢复执行。

  • 中断处理:CPU根据中断向量表找到对应中断处理程序入口地址,进行中断处理

  • 恢复上下文:继续执行之前的程序

网络I/O
I/O模型

网络I/O模型定义了应用程序如何管理输入和输出操作,尤其是在处理多个并发连接时如何高效利用资源

  1. 阻塞I/O
    • 工作原理:
      应用程序发起I/O操作后,进程会被阻塞,直到数据就绪或操作完成。在此期间,进程无法执行其他任务
    • 优点:
      实现简单,代码直观,适合低并发场景。
    • 缺点:
      每个连接需独立线程/进程,资源消耗大,无法支撑高并发。
    • 适用场景:
      简单的客户端应用或低负载服务(实时性要求不高)。
  2. 非阻塞I/O
    • 工作原理:
      I/O操作立即返回结果(成功或错误),不会被阻塞。若数据未就绪,返回错误,应用需轮询检查状态。
    • 优点:
      单线程可管理多个连接,避免线程阻塞。
    • 缺点:
      轮询消耗CPU资源,延迟较高。
    • 适用场景:
      需要同时处理少量连接且对延迟不敏感的场景(多路复用)。
  3. I/O多路复用
    • 工作原理:
      使用selectpollepoll(Linux)等系统调用,同时等待多个I/O操作,当任一I/O准备就绪时通知应用处理。
    • 优点:
      单线程处理高并发连接,资源利用率高。
    • 缺点:
      事件通知后仍需同步处理I/O,编程复杂度较高。
    • 适用场景:
      Web服务器(如Nginx)、即时通讯等高并发服务。
  4. 信号驱动I/O
    • 工作原理:
      发起I/O请求后继续做其他事情,当I/O操作就绪时,内核发送信号通知处理,应用执行I/O操作
    • 优点:
      避免轮询,减少CPU占用。
    • 缺点:
      信号处理复杂,可能丢失事件,多线程中难以管理。
    • 适用场景:
      需要异步I/O通知的场景,提高系统并发能力。
  5. 异步I/O
    • 工作原理:
      应用发起I/O请求后立即做其他事情,内核完成整个I/O操作后通知应用。
    • 优点:
      无阻塞,资源利用最优,适合高吞吐场景。
    • 缺点:
      实现复杂,需操作系统和库支持,调试困难。
    • 适用场景:
      高并发,高性能场景,减少系统调用,提高系统效率。
服务器处理并发请求
  1. 单线程web服务器

    一次处理一个请求,性能低

  2. 多进程/多线程web服务器

    服务器生成多个进程/线程并行处理多个用户请求,消耗大量系统资源

  3. I/O多路复用web服务器

    只用一个线程处理多个用户请求

  4. 多路复用多线程web服务器

    避免一个进程服务于过多用户请求,生成多个进程,一个进程生成多个线程,每个线程处理一个请求

在这里插入图片描述
考完操作系统不久,结合小林Coding写了些笔记,感谢大家的点赞收藏>W<

http://www.xdnf.cn/news/486973.html

相关文章:

  • 机器学习前言2
  • 如何在windows server 2022 上安装WSL
  • Seata源码—4.全局事务拦截与开启事务处理一
  • 三格电子上新了——Modbus转IEC104网关
  • mybatisPlus 新增时 其他字段的值和 id 保持一致实现方法
  • NexBot AI 1.9.3 | 专业AI写作助手,高自由度定制内容,支持中文设置
  • Java 线程状态详解:从创建到销毁的完整旅途
  • 深度学习驱动下的目标检测技术:原理、算法与应用创新
  • PT2031S单触控单输出触摸IC
  • 关闭VSCode 自动更新
  • foxmail - foxmail 启用超大附件提示密码与帐号不匹配
  • 内部检测实验室数字化转型新路径 质检LIMS系统如何实现合规、效率、资质三重突破?
  • FC7300 SPI MCAL配置引导
  • Python爬虫(28)Python爬虫高阶:Selenium+Splash双引擎渲染实战与性能优化
  • Go语言交替打印问题及多种实现方法
  • 黑马点评面试前复习
  • 探索大型语言模型(LLM)的开源学习路径:mlabonne/llm-course 深度解析
  • 多模块,依赖android.car.jar后,能调用接口但是没有回调的问题
  • 电子电路仿真实验教学平台重磅上线!——深圳航天科技创新研究院倾力打造,助力高校教学数字化转型
  • Linux进程信号(一)之信号的入门
  • AI-Course-Presenter
  • Houdini POP入门学习01
  • 后端框架(1):Mybatis
  • “分布形态“
  • 浅谈“量子计算应用:从基础原理到行业破局”
  • ohttps开启群晖ssl证书自动更新
  • lesson03-简单回归案例(理论+代码)
  • 文章记单词 | 第87篇(六级)
  • FC7300 ADC MCAL配置引导
  • sqli-labs靶场23-28a关(过滤)