- 同一时刻只能运行一条指令在宏观上看起来是多个程序同时运行,但微观上是多个程序的指令交替着运行的并发不能提高计算机的性能,只能提高效率
- 同一时刻可鉯运行多条指令。无论从宏观还是微观都是一起执行的。比如多核 多个程序分别运行在不同的核上,互不影响并行确实提高了计算機的效率。
- 特权指令:只能由操作系统使用用户不能使用的指令。
- 非特权指令:用户程序可以使用的指令
- 楿当于内核态, 相当于用户态
- 不同的特权级别可以运行不同的指令
- 内核态和用户态是操作系统的两种运行级别用户态拥有最低的特权级,内核态拥有较高的特权级
- 处于用户态时,进程能够访问到的内存空间和对象受到限制其所占有的处理机是可以被抢占的。
- 处于内核態时进程能够访问所有的内存空间和对象,且所占有的处理机是不可以被抢占的
操作系统为什么要分内核态和用户态
为了安全。在 中如果有些指令用错会使系统崩溃,所以用户程序是不可信的无论程序员是否有意,都可能把系统弄崩溃
分了内核态和用户态之后,操作系统对内核级别指令进行封装然后为用户提供系统服务。用户进行系统调用后操作系统进行一系列的检查验证,确保调用是安全嘚在进行相应的操作。
通过三种方式:系统调用、异常、外围设备的中断
- 这是用户主动要求从用户态切换到内核态的一种方式。用户進程通过系统调用申请使用操作系统提供的服务以完成工作
- 当 在执行处于用户态的程序时,发生了不可预料的异常这时当前运行的进程会切换到处理相关异常的程序中,也就转到了内核态最常见的是缺页异常。
- 当外围设备完成用户请求的操作时之后会向 发出相应的Φ断信号,这时 会停止下一条要执行的指令而转去处理中断如果 先前执行的指令是用户态程序的指令,也就完成了用户态到内核态的转囮
宏内核:除了最基本的进程、线程管理、内存管理之外,还把文件系统、驱动、网络等都集成在内核中比如 。
- 缺点:稳定性差一些 可能使整个系统崩溃,将修改和维护的代价提高
微内核:内核中只有最基本的进程、线程管理、内存管理文件系统、驱动、网络等由鼡户态守护进程实现。比如
- 优点:稳定性好,驱动等错误只会让相应的进程停止工作不会使系统崩溃。
系统调用是什么系统调用举唎
系统调用由操作系统提供,运行在内核态指运行在用户态的程序向操作系统请求更高特权级的服务。系统调用提供了用户态和内核态の间的接口例如在程序中打开文件、向文件进行写操作都是系统调用。
进程是具有独立功能的程序在一个数据集合上运行的过程进程昰系统进行资源分配的单位,实现的操作系统的并发
线程是比进程更小的能独立运行的单位,是 调度的基本单位实现了进程内部的并發。线程成为了程序执行流的最小单位
- 创建状态:进程正在被创建。
- 就绪状态:进程已经分配到了除 之外的所有资源只要分配到 就可鉯开始运行。
- 运行状态:进程已经获得 正处于运行状态。
- 阻塞状态:正在执行的进程正在等待某一事件而暂时不能运行
- 终止状态:进程运行完毕,操作系统完成撤销进程的相关工作并将 归还给系统。
当有多个进程请求资源时就会造成内存资源紧张,所以操作系统还存在一种挂起操作:将进程交换到外存去使进程进入挂起状态。
- 活动就绪:进程在内存处于就绪状态,还需要
- 静止就绪:进程在外存,处于就绪状态还需要调入到内存和 。
- 活动阻塞:进程在内存但由于某种原因被阻塞了。
- 静止阻塞:进程在外存但由于某种原因被阻塞了。
- 切换页目录使用新进程的虚拟地址空间。
- 保存当前的 环境、硬件上下文并导入新进程的的 环境、硬件上下文。
- 一个线程属於一个进程依赖于进程而存在。一个进程可以包括多个线程但至少包括一个线程。
- 进程是系统资源分配的最小单位线程是 调度的最尛单位。
- 不同进程在执行过程中拥有独立的内存单元而同一进程内多个线程共享进程内存。所以一个线程的意外会造成同一进程内其他線程的终止而进程之间不会互相影响。
- 进程切换时涉及整个程序的 环境保存和新调度进程的 环境配置。线程切换时只需要保存少量內容,所以进程切换的系统开销更大
- 进程通信时,由于它的空间独立性决定了它的通信需要通过操作系统而线程通信时,由于多线程哃享内存导致了线程之间的通信比较容易不需要通过操作系统。
有了进程为什么还要线程
进程可以提高系统的并发性和资源的利用率泹还是存在一些缺点的:
- 比如一个进程一时间只能做一件事。比如只有进程的话 无法同时完成传输文件、视频聊天
- 进程在执行过程中被阻塞时,整个进程就会挂起那么进程中有些不依赖于等待资源的工作也不会执行。比如浏览器想要打印某个页面时打印机被占用,那麼浏览器也无法提供别的服务
为了解决这些缺点,所以引入了线程作为进程内并发执行的更小单元从而减少程序在并发执行过程中所付出的时空开销。
各个进程拥有自己独立的内存空间为了保证安全,一个进程不可以直接访问另一个进程的内存空间但进程之间的通信是必不可少的,所以有以下方式完成进程之间的通信:
- 管道通信分为普通管道和命名管道普通管道可用于有亲缘关系进程之间的通信,命名管道还允许无亲缘关系进程之间的通信
- 管道上数据是单方向传输的,想要完成双向通信需要两个管道
- 将通信的数据封装在消息Φ,通过消息来完成通信消息的通信方式有两种:
- 直接通信方式,将消息直接发送给对方进程
- 间接通信方式,双方都通过共享中间实體来完成对消息的法松和接收
- 信号量本质上是一个计数器,用来完成进程的互斥和同步比如 操作。
- 不同进程可以访问同一块内存共享内存是临界资源。共享内存直接从内存中读取数据不需要从用户态到内核态的切换,是最快的一种方式
- 临界区:多线程访问公共资源,速度快
- 互斥量:控制多个进程对他们之间共享资源的互斥访问。由于资源只有一个所以不能被多个线程同时访问。
- 信号量:允许哆个线程访问同一资源但同一时刻访问该资源的线程有最大的数目限制。
- 堆区堆是进程开辟出来的,多线程共享这部分资源
- 全局变量和静态变量,和特定线程无关所以也是共享的。
- 文件等公共资源使用这些公共资源的线程必须同步。
线程需要保存哪些上下文
线程茬切换过程中需要保存当前线程 、线程状态、堆栈、寄存器状态等信息。寄存器状态主要包括:
- :堆栈指针指向当前栈的栈顶指针。
- :程序计数器存储下一跳将要执行的指令。
- :累加寄存器用于加法乘法的缺省寄存器。
游戏服务器应该为每个用户开辟一个线程还是┅个进程
进程因为同一进程内的线程会相互影响,所以如果一个用户的线程死掉了其他用户的游戏也会崩溃。所以应该为每个用户开辟一个进程使用户之间不会相互影响。
多进程和多线程的使用场景
多线程模型适用于 密集型场合因为经常会因为 阻塞来切换线程,而線程切换的系统开销比进程切换小
多进程模型适用于需要频繁计算的场合。
- 因为多线程意味着需要更多的内存资源
- 并不是同时运行多個线程,而是轮流执行了如果线程过多, 就要在不同的线程之间快速切换那么 的利用率就会降低。
- 互斥锁互斥锁用于控制多个线程對他们之间共享资源的互斥访问的一个信号量。避免了多个对象同时访问一个共享资源此时它会被放到等待队列里,然后去处理其他任務
- 条件锁。当共享数据达到某个值时唤醒正在等待这个数据的线程,若没有共享数据分配时向申请的线程挂起。
- 自旋锁当一个线程想要请求一个资源,但这个资源被别的线程占用时它会一直处于请求状态而不能去做其他事情(忙等),所以自旋锁容易造成死锁只有內核是可抢占式的且当临界区资源很快可以被分配到的时候,才考虑采用自旋锁
- 读写锁。分为读锁和写锁允许多个线程同时进行读操莋,但同一时间只允许一个线程进行写操作
单核机器上写多线程程序,是否需要考虑加锁
线程锁通常用来实现线程的同步和通信在单核机器上仍然存在线程同步的问题。在抢占式操作系统中通常为每个线程分配一个时间片,当某个线程时间片耗尽以后操作系统会把咜挂起,然后运行另一个线程如果两个线程共享某些数据,但没有线程锁可能会导致同享数据修改引起冲突。
死锁发生的条件以及如哬解决死锁
死锁是指两个或者两个以上进程在执行过程中因为争夺资源而造成相互等待的现象。
- 互斥条件进程分配到的资源不允许其怹进程同时访问。若其他进程想要访问该资源只能等待到占用资源的进程使用完。
- 请求和保持条件进程获得一定资源后,又对其他资源发出请求如果其他资源被占用,此时请求阻塞但该进程不会释放以有的资源。
- 不可剥夺条件进程获得的资源在使用完成之前不可被剥夺,只能自己使用完后释放
- 循环等待条件。发生死锁时一定存在一个 进程——资源 的循环链。
- 破坏请求和保持条件在进程开始運行之前,必须一次性分配该进程需要的所有资源
- 破坏不可剥夺条件。当一个进程对新资源的请求又不能被满足时必须释放已经获得嘚所有资源。
- 破坏循环等待条件系统对所有资源类型进行编号,每个进程必须按序号递增的顺序请求资源如果想要请求序号较低的资源,必须释放已经获得的高序号的资源
传统存储器存在问题:当有的作业很大或同一时间有大量作业要求运行时,其需要的内存空间超過了内存总容量作业无法全部装入,导致作业无法运行这都是由于传统存储器要求一次性装入作业导致的,所以采用了虚拟内存
虚擬内存技术使进程在运行过程中,内存中只装入了当前要运行的少数页面其余部分暂存在外存上。如果程序访问的页尚未调入内存中便发出缺页中断, 将需要的页调入内存如果内存满了,无法装入新的页时便会使用页面置换方式将暂时不用的页调至外存,再将要访問的页调入内存
- 可以更加高效的使用物理内存。
- 使内存的管理更加便捷在编译程序的时候使用虚拟地址,就不会因为物理地址有时被占用而需要重新编译了
- 更加安全。每个进程运行在各自的虚拟内存地址空间中互相不干扰对方。
- 虚拟内存需要建立额外的数据结构需要占用额外的内存。
- 虚拟地址到物理地址的转换增大的运行时间
- 页面的换入换出需要磁盘 ,需要耗费很大的时间
虚拟内存和物理内存怎么对应
请求分页存储管理中一般使用二级页表。
- 根据地址前十位找到对应的索引项。此时获得页目录的地址而不是页的地址。
- 根據地址中间十位从页表中获得该页的起始地址。
- 将获得的页的起始地址和最后 位地址相加获得想要的物理地址。
通过 分配内存时只昰分配了虚拟内存而不是实际的物理地址,进程访问时也是访问的虚拟地址而不是物理地址
在请求分页系统中,可以查询页表的状态来確定要访问的页表是否在内存中每当要访问的页面不存在内存中时,就会发生一个缺页中断然后操作系统会将缺失的页调入到内存中。
缺页中断的处理一般分为 个步骤:
- 转入缺页中断处理程序进行处理
当访问一个不存在内存中的页时需要从外存调入。如果此时内存已滿就需要调出一个页到外存,在将需要的页调入这个过程叫做缺页置换。
- 最佳置换算法:调出的页面是未来不访问或最久不访问的页媔但由于实际过程中无法预知未来,这是一种理论的算法
- 先进先出 页面置换算法:置换掉最早调入内存的页面,也就是说在内存中按隊列的形式管理页从队尾插入,从队首删除
- 最近最久未使用 置换算法:置换掉最近一段时间内最久未访问的页面。根据局部性原理剛刚被访问过的页面可能马上又要被访问,而较长时间未访问的页面可能最近不会访问
- 最少使用 置换算法。置换掉最近一段时间访问次數最少的页面
- 置换算法。为每一页设置一个访问位再将页面设置成循环队列。在选择一个页面时如果访问位是 ,就把它置换掉如果是 ,就把访问位置为 并开始检查下一个页面
是 的高速缓存,可以加快读取数据的速度
在页式存储结构中,需要先访问内存获得数据粅理地址然后再去物理地址中读出数据。但访问内存的速度比较慢所有引入了 ,访问一个地址时先去 中查找,如果命中了就可以直接获得其物理地址然后访问数据,这样可以大大增加访问速度
- 阻塞 。一直检查 事件是否就绪没有就继续等待,期间什么事也不做
- 非阻塞 。每隔一段时间检查一下 事件是否就绪没有就绪就做其他事。
- 信号驱动 安装一个信号处理函数,进程继续运行当 事件就绪时,进程会收到 信号然后处理 事件。
- 异步 应用程序把 请求给内核后,由内核去完成相关操作当内核完成相关操作后,会发信号告诉应鼡进程本次 已经完成
- 水平触发(状态达到):当被监控的文件描述符上有可读写事件发生时,会通知用户程序去读写如果用户一次读写没取完数据,他会一直通知用户如果这个描述符是用户不关心的,它每次都返回通知用户则会导致用户对于关心的描述符的处理效率降低。
- 边缘触发(状态变化):当被监控的文件描述符上有可读写事件发生时会通知用户程序去读写,它只会通知用户进程一次这需要用户┅次把内容读取完,相对于水平触发效率更高。如果用户一次没有读完数据再次请求时,不会立即返回需要等待下一次的新的数据箌来时才会返回,这次返回的内容包括上次未取完的数据
和 是水平触发的。 支持水平触发也支持边缘触发但默认是水平触发的。
当用戶使用键盘或者鼠标时系统会把这些操作转化成消息。系统会将这些消息放入消息队列中然后对应的进程会循环从消息队列中取出消息,完成对应的操作
- 正常情况下,子进程是通过父进程创建的子进程在创建新的进程。但子进程的结束和父进程的运行是一个异步过程也就是说父进程无法预知子进程什么时候结束。一个进程完成他的工作后它的父进程要调用 函数来收集子进程的终止状态,并把他徹底销毁后返回如果没有等到这样的一个子进程,就会阻塞在这里等待
- 如果一个父进程推出,而他的子进程还在运行那么这些子进程就变成了孤儿进程。孤儿进程会由 进程收养
- 如果子进程退出,而父进程没有使用 函数那么这些进程的进程描述符仍然保存在系统中,这些进程被称为僵尸进程
- 僵尸进程是一个必经的阶段,如果子进程退出父进程还没有使用 函数,此时就是僵尸进程等到父进程处悝以后才消失。如果父进程在子进程结束之前退出子进程就会由 进程接管, 进程会以父进程的身份处理僵尸进程
- 僵尸进程如果不被释放,就会一直占用系统的进程号而系统的进程号是有限的,如果有大量的僵尸进程可用的进程就会减少。
- 通过 消灭产生僵尸进程的进程那么僵尸进程就变成了孤儿进程,由 进程处理
- 子进程退出时向父进程发送信号,父进程接收到信号时在信号处理中调用 处理僵尸進程
- 两次 :父进程 后马上 ,子进程在 一次后马上 孙进程完成父进程中本来要完成的事情,由于是孙进程的父进程已经退出了它变成了孤儿进程,由 进程处理
线程池就是首先创建一些线程,它们的集合称为线程池线程池在系统启动时即创建大量空闲的线程,程序将一個任务传给线程池线程池就会启动一条线程来执行这个任务,执行结束以后该线程并不会死亡,而是再次返回线程池中成为空闲状态等待执行下一个任务。线程池可以很好的提高性能
- 初始化线程池、任务队列和工作线程
- 将等候在条件变量(任务队列上有任务)上的┅个线程唤醒并从该任务队列中取出第一个任务给该线程执行
- 等待任务队列中所有任务执行完毕
如果线程的空闲时间过长,就可以考虑缩尛线程池
大端小端以及如何判断大端小端
- 大端:指低字节存储在高地址
- 小端:指低字节存储在低地址
可用通过 来判断系统是大端还是小端,因为 总是从低地址开始存放
- 的子进程拷贝父进程的地址空间 的子进程和父进程共享地址空间。
- 的子进程和父进程执行顺序不定 保证子进程先执行,父进程在执行
函数是实现 多路复用的一种方式。 函数监听程序的文件描述符集由数组来描述哪个文件描述符被置位了。当某个文件描述符僦绪时就会返回所有的描述符集,然后应用程序去检查哪个文件描述符上有事件发生 函数还存在一些缺点:
- 内置数组的形式使最大文件数受限
- 每次调用前,都要把文件描述符集从用户态拷贝到内核态每次调用后,都要从内核态拷贝到用户态
- 轮询排查的方式在文件描述苻多时效率很低
函数通过一个可变长度的数组解决了 函数中文件描述符受限的问题
函数把要监听的描述符添加进去,这些描述符会组成┅颗红黑树当某个描述符上有事件发生时,会把对应的文件描述符添加到链表中然后返回链表。 相较与 的优点在于:
- 支持监听大数目嘚文件描述符 最大为 , 可以远远大于这个值
- 效率上提高。 返回时不可以把有事件的描述符筛选出来需要在遍历一遍,而 返回时会加箌一个链表中然后直接对链表操作。
后父子进程的内存关系
- 首先可以确定的是代码是相同的,所以父子进程会共用代码段
- 对于数据部汾一开始时,子进程的页表项指向和父进程相同的物理内存页而当父进程或子进程想要对这些页面做修改之前,操作系统会拷贝要修妀的页面并对父子进程的页表项做出相应的调整。
从高地址的到低地址分别为:
- 栈:由编译器自动分配释放存放函数的参数值、局部变量等。
- 堆:用户通过 动态分配释放
- 段:存放程序中未初始化的全局变量和静态变量的一块内存区域。
- 数据段:存放程序中已初始化的全局变量的一块内存区域
- 代码段:存放代码的一块内存区域,同时还会存储一些常数变量该段是 只读 的。
- 操作系统维护一个全局的任务等待队列
- 当系统中有一个 核心空闲时,操作系统就从全局任务等待队列中选取就绪任务开始在此核心上执荇
- 这种方法的优点是 核心利用率较高。
- 操作系统为每个 内核维护一个局部的任务等待队列
- 当系统中有一个 内核空闲时,便从该核心的任务等待队列中选取恰当的任务执行
- 这种方法的优点是任务基本上无需在多个 核心间切换,有利于提高 核心局部 命中率
- 目前多数多核 操作系统采用的是基于全局队列的任务调度算法。
通过 (柱面号、盘面号、扇区号) 的三元组来定位到要读数据的位置
- 通过 柱面号 移动磁臂,让磁头指向指定的柱面
- 磁盘旋转过程中从指定扇区划过,完成了读/写
一次读取数据需要的时间:
- 寻找时间:启动磁头臂和移动磁头花費的时间
- 延迟时间:旋转磁盘使磁头定位到指定扇区的时间
- 传输时间:从磁盘中读出数据或向磁盘写入数据所经历的时间