操作系统学习并发操作系统

操作系统 --- 学习笔记

2021-06-25  本文已影响0人  _code_x

1. 操作系统基本特征

(1) 并发

(2) 共享

(3)虚拟

(4)异步

2. 操作系统基本功能

操作系统的基本功能:进程管理、内存管理、文件管理与设备管理。

(1)进程管理

(2)内存管理

(3)文件管理

(4)设备管理

3. 系统调用

介绍系统调用之前,我们先来了解一下用户态和系统态。根据进程访问资源的特点,操作系统需要两种CPU状态:

拓展:指令划分

特权级别:

系统调用:如果一个进程在用户态需要使用内核态的功能,就进行系统调用从而陷入内核,由操作系统代为完成。

也就是说在我们运行的用户程序中,凡是与系统态级别的资源有关的操作(如文件管理、进程控制、内存管理等),都必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。

系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于系统态,而普通的函数调用由函数库或用户自己提供,运行于用户态。

4. 内核态与用户态,之间如何转换

为什么要有用户态和内核态?

1)用户态切换到内核态的3种方式:

a. 系统调用

b. 异常

c. 外围设备的中断

这3种方式是系统在运行时由用户态转到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的。

内核态切换到用户态的途径——>设置程序状态字PSW,在PSW中有一个二进制位控制这两种模式。

内核态与用户态的区别:

5. 大内核和微内核

1.大内核

2.微内核

进程与线程

1. 中断

(1)外中断

(2)异常

(3)陷入

2. 进程与线程

进程是什么?

进程的组织形式:

进程的最大线程数

(1)32位windows下,一个进程空间4G,内核占2G,留给用户只有2G,一个线程默认栈是1M,所以一个进程最大开2048个线程。当然内存不会完全拿来做线程的栈,所以最大线程数实际值要小于2048,大概2000个。
(2)32位Linux下,一个进程空间4G,内核占1G,用户留3G,一个线程默认8M,所以最多380个左右线程。

进程在操作系统中的主要状态

(1)就绪状态(ready):已经具备运行条件,因为其他线程正在运行而停止或者时间片已经用完,等待被调度(等待获取时间片)
(2)运行状态(running):占用cpu
(3)阻塞状态(waiting):等待资源/事件(如等待输入/输出而暂停运行)

ps:还有创建状态和结束状态。相互转换关系如下:

进程状态切换

注意:不能由阻塞态直接转换为运行态,也不能由就绪态直接抓换为阻塞态(因为进入阻塞态是进程主动请求的,必然需要进程在运行时才能发出这种请求);运行态到阻塞态是一种进程自身做出的主动行为;

线程是什么?

总结:两者的区别?

ps:协程,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换(上下文切换)那样消耗资源。

3. 什么是上下文切换?

上下文切换的什么?

上下文切换场景?

进程切换与线程切换的区别?

4. 进程控制(状态切换)

(1)进程的创建

(2)进程的终止

进程终止包括:正常结束,表示进程已经完成并准备退出;异常结束,表示进程在运行时发生异常,程序无法继续运行,例如非法指令,IO 故障等;外界干预,指进程因为外界请求而终止,例如操作系统干预等。

(3)进程的阻塞与唤醒

正在执行的进程由于等待的事件未发生,由系统执行阻塞原语,由运行态变为阻塞态。阻塞过程:

唤醒过程:

(4)进程切换

进程切换是指CPU从一个进程的运行转到另一个进程上运行。进程切换过程:

5.进程调度算法

进程调度算法评价指标:

image.png

说明:不同环境的调度算法目标不同,因此需要针对不同环境来讨论调度算法。

批处理系统

批处理系统没有太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。

(1)先来先服务 first-come first-serverd(FCFS)

(2)短作业优先 shortest job first(SJF)

(3)最短剩余时间优先 shortest remaining time next(SRTN)

交互式系统

交互式系统有大量的用户交互操作,在该系统中调度算法的目标是快速地进行响应。

(1)时间片轮转

时间片轮转算法的效率和时间片的大小有很大关系:

(2)优先级调度

(3)多级反馈队列

实时系统

6.进程通信与同步

进程同步与进程通信很容易混淆,它们的区别在于:

(1)进程同步:控制多个进程按一定顺序执行,一种目的;
(2)进程通信:进程间传输信息(包括进程同步所需要的信息,是一种手段)。

进程通信是一种手段,而进程同步是一种目的。也可以说,为了能够达到进程同步的目的,需要让进程进行通信,传输一些进程同步所需要的信息。

进程间的通信方式?

进程之间的通信方式有管道、FIFO(命名管道),消息队列、信号量、共享内存与套接字。

(1)管道

(2)FIFO(命名管道)

(3)消息队列

(4)信号量

(5)共享内存

(6)套接字(socket)远程调用

总结:IPC进程通信

同一主机:

不同主机:

为什么要进程同步?

多进程虽然提高了系统资源利用率和吞吐量,但是进程的异步性(可能是多次执行完成)可能造成系统的混乱。

进程同步的任务就是对多个相关进程在执行顺序上进行协调,使并发执行的多个进程之间可以有效的共享资源和相互合作,保证程序执行的可再现性。

(1)临界区

(2)同步与互斥

(3)信号量

(4)管程

7.线程的通信方式

线程通信主要通过共享内存和消息传递两种模型实现的,具体来说线程通信常用的方式有:

(1)Volatile 内存共享

volatile有两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程之间进行通信。volatile语义保证线程可见性有两个原则保证:

两个线程操作一个对象时,如果不加volitile关键字,就会使一个线程一直循环等待,volatile解决了一个线程空等待的问题。

(2)wait/notify(等待/通知) 机制

Object类提供了线程间通信的方法:wait()、notify()、notifyaAl(),

等待通知机制是基于wait和notify方法来实现的,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被通知或者被唤醒。

为什么必须获取锁?

因为调用wait方法时(使线程处于等待状态),必须要先释放锁,如果没有持有锁将会抛出异常。notify唤醒一个线程,但是还会持有线程的锁。ps:sleep使线程休眠一段时间,会持有线程的锁。

(3)CountDownLatch 并发工具

但是如果对于高并发情况下大量数据,当执行到某个业务逻辑节点时,需要唤醒另外一个线程对当前节点数据处理,使用notify通知,并不解决线程间的实时通信问题(notify不释放线程的锁)

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

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

谈一谈fork()函数

在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。

在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;

子进程会继承父进程的哪些东西?

子进程从父进程继承了:

(1)用户号UIDs和用户组号GIDs(2)进程组号(3)当前工作目录(4)根目录(5)环境(6)打开文件的描述符(7)共享内存(8)堆栈等

子进程与父进程不同的:

(1)进程号PID(2)各自的父进程号(3)自己的文件描述符和目录流的拷贝(4)子进程不继承父进程的进程正文, 数据和其它锁定内存(5)不继承异步输入和输出(6)父进程设置的锁

死锁

什么是死锁?典型:哲学家进餐问题。

1.死锁、饥饿与死循环的区别

1.死锁产生条件(同时满足四个)

(1)互斥:每个资源要么已经分配给了一个进程,要么就是可用的(打印机)。
(2)请求与保持:已经得到了某个资源的进程,可以再请求新的资源,此时请求被阻塞,原资源保持不放
(3)不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式(主动)地释放
(4)环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。

2.处理方法

主要有以下四种方法:

(1)鸵鸟策略

(2)死锁检测与死锁恢复

死锁检测算法: 用于检测系统状态,以确定系统中是否发生了死锁。

  1. 每种类型一个资源的死锁检测
  2. 每种类型多个资源的死锁检测

步骤:

死锁恢复:解除死锁的主要方法有:

(3)死锁预防

在程序运行之前预防发生死锁。

  1. 破坏互斥条件。例如假脱机打印机技术允许若干个进程同时输出,唯一真正请求物理打印机的进程是打印机守护进程。
  2. 破坏占有和等待条件。一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。
  3. 破坏不可抢占条件。
  4. 破坏环路等待。给资源统一编号,进程只能按编号顺序来请求资源。

(4)死锁避免

避免死锁并不是事先采取某种限制措施破坏死锁的必要条件,而是在资源动态分配过程中,防止系统进入不安全状态,以避免发生死锁,比如银行家算法、系统安全状态、安全性算法。

系统安全状态

银行家算法

内存管理

1. 内存管理的方式

操作系统中的内存管理有三种,段式,页式,段页式。

为什么需要三种管理方式?

由于【连续内存分配方式】会导致【内存利用率偏低】以及【内存碎片】的问题,因此需要对这些离散的内存进行管理。引出了三种内存管理方式。

分页存储管理:

(1)基本分页存储管理中不具备页面置换功能,因此需要整个程序的所有页面都装入内存之后才可以运行。
(2)需要一个页表来记录逻辑地址(页号)和实际存储地址(物理块号)之间的映射关系,以实现从页号到物理块号的映射。
(3)由于页表也是存储在内存中的,因此内存数据需要两次的内存访问(一次是从内存中访问页表,从中找到指定的物理块号,加上页内偏移得到实际物理地址;第二次就是根据第一次得到的物理地址访问内存取出数据)。
(4)为了减少两次访问内存导致的效率影响,分页管理中引入了快表。当要访问内存数据的时候,首先将页号在快表中查询,如果在快表中,直接读取相应的物理块号;如果没有找到,那么访问内存中的页表,从页表中得到物理地址,同时将页表中的该映射表项添加到快表中。
(5)在某些计算机中如果内存的逻辑地址很大,将会导致程序的页表项会很多,而页表在内存中是连续存放的,所以相应的就需要较大的连续内存空间。为了解决这个问题,可以采用两级页表或者多级页表的方法,其中外层页表一次性调入内存且连续存放,内层页表离散存放。相应的访问内存页表的时候需要一次地址变换,访问逻辑地址对应的物理地址的时候也需要一次地址变换,而且一共需要访问内存3次才可以读取一次数据。引入多级页表的主要目的是为了避免把全部页表一直放在内存中占用过多空间(时间换空间),特别是那些根本就不需要的页表就不需要保留在内存中。多级页表属于时间换空间的典型场景。

分段存储管理:

分页是为了提高内存利用率,而分段是为了满足程序员在编写代码的时候的一些逻辑需求(比如数据共享,数据保护,动态链接等)。

(1)分段内存管理当中,地址是二维的,一维是段号,一维是段内地址;
(2)其中每个段的长度是不一样的,而且每个段内部都是从0开始编址的。由于分段管理中,每个段内部是连续内存分配,但是段和段之间是离散分配的,因此也存在一个逻辑地址到物理地址的映射关系,相应的就是段表机制。段表中的每一个表项记录了该段在内存中的起始地址和该段的长度。段表可以放在内存中也可以放在寄存器中。
(3)访问内存的时候根据段号和段表项的长度计算当前访问段在段表中的位置,然后访问段表,得到该段的物理地址,根据该物理地址以及段内偏移量就可以得到需要访问的内存。由于也是两次内存访问,所以分段管理中同样引入了联想寄存器。

分段和分页的对比

(1)页是信息的物理单位,是出于系统内存利用率的角度提出的离散分配机制;段是信息的逻辑单位,每个段含有一组意义完整的信息,是出于用户角度提出的内存管理机制。
(2)页的大小是固定的,由系统决定;段的大小是不确定的,由用户决定。
(3)页地址空间是一维的,段地址空间是二维的。
(4)分段比分页更容易实现信息的共享和保护,分页系统的虚拟内存功能。

段页存储方式

段页存储方式,既拥有分段系统的共享和保护,又拥有分页系统的虚拟内存功能。

(1)先将用户程序分为若干个段,然后再把每个段分成若干个页,并且为每一个段赋予一个段名称。这样在段页式管理中,一个内存地址就由段号,段内页号以及页内地址三个部分组成。
(2)段页式内存访问:系统中设置了一个段表寄存器,存放段表的起始地址和段表的长度。地址变换时,根据给定的段号(还需要将段号和寄存器中的段表长度进行比较防止越界)以及寄存器中的段表起始地址,就可以得到该段对应的段表项,从段表项中得到该段对应的页表的起始地址,然后利用逻辑地址中的段内页号从页表中找到页表项,从该页表项中的物理块地址以及逻辑地址中的页内地址拼接出物理地址,最后用这个物理地址访问得到所需数据。由于访问一个数据需要三次内存访问,所以段页式管理中也引入了高速缓冲寄存器。

如何实现逻辑地址到物理地址的映射管理?

2. 页表项中各个位的作用,什么是缺页中断?

页表项中各个位的作用:

在请求分页系统中,可以通过【查询页表中的中断位】来确定所要访问的页面是否存在于内存中。每当所要访问的页面不在内存时,会产生一次缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。

缺页本身是一种中断,与一般的中断一样,需要经过4个处理步骤:

3. 页面置换算法

就是说 当系统内存不足的时候,而我们想往内存中添加现在立刻要用的缓存时,需要从现在的内存中踢出一部分东西。那么选取哪一部分被踢出来呢?这个选择的部分 就叫页面置换算法。

我们为什么需要页面置换算法?因为在地址映射过程中,如果发现要访问的页面不在内存中,会产生缺页中断;当发生此现象的时候,如果操作系统内存中没有空余,则操作系统必须在内存里面选择一个页面将其移出内存。

(1)最佳置换算法(OPT):理想的置换算法。置换策略是将当前页面中在未来最长时间内不会被访问的页置换出去。操作系统无法提前预判页面访问序列(无法实现)。 因此, 最佳置换算法是无法实现的。
(2)先进先出置换算法(FIFO):每次淘汰最早调入的页面 。
(3)最近最久未使用算法(LRU):每次淘汰最久没有使用的页面。使用了一个时间标志
(4)时钟算法clock(最近未使用算法NRU):页面设置一个访问位,并将页面链接为一个环形队列,页面被访问的时候访问位设置为1。页面置换的时候,如果当前指针所指页面访问为为0,那么置换,否则将其置为0,循环直到遇到一个访问为位0的页面
(5)改进型Clock算法:在Clock算法的基础上添加一个修改位,替换时根究访问位和修改位综合判断。优先替换访问为何修改位都是0的页面,其次是访问位为0修改位为1的页面。
(6)最少使用算法(LFU):设置寄存器记录页面被访问次数,每次置换的时候置换当前访问次数最少的。LFU和LRU是很类似的,支持硬件也是一样的,但是区分两者的关键在于一个以时间为标准,一个以次数为标准。

4. 虚拟内存(分页存储管理)

为了更好的管理内存,操作系统将【内存抽象成地址空间】(虚拟的存在,便于管理,实际是一种映射)

(1)每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。
(2)通过虚拟内存可以让程序可以拥有超过系统物理内存大小的可用内存空间。
(3)虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)。这样会更加有效地管理内存并减少出错。

5. 快表地址转化原理(交换空间)

为了解决虚拟地址到物理地址的转换速度,操作系统在页表方案基础之上引入了快表来加速虚拟地址到物理地址的转换。可以把快表理解为一种特殊的高速缓冲存储器(Cache),其中的内容是页表的一部分或者全部内容。作为页表的 Cache,它的作用与页表相似,但是提高了访问速率。由于采用页表做地址转换,读写内存数据时 CPU 要访问两次主存。有了快表,有时只要访问一次高速缓冲存储器,一次主存,这样可加速查找并提高指令执行速度。

使用快表之后的地址转换流程是这样的:

(1)根据虚拟地址中的页号查快表(快表中存放虚拟地址(逻辑地址)到物理地址的映射,这和页表相似);
(2)如果该页在快表中,直接从快表中读取相应的物理地址;
(3)如果该页不在快表中,就访问内存中的页表,再从页表中得到物理地址,同时将页表中的该映射表项添加到快表中;
(4)当快表填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页

6. 局部性原理

局部性原理表现在以下两个方面:

(1)时间局部性 :如果程序中的某条指令一旦执行,不久以后该指令可能再次执行;如果某数据被访问过,不久以后该数据可能再次被访问。产生时间局部性的典型原因,是由于在程序中存在着大量的循环操作。
(2)空间局部性 :一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,这是因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。

局部性原理主要是实现高速缓存:

7.内存泄露与溢出

内存溢出的解决方案

文件系统

1. 磁盘结构

(1)盘面(Platter):一个磁盘有多个盘面;
(2)磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道;
(3)扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小;
(4)磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写);
(5)制动手臂(Actuator arm):用于在磁道之间移动磁头;
(6)主轴(Spindle):使整个盘面转动。

2. 磁盘调度算法

读写一个磁盘块的时间的影响因素有:

(1)旋转时间(主轴转动盘面,使得磁头移动到适当的扇区上)
(2)寻道时间(制动手臂移动,使得磁头移动到适当的磁道上)
(3)实际的数据传输时间

其中,寻道时间最长,因此磁盘调度的主要目标是使磁盘的平均寻道时间最短。

先来先服务(FCFS, First Come First Served)

最短寻道时间优先(SSTF, Shortest Seek Time First)

电梯算法(SCAN)

Linux内核补充

1.linux终端按下Ctrl+C发生了什么

2.IO模型

(1)阻塞式 I/O

(2)非阻塞式 I/O

(3)IO多路复用

(4)信号驱动IO

(5)异步 I/O

异步 I/O 与信号驱动 I/O 的区别在于,异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O。

注意:阻塞I/O,非阻塞I/O,信号驱动I/O和I/O复用都是同步I/O。

3.select/poll/epoll

select/poll/epoll 都是 I/O 多路复用的具体实现,select 出现的最早,之后是 poll,再是 epoll。

(1)select

(2)poll

(3)epoll

具体是通过红黑树和就绪链表实现的,红黑树存储所有的文件描述符,就绪链表存储有事件发生的文件描述符;

(1)epoll_ctl可以对文件描述符结点进行增、删、改、查,并且告知内核注册回调函数(事件)。
(2)一旦文件描述符上有事件发生时,那么内核将该文件描述符节点插入到就绪链表里面
(3)这时候epoll_wait将会接收到消息,并且将数据拷贝到用户空间。

表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。

epoll工作模式

  1. LT 模式
  1. ET 模式

EPOLLONESHOT

4.select poll epoll区别

(1)消息传递方式(拷贝或者共享内存):

(2)文件句柄剧增后带来的IO效率问题:

(3)支持一个进程所能打开的最大连接数:

补充

1.使用信号量实现生产者-消费者问题

问题描述:使用一个缓冲区来保存物品,只有缓冲区没有满,生产者才可以放入物品;只有缓冲区不为空,消费者才可以拿走物品。

因为缓冲区属于临界资源,因此需要使用一个互斥量 mutex 来控制对缓冲区的互斥访问。

为了同步生产者和消费者的行为,需要记录缓冲区中物品的数量。数量可以使用信号量来进行统计,这里需要使用两个信号量:empty 记录空缓冲区的数量,full 记录满缓冲区的数量。其中,empty 信号量是在生产者进程中使用,当 empty 不为 0 时,生产者才可以放入物品;full 信号量是在消费者进程中使用,当 full 信号量不为 0 时,消费者才可以取走物品。

注意,不能先对缓冲区进行加锁,再测试信号量。也就是说,不能先执行 down(mutex) 再执行 down(empty)。如果这么做了,那么可能会出现这种情况:生产者对缓冲区加锁后,执行 down(empty) 操作, 发现 empty = 0,此时生产者睡眠。消费者不能进入临界区,因为生产者对缓冲区加锁了,消费者就无法执行 up(empty) 操作,empty 永远都为 0,导致生产者永远等待下,不会释放锁,消费者因此也会永远等待下去。

2. 哲学家进餐问题

五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。

下面是一种错误的解法,如果所有哲学家同时拿起左手边的筷子,那么所有哲学家都在等待其它哲学家吃完并释放自己手中的筷子,导致死锁。

为了防止死锁的发生,可以设置两个条件:

(1)必须同时拿起左右两根筷子;
(2)只有在两个邻居都没有进餐的情况下才允许进餐。

3.C语言编译的整个过程

(1)预处理阶段:处理以 # 开头的预处理命令;
(2)编译阶段:翻译成汇编文件;
(3)汇编阶段:将汇编文件翻译成可重定位目标文件;
(4)链接阶段:将可重定位目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。

ps: 目标文件

(1)可执行目标文件:可以直接在内存中执行;

(2)可重定位目标文件:可与其它可重定位目标文件在链接阶段合并,创建一个可执行目标文件;

(3)共享目标文件:这是一种特殊的可重定位目标文件,可以在运行时被动态加载进内存并链接;

ps: 静态链接和动态链接

  1. 静态链接

静态链接器以一组可重定位目标文件为输入,生成一个完全链接的可执行目标文件作为输出。链接器主要完成以下两个任务:

(1)符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。

(2)重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。。

  1. 动态链接

静态库有以下两个问题:

(1)当静态库更新时那么整个程序都要重新进行链接;

(2)对于 printf 这种标准函数库,如果每个程序都要有代码,这会极大浪费资源。

共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中通常用 .so 后缀来表示,Windows 系统上它们被称为 DLL。它具有以下特点:

(1)在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中;

(2)在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。

4.读者-写者问题

读者写者问题描述非常简单,有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者在读文件时写者也不去能写文件。类似于生产者消费者问题的分析过程,首先来找找哪些是属于“等待”情况。

“读者--写者问题”是保证一个Writer进程必须与其他进程互斥地访问共享对象的同步问题。要解决的问题:读、读共享; 写、写互斥; 写、读互斥

实现:

上一篇 下一篇

猜你喜欢

热点阅读