操作系统基础知识

1. 进程和线程的联系和区别:

  • 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
  • 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

  进程和线程的关系

  (1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。

  (2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。

  (3)处理机分给线程,即真正在处理机上运行的是线程。

  (4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

  进程与线程的区别?

  (1)进程有自己的独立地址空间,线程没有

  (2)进程是资源分配的最小单位,线程是CPU调度的最小单位

  (3)进程和线程通信方式不同(线程之间的通信比较方便。同一进程下的线程共享数据(比如全局变量,静态变量),通过这些数据来通信不仅快捷而且方便,当然如何处理好这些访问的同步与互斥正是编写多线程程序的难点。而进程之间的通信只能通过进程通信的方式进行。)

  (4)进程上下文切换开销大,线程开销小

  (5)一个进程挂掉了不会影响其他进程,而线程挂掉了会影响其他线程

  (6)对进程进程操作一般开销都比较大,对线程开销就小了 

  线程分为用户和内核级。

  用户态切换到内核态的方式如下:

  • 系统调用:程序的执行一般是在用户态下执行的,但当程序需要使用操作系统提供的服务时,比如说打开某一设备、创建文件、读写文件(这些均属于系统调用)等,就需要向操作系统发出调用服务的请求,这就是系统调用。

  • 异常:当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。

  • 外围设备的中断:当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

2. 进程之间的通信方式

  进程通信是指进程之间的信息交换,包括一个状态、字节等。

  通信方式有5种:  

  管道:

  管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的道端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样地,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。

          注1:无名管道只能实现父子或者兄弟进程之间的通信,有名管道(FIFO)可以实现互不相关的两个进程之间的通信。

      2:用FIFO让一个服务器和多个客户端进行交流时候,每个客户在向服务器发送信息前建立自己的读管道,或者让服务器在得到数据后再建立管道。使用客户的进程号(pid)作为管道名是一种常用的方法。客户可以先把自己的进程号告诉服务器,然后再到那个以自己进程号命名的管道中读取回复。

  信号量:

  信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其它进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

  消息队列:

  一个在系统内核中用来保存消  息的队列,它在系统内核中是以消息链表的形式出现的。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

  共享内存:

  共享内存允许两个或多个进程访问同一个逻辑内存。这一段内存可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取读出,从而实现了进程间的通信。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。共享内存是最快的IPC方式,它是针对其它进程间通信方式运行效率低而专门设计的。它往往与其它通信机制(如信号量)配合使用,来实现进程间的同步和通信。

  套接字:

  套接字也是一种进程间通信机制,与其它通信机制不同的是,它可用于不同机器间的进程通信。

3. 守护、僵尸、孤儿进程的概念

  • 守护进程:运行在后台的一种特殊进程,独立于控制终端并周期性地执行某些任务。
  • 僵尸进程:一个进程 fork 子进程,子进程退出,而父进程没有wait/waitpid子进程,那么子进程的进程描述符仍保存在系统中,这样的进程称为僵尸进程。
  • 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,这些子进程称为孤儿进程。(孤儿进程将由 init 进程收养并对它们完成状态收集工作)

4. fork()函数

  fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,这个新产生的进程称为子进程。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。需要注意的一点:就是调用fork函数之后,一定是两个进程同时执行的代码段是fork函数之后的代码,而之前的代码以及由父进程执行完毕。下面来看一个很简单的例子。
  fork函数返回两个值

  • 返回一个大于0的值给父进程
  • 返回0给子进程
  • 返回其他值说明fork失败了

  fork出错可能有两种原因:

    1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。

    2)系统内存不足,这时errno的值被设置为ENOMEM。

5. 磁盘读写数据的时间-磁盘速度慢:

  磁盘上数据读取和写入所花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分。

  1.寻道时间

  所谓寻道时间,其实就是磁臂移动到指定磁道所需要的时间,这部分时间又可以分为两部分:

  寻道时间=启动磁臂的时间+常数*所需移动的磁道数,其中常数和驱动器的的硬件相关,启动磁臂的时间也和驱动器的硬件相关。

   2.旋转延迟

  旋转延迟指的是把扇区移动到磁头下面的时间。这个时间和驱动器的转数有关,我们通常所说的7200转的硬盘的转就是这个。

  平均旋转延迟=1/(2*转数每秒),比如7200转的硬盘的平均旋转延迟等于1/2*120≈4.17ms。旋转延迟只和硬件有关。

   3.传输时间

    传输时间指的是从磁盘读出或将数据写入磁盘的时间。传输时间=所需要读写的字节数/每秒转速*每扇区的字节数。

6. 磁盘调度算法

  通过上面硬盘读写数据所分的三部分时间不难看出,大部分参数是和硬件相关的,操作系统无力优化。只有所需移动的磁道数是可以通过操作系统来进行控制的,所以减少所需移动的磁道数是减少整个硬盘的读写时间的唯一办法。

   因为操作系统内可能会有很多进程需要调用磁盘进行读写,因此合理的安排磁头的移动以减少寻道时间就是磁盘调度算法的目的所在,几种常见的磁盘调度算法如下。

   1.最短寻道时间算法(SSFT)

  这种算法优先执行所需读写的磁道离当前磁头最近的请求。这保证了平均寻道时间的最短,但缺点显而易见:离当前磁头比较远的寻道请求有可能一直得不到执行,这也就是所谓的“饥饿现象”。 

  2.先来先服务算法(FCFS)

  这种算法将对磁盘的IO请求进行排队,按照先后顺序依次调度磁头。此算法的优点是公平、简单,且每个进程的请求都能依次得到处理,不会出现某一进程的请求长期得不到满足的情况。但由于此算法未对寻道进行优化,在对磁盘的访问请求比较多的情况下,此算法将降低设备服务的吞吐量,致使平均寻道时间可能较长,但各进程得到服务的响应时间的变化幅度较小。

   适用场景: 由于长作业会长时间占据处理机,该算法比较有利于长作业,而不利于短作业。即有利于CPU繁忙的作业,而不利于I/O繁忙的作业。

   3.循环扫描算法(CSCAN)

  也就是俗称的电梯算法,这种算法是对最短寻道时间算法的改进。这种算法就像电梯一样,只能从1楼上到15楼,然后再从15楼下到1楼。这种算法的磁头调度也是如此,磁头只能从最里磁道到磁盘最外层磁道。然后再由最外层磁道移动到最里层磁道,磁头是单向移动的,在此基础上,才执行和最短寻道时间算法一样的,离当前磁头最近的寻道请求。这种算法改善了SCAN算法,消除了对两端磁道请求的不公平。 

   4.扫描算法(SCAN)

  优点:扫描算法不仅考虑到欲访问的磁道与当前磁道的距离,更优先考虑的是磁头的当前移动方向。这种算法在磁头的移动方向上选择离当前磁头所在磁道最近的请求作为下一次服务对象,这种改进有效避免了饥饿现象(饥饿:当一个进程一直无法得到自己的资源而一直无法进行后续的操作时(不一定是阻塞),我们称这个进程会饥饿而死。),并且减少了寻道时间。

  缺点:不利于最远一端的磁道访问请求。

 7. 线程同步的方式

  互斥量 Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程(一个线程)同时访问

  信号量 Semphare:它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量

  事件(信号),Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作

  线程通信常用的方式有:

  • wait/notify 等待
  • Volatile 内存共享
  • CountDownLatch 并发工具
  • CyclicBarrier 并发工具

8. 内存管理中分页和分段的区别

  段式存储管理:

  一种符合用户视角的内存分配管理方案。在段式存储管理中,将程序的地址空间划分为若干段(segment),如代码段,数据段,堆栈段;这样每个进程有一个二维地址空间,相互独立,互不干扰。段式管理的优点是:没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)

  页式存储管理:

  一种用户视角内存与物理内存相分离的内存分配管理方案。在页式存储管理中,将程序的逻辑地址划分为固定大小的页(page),而物理内存划分为同样大小的帧,程序加载时,可以将任意一页放入内存中任意一个帧,这些帧不必连续,从而实现了离散分离。页式存储管理的优点是:没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满)。

  两者的不同点:

    目的不同:分页是由于系统管理的需要而不是用户的需要,它是信息的物理单位;分段的目的是为了能更好地满足用户的需要,它是信息的逻辑单位,它含有一组其意义相对完整的信息;

    大小不同:页的大小固定且由系统决定,而段的长度却不固定,由其所完成的功能决定;

    地址空间不同: 段向用户提供二维地址空间;页向用户提供的是一维地址空间;

    信息共享:段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制;

    内存碎片:页式存储管理的优点是没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满);而段式管理的优点是没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)。

8. 进程调度算法

  FCFS(先来先服务,队列实现,非抢占的):先请求CPU的进程先分配到CPU

  SJF(最短作业优先调度算法):平均等待时间最短,但难以知道下一个CPU区间长度

  优先级调度算法(可以是抢占的,也可以是非抢占的):优先级越高越先分配到CPU,相同优先级先到先服务,存在的主要问题是:低优先级进程无穷等待CPU,会导致无穷阻塞或饥饿;解决方案:老化

  时间片轮转调度算法(可抢占的):队列中没有进程被分配超过一个时间片的CPU时间,除非它是唯一可运行的进程。如果进程的CPU区间超过了一个时间片,那么该进程就被抢占并放回就绪队列。

  多级队列调度算法:将就绪队列分成多个独立的队列,每个队列都有自己的调度算法,队列之间采用固定优先级抢占调度。其中,一个进程根据自身属性被永久地分配到一个队列中。

  多级反馈队列调度算法:与多级队列调度算法相比,其允许进程在队列之间移动:若进程使用过多CPU时间,那么它会被转移到更低的优先级队列;在较低优先级队列等待时间过长的进程会被转移到更高优先级队列,以防止饥饿发生。

9. 进程同步机制

  原子操作、信号量机制、自旋锁管程、会合、分布式系统

10. 页面置换算法

  FIFO先进先出算法:在操作系统中经常被用到,比如作业调度(主要实现简单,很容易想到);

  LRU(Least recently use)最近最少使用算法:根据使用时间到现在的长短来判断;

  LFU(Least frequently use)最少使用次数算法:根据使用次数来判断;

  OPT(Optimal replacement)最优置换算法:理论的最优,理论;就是要保证置换出去的是不再被使用的页,或者是在实际内存中最晚使用的算法。

11.  颠簸

  颠簸本质上是指频繁的页调度行为,具体来讲,进程发生缺页中断,这时,必须置换某一页。然而,其他所有的页都在使用,它置换一个页,但又立刻再次需要这个页。因此,会不断产生缺页中断,导致整个系统的效率急剧下降,这种现象称为颠簸(抖动)。

  内存颠簸的解决策略包括:

    如果是因为页面替换策略失误,可以修改替换算法来解决这个问题;

    如果是因为运行的程序太多,造成程序无法同时将所有频繁访问的页面调入内存,则要降低多道程序的数量;

    否则,还剩下两个办法:终止该进程或增加物理内存容量。

12. 虚拟内存概念、优缺点

  定义:具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充得一种存储器系统。其逻辑容量由内存之和和外存之和决定。

  与传统存储器比较虚拟存储器有以下三个主要特征:

  • 多次性,是指无需在作业运行时一次性地全部装入内存,而是允许被分成多次调入内存运行。
  • 对换性,是指无需在作业运行时一直常驻内存,而是允许在作业的运行过程中,进行换进和换出。
  • 虚拟性,是指从逻辑上扩充内存的容量,使用户所看到的内存容量,远大于实际的内存容量。

  虚拟内存的实现有以下两种方式:

  • 请求分页存储管理。
  • 请求分段存储管理。

13. IO多路复用

  IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。

  IO多路复用适用如下场合:

  • 当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。

  • 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。

  • 如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。

  • 如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。

  • 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。

  • 与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

  举例:

  【服务员,加菜】【同步阻塞设计模式后异步阻塞io模型】

  定义:  

  其中,多路是指多个socket连接,复用是指复用一个线程,流是指事件,这样,单个线程通过记录跟踪每一个scoket的状态来同时管理多个I/O流。

  优点:

  当进程需要等待多个描述符的时候,通常情况下进程会开启多个线程,每个线程等待一个描述符就绪。但是多路复用可以同时监听多个描述符,进程中不需要开启线程,减少系统的开销,尽量多的提高服务器的吞吐能力,性能高于多线程。

  目前,按照技术先进性,多路复用主要有select、poll和epollepoll是最新的。

  select:【异步阻塞】

  过程:

  用户首先将需要进行io操作的socket添加到select中,然后阻塞等待select系统返回。当数据到达时,socket被激活(可读可写),select函数返回(阻塞)。用户线程正式发起read请求,读取数据并继续执行。

  相比同步阻塞模型:

  添加了监视socket,以及调用select函数的额外操作,效率差。但是用户可以在一个线程内同时处理多个socket的io请求,也可以注册多个socket,然后不断地调用select读取被激活的socket,即可以在同一个线程内同时处理多个io请求,但是每个io请求的过程在select函数上还是阻塞的。而同步阻塞中需要多线程。

  特点:

    1.会修改传入的参数,对于多个调用的函数不友好

    2.select只能轮询着去找数据,当任何一个sock出现了数据,select仅仅会返回,但是并不会告诉我们是哪个sock有数据,于是只能自己找,对于大量socket来说,开销很大【轮番去问】

    3.线程不安全

    4.只能监视1024个连接

  poll

  1.不会修改传入的参数

  2.线程不安全,只能在一个线程里面处理一组I/O流。

  3.去掉了1024个连接的限制

  epoll

  1.线程安全

  2.不仅知道scoket有数据,还知道那个scoket有数据【有人会直接呼叫】

  epoll缺点:只有linux支持。

  设计模式:

  Reactor,可以将用户线程轮询io操作状态的工作统一交给事件处理器循环进行处理,用户线程注册事件处理器后可以继续执行其他工作(异步),而reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时,通知相应的用户线程,event_handle执行数据读取和处理,select函数执行时线程被阻塞。

posted @ 2020-03-11 15:26  ~花开不败~  阅读(465)  评论(0编辑  收藏  举报