小程序视频网站开发,typecho wordpress,深圳网络科技有限公司,ftp上传网站步骤特点#xff1a;并发性、共享性、虚拟性、异步性。 Windows 和 Linux 内核差异 对于内核的架构⼀般有这三种类型#xff1a; ● 宏内核#xff0c;包含多个模块#xff0c;整个内核像⼀个完整的程序#xff1b; ● 微内核#xff0c;有⼀个最⼩版本的内核#xff0… 特点并发性、共享性、虚拟性、异步性。 Windows 和 Linux 内核差异 对于内核的架构⼀般有这三种类型 ● 宏内核包含多个模块整个内核像⼀个完整的程序 ● 微内核有⼀个最⼩版本的内核⼀些模块和服务则由⽤户态管理 ● 混合内核是宏内核和微内核的结合体内核中抽象出了微内核的概念也就是内核中会有⼀个⼩型的内核其他模块就在这个基础上搭建整个内核是个完整的程序 Linux 的内核设计是采⽤了宏内核Window 的内核设计则是采⽤了混合内核。 Linux 可执⾏⽂件格式叫作 ELFWindows 可执⾏⽂件格式叫作 PE。 键盘敲⼊字⺟时期间发⽣了什么 ● 当⽤户输⼊了键盘字符键盘控制器就会产⽣扫描码数据并将其缓冲在键盘控制器的寄存器中 紧接着键盘控制器通过总线给 CPU 发送中断请求。 ● CPU 收到中断请求后操作系统会保存被中断进程的 CPU 上下⽂然后调⽤键盘的中断处理程 序。 ● 键盘的中断处理程序是在键盘驱动程序初始化时注册的其功能就是从键盘控制器的寄存器的缓冲 区读取扫描码再根据扫描码找到⽤户在键盘输⼊的字符如果输⼊的字符是显示字符那就会把 扫描码翻译成对应显示字符的 ASCII 码⽐如⽤户在键盘输⼊的是字⺟ A是显示字符于是就会 把扫描码翻译成 A 字符的 ASCII 码。 ● 得到了显示字符的 ASCII 码后就会把 ASCII 码放到「读缓冲区队列」接下来就是要把该字符显 示屏幕了显示设备的驱动程序会定时从「读缓冲区队列」读取数据放到「写缓冲区队列」最后 把[写缓冲区队列]的数据⼀个⼀个写⼊到显示设备控制器的寄存器中的数据缓冲区最后将这些 数据显示在屏幕⾥。 ● 显示出结果后恢复被中断进程的上下⽂。 3.1 ⽤户态 和 内核态 陷⼊内核态⽅式 1. 系统调⽤ 系统调⽤是⽤户进程主动发起的操作。发起系统调⽤陷⼊ 内核由操作系统执⾏系统调⽤然后再返回到进程。系统调⽤实质上就是函数调⽤只不过调⽤的是系统函数处于内核态⽽已。 ⽤户在调⽤系统调⽤时会向内核传递⼀个系统调⽤号然后系统调⽤处理程序通过此号从系统调⽤表中找到相应的内核函数执⾏最后返回。 2. 异常 异常包括程序运算引起的各种错误如除 0、缓冲区溢出、缺⻚等。异常是⼀种错误情况是执⾏当前指令的结果可能被错误处理程序修正也可能直接终⽌应⽤程序。异常是同步的。 3. 中断 中断包括 I/O 设备发出的 I/O 中断、各种定时器引起的时钟中断、调试程序中设置的断点等引起的调试中断等。中断由处理器外部的 硬件 产⽣不是执⾏某条指令的结果也⽆法预测发⽣时机。由于中断独⽴于当前执⾏的程序因此中断是异步事件。 执⾏系统调⽤时 OS 状态 1. ⽤户运⾏库函数系统调⽤的封装函数⾥⾯其实是执⾏的 int 0x80 指令。系统调⽤先把系统调⽤号保存在 eax 寄存器中然后执⾏ int0x80 指令。 2. int 0x80 指令先进⾏切换堆栈找到进程的堆栈将寄存器值压⼊到内核栈中 3. 然后查找相应 中断向量表 的中断处(system_call)并调⽤ 4. 随后 system_call 从 系统调⽤表 中找到相应的系统调⽤进⾏执⾏调⽤结束后从 system_call 中返回。 CPU 中断 CPU中断是什么 1. 计算机处于执⾏期间系统内发⽣了⾮预期的急需处理事件 2. CPU暂时中断当前正在执⾏的程序⽽转去执⾏相应的事件处理程序 3. 处理完毕后返回原来被中断处继续执⾏ CPU中断的作⽤ 1. 可以使CPU和外设同时⼯作使系统可以 及时地响应 外部事件 2. 可以允许多个外设同时⼯作⼤⼤提⾼CPU的 利⽤率 3. 可以使CPU及时处理各种软硬件故障 零拷⻉ 零拷⻉指的是从⼀个存储区域到另⼀个存储区域的 拷⻉任务没有CPU参与。零拷⻉通常⽤于⽹络⽂件 传输以减少CPU消耗和内存带宽占⽤减少 ⽤户空间⽤户可以操作的内存缓存区域与CPU内核 空间CPU可以操作的内存缓存区域及寄存器的 拷⻉过程减少 ⽤户上下⽂⽤户状态环境与 CPU内核上下⽂CPU内核状态环境间的 切换提⾼系统效率。 [1] 传统拷⻉⽅式发⽣4次空间切换1、4、5、7发⽣4次copy3、4、5、6其中有2次CPU 4、5参与。 零拷⻉⽅式零拷⻉是通过 sendfile 系统调⽤ 实现的。发⽣2次空间切换(1、6)发⽣3次copy3、4、5其中有0次CPU参与。mmap零拷⻉在此基础上增加⽤户空间与内核空间中内核缓冲区的共享会多2次空间切换DMA全称Direct Memory Access直接内存存取它允许不同速度的硬件装置来沟通⽽不需要依赖于 CPU 的⼤量中断负载。DMA主要是解决外围设备可以直接访问内存从中减少CPU参与。DMA⽅式优先级⾼于中断主要适⽤于⼀些⾼速的 I/O设备 。 传输⽂件的时候我们要根据⽂件的⼤⼩来使⽤不同的⽅式 ● 传输⼤⽂件的时候使⽤「异步 I/O 直接 I/O」。因为可能由于 PageCache 被⼤⽂件占据⽽ 导致「热点」⼩⽂件⽆法利⽤到 PageCache并且⼤⽂件的缓存命中率不⾼这时就需要使⽤ 「异步 IO 直接 IO 」的⽅式。 ● 传输⼩⽂件的时候则使⽤「零拷⻉技术」 3.2 进程、线程、协程 惊群效应是指多进程多线程在同时阻塞等待同⼀个事件的时候休眠状态如果等待的这个事件发⽣那么他就会唤醒等待的所有进程或者线程但是最终却只能有⼀个进程线程获得这个事件的“控制权”对该事件进⾏处理⽽其他进程线程只能重新进⼊休眠状态这种现象和性能浪费就叫做惊群效应。 PCB 存储信息 ● 进程id。系统中每个进程有唯⼀的id⾮负整数。 ● 进程的状态。有就绪运⾏挂起停⽌等状态 ● 进程切换时需要保存和恢复的⼀些CPU寄存器 ● 描述虚拟地址空间的信息。 ● 描述控制终端的信息。 ● 当前⼯作⽬录 ● umask掩码 ● ⽂件描述符表包含很多指向结构体的指针 ● 和信号相关的信息 ● ⽤户id和组id。 ● 会话Session和进程组 ● 进程可以使⽤的资源上限Resource Limit 进程与线程区别 ⼀个进程最多可以创建多少个线程 进程的虚拟内存空间上限因为创建⼀个线程操作系统需要为其分配⼀个栈空间如果线程数量 越多所需的栈空间就要越⼤那么虚拟内存就会占⽤的越多。 系统参数限制虽然 Linux 并没有内核参数来控制单个进程创建的最⼤线程个数但是有系统级别 的参数来控制整个系统的最⼤线程个数。 线程的实现⽅式 在引⼊线程的操作系统中进程是资源分配的基本单位线程是独⽴调度的基本单位。线程也像进程⼀样有多个状态运⾏、就绪、阻塞… 从 Linux 内核的⻆度来看线程和进程并没有被区别对待。⽆论线程还是进程都是⽤ task_struct 结构表示的。线程分为以下两种实现⽅式 1. ⽤户级线程实现 ⽤户线程多⻅于⼀些历史悠久的操作系统例如Unix操作系统。有关线程管理的所有⼯作都由应⽤程序完成内核意识不到多线程的存在。⽤户级线程仅存在于⽤户空间中此类线程的创建、撤销、线程之间的同步与通信功能都⽆法利⽤系统调⽤来实现。 应⽤程序需要通过使⽤线程库来控制线程。 通常应⽤程序从单线程起始在该线程中开始运⾏在其运⾏的任何时刻可以通过调⽤线程库中的派⽣创建⼀个在相同进程中运⾏的新线程。由于线程在进程 内切换的规则远⽐进程调度和切换的规则简单不需要进⾏⽤户态/核⼼态切换所以切换速度快。因为⽤户级线程驻留在⽤户空间且管理和控制它们的线程也在⽤户空间每个线程并不具有⾃身的线程上下⽂所以它们对于操作系统是不可⻅的这也就是它⽆法被调度到处理器内核的原因。 ⽤户线程的优点 可以在不⽀持线程的操作系统中实现。 创建和销毁线程、线程切换代价等线程管理的代价⽐内核线程少, 因为保存线程状态的过程和调⽤ 程序都只是本进程空间的操作允许每个进程定制⾃⼰的调度算法线程管理⽐较灵活。线程能够利⽤的表空间和堆栈空间⽐内核级线程多不需要trap不需要上下⽂切换context switch也不需要对内存⾼速缓存进⾏刷新使得线程调⽤⾮常快捷线程的调度不需要内核直接参与控制简单。 ⽤户线程的缺点 如果线程发⽣I/O等阻塞从⽽引起系统调⽤时由于内核不知道有多线程的存在会阻塞整个进程进⽽阻塞所有线程, 因此同⼀进程中只能同时有⼀个线程在运⾏。⼀个单独的进程内部没有时钟中断所以不可能⽤轮转调度的⽅式调度线程。资源调度按照进程进⾏多个处理机下同⼀个进程中的线程只能在同⼀个处理机下分时复⽤因此对于多线程并不能被多核系统加速。 2. 内核级线程实现 内核线程建⽴和销毁都是在内核的⽀持下运⾏由操作系统负责管理通过系统调⽤完成的。线程管理 的所有⼯作由内核完成应⽤程序没有进⾏线程管理的代码只有⼀个到内核级线程的编程接⼝。内核为进程及其内部的每个线程维护上下⽂信息调度也是在内核基于线程架构的基础上完成。内核线程驻留在内核空间它们是内核对象。操作系统调度器管理、调度并分派这些线程。运⾏时库为每个⽤户级线程请求⼀个内核级线程将⽤户进程映射或绑定到上⾯。⽤户线程在其⽣命期内都会绑定到该内核线程。⼀旦⽤户线程终⽌两个线程都将离开系统。这被称作”⼀对⼀”线程映射。内核空间内为每⼀个内核⽀持线程设置了⼀个线程控制块TCB内核根据该控制块感知线程的存在并进⾏控制。 内核线程的特点 ● 当某个线程希望创建⼀个新线程或撤销⼀个已有线程时它进⾏⼀个系统调⽤。 ● 多处理器系统中内核能够并⾏执⾏同⼀进程内的多个线程。 ● 如果进程中的⼀个线程被阻塞能够切换同⼀进程内的其他线程继续执⾏⽤户级线程不具备。 ● 所有能够阻塞线程的调⽤都以系统调⽤的形式实现代价较⼤。 ⽤户级线程和内核级线程的区别 ● 内核级线程是OS内核可感知的⽽⽤户级线程是OS内核不可感知的。 ● ⽤户级线程的创建、撤消和调度不需要OS内核的⽀持⽽内核级线程创建、撤消和调度都需OS内核提供⽀持⽽且与进程的创建、撤消和调度⼤体是相同的。 ● ⽤户级线程执⾏系统调⽤指令时将导致其所属进程被中断⽽内核级线程执⾏系统调⽤指令时只导致该线程被中断。 ● 在只有⽤户级线程的系统内CPU调度还是以进程为单位处于运⾏状态的进程中的多个线程由⽤户程序控制线程的轮换运⾏在有内核级线程的系统内CPU调度则以线程为单位由OS的线程调度程序负责线程的调度。 ● ⽤户级线程的程序实体是运⾏在⽤户态下的程序⽽内核级线程的程序实体则是可以运⾏在任何状态下的程序。 线程安全 线程安全如果代码所在的进程中有多个线程在同时运⾏⽽这些线程可能会同时运⾏这段代码。如果每次运⾏结果和单线程运⾏的结果是⼀样的⽽且其他的变量值也和预期的是⼀样的就是线程安全的。线程安全问题都是由 全局变量 和 静态变量 引起的 实现线程安全的⽅式 1. 加锁。如标准库中的 mutex 2. 原⼦操作 atomic。在多线程并发执⾏时原⼦操作是不会被线程打断的执⾏⽚段。 线程的同步⽅式 线程同步 是指多线程通过特定的设置来控制线程之间的执⾏顺序。主要有以下四种⽅式 1. 临界区通过多线程的串⾏化来控制对公共资源的访问速度快。在任意⼀个时刻只允许⼀个线程对共享资源进⾏访问。 2. 互斥对象只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有⼀个所以能保证公共资源不会被多个线程同时访问。 3. 信号量它允许多个线程在同⼀时刻访问同⼀资源但是需要限制在同⼀时刻访问此资源的最⼤线程数⽬。 4. 事件对象利⽤通知的⽅式来保持线程的同步还可以⽅便实现对多个线程的优先级⽐较操作。 进程状态 “挂起”是指将暂不执⾏的进程换出到外存节省内存空间。“挂起”和“阻塞”都是进程暂停执⾏的状态但是这是两个维度的概念 ● 阻塞表示进程正在等待⼀个事件的发⽣阻塞状态下收到信号会切换为就绪状态 ● 挂起表示进程被换出到外存挂起状态下被激活时会被载⼊到内存切换为⾮挂起状态 综上所属挂起状态的进程按照是否阻塞可以分为 ● 挂起就绪状态进程在外存中但是只要被载⼊内存就可以执⾏ ● 挂起阻塞状态进程在外存中并等待⼀个事件即使被载⼊内存也⽆法运⾏ Linux 将进程的 阻塞状态 进⼀步细分为暂停、浅睡眠、深睡眠。 其中若不需要等待资源则切换为“暂停”若需要等待资源切换为“睡眠”如果睡眠状态能被信号唤醒则是“浅睡眠”否则是“深睡眠”。 进程/CPU调度算法 进程的上下⽂切换不仅包含了虚拟内存、栈、全局变量等⽤户空间的资源还包括了内核堆栈、寄存器等内核空间的资源。 1. 先来先服务First Come First ServerdFCFS优点是易于理解便于实现只需⼀个就绪队列。缺点是对短作业不公平对 I/O 密集型进程不利⻓时间等待设备响应时间不确定 2. 最短作业优先Shortest Job FirstSJF优点是提⾼平均周转时间。缺点是对⻓作业不公平可能导致饥饿问题 3. 最⾼响应⽐优先算法Highest Response Ratio NextHRRN作业运⾏所需时间越⼩、作业 等待时间越⻓响应⽐越⼤。优点同时考虑了等待时间和执⾏时间既优先考虑短作业也防⽌ ⻓作业⽆限等待的饥饿. 4. 时间⽚轮转Round RobinRR优点没有饥饿问题。问题若时间⽚⼩进程切换频繁吞吐量低若时间⽚⻓则响应时间过⻓实时性得不到保证 5. 最⾼优先级调度 6. 多级反馈队列Multilevel Feedback QueueMFQ优先级⾼的队列先执⾏优先级越⾼时间⽚越短如果⼀个进程在当前队列规定的时间⽚内⽆法执⾏完毕则移动到下⼀个队列的队尾。缺点也有可能出现饥饿问题⽐如不断有新的更⾼优先级的进程加⼊。可在⼀定时间后全部提升到优先级⾼的队列解决。 7. 最早截⽌时间优先算法先把截⽌时间早的任务给完成否则这个任务如果在截⽌时间后才完成就没有意义了。 8. 最短剩余时间优先Shortest Remaining Time NextSRTN最短作业优先的抢占式版本如果新作业⽐正在执⾏的作业剩余时间短则它优先执⾏。缺点是对⻓作业不公平可能导致饥饿问 题。 9. 彩票法向进程提供各种系统资源的彩票。调度时随机抽取彩票拥有该彩票的进程得到资源。可 给重要的进程更多的彩票协作进程可以交换彩票 10. 公平分享法为每⽤户分配⼀定⽐例的 CPU 时间⽽不是按照进程。各⽤户之间按照⽐例挑选进程. 进程的分类 僵⼫进程 概念僵⼫进程是指终⽌但还未被回收的进程。如果⼦进程退出⽽⽗进程并没有调⽤ wait() 或 waitpid() 来回收那么就会产⽣僵⼫进程。僵⼫进程是⼀个已经死亡的进程但是其进程描述符仍然保存在系统的进程表中。 危害占⽤进程号系统所能使⽤的进程号是有限的可能导致不能产⽣新的进程占⽤⼀定的内存。 如何避免产⽣僵⼫进程 1. ⽗进程调⽤ wait 或者 waitpid 等待⼦进程结束 2. ⼦进程结束时内核会发送 SIGCHLD 信号给⽗进程。⽗进程可以注册⼀个信号处理函数在该函数中调⽤ waitpid等待所有结束的⼦进程也可以⽤ signal(SIGCLD, SIG_IGN) 忽略 SIGCHLD信号那么⼦进程结束后内核会进⾏回收 3. 杀死⽗进程僵⼫进程就会变成孤⼉进程由 Init 进程接管并处理 孤⼉进程 如果某个进程的⽗进程先结束了那么它的⼦进程会成为孤⼉进程。每个进程结束的时候系统都会扫描是否存在⼦进程如果有则⽤ Init 进程pid 1接管并由 Init 进程调⽤ wait 等待其结束完成状态收集⼯作。孤⼉进程不会对系统造成危害。 守护进程 守护进程是⼀种在后台执⾏的电脑程序。此类程序会被以进程的形式初始化。 创建守护进程 1. 在⽗进程中执⾏ fork 并退出⽗进程⼦进程继续 2. 在⼦进程中调⽤ setsid 函数创建新的会话。由于会话过程对控制终端的独占性进程同时与控制终端脱离。 3. 在⼦进程中调⽤ chdir 函数让根⽬录 / 成为⼦进程的⼯作⽬录。进程活动时其⼯作⽬录所在的⽂件系统不能卸下⼀般需要将⼯作⽬录改变到根⽬录 4. 在⼦进程中关闭任何不需要的⽂件描述符。进程从创建它的⽗进程那⾥继承了打开的⽂件描述符。如不关闭将会浪费系统资源造成进程所在的⽂件系统⽆法卸下以及引起⽆法预料的错误。 5. 在⼦进程中调⽤ umask 函数设置进程的 umask 为0。进程从创建它的⽗进程那⾥继承了⽂件创 建掩模。它可能修改守护进程所创建的⽂件的存取位。为防⽌这⼀点将⽂件创建掩模清除. 进程间的通信⽅式 1. 信号 Signal 信号是系统为响应某些条件⽽产⽣的⼀个事件接收到该信号的进程可以采取事先⾃定义的⾏为。这是⼀种“订阅-发布”的模式。 信号来源 1. 硬件来源。如按下 CTRLC、除 0、⾮法内存访问等等 2. 软件来源。如 Kill 命令等等 进程如何发送信号 1. 使⽤系统调⽤将信号放到⽬标进程的 信号队列 中 2. 如果⽬标进程处于未执⾏状态则该信号就由内核保存起来直到该进程恢复执⾏并传递给它为 ⽌如果⼀个信号被进程设置为阻塞则该信号的传递被延迟直到其阻塞被取消时才被传递给进 程 进程如何接收信号 1. 把接收到的信息放⼊信号队列中 2. 执⾏中的进程会在特定时刻如从系统空间返回到⽤户空间之前检查并处理⾃⼰的信号队列。 2. 信号量 Semaphore 信号量是⼀种特殊的变量对它的操作都是原⼦的屏蔽中断有两种操作Vsignal()和 P wait()。V 操作会增加信号量 S 的数值P 操作会减少它。信号量 S 的值相当于记录资源的个数底层实现通过硬件提供的原⼦指令如 Test And Set上锁并检查、Compare And Swap⽐较并交换仅当预期值与内存值相等时才修改否则不操作 等。 种类整型信号量通过PV操作访问、记录型信号量让权等待策略、AND型信号量资源⼀次性分配、信号量集⼀次申请多个资源每次分配前都要测试资源数⽬根据可分配下限值决定是否分配 3. 管道 Pipe 管道是⼀种半双⼯的通信⽅式数据只能单向流动。如果想实现双向通信那么需要建⽴两个管道。管道适合于传输⼤量信息传输的内容是没有格式的字节流。 创建管道通过 pipe() 系统调⽤来创建并打开⼀个管道当最后⼀个使⽤它的进程关闭对他的引⽤时pipe 将⾃动撤销。通过 pipe() 创建的是匿名管道只能⽤于具有亲缘关系的进程之间⽗⼦进程或兄弟进程。 管道的实现管道是⼀个由内核管理的缓冲区缓冲区被设计为环形的数据结构以便管道可被循环利⽤。管道的同步管道是⼀个具有特定⼤⼩的缓冲区操作系统会通过加锁保证读写进程的同步下游进程或者上游进程需要等另⼀⽅释放锁后才能操作管道。当管道为空时下游进程读阻塞当管道满时上游进程写阻塞。 4. 命名管道 FIFO 命名管道可⽤于任何有访问权的进程通过⽂件名将其打开和进⾏读写。Pipe 和 FIFO 除了建⽴、打开、 删除的⽅式不同外⼆者⼏乎⼀模⼀样。 FIFO的实现通过 mkfifo() 函数建⽴命名管道。命名管道实质上也是通过内核缓冲区来实现数据传 输。建⽴命名管道时会在磁盘中创建⼀个索引节点命名管道的名字就相当于索引节点的⽂件名。索 引节点设置了进程的访问权限有访问权限的进程可以通过磁盘的索引节点来读写这块缓冲区。当不 再被任何进程使⽤时命名管道在内存中释放但磁盘节点仍然存在。 5. 消息队列 Message Queue 消息队列是消息组成的链表保存在内核中。消息队列中的消息是⼀个具有特定格式的数据块。操作系统中可以存在多个消息队列每个消息队列有唯⼀的 key 进⾏标识。 优缺点和信号相⽐消息队列能够传递更多的信息。与管道相⽐消息队列提供了有格式的数据。消息队列允许⼀个或多个进程向它写⼊与读取消息消息队列是异步的。 实现操作系统提供创建消息队列、取消息、发消息等系统调⽤。操作系统负责读写同步若消息队列已满则写消息进程排队等待若取消息进程没有找到需要的消息则在消息队列中轮询。 6. 共享内存 Shared Memory 共享内存允许多个进程映射同⼀段物理空间然后像访问普通内存⼀样访问它并交换数据。 优点 ● 访问共享内存区域和访问进程独有的内存区域⼀样快原因是不需要系统调⽤不涉及⽤户态到内 核态的转换也不需要对数据过多复制。 ● ⽐如管道和消息队列需要在内核和⽤户空间进⾏四次的数据拷⻉读输⼊⽂件、写到管道读管 道、写到输出⽂件⽽共享内存则只拷⻉两次⼀次从输⼊⽂件到共享内存区另⼀次从共享内 存到输出⽂件。 ● 消息队列的实现需要系统调⽤也就经常需要⽤户态和内核态互相转换⽽共享内存只在建⽴时需要系统调⽤之后所有访问都可作为常规内存访问⽆需借助内核。 缺点存在并发问题有可能出现多个进程修改同⼀块内存因此共享内存⼀般与信号量结合使⽤。实现mmap() 不是专⻔⽤来共享内存的mmap() 系统调⽤的主要作⽤是将普通⽂件映射到进程的地址空间然后可以像访问普通内存⼀样对⽂件进⾏访问不必再调⽤ read()write() 等操作。因此多个进程可以通过 mmap() 映射同⼀个普通⽂件来实现共享内存。 7. 套接字 Socket 不同的计算机的进程之间通过 socket 通信也可⽤于同⼀台计算机的不同进程。需要通信的进程之间⾸先要各⾃创建⼀个 socket内容包括主机地址与端⼝号。进程通过 socket 把消息发送到⽹络层中⽹络层通过主机地址将其发到⽬的主机⽬的主机通过端⼝号发给对应进程。实现操作系统提供创建 socket、发送、接收的系统调⽤为每个 socket 设置发送缓冲区、接收缓冲区。 协程 协程是⼀个⽤户态的线程⽤户在堆上模拟出协程的栈空间。当需要进⾏协程上下⽂切换的时候主线程只需要交换栈空间和恢复协程的⼀些相关的寄存器的状态就可以实现上下⽂切换。没有了从⽤户态转换到内核态的切换成本协程的执⾏也就更加⾼效。和传统的线程不同的是线程是抢占式执⾏当发⽣系统调⽤或者中断的时候交由OS调度执⾏⽽协程是通过 yield 主动让出cpu所有权切换到其他协程执⾏。 3.3 锁 类型 ● 互斥锁 加锁失败时会⽤「线程切换」来应对当加锁失败的线程再次加锁成功后的这⼀过程会 有两次线程上下⽂切换的成本性能损耗⽐较⼤。 ● ⾃旋锁 加锁失败时并不会主动产⽣线程切换⽽是⼀直忙等待直到获取到锁。如果被锁住的代 码执⾏时间很短那这个忙等待的时间相对应也很短。 ● 读写锁 允许多个读线程同时持有读锁提⾼了读的并发性。根据偏袒读⽅还是写⽅可以分为读优 先锁和写优先锁读优先锁并发性很强但是写线程会被饿死⽽写优先锁会优先服务写线程读 线程也可能会被饿死那为了避免饥饿的问题于是就有了公平读写锁它是⽤队列把请求锁的线 程排队并保证先⼊先出的原则来对线程加锁这样便保证了某种线程不会被饿死通⽤性也更 好。互斥锁和⾃旋锁都是最基本的锁读写锁可以根据场景来选择这两种锁中的⼀个进⾏实现。 ● 互斥锁、⾃旋锁、读写锁都属于 悲观锁悲观锁认为并发访问共享资源时冲突概率可能⾮常⾼ 所以在访问共享资源前都需要先加锁。 ● 如果并发访问共享资源时冲突概率⾮常低的话就可以使⽤ 乐观锁它的⼯作⽅式是在访问共 享资源时不⽤先加锁修改完共享资源后再验证这段时间内有没有发⽣冲突如果没有其他线 85 程修改资源那么操作完成如果发现有其他线程已经修改过这个资源就放弃本次操作。但是 ⼀旦冲突概率上升就不适合使⽤乐观锁因为它解决冲突的重试成本⾮常⾼。 CAS 不是乐观锁吗为什么基于 CAS 实现的⾃旋锁是悲观锁 CAS 是乐观锁但是基于 CAS 的⾃旋锁加了while 或者睡眠 CPU 的操作⽽产⽣⾃旋的效果加锁失 败会忙等待直到拿到锁即需要先拿到锁才能修改数据所以算悲观锁。 死锁 死锁是指多个进程循环等待其他进程占有的资源⽽⽆限期地僵持下去的局⾯。当两个或两个以上的进程同时对多个互斥资源请求使⽤时有可能导致死锁。 造成死锁必要条件 1. 互斥条件。即某个资源在⼀段时间内只能由⼀个进程占有不能同时被两个或两个以上的进程占 有。 2. 不可抢占条件。进程所获得的资源在未使⽤完毕之前资源申请者不能强⾏地从资源占有者⼿中夺取资源⽽只能由该资源占有者进程⾃⾏释放。 3. 占有且申请条件。进程⾄少已占有⼀个资源但⼜申请新资源由于该资源已被另外进程占有此时该进程阻塞但是它在等待新资源的同时仍继续占⽤已有的资源。 4. 循环等待条件。存在⼀个进程等待序列{P1, P2, P3..., Pn}其中P1等待P2所占有的某⼀资源 等待P3占有的某⼀资源...⽽Pn等待P1所占有的某⼀资源形成⼀个进程循环等待环。 死锁预防 死锁预防是保证系统不进⼊死锁状态的⼀种策略基本思想是要求进程申请资源时遵循某种协议从⽽ 打破产⽣死锁的必要条件中的⼀个或⼏个保证系统不进⼊死锁状态。 1. 打破互斥条件。即允许进程同时访问某些资源 2. 打破不可抢占条件。即允许进程强⾏从占有者那⾥夺取某些资源就是说当⼀个进程已经占有了某些资源⽽⼜申请新的资源但⼜不能被⽴即满⾜时它必须释放所占有的全部资源以后再重新申请。它所释放的资源可以分配给其他进程这就相当于该进程占有的资源被隐蔽地抢占了。该⽅法实现起来困难会降低系统性能 3. 打破占有且申请条件。可以实⾏资源预先分配策略即进程在运⾏前⼀次性地向系统申请它所需要的全部资源如果某个进程所需的全部资源得不到满⾜则不分配资源此进程暂不运⾏。只有当系统能够满⾜当前进程的全部资源需求时才⼀次性地将所申请的资源全部分配给该进程。 4. 打破循环等待条件。实⾏资源有序分配策略即把资源事先编号按号分配。所有进程对资源的请 求必须严格按资源序号递增的顺序提出进程占⽤了⼩号资源才能申请⼤号资源就不会产⽣环路。在数据库层⾯有两种设置参数策略通过「打破循环等待条件」来解除死锁状态 ● 设置事务等待锁的超时时间。当⼀个事务的等待时间超过该值后就对这个事务进⾏回滚于是锁就释放了。 ● 开启主动死锁检测。主动死锁检测在发现死锁后主动回滚死锁链条中的某⼀个事务让其他事务得以继续执⾏。 死锁避免银⾏家算法 基本思想是分配资源之前判断系统是否是安全的若是则分配。即已知当前所有进程对各个资源的需求以及现在已占⽤的资源还有系统可⽤资源Available。 1. 求出每个进程当前还需要多少资源Need 2. 选取 Need Availadble 的某进程分配若⽆则等待更多资源释放到系统 3. 执⾏安全性检测算法若安全则进⾏分配否则到第2步找其他进程 4. 回收原进程已有资源和已分配的资源找其他进程分配