diff --git a/docs/cs-basics/data-structure/linear-data-structure.md b/docs/cs-basics/data-structure/linear-data-structure.md index 17a61ddc386..5a55d496696 100644 --- a/docs/cs-basics/data-structure/linear-data-structure.md +++ b/docs/cs-basics/data-structure/linear-data-structure.md @@ -311,6 +311,6 @@ myStack.pop();//报错:java.lang.IllegalArgumentException: Stack is empty. - **阻塞队列:** 阻塞队列可以看成在队列基础上加了阻塞操作的队列。当队列为空的时候,出队操作阻塞,当队列满的时候,入队操作阻塞。使用阻塞队列我们可以很容易实现“生产者 - 消费者“模型。 - **线程池中的请求/任务队列:** 线程池中没有空闲线程时,新的任务请求线程资源时,线程池该如何处理呢?答案是将这些请求放在队列中,当有空闲线程的时候,会循环中反复从队列中获取任务来执行。队列分为无界队列(基于链表)和有界队列(基于数组)。无界队列的特点就是可以一直入列,除非系统资源耗尽,比如 :`FixedThreadPool` 使用无界队列 `LinkedBlockingQueue`。但是有界队列就不一样了,当队列满的话后面再有任务/请求就会拒绝,在 Java 中的体现就是会抛出`java.util.concurrent.RejectedExecutionException` 异常。 - Linux 内核进程队列(按优先级排队) -- 现实生活中的派对,播放器上的播放列表; +- 现实生活中的排队,播放器上的播放列表; - 消息队列 - 等等...... diff --git a/docs/cs-basics/operating-system/operating-system-basic-questions-01.md b/docs/cs-basics/operating-system/operating-system-basic-questions-01.md index 9da46d063cf..4d80f1b2a52 100644 --- a/docs/cs-basics/operating-system/operating-system-basic-questions-01.md +++ b/docs/cs-basics/operating-system/operating-system-basic-questions-01.md @@ -110,7 +110,7 @@ head: 1. **管道/匿名管道(Pipes)** :用于具有亲缘关系的父子进程间或者兄弟进程之间的通信。 1. **有名管道(Names Pipes)** : 匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循**先进先出(first in first out)**。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。 1. **信号(Signal)** :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生; -1. **消息队列(Message Queuing)** :消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比 FIFO 更有优势。**消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺点。** +1. **消息队列(Message Queuing)** :消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显示地删除一个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比 FIFO 更有优势。**消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺。** 1. **信号量(Semaphores)** :信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。 1. **共享内存(Shared memory)** :使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有用的进程间通信方式。 1. **套接字(Sockets)** : 此方法主要用于在客户端和服务器之间通过网络进行通信。套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。 @@ -122,8 +122,8 @@ head: 🙋 **我** :线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。操作系统一般有下面三种线程同步的方式: 1. **互斥量(Mutex)**:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。 -1. **信号量(Semaphore)** :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。 -1. **事件(Event)** :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。 +1. **信号量(Semphares)** :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量 +1. **事件(Event)** :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操 ### 进程的调度算法 @@ -143,7 +143,7 @@ head: 👨‍💻**面试官** :**你知道什么是死锁吗?** -🙋 **我** :死锁描述的是这样一种情况:多个进程/线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于进程/线程被无限期地阻塞,因此程序不可能正常终止。 +🙋 **我** :多个进程可以竞争有限数量的资源。当一个进程申请资源时,如果这时没有可用资源,那么这个进程进入等待状态。有时,如果所申请的资源被其他等待进程占有,那么该等待进程有可能再也无法改变状态。这种情况成为**死锁**。 ### 死锁的四个条件 @@ -158,91 +158,9 @@ head: 注意,只有四个条件同时成立时,死锁才会出现。 -### 解决死锁的方法 +## 三 操作系统内存管理基础 -解决死锁的方法可以从多个角度去分析,一般的情况下,有**预防,避免,检测和解除四种**。 - -- **预防** 是采用某种策略,**限制并发进程对资源的请求**,从而使得死锁的必要条件在系统执行的任何时间上都不满足。 - -- **避免**则是系统在分配资源时,根据资源的使用情况**提前做出预测**,从而**避免死锁的发生** - -- **检测**是指系统设有**专门的机构**,当死锁发生时,该机构能够检测死锁的发生,并精确地确定与死锁有关的进程和资源。 -- **解除** 是与检测相配套的一种措施,用于**将进程从死锁状态下解脱出来**。 - -#### 死锁的预防 - -死锁四大必要条件上面都已经列出来了,很显然,只要破坏四个必要条件中的任何一个就能够预防死锁的发生。 - -破坏第一个条件 **互斥条件**:使得资源是可以同时访问的,这是种简单的方法,磁盘就可以用这种方法管理,但是我们要知道,有很多资源 **往往是不能同时访问的** ,所以这种做法在大多数的场合是行不通的。 - -破坏第三个条件 **非抢占** :也就是说可以采用 **剥夺式调度算法**,但剥夺式调度方法目前一般仅适用于 **主存资源** 和 **处理器资源** 的分配,并不适用于所以的资源,会导致 **资源利用率下降**。 - -所以一般比较实用的 **预防死锁的方法**,是通过考虑破坏第二个条件和第四个条件。 - -**1、静态分配策略** - -静态分配策略可以破坏死锁产生的第二个条件(占有并等待)。所谓静态分配策略,就是指一个进程必须在执行前就申请到它所需要的全部资源,并且知道它所要的资源都得到满足之后才开始执行。进程要么占有所有的资源然后开始执行,要么不占有资源,不会出现占有一些资源等待一些资源的情况。 - -静态分配策略逻辑简单,实现也很容易,但这种策略 **严重地降低了资源利用率**,因为在每个进程所占有的资源中,有些资源是在比较靠后的执行时间里采用的,甚至有些资源是在额外的情况下才是用的,这样就可能造成了一个进程占有了一些 **几乎不用的资源而使其他需要该资源的进程产生等待** 的情况。 - -**2、层次分配策略** - -层次分配策略破坏了产生死锁的第四个条件(循环等待)。在层次分配策略下,所有的资源被分成了多个层次,一个进程得到某一次的一个资源后,它只能再申请较高一层的资源;当一个进程要释放某层的一个资源时,必须先释放所占用的较高层的资源,按这种策略,是不可能出现循环等待链的,因为那样的话,就出现了已经申请了较高层的资源,反而去申请了较低层的资源,不符合层次分配策略,证明略。 - -#### 死锁的避免 - -上面提到的 **破坏** 死锁产生的四个必要条件之一就可以成功 **预防系统发生死锁** ,但是会导致 **低效的进程运行** 和 **资源使用率** 。而死锁的避免相反,它的角度是允许系统中**同时存在四个必要条件** ,只要掌握并发进程中与每个进程有关的资源动态申请情况,做出 **明智和合理的选择** ,仍然可以避免死锁,因为四大条件仅仅是产生死锁的必要条件。 - -我们将系统的状态分为 **安全状态** 和 **不安全状态** ,每当在未申请者分配资源前先测试系统状态,若把系统资源分配给申请者会产生死锁,则拒绝分配,否则接受申请,并为它分配资源。 - -> 如果操作系统能够保证所有的进程在有限的时间内得到需要的全部资源,则称系统处于安全状态,否则说系统是不安全的。很显然,系统处于安全状态则不会发生死锁,系统若处于不安全状态则可能发生死锁。 - -那么如何保证系统保持在安全状态呢?通过算法,其中最具有代表性的 **避免死锁算法** 就是 Dijkstra 的银行家算法,银行家算法用一句话表达就是:当一个进程申请使用资源的时候,**银行家算法** 通过先 **试探** 分配给该进程资源,然后通过 **安全性算法** 判断分配后系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待,若能够进入到安全的状态,则就 **真的分配资源给该进程**。 - -银行家算法详情可见:[《一句话+一张图说清楚——银行家算法》](https://blog.csdn.net/qq_33414271/article/details/80245715) 。 - -操作系统教程树中讲述的银行家算法也比较清晰,可以一看. - -死锁的避免(银行家算法)改善解决了 **资源使用率低的问题** ,但是它要不断地检测每个进程对各类资源的占用和申请情况,以及做 **安全性检查** ,需要花费较多的时间。 - -#### 死锁的检测 - -对资源的分配加以限制可以 **预防和避免** 死锁的发生,但是都不利于各进程对系统资源的**充分共享**。解决死锁问题的另一条途径是 **死锁检测和解除** (这里突然联想到了乐观锁和悲观锁,感觉死锁的检测和解除就像是 **乐观锁** ,分配资源时不去提前管会不会发生死锁了,等到真的死锁出现了再来解决嘛,而 **死锁的预防和避免** 更像是悲观锁,总是觉得死锁会出现,所以在分配资源的时候就很谨慎)。 - -这种方法对资源的分配不加以任何限制,也不采取死锁避免措施,但系统 **定时地运行一个 “死锁检测”** 的程序,判断系统内是否出现死锁,如果检测到系统发生了死锁,再采取措施去解除它。 - -##### 进程-资源分配图 - -操作系统中的每一刻时刻的**系统状态**都可以用**进程-资源分配图**来表示,进程-资源分配图是描述进程和资源申请及分配关系的一种有向图,可用于**检测系统是否处于死锁状态**。 - -用一个方框表示每一个资源类,方框中的黑点表示该资源类中的各个资源,每个键进程用一个圆圈表示,用 **有向边** 来表示**进程申请资源和资源被分配的情况**。 - -图中 2-21 是**进程-资源分配图**的一个例子,其中共有三个资源类,每个进程的资源占有和申请情况已清楚地表示在图中。在这个例子中,由于存在 **占有和等待资源的环路** ,导致一组进程永远处于等待资源的状态,发生了 **死锁**。 - -![进程-资源分配图](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/cs-basics/operating-system/process-resource-allocation-diagram.jpg) - -进程-资源分配图中存在环路并不一定是发生了死锁。因为循环等待资源仅仅是死锁发生的必要条件,而不是充分条件。图 2-22 便是一个有环路而无死锁的例子。虽然进程 P1 和进程 P3 分别占用了一个资源 R1 和一个资源 R2,并且因为等待另一个资源 R2 和另一个资源 R1 形成了环路,但进程 P2 和进程 P4 分别占有了一个资源 R1 和一个资源 R2,它们申请的资源得到了满足,在有限的时间里会归还资源,于是进程 P1 或 P3 都能获得另一个所需的资源,环路自动解除,系统也就不存在死锁状态了。 - -##### 死锁检测步骤 - -知道了死锁检测的原理,我们可以利用下列步骤编写一个 **死锁检测** 程序,检测系统是否产生了死锁。 - -1. 如果进程-资源分配图中无环路,则此时系统没有发生死锁 -2. 如果进程-资源分配图中有环路,且每个资源类仅有一个资源,则系统中已经发生了死锁。 -3. 如果进程-资源分配图中有环路,且涉及到的资源类有多个资源,此时系统未必会发生死锁。如果能在进程-资源分配图中找出一个 **既不阻塞又非独立的进程** ,该进程能够在有限的时间内归还占有的资源,也就是把边给消除掉了,重复此过程,直到能在有限的时间内 **消除所有的边** ,则不会发生死锁,否则会发生死锁。(消除边的过程类似于 **拓扑排序**) - -#### 死锁的解除 - -当死锁检测程序检测到存在死锁发生时,应设法让其解除,让系统从死锁状态中恢复过来,常用的解除死锁的方法有以下四种: - -1. **立即结束所有进程的执行,重新启动操作系统** :这种方法简单,但以前所在的工作全部作废,损失很大。 -2. **撤销涉及死锁的所有进程,解除死锁后继续运行** :这种方法能彻底打破**死锁的循环等待**条件,但将付出很大代价,例如有些进程可能已经计算了很长时间,由于被撤销而使产生的部分结果也被消除了,再重新执行时还要再次进行计算。 -3. **逐个撤销涉及死锁的进程,回收其资源直至死锁解除。** -4. **抢占资源** :从涉及死锁的一个或几个进程中抢占资源,把夺得的资源再分配给涉及死锁的进程直至死锁解除。 - -## 操作系统内存管理基础 - -### 内存管理介绍 +### 3.1 内存管理介绍 👨‍💻 **面试官**: **操作系统的内存管理主要是做什么?** diff --git "a/docs/high-availability/\347\201\276\345\244\207\350\256\276\350\256\241\345\222\214\345\274\202\345\234\260\345\244\232\346\264\273.md" "b/docs/high-availability/\347\201\276\345\244\207\350\256\276\350\256\241\345\222\214\345\274\202\345\234\260\345\244\232\346\264\273.md" new file mode 100644 index 00000000000..18756b69127 --- /dev/null +++ "b/docs/high-availability/\347\201\276\345\244\207\350\256\276\350\256\241\345\222\214\345\274\202\345\234\260\345\244\232\346\264\273.md" @@ -0,0 +1,14 @@ +# 灾备设计&异地多活 + +**灾备** = 容灾+备份。 + +- **备份** : 将系统所产生的所有重要数据多备份几份。 +- **容灾** : 在异地建立两个完全相同的系统。当某个地方的系统突然挂掉,整个应用系统可以切换到另一个,这样系统就可以正常提供服务了。 + +**异地多活** 描述的是将服务部署在异地并且服务同时对外提供服务。和传统的灾备设计的最主要区别在于“多活”,即所有站点都是同时在对外提供服务的。异地多活是为了应对突发状况比如火灾、地震等自然或者人为灾害。 + +相关阅读: + +- [搞懂异地多活,看这篇就够了](https://mp.weixin.qq.com/s/T6mMDdtTfBuIiEowCpqu6Q) +- [四步构建异地多活](https://mp.weixin.qq.com/s/hMD-IS__4JE5_nQhYPYSTg) +- [《从零开始学架构》— 28 | 业务高可用的保障:异地多活架构](http://gk.link/a/10pKZ) \ No newline at end of file diff --git "a/docs/high-availability/\350\266\205\346\227\266\345\222\214\351\207\215\350\257\225\346\234\272\345\210\266.md" "b/docs/high-availability/\350\266\205\346\227\266\345\222\214\351\207\215\350\257\225\346\234\272\345\210\266.md" new file mode 100644 index 00000000000..6fae8f9a79d --- /dev/null +++ "b/docs/high-availability/\350\266\205\346\227\266\345\222\214\351\207\215\350\257\225\346\234\272\345\210\266.md" @@ -0,0 +1,6 @@ + +# 超时&重试机制 + +**一旦用户的请求超过某个时间得不到响应就结束此次请求并抛出异常。** 如果不进行超时设置可能会导致请求响应速度慢,甚至导致请求堆积进而让系统无法在处理请求。 + +另外,重试的次数一般设为 3 次,再多次的重试没有好处,反而会加重服务器压力(部分场景使用失败重试机制会不太适合)。