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

广西专业建网站企业工商年报网上申报系统官网

广西专业建网站,企业工商年报网上申报系统官网,网站侧栏软件排行榜怎么做的,网站seo优化分析文章目录 力推操作系统的三门神课操作系统的作用和功能线程、进程和协程的区别并行与并发的区别什么是文件描述符操作系统内核态和用户态的区别用户态切换到内核态的方式大内核和微内核的区别用户级线程和内核级线程的区别线程的七态模型进程调度算法有哪些进程间通信的七种方式… 文章目录 力推操作系统的三门神课操作系统的作用和功能线程、进程和协程的区别并行与并发的区别什么是文件描述符操作系统内核态和用户态的区别用户态切换到内核态的方式大内核和微内核的区别用户级线程和内核级线程的区别线程的七态模型进程调度算法有哪些进程间通信的七种方式进程间同步与互斥的区别什么是僵尸进程和孤儿进程linux底层的零拷贝技术linux的各种IO模型详解IO多路复用 epoll原理虚拟内存解决了什么问题动态链接库与静态链接库的区别 力推操作系统的三门神课 标题链接清华《操作系统原理》视频链接哈工大 李治军《操作系统原理》视频链接南大 蒋炎岩《操作系统概述》视频链接 操作系统的作用和功能 定义 很难有一个公认、精确的定义一般是根据功能特点来定义。总的来说是一个承上应用程序启下计算机硬件资源的系统软件。对上操作系统可以管理程序给应用程序提供服务确保它们有序、有效地执行。对下操作系统是计算机资源cpu、内存、磁盘、外设的管理者实现了资源的抽象。 功能 CPU调度器进程和线程管理进程线程的状态、控制、同步互斥、通信调度等内存管理物理内存、虚拟内存、分配/回收、地址转换、存储保护等文件管理文件目录、文件操作、磁盘空间、文件存取控制 线程、进程和协程的区别 1进程process 程序在数据集中的一次动态执行过程可以简单理解为“正在执行的程序”它是系统进行资源分配和调度的基本单位。 进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成数据集则是程序在执行过程中所需要使用的资源进程控制块PCB用来记录进程的外部特征描述进程的执行变化过程系统可以利用它来控制和管理进程它是系统感知进程存在的唯一标志。 2线程(thread) 线程包含在进程中是进程中一个单一顺序的控制流像“线”一样。它是CPU调度的最小单元一个进程的多个线程在执行不同任务的同时共享进程的系统资源如虚拟地址空间文件描述符等。线程由线程ID、程序计数器(PC)、堆栈寄存器和线程控制块组成。 线程的出现是为了减少任务切换的消耗提高系统的并发性实现让一个进程也能执行多个任务。例如一个浏览器网页需要加载TechGuide网站内容、显示文本信息和图片信息。如果使用多个进程来执行这些任务需要频繁的进行上下文切换和进程间通信。考虑到这些任务是相互关联且共享资源的它们都要用到网站资源用一个进程中的多个线程来执行可以减少上下文切换和进程间通信的消耗。 3协程Coroutines 协程是一种比线程更加轻量级的存在协程不是被操作系统内核所管理内核不可见而完全是由用户程序所控制的用户空间线程。 当出现IO阻塞的时候由协程的调度器进行调度通过将数据流立刻yield()掉主动让出并且记录当前栈上的数据阻塞完后立刻再通过线程恢复栈并把阻塞的结果放到这个线程上去跑。协程的目的就是当出现长时间的I/O操作时通过让出目前的协程调度执行下一个任务的方式来消除Context Switch上的开销。 链接https://www.cnblogs.com/Survivalist/p/11527949.html 4总结 资源 线程是任务调度的最小单位而进程是操作系统分配资源的最小单位。进程是拥有系统资源的独立单位线程不拥有系统资源但可以访问隶属于进程的资源内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)。这里稍微扩展下线程共享和独占的资源加深理解。 线程共享的环境包括 进程代码段进程的公有数据(利用这些共享的数据线程很容易的实现相互之间的通讯)进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。 线程独立的资源包括 线程ID每个线程都有自己的线程ID这个ID在本进程中是唯一的。进程用此来标识线程。 寄存器组的值由于线程间是并发运行的每个线程有自己不同的运行线索当从一个线程切换到另一个线程上 时必须将原有的线程的寄存器集合的状态保存以便将来该线程在被重新切换到时能得以恢复。 线程的堆栈堆栈是保证线程独立运行所必须的。线程函数可以调用函数而被调用函数中又是可以层层嵌套的所以线程必须拥有自己的函数堆栈 使得函数调用可以正常执行不受其他线程的影响。 错误返回码由于同一个进程中有很多个线程在同时运行可能某个线程进行系统调用后设置了errno值而在该线程还没有处理这个错误另外一个线程就在此时被调度器投入运行这样错误值就有可能被修改。所以不同的线程应该拥有自己的错误返回码变量。 线程的信号屏蔽码由于每个线程所感兴趣的信号不同所以线程的信号屏蔽码应该由线程自己管理。但所有的线程都共享同样的信号处理器。 线程的优先级由于线程需要像进程那样能够被调度那么就必须要有可供调度使用的参数这个参数就是线程的优先级。 系统开销 在创建或撤消进程时由于系统都要为之分配和回收资源导致系统的开销明显大于创建或撤消线程时的开销。但是进程有独立的地址空间一个进程崩溃后在保护模式下不会对其它进程产生影响而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量但线程没有单独的地址空间一个进程死掉就等于所有的线程死掉所以多进程的程序要比多线程的程序健壮但在进程切换时耗费资源较大效率要差一些。 管理方式 进程的实现只能由操作系统内核来实现而不存在用户态实现的情况。但是对于线程就不同了线程的管理者可以是用户也可以是操作系统本身(分为用户级和内核级)。 并行与并发的区别 并发性(concurrency) 是指能处理多个同时性活动的能力并发事件之间不一定要同一时刻发生现在主流的os都是时间片轮询的抢占式调度同一时刻只有获得时间片的线程执行。【可以理解为共同出发】并行(parallelism) 是指同时发生的两个并发事件具有并发的含义而并发则不一定并行。比如对单核CPU因为一个CPU一次只能执行一条指令是无法做到并行只能做到并发【可以理解为同时进行】 什么是文件描述符 文件描述符file descriptor是内核为了高效管理已被打开的文件所创建的索引在形式上是一个非负整数。实际上它是一个索引值指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时内核向进程返回一个文件描述符。 每一个文件描述符会与一个打开文件相对应同时不同的文件描述符也会指向同一个文件。相同的文件可以被不同的进程打开也可以在同一个进程中被多次打开。系统为每一个进程维护了一个文件描述符表该表的值都是从0开始的所以在不同的进程中你会看到相同的文件描述符这种情况下相同文件描述符有可能指向同一个文件也有可能指向不同的文件。 inode号是用来储存文件的元信息的区域比如文件的创建者、文件的创建日期、文件的大小等等可以使用ls -i命令查看当前目录中的inode号。 链接https://blog.csdn.net/cywosp/article/details/38965239 操作系统内核态和用户态的区别 先介绍下为什么会有内核态和用户态系统的资源是固定且有限的例如内存、CPU、磁盘、网络端口等所以需要操作系统对资源进行有效的利用。如果某个应用程序过分的访问这些资源就会造成整个系统的资源无序混乱的使用如果不对这种行为进行限制和区分就会导致资源访问的冲突甚至出错。所以Linux内核设计的初衷是给不同的操作给与不同的权限用户态与内核态只是不同权限的资源范围。 CPU将指令分为特权指令和非特权指令对于那些危险的指令只允许操作系统及其相关模块使用普通的应用程序只能使用那些不会造成灾难的指令。Intel的CPU将特权级别分为4个级别RING0内核、RING1、RING2、RING3用户级。Linux只用到了RING0和RING3。 内核态执行内核空间的代码具有RING0保护级别有对硬件的所有操作权限可以执行所有cpu指令集访问任意地址的内存在内核模式下的任何异常都是灾难性的将会导致整台机器停机。 用户态下具有RING3保护级别代码没有对硬件的直接控制权限也不能直接访问地址的内存程序是通过调用系统接口(System Call)来达到访问硬件和内存。 链接https://juejin.cn/post/6923863670132850701 用户态切换到内核态的方式 系统调用主动 用户态进程主动切换到内核态的方式用户态进程通过系统调用向操作系统申请资源完成工作例如 fork就是一个创建新进程的系统调用。系统调用本身就是一种软件中断通过操作系统为用户开放的一个中断来实现。 补充说下中断原理面试装b必备中断有两个属性一个称为中断号从0开始一个称为中断处理程序Interrupt Service Routine, ISR。不同的中断具有不同的中断号并且一一对应。在内核中有一个数组称为中断向量表Interrupt vector table这个数组的第n项包含了指向第n号中断的中断处理程序的指针。当中断到来时CPU会暂时中断当前执行的代码根据中断的中断号在中断向量表中找到对应的中断处理程序并调用它。中断处理程序执行完成以后CPU会继续执行之前的代码。 中断有两种类型一种称为硬件中断这种中断来自于硬件的异常或其他事件的发生如电源掉电、键盘被按下等另一种称为软中断软件中断通常是一条执行int中断号的指令使用这条指令可以主动触发某个中断并执行其中断处理函数。例如在i386下int 0x80这条指令会调用第0x80号中断的处理程序。 由于中断号是有限的操作系统不舍得用一个中断号来对应一个系统调用而倾向于用一个或少数几个中断号对应所有的系统调用。例如i386下Windows里绝大多数系统调用都是由int 0x2e来触发的而linux则使用int 0x80来触发所有的系统调用。 那么问题来了对于同一个中断号操作系统如何知道是哪一个系统调用要被调用呢 和中断一样系统调用都有一个系统调用号每个系统调用号都唯一对应一个系统调用处理函数。例如Linux里fork的系统调用号是2这个系统调用号在执行int指令前会被放置在某个固定的寄存器里对应的中断代码会受到这个系统调用号并且调用正确的函数。 链接 用户态内核态https://juejin.cn/post/6923863670132850701 中断原理https://blog.csdn.net/kang___xi/article/details/80556633 异常被动 当CPU在执行运行在用户态下的程序时发生了某些事先不可知的异常这时会触发由当前运行进程切换到处理此异常的内核相关程序中也就转到了内核态比如缺页异常。 什么是缺页异常CPU通过地址总线可以访问连接在地址总线上的所有外设包括物理内存、IO设备等等但从CPU发出的访问地址并非是这些外设在地址总线上的物理地址而是一个虚拟地址由MMU将虚拟地址转换成物理地址再从地址总线上发出CPU给MMU的虚拟地址在快表和页表都没有找到对应的物理页帧就会产生一个缺页异常。 缺页异常实际上并不一定是一种错误而是用虚拟内存来增加程序可用内存空间的一种机制只有程序运行时用到了才去内存中寻找虚拟地址对应的页帧找不到才可能进行分配这就是内存的惰性(延时)分配机制。这部分涉及到的连续内存分配、非连续内存分配中的分段、分页、段页式以及虚拟内存、地址翻译、内存换入、换出的部分没有详细展开后面有部分论述以后有机会再更新。 当发生缺页异常时如果是物理内存中没有对应的页帧需要CPU打开磁盘设备读取到物理内存中再让MMU建立VA和PA的映射。 更复杂的情况是如果操作系统物理内存中恰好也没有空闲页面了则操作系统必须在物理内存选择一个页面将其移出以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。OPT最佳不可能实现、FIFO性能差、LRU接近OPT但是开销大、CLOCK√算法 链接 缺页异常https://cloud.tencent.com/developer/article/1807351 页面置换算法https://www.cnblogs.com/fkissx/p/4712959.html 硬件中断当外围设备完成用户请求的操作后会向CPU发出相应的中断信号这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序如果先前执行的指令是用户态下的程序那么这个转换的过程自然也就发生了由用户态到内核态的切换。如硬盘读写操作完成系统会切换到硬盘读写的中断处理程序中执行后边的操作等。 大内核和微内核的区别 大内核系统 将操作系统的主要功能模块都作为一个紧密联系的整体运行在核心态从而为应用提供高性能的系统服务。因为各管理模块之间共享信息能有效利用相互之间的有效特性所以具有无可比拟的性能优势。 微内核 将内核中最基本的功能如进程管理等保留在内核而将那些不需要在核心态执行的功能移到用户态执行从而降低了内核的设计复杂性。而那些移出内核的操作系统代码根据分层的原则被划分成若干服务程序它们的执行相互独立交互则都借助于微内核进行通信。 微内核结构有效地分离了内核与服务、服务与服务使得它们之间的接口更加清晰维护的代价大大降低各部分可以独立地优化和演进从而保证了操作系统的可靠性。 但是微内核结构的最大问题是性能问题因为需要频繁地在核心态和用户态之间进行切换操作系统的执行开销偏大。因此有的操作系统将那些频繁使用的系统服务又移回内核从而保证系统性能。 用户级线程和内核级线程的区别 用户级线程 是指不需要内核支持而在用户程序中实现的线程它的线程切换是由用户态程序自己控制线程的切换yield不需要内核的干涉。 举个栗子由于用户线程的透明性操作系统是不能主动切换线程的换句话讲如果 AB 是同一个进程的两个线程的话 A 正在运行的时候线程 B 想要运行的话只能等待 A 主动放弃 CPU也就是主动调用 pthread_yield 函数。 内核级线程 线程切换由内核控制当线程进行切换的时候由用户态转化为内核态。切换完毕要从内核态返回用户态。它的优势是可以很好的运用多核CPU。为了实现内核级线程内核里就需要有用来记录系统里所有线程的线程表。当需要创建一个新线程的时候就需要进行一个系统调用然后由操作系统进行线程表的更新。开销会更大。 线程的七态模型 线程的生命周期模型可以从三态 到五态 再到七态逐步扩充递进介绍下 三态 运行态→等待态往往是由于等待外设等待主存等资源分配或等待人工干预而引起的。 等待态→就绪态则是等待的条件已满足只需分配到处理器后就能运行。 运行态→就绪态 不是由于自身原因而是由外界原因使运行状态的进程让出处理器这时候就变成就绪态。例如时间片用完或有更高优先级的进程来抢占处理器等。 就绪态→运行态系统按某种调度策略选中就绪队列中的一个进程占用处理器此时就变成了运行态。 五态 五态在三态的基础上引入了新建态和终止态描述一个进程创建和终结的过程。 NULL→新建态执行一个程序创建一个子进程fork系统调用。 新建态→就绪态当操作系统完成了进程创建的必要操作并且当前系统的性能和虚拟内存的容量均允许。 运行态→终止态当一个进程到达了自然结束点或是出现了无法克服的错误或是被操作系统所终结或是被其他有终止权的进程所终结。 等待态/就绪态→终止态未在状态转换图中显示但某些操作系统允许父进程终结子进程。 终止态→NULL完成资源回收。 七态 七态在五态的基础上引入了挂起就绪态ready suspend和挂起等待态blocked suspend。挂起就绪态表明进程具备运行条件但目前在外存中只有它被对换到内存才能被调度执行。 挂起等待态表明进程正在等待某一个事件发生且在外存中。其产生的原因是当系统资源主要是内存资源已经不能满足进程运行的要求时必须把某些进程挂起对换到磁盘对换区中释放它占有的某些资源。 等待态→挂起等待态操作系统根据当前资源状况和性能要求可以决定把等待态进程对换到磁盘成为挂起等待态。 ​ 挂起等待态→挂起就绪态引起进程等待的事件发生之后相应的挂起等待态进程将转换为挂起就绪态 ​ 挂起就绪态→就绪态当内存中没有就绪态进程或者挂起就绪态进程具有比就绪态进程更高的优先级系统将把挂起就绪态进程转换成就绪态。 ​ 就绪态→挂起就绪态操作系统根据当前资源状况和性能要求也可以决定把就绪态进程对换到磁盘成为挂起就绪态。 ​ 挂起等待态→等待态当一个进程等待一个事件时原则上不需要把它调入内存。但是在下面一种情况下这一状态变化是可能的。当一个进程退出后主存已经有了一大块自由空间,而某个挂起等待态进程具有较高的优先级并且操作系统已经得知导致它阻塞的事件即将结束此时便发生了这一状态变化。 ​ 运行态→挂起就绪态当一个具有较高优先级的挂起等待态进程的等待事件结束后它需要抢占 CPU而此时主存空间不够从而可能导致正在运行的进程转化为挂起就绪态。另外处于运行态的进程也可以自己挂起自己。 进程挂起原因 终端用户的请求 当终端用户在自己的程序运行期间发现有可疑问题时希望暂停使自己的程序静止下来比如手动调用wait()方法。亦即使正在执行的进程暂停执行若此时用户进程正处于就绪状态而未执行则该进程暂不接受调度以便用户研究其执行情况或对程序进行修改。我们把这种静止状态成为“挂起状态”。 父进程的请求 有时父进程希望挂起自己的某个子进程以便考察和修改子进程或者协调各子进程间的活动。 负荷调节的需要 当实时系统中的工作负荷较重已可能影响到对实时任务的控制时可由系统把一些不重要的进程挂起以保证系统能正常运行。 操作系统的需要 操作系统有时希望挂起某些进程以便检查运行中的资源使用情况或进行记账。 对换的需要 常见原因为了缓和内存紧张的情况将内存中处于阻塞状态的进程换至外存上。 进程调度算法有哪些 先来先服务FCFSfirst come first served 该算法既可用于作业调度也可用于进程调度。当在作业调度中采用该算法时每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业将它们调入内存为它们分配资源、创建进程然后放入就绪队列。在进程调度中采用FCFS算法时则每次调度是从就绪队列中选择一个最先进入该队列的进程为之分配CPU使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃cpu。 最短作业优先SJFShortest Job First 短作业优先又称为短进程优先这是对FCFS算法的改进目的是减少平均周转时间。对预计执行时间短的进程优先分派处理器。如果一个进程正在执行也就是执行时间越短的进程越先被执行而且后来的短进程通常不会打断它。 最高响应比优先HRRNHight Response Ratio Next 最高响应比优先算法是对FCFS和SJF的一种平衡算法。FCFS只考虑了进程等待的时间长短而SJF考虑了进程执行的时间长短因此这两种算法在某种程度上会降低系统调度性能。HRRN这种算法既会考虑每个进程的等待时间长短也会考虑进程预计执行时间长短从中选出响应比最高的进程执行。这种算法是介于FCFS算法和SJF算法中间的一种这种算法。 时间片轮转RRRound-Robin 该算法采用剥夺策略。每个进程都被分配好一个时间段也就是它的时间片这就是该进程允许允许的时间。如果该进程超过了时间片的时间就会发生时间中断调度程序暂停当前进程的执行将其送到就绪队列的末尾并通过上下文切换执行当前的队首进程。进程可以未使用完一个时间片就让出CPU如阻塞。 抢占式优先权调度算法 在这种方式下系统同样是把处理机分配给优先权最高的进程使之执行。但在其执行期间只要又出现了另一个其优先权更高的进程进程调度程序就立即停止也有在基于事件中断的当前进程(原优先权最高的进程)的执行重新将cpu分配给新到的优先权最高的进程。 进程间通信的七种方式 每个进程各自有不同的用户地址空间任何一个进程的全局变量在另一个进程中都看不到所以进程之间要交换数据必须通过内核在内核中开辟一块缓冲区进程A把数据从用户空间拷到内核缓冲区进程B再从内核缓冲区把数据读走内核提供的这种机制称为进程间通信具体实现方式有下面几种 匿名管道 1父进程调用pipe()函数创建管道得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。 2父进程fork出子进程⼦进程也有两个⽂件描述符指向同⼀管道。 3父进程关闭fd[0]子进程关闭fd[1]即⽗进程关闭管道读端⼦进程关闭管道写端因为管道只支持单向通信。⽗进程可以往管道⾥写⼦进程可以从管道⾥读管道是⽤环形队列实现的数据从写端流⼊从读端流出这样就实现了进程间通信。 限制 1只支持单向数据流管道只允许单向通信 2只能用于具有亲缘关系的进程之间 3没有名字 4管道的缓冲区是有限的 5管道内部保证同步机制从而保证访问数据的一致性 有名管道 有名管道不同于匿名管道之处在于它提供了一个路径名与之关联以有名管道的文件形式存在于文件系统中这样即使与有名管道的创建进程不存在亲缘关系的进程只要可以访问该路径就能够彼此通过有名管道相互通信因此通过有名管道不相关的进程也能交换数据。 消息队列 1消息队列是由消息组成的链表存放在内核中并由消息队列标识符标识。 2消息队列允许一个或多个进程向它写入与读取消息. 3管道和消息队列的通信数据都是先进先出的原则。 4消息队列可以实现消息的随机查询消息不一定要以先进先出的次序读取也可以按消息的类型读取比FIFO更有优势。 5消息队列克服了信号承载信息量少只能承载无格式字节流以及缓冲区大小受限等缺点。 信号 信号是Linux系统中用于进程间互相通信或者操作的一种机制信号可以在任何时候发给某一进程而无需知道该进程的状态。 1不可靠信号 也称为非实时信号不支持排队信号可能会丢失比如发送多次相同的信号进程只能收到一次信号值取值区间为1~31 2可靠信号 也称为实时信号支持排队信号不会丢失发多少次就可以收到多少次信号值取值区间为32~64。 3硬件方式产生信号 用户输入比如在终端上按下组合键ctrlC产生SIGINT信号 硬件异常CPU检测到内存非法访问等异常通知内核生成相应信号并发送给发生事件的进程 4软件方式产生信号 通过系统调用发送signal信号例如 kill()raise()sigqueue()alarm()setitimer()abort() 补充信号处理流程 1信号被某个进程产生并设置此信号传递的对象一般为对应进程的pid然后传递给操作系统 2操作系统根据接收进程的设置是否阻塞而选择性的发送给接收者如果接收者阻塞该信号且该信号是可以阻塞的操作系统将暂时保留该信号而不传递直到该进程解除了对此信号的阻塞如果对应进程已经退出则丢弃此信号如果对应进程没有阻塞操作系统将传递此信号。 3目的进程接收到此信号后将根据当前进程对此信号设置的预处理方式暂时终止当前代码的执行保护上下文主要包括临时寄存器数据当前程序位置以及当前CPU的状态、转而执行中断服务程序执行完成后在回复到中断的位置。当然对于抢占式内核在中断返回时还将引发新的调度。 常用信号 1SIGINT程序终止信号。程序运行过程中按CtrlC键将产生该信号。 2SIGKILL用户终止进程执行信号。shell下执行kill -9发送该信号。 3SIGTERM结束进程信号。shell下执行kill 进程pid发送该信号。 4The SIGTERM can also be referred as soft kill because the process that receives the SIGTERM signal may choose to ignore it. ------------kill process_id The SIGKILL is used for immediate termination of a process. This signal cannot be ignored or blocked. ------------------kill -9 process_id 链接 https://blog.csdn.net/violet_echo_0908/article/details/51201278 http://gityuan.com/2015/12/20/signal/ 信号量 信号量本质上是一个计数器用于多进程对共享数据对象的读取主要是用来保护共享资源信号量也属于临界资源使得资源在一个时刻只有一个进程独享。 由于信号量只能进行两种操作等待和发送信号即P(sv)和V(sv)。 P(semaphore)如果semaphore的值大于零就给它减1如果它的值为零就挂起该进程的执行V(semaphore)如果有其他进程因等待semaphore而被挂起就让它恢复运行如果没有进程因等待semaphore而挂起就给它加1 共享内存 使得多个进程可以直接读写同一块内存空间是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。 为了在多个进程间交换信息内核专门留出了一块内存区可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝从而大大提高效率。 由于多个进程共享一段内存因此需要依靠某种同步机制如信号量来达到进程间的同步及互斥。 补充为什么共享内存效率高 因为进程可以直接读写内存而不需要任何数据的拷贝。对于像管道和消息队列等通信方式则需要在内核和用户空间进行四次的数据拷贝而共享内存则只拷贝两次数据一次从输入文件到共享内存区另一次从共享内存区到输出文件。实际上进程之间在共享内存时并不总是读写少量数据后就解除映射有新的通信时再重新建立共享内存区域。而是保持共享区域直到通信完毕为止这样数据内容一直保存在共享内存中并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此采用共享内存的通信方式效率是非常高的。 套接字socket 一切皆为文件网络连接也是一个文件它也有文件描述符。 socket直接翻译为插座很形象建立网络连接就像把插头插在这个插座上创建一个Socket实例开始监听后这个电话插座就时刻监听着消息的传入谁拨通我这个“IP地址和端口”我就接通谁。Socket是在应用层和传输层之间的一个抽象层它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用实现进程在网络中的通信。这里涉及到计算机网络我们不全部展开可以查看计算机网络篇。 socket 可以让不在同一台计算机上的进程进行通信。 socket 编程是站在传输层的基础上所以可以使用 TCP/UDP 协议但是不能干诸如「访问网页」这样的事情因为访问网页所需要的 http 协议位于应用层。 补充socket通信步骤 服务器端 1首先服务器应用程序用系统调用socket()来创建一个套接字它是系统分配给该服务器进程的类似文件描述符的资源它不能与其他的进程共享。 2然后服务器进程会给套接字起个名字我们使用系统调用bind()来给套接字绑定一个专门的监听端口比如serverSocket.bind(new InetSocketAddress(host, port));。然后服务器进程就开始等待客户连接到这个套接字。 3接下来系统调用listen()来创建一个队列并将其用于存放来自客户的进入连接。 4最后服务器通过系统调用accept来接受客户的连接。它会创建一个与原有的命名套接不同的新套接字这个套接字只用于与这个特定客户端进行通信而命名套接字即原先的套接字则被保留下来继续处理来自其他客户的连接建立客户端和服务端的用于通信的流进行通信。比如while ((socket serverSocket.accept()) ! null) 阻塞监听。 客户端 1客户应用程序首先调用socket来创建一个未命名的套接字然后将服务器的命名套接字作为一个地址来调用connect与服务器建立连接。 2一旦连接建立我们就可以像使用底层的文件描述符那样用套接字来实现双向数据的通信通过流进行数据传输 链接https://zhuanlan.zhihu.com/p/109826876 进程间同步与互斥的区别 进程互斥、同步的概念都是并发进程下存在的概念有了并发进程就产生了资源的竞争与协作从而就要通过进程的互斥、同步、通信来解决资源竞争与同步协作的问题。 进程互斥 实际上是进程同步的一种特殊情况即逐次使用互斥共享资源也是对进程使用资源次序上的一种协调。进程互斥是进程间竞争共享资源的使用权例如若干个进程要使用同一共享资源时任何时刻最多允许一个进程去使用其他要使用该资源的进程必须等待直到占有资源的进程释放该资源。 进程同步 把异步环境下的一组并发进程当其中一个到达协调点后在尚未得到其他合作进程发来的消息或信号之前应阻塞自己直到其他合作进程发来协调信号或消息后方被唤醒并继续执行。这种各进程互相协作走走停停的过程称为进程间的同步。 什么是僵尸进程和孤儿进程 先介绍下背景僵尸进程的概念和成因就清晰了。unix提供了一种机制可以保证父进程肯定可以获取到子进程结束时的状态信息。这种机制就是在每个进程退出调用exit()系统调用的时候内核释放该进程所有的资源包括打开的文件占用的内存等。但是仍然为其保留一定的信息包括进程号、退出状态、CPU运行时间等这个阶段称为僵尸进程(Zombie)这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理可能用ps命令就来不及看到子进程的僵尸状态但这并不代表子进程不经过僵尸状态。 僵尸状态直到父进程通过wait() / waitpid() 系统调用来取时才释放/结束。如果父进程在子进程结束之前退出则子进程将由init接管。这时候并不会产生什么危害因为init进程将会以父进程的身份对僵尸状态的子进程进行处理。 所以问题的根源是 产生出大量僵尸进程的那个父进程。如果这个糟糕的父进程不调用wait() / waitpid() 系统调用的话 那么保留的那段信息就不会释放其进程号就会一直被占用系统所能使用的进程号是有限的如果大量的产生僵尸进程将因为没有可用的进程号而导致系统不能产生新的进程此即为僵尸进程的危害应当避免。 因此当我们寻求如何消灭系统中大量的僵尸进程时答案就是把产生大量僵尸进程的那个元凶枪毙掉也就是通过kill发送SIGTERM或者SIGKILL信号啦。枪毙了元凶进程之后它产生的僵尸进程就变成了孤儿进程孤儿进程并没有什么危害这些孤儿进程会被init进程接管init进程会wait()这些孤儿进程释放它们占用的系统进程表中的资源这样这些已经僵尸的孤儿进程就能瞑目而去了。 当然也要谨慎使用SIGKILL系统调用With SIGTERM, a process gets the time to send the information to its parent and child processes. It’s child processes are handled by init. Use of SIGKILL may lead to the creation of a zombie process because the killed process doesn’t get the chance to tell its parent process that it has received a kill signal. ok总结下上面说的僵尸进程和孤儿进程的概念。 孤儿进程 一个父进程退出而它的一个或多个子进程还在运行那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养并由init进程对它们完成状态收集工作所以并不会有什么危害。 僵尸进程 一个进程使用fork创建子进程如果子进程退出而父进程并没有调用wait或waitpid获取子进程的状态信息不负责任那么子进程的进程id等仍然保存在系统中。这种进程称之为僵尸进程。 linux底层的零拷贝技术 零拷贝Zero-copy技术指将数据从文件系统移动到网络接口的过程中不需要将其从内核空间复制到用户空间从而可以减少上下文切换以及 CPU 的拷贝时间。 零拷贝技术可以减少(CPU)数据拷贝次数减少系统调用实现 CPU 的零参与彻底消除 CPU 在这方面的负载。实现零拷贝用到的最主要技术是 DMA 数据传输技术和内存区域映射技术。 Linux 提供了轮询、I/O 中断以及 DMA 传输这 3 种磁盘与主存之间的数据传输机制。其中轮询方式是基于死循环对 I/O 端口进行不断检测。I/O 中断方式是指当数据到达时磁盘主动向 CPU 发起中断请求由 CPU 自身负责数据的传输过程。 传统IO中断数据拷贝 用户进程向 CPU 发起 read 系统调用读取数据由用户态切换为内核态然后一直阻塞等待数据的返回。CPU 在接收到指令以后对磁盘发起 I/O 请求将磁盘数据先放入磁盘控制器缓冲区。数据准备完成以后磁盘向 CPU 发起 I/O 中断。CPU 收到 I/O 中断以后将磁盘缓冲区中的数据拷贝到内核缓冲区然后再从内核缓冲区拷贝到用户缓冲区。用户进程由内核态切换回用户态解除阻塞状态然后等待 CPU 的下一个执行时间钟。 基于IO中断的DMA传输 DMA (Direct Memory Access)传输则在 I/O 中断的基础上引入了 DMA 磁盘控制器由 DMA 磁盘控制器负责数据的传输降低了 I/O 中断操作对 CPU 资源的大量消耗。DMA是一种允许外围设备硬件子系统直接访问系统主内存的机制。也就是说基于 DMA 访问方式系统主内存与硬盘或网卡之间的数据传输可以绕开 CPU 的全程调度。 有了 DMA 磁盘控制器接管数据读写请求以后CPU 从繁重的 I/O 操作中解脱。 数据读取操作的流程如下: 用户进程向 CPU 发起 read 系统调用读取数据由用户态切换为内核态然后一直阻塞等待数据的返回。CPU 在接收到指令以后对 DMA 磁盘控制器发起调度指令。DMA 磁盘控制器对磁盘发起 I/O 请求将磁盘数据先放入磁盘控制器缓冲区CPU 全程不参与此过程。数据读取完成后DMA 磁盘控制器会接受到磁盘的通知将数据从磁盘控制器缓冲区拷贝到内核缓冲区。DMA 磁盘控制器向 CPU 发出数据读完的信号由 CPU 负责将数据从内核缓冲区拷贝到用户缓冲区。用户进程由内核态切换回用户态解除阻塞状态然后等待 CPU 的下一个执行时间钟。 传统基于IO操作的DMA传输4次 以下模拟了一次读取文件并通过socket发送到网络的过程。 在 Linux 系统中传统的访问方式是通过 write() 和 read() 两个系统调用实现的通过 read() 函数读取文件到到缓存区中然后通过 write() 方法把缓存中的数据输出到网络端口整个过程涉及 2 次 CPU 拷贝、2 次 DMA 拷贝总共 4 次拷贝以及 4 次上下文切换。 零拷贝技术 用户态直接 I/O应用程序可以直接访问硬件存储操作系统内核只是辅助数据传输。这种方式依旧存在用户空间和内核空间的上下文切换硬件上的数据直接拷贝至了用户空间不经过内核空间。因此直接 I/O 不存在内核空间缓冲区和用户空间缓冲区之间的数据拷贝。 减少数据拷贝次数在数据传输过程中避免数据在用户缓冲区和内核缓冲区之间的CPU拷贝以及数据在系统内核空间内的CPU拷贝这也是当前主流零拷贝技术的实现思路。 写时复制技术写时复制指的是当多个进程共享同一块数据时如果其中一个进程需要对这份数据进行修改那么将其拷贝到自己的进程地址空间中如果只是数据读取操作则不需要进行拷贝操作。 1mmap write3次 mmap write 代替read write 使用 mmap 的目的是将内核读缓冲区的地址与用户缓冲区进行映射从而实现内核缓冲区与应用程序内存的共享省去了将数据从内核读缓冲区拷贝到用户缓冲区的过程然而内核读缓冲区仍需将数据写到内核中的socket 缓冲区。整个拷贝过程会发生 4 次上下文切换1 次 CPU 拷贝和 2 次 DMA 拷贝。 问题 mmap 处理小文件可能会导致碎片空间的浪费因为内存映射总是要对齐页边界最小单位是 4 KB一个 5 KB 的文件将会映射占用 8 KB 内存也就会浪费 3 KB 内存。mmap虽然减少了 1 次cpu拷贝但也存在问题。当 mmap 一个文件时如果这个文件被另一个进程所截获那么 write 系统调用会因为访问非法地址被 SIGBUS 信号终止SIGBUS 默认会杀死进程并产生一个 coredump服务器可能因此被终止。 2sendfile系统调用3次 sendfile系统调用在两个文件描述符之间直接传递数据(完全在内核中操作)从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝。 sendfile 系统调用的引入不仅减少了 CPU 拷贝的次数还减少了上下文切换的次数只有一次系统调用。通过 sendfile 系统调用数据可以直接在内核空间内部进行 I/O 传输从而省去了数据在用户空间和内核空间之间的来回拷贝。整个拷贝过程会发生 2 次上下文切换1 次 CPU 拷贝和 2 次 DMA 拷贝。 问题 sendfile() 系统调用不需要将数据拷贝或者映射到应用程序地址空间中去所以 sendfile() 只是适用于应用程序地址空间不需要对所访问数据进行处理的情况。因为 sendfile 传输的数据没有越过用户应用程序 / 操作系统内核的边界线所以也极大地减少了存储管理的开销。 链接https://zhuanlan.zhihu.com/p/20768200 3sendfile DMA gather copy2次DMA拷贝0次CPU拷贝 DMA 拷贝引入了 gather 操作。它将内核读缓冲区中的数据描述信息内存地址、地址偏移量记录到相应的socket缓冲区中cpu拷贝量很小可忽略由 DMA 根据socket缓冲区的内存地址、地址偏移量将数据批量地从读缓冲区read buffer拷贝到网卡设备中本质上是虚拟内存映射的思路这样就省去了内核空间中仅剩的 1 次 CPU 拷贝操作。整个拷贝过程会发生 2 次上下文切换、0 次 CPU 拷贝以及 2 次 DMA 拷贝。 问题 sendfile DMA gather copy 拷贝方式同样存在用户程序不能对数据进行修改的问题而且需要硬件的支持它只适用于将数据从文件拷贝到 socket 套接字上的传输过程。 4splice系统调用 splice 系统调用不仅不需要硬件支持还通过可以在内核空间的读缓冲区read buffer和网络缓冲区socket buffer之间建立管道pipeline从而避免了两者之间的 CPU 拷贝操作实现了两个文件描述符之间的数据零拷贝。整个拷贝过程会发生 2 次上下文切换0 次 CPU 拷贝以及 2 次 DMA 拷贝。 问题 splice 拷贝方式也同样存在用户程序不能对数据进行修改的问题。除此之外它使用了 Linux 的管道缓冲机制可以用于任意两个文件描述符中传输数据但是它的两个文件描述符参数中有一个必须是管道设备。 链接 零拷贝技术https://juejin.cn/post/6844903949359644680 linux的各种IO模型 缓存IO 第一阶段数据会先被拷贝到操作系统的内核缓冲区中第二阶段才会从操作系统内核缓冲区拷贝到应用程序的地址空间。 同步I/O与异步I/O 最基本的定义是否在I/O操作过程中阻塞用户线程。 A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes An asynchronous I/O operation does not cause the requesting process to be blocked 1阻塞 I/Oblocking IO 当用户进程调用了recvfrom这个系统调用kernel就开始了IO的第一个阶段准备数据对于网络IO来说很多时候数据在一开始还没有到达。比如还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来。这个过程需要等待也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边整个进程会被阻塞当然是进程自己选择的阻塞。当kernel一直等到数据准备好了它就会将数据从kernel中拷贝到用户内存然后kernel返回结果用户进程才解除block的状态重新运行起来。 blocking IO的特点就是在IO执行的两个阶段都被block了 2非阻塞 I/Ononblocking IO 当用户进程发出read操作时如果kernel中的数据还没有准备好那么它并不会block用户进程而是立刻返回一个error。从用户进程角度讲 它发起一个read操作后并不需要等待而是马上就得到了一个结果。用户进程判断结果是一个error时它就知道数据还没有准备好于是它可以再次发送read操作。一旦kernel中的数据准备好了并且又再次收到了用户进程的system call那么它马上就将数据拷贝到了用户内存然后返回。 nonblocking IO的特点是用户进程需要不断的主动询问kernel数据是否已准备好第一阶段用户线程是可以选择做其他事情的。 3I/O 多路复用 IO multiplexing select/epoll的优势在于单个process可以同时处理多个网络连接的IO。它的基本原理就是selectpollepoll这个function会不断的轮询所负责的所有socket当某个socket有数据到达了就通知用户进程。 当用户进程调用了select那么整个进程会被block而同时kernel会“监视”所有select负责的socket当任何一个socket中的数据准备好了select就会返回。这个时候用户进程再调用read操作将数据从kernel拷贝到用户进程。 I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符而这些文件描述符套接字描述符其中的任意一个进入读就绪状态select()函数就可以返回。 以上三种都是同步IO他们在第二阶段都是阻塞的。 4异步 I/Oasynchronous IO 用户进程发起read操作之后立刻就可以开始去做其它的事。而另一方面从kernel的角度当它受到一个asynchronous read之后首先它会立刻返回所以不会对用户进程产生任何block。然后kernel会等待数据准备完成然后将数据拷贝到用户内存当这一切都完成之后kernel会给用户进程发送一个signal告诉它read操作完成了。 异步IO的特点是IO两个阶段都不阻塞真正的“不阻塞”另外三种第二阶段都阻塞在了将数据从内核缓冲区拷贝到用户缓冲区的IO操作上而真正的异步IO的过程只有 发起和用户进程被通知中间是没有任何阻塞的。 链接https://blog.csdn.net/qq_36573828/article/details/89149057 详解IO多路复用 epoll原理 为什么能高效管理数百万连接弄懂ET模式和LT模式。 1select select 函数监视的文件描述符分3类分别是writefds、readfds、和exceptfds。调用后select函数会阻塞直到有描述符就绪有数据可读、可写、或者有exceptions或者超时timeout指定等待时间如果立即返回设为null即可函数返回。当select函数返回后可以 通过遍历fdset来找到就绪的描述符。 int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);问题 fd集合从用户态到内核态的拷贝开销 内核遍历所有fd的开销 单个进程能够监视的文件描述符的数量存在最大限制默认1024可更改但性能随着数量上升会降低。 2poll int poll (struct pollfd *fds, unsigned int nfds, int timeout);struct pollfd { int fd; /* file descriptor / short events; / requested events to watch / short revents; / returned events witnessed */ };pollfd结构包含了要监视的event和发生的event不再使用select“参数-值”传递的方式。同时pollfd并没有最大数量限制但是数量过大后性能也是会下降。 和select函数一样poll通过遍历文件描述符来获取已经就绪的socket。 综上poll本质上和select没有区别它将用户传入的数组拷贝到内核空间然后查询每个fd对应的设备状态如果设备就绪则在设备等待队列中加入一项并继续遍历如果遍历完所有fd后没有发现就绪设备则挂起当前进程直到设备就绪或者主动超时被唤醒后它又要再次遍历fd。 以上两种方式会产生大量无效的遍历并不是最优的解法。 3epoll 调用epoll_create()建立一个epoll对象在epoll文件系统中为这个句柄对象分配资源 调用epoll_ctl向epoll对象中添加这100万个连接的套接字 调用epoll_wait收集发生的事件的连接 int epoll_create(int size) //创建一个epoll的句柄epfdsize用来告诉内核这个监听的数目一共有多大这个参数不同于select()中的第一个参数参数size并不是限制了epoll所能监听的描述符最大个数只是对内核初始分配内部数据结构的一个建议。int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) //函数是对指定描述符fd执行op操作添加、删除和修改对fd的监听事件epoll_event告诉内核需要监听什么事fd是告诉内核需要监听什么socket。int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); //等待epfd上的io事件该函数返回需要处理的事件数目最多maxevents个事件。struct epoll_event {__uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */ };epoll的高效在于当我们调用epoll_ctl塞入百万个句柄fd时epoll_wait仍然可以快速返回并有效的将发生事件的句柄fd给我们用户怎么做到的 这是由于在调用epoll_create时内核除了帮我们在epoll文件系统里建了个file结点在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外还会再建立一个list链表用于存储准备就绪的事件当epoll_wait调用时仅仅观察这个list链表里有没有数据即可。有数据就返回没有数据就sleep等到timeout时间到后即使链表没数据也返回。 通常情况下即使我们要监控百万计的句柄fd大多一次也只返回很少量的准备就绪句柄而已所以epoll_wait仅需要从内核态copy少量的句柄fd到用户态而已。所以epoll_wait非常高效。 新的问题又来了如何维护这个list链表的 这是因为当我们执行epoll_ctl时除了把socket放到epoll文件系统里file对象对应的红黑树上之外还会给内核中断处理程序注册一个回调函数告诉内核如果这个句柄的中断到了就把它放到准备就绪list链表里。所以当一个socket上有数据到了内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。 总的来说红黑树是为了高效查找句柄就绪链表是为了保存已就绪的句柄方便返回而链表的维护是依靠中断处理函数的回调。执行epoll_create时创建了红黑树和就绪链表执行epoll_ctl时如果增加socket句柄则检查在红黑树中是否存在存在立即返回不存在则添加到树干上然后向内核注册回调函数用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。 epoll优势 1监视的描述符数量不受限制 2IO的效率不会随着监视fd的数量的增长而下降 3利用mmap()文件映射内存加速与内核空间的消息传递 链接 https://zhuanlan.zhihu.com/p/39970630 https://www.cnblogs.com/aspirant/p/9166944.html LT模式和ET模式 epoll对文件描述符的操作有两种模式LTlevel trigger和ETedge trigger。LT模式与ET模式的区别如下 LT模式 当epoll_wait检测到描述符事件发生并将此事件通知应用程序应用程序可以不立即处理该事件。下次调用epoll_wait时会再次响应应用程序并通知此事件。 使用LT模式默认意味着只要fd处于可读或者可写状态每次epoll_wait都会返回该fd这样的话会带来很大的系统开销且处理时候每次都需要把这些fd轮询一遍如果fd的数量巨大不管有没有事件发生epoll_wait都会触发这些fd的轮询判断。 ET模式 当epoll_wait检测到描述符事件发生并将此事件通知应用程序应用程序必须立即处理该事件。如果不处理下次调用epoll_wait时不会再次响应应用程序并通知此事件需要用户自己保证可靠。 ET/LT模式的处理逻辑几乎完全相同差别仅在于 LT模式在 event 发生时不会将其从 ready list 中移除。 综上在 select/poll中进程只有在调用一定的方法后内核才对所有监视的文件描述符进行扫描而epoll事先通过epoll_ctl()来注册一个文件描述符一旦基于某个文件描述符就绪时内核会采用类似callback的回调机制迅速把这个句柄放入list中当进程调用epoll_wait() 时便得到通知快速返回。 链接https://segmentfault.com/a/1190000003063859 补充epoll底层数据结构 当某一进程调用epoll_create方法时Linux内核会创建一个eventpoll结构体这个结构体中有两个成员与epoll的使用方式密切相关。eventpoll结构体如下所示 struct eventpoll{..../*红黑树的根节点这颗树中存储着所有添加到epoll中的需要监控的事件*/struct rb_root rbr;/*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/struct list_head rdlist;.... };每一个epoll对象都有一个独立的eventpoll结构体用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中如此重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn其中n为树的高度)。 而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系也就是说当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。 在epoll中对于每一个事件都会建立一个epitem结构体 struct epitem{struct rb_node rbn;//红黑树节点struct list_head rdllink;//双向链表节点struct epoll_filefd ffd; //事件句柄信息struct eventpoll *ep; //指向其所属的eventpoll对象struct epoll_event event; //期待发生的事件类型 }链表https://www.cnblogs.com/developing/articles/10849288.html 虚拟内存解决了什么问题 一句话概括虚拟内存最大的意义是为每个进程提供了一个一致的、私有的地址空间它让每个进程产生了一种自己在独享主存的错觉似乎拥有了一片连续完整的内存空间。 1分页 优点是页长固定因而便于构造页表、易于管理且不存在外碎片。但分页方式的缺点是页长与程序的逻辑大小不相关。例如某个时刻一个子程序可能有一部分在主存中另一部分则在磁盘中。这不利于编程时的独立性并给换入换出处理、存储保护和存储共享等操作造成麻烦。 2分段 段是按照程序的自然分界划分的长度可以动态改变的区域。通常程序员把子程序、操作数和常数等不同类型的数据划分到不同的段中并且每个程序可以有多个相同类型的段。 3段页式 是分段式和分页式结合的存储组织方法这样可充分利用分段管理和分页管理的优点对用户是分段对系统是分页。虚拟内存是连接分段分页的桥梁。用户角度虚拟地址分成与程序逻辑相关的不同的段段表实际执行时段又会分成不同的页分布在内存或磁盘中涉及到换入换出这个过程由MMU操作页表PCB中完成由逻辑地址向物理地址的转换地址翻译。 现代处理器使用的是一种称为虚拟寻址Virtual Addressing的寻址方式。使用虚拟寻址CPU需要将虚拟地址翻译成物理地址这样才能访问到真实的物理内存。CPU中含有一个被称为内存管理单元Memory Management Unit, MMU的硬件它的功能是将虚拟地址转换为物理地址。MMU需要借助存放在内存中的页表来动态翻译虚拟地址该页表由操作系统管理。 用户进程申请并访问物理内存或磁盘存储空间的过程 用户进程向操作系统发出内存申请请求系统会检查进程的虚拟地址空间是否被用完如果有剩余给进程分配虚拟地址系统为这块虚拟地址创建的内存映射Memory Mapping并将它放进该进程的页表Page Table系统返回虚拟地址给用户进程用户进程开始访问该虚拟地址CPU 根据虚拟地址在此进程的页表Page Table中找到了相应的内存映射Memory Mapping但是这个内存映射Memory Mapping没有和物理内存关联于是产生缺页中断操作系统收到缺页中断后分配真正的物理内存并将它关联到页表相应的内存映射Memory Mapping。中断处理完成后 CPU 就可以访问内存了当然缺页中断不是每次都会发生只有系统觉得有必要延迟分配内存的时候才用的着也即很多时候在上面的第 3 步系统会分配真正的物理内存并和内存映射Memory Mapping进行关联。 在用户进程和物理内存磁盘存储器之间引入虚拟内存的优点 地址空间提供更大的地址空间并且地址空间是连续的使得程序编写、链接更加简单进程隔离不同进程的虚拟地址之间没有关系所以一个进程的操作不会对其它进程造成影响数据保护每块虚拟内存都有相应的读写属性这样就能保护程序的代码段不被修改数据块不能被执行等增加了系统的安全性内存映射有了虚拟内存之后可以直接映射磁盘上的文件可执行文件或动态库到虚拟地址空间。这样可以做到物理内存延时分配只有在需要读相应的文件的时候才将它真正的从磁盘上加载到内存中来而在内存吃紧的时候又可以将这部分内存清空掉提高物理内存利用效率并且所有这些对应用程序是都透明的共享内存比如动态库只需要在内存中存储一份然后将它映射到不同进程的虚拟地址空间中让进程觉得自己独占了这个文件。进程间的内存共享也可以通过映射同一块物理内存到进程的不同虚拟地址空间来实现共享物理内存管理物理地址空间全部由操作系统管理进程无法直接分配和回收从而系统可以更好的利用内存平衡进程间对内存的需求 链接https://juejin.cn/post/6844903507594575886 动态链接库与静态链接库的区别 库是一种可执行代码的二进制形式可以被操作系统载入内存执行。库有两种静态库.a、.lib和动态库.so、.dll。 静态库 在链接阶段会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。试想一下静态库与汇编生成的目标文件一起链接为可执行文件那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件.o/.obj文件的集合即很多目标文件经过压缩打包后形成的一个文件。静态库存在的问题如下 空间浪费。另一个问题是静态库对程序的更新、部署和发布带来麻烦。如果静态库liba.lib更新了所以使用它的应用程序都需要重新编译、发布给用户对于使用者来说可能是一个很小的改动却导致整个程序重新下载全量更新。 动态库 动态库在程序编译时并不会被连接到目标代码中而是在程序运行时才被载入。不同的应用程序如果调用相同的库那么在内存里只需要有一份该共享库的实例规避了空间浪费问题。动态库在程序运行是才被载入也解决了静态库对程序的更新、部署和发布也会带来麻烦的问题。用户只需要更新动态库即可增量更新。【拓展】
http://www.zqtcl.cn/news/950240/

相关文章:

  • 品牌型网站制作有哪些公司石家庄广告制作公司
  • 做网站赚几百万网站效果图怎么做的
  • 哪些网站做企业招聘不要花钱wordpress底部导航代码
  • 怎么用链接进自己做的网站企业组织架构
  • 建设新网站征求意见网站设计佛山
  • 重庆建设造价工程信息网站东莞电商页面设计公司
  • 乔拓云智能建站官网登录入口怎么样做网站卖农产品
  • 怎么维护好网站网站的域名每年都要续费
  • 运动网站模板佛山三水区有没有网站建设公司
  • 申请微官网的网站国外域名注册商网站
  • 集团公司网站建设建设中学校园网站的来源
  • 产品展示网站含后台网站模板下载网站开发什么语言好
  • 做知乎网站的图片如何设计好网站
  • 广州企业网站推广织梦学校网站模板
  • 国内响应式网站案例深圳住房和城乡建设局网站
  • 网页制作网站首页中国建筑论坛网
  • 众创空间网站建设少年宫网站建设模块
  • 企业营销型网站的内容科技公司取名大全
  • 哈尔滨云建站模板投资公司的钱从哪里来
  • 海南做网站公司哪家好中国人做外贸生意的网站
  • 没有网站怎么做cpa成都百度推广公司地址
  • 龙湖地产 网站建设高端上海网站设计公司
  • 触屏手机网站模板装修设计软件排名
  • 怎么做盗文网站郑州建设教育培训中心
  • 网站安全解决方案嵌入式软件工程师培训
  • 怎么做一种网站为别人宣传网站界面切片做程序
  • 麻涌网站建设河北网站建设联系方式
  • 建设银行官方网站打不开啊寮步仿做网站
  • 一个人可做几次网站备案峰峰网站建设
  • 怎么盗号网站怎么做北京高端网站设计外包公司