Browse Source

init commit

usuiforhe 4 years ago
parent
commit
30d0e17d0e

BIN
img/UE_Asset1.png


BIN
img/UE_Asset10.png


BIN
img/UE_Asset11.png


BIN
img/UE_Asset12.png


BIN
img/UE_Asset13.png


BIN
img/UE_Asset14.png


BIN
img/UE_Asset15.png


BIN
img/UE_Asset16.png


BIN
img/UE_Asset17.png


BIN
img/UE_Asset18.png


BIN
img/UE_Asset19.png


BIN
img/UE_Asset2.png


BIN
img/UE_Asset20.png


BIN
img/UE_Asset21.png


BIN
img/UE_Asset22.png


BIN
img/UE_Asset23.png


BIN
img/UE_Asset24.png


BIN
img/UE_Asset25.png


BIN
img/UE_Asset26.png


BIN
img/UE_Asset27.png


BIN
img/UE_Asset28.png


BIN
img/UE_Asset29.png


BIN
img/UE_Asset3.png


BIN
img/UE_Asset30.png


BIN
img/UE_Asset31.png


BIN
img/UE_Asset32.png


BIN
img/UE_Asset33.png


BIN
img/UE_Asset34.png


BIN
img/UE_Asset4.png


BIN
img/UE_Asset5.png


BIN
img/UE_Asset6.png


BIN
img/UE_Asset7.png


BIN
img/UE_Asset8.png


BIN
img/UE_Asset9.png


+ 36 - 0
typescript.md

@@ -0,0 +1,36 @@
+<!--
+ * @Author: lc
+ * @Date: 2021-09-02 09:34:30
+ * @LastEditTime: 2021-09-02 10:00:44
+ * @LastEditors: lc
+ * @Description: TS语言的学习
+ * @FilePath: d:\Projects\markdown\typescript.md
+-->
+- vscode 插件
+	- ESLint
+	- Javascript ans typescript Nightly
+	- koroFileHeader
+	- vscode-icons
+	- Todo Tree
+	- Material Theme
+	- GitLens
+	- Makrdown All in One
+
+- TS学习
+	- 基础类型 https://www.tslang.cn/docs/handbook/basic-types.html
+	- 变量声明 https://www.tslang.cn/docs/handbook/variable-declarations.html
+	- 接口 https://www.tslang.cn/docs/handbook/interfaces.html
+	- 类 https://www.tslang.cn/docs/handbook/classes.html
+	- 函数 https://www.tslang.cn/docs/handbook/functions.html
+	- 泛型  https://www.tslang.cn/docs/handbook/generics.html
+	- 枚举 https://www.tslang.cn/docs/handbook/enums.html
+	- 类型推论 https://www.tslang.cn/docs/handbook/type-inference.html
+	- 类型兼容 https://www.tslang.cn/docs/handbook/type-compatibility.html
+	- 高级类型 https://www.tslang.cn/docs/handbook/advanced-types.html
+	- 迭代器和生成器 https://www.tslang.cn/docs/handbook/iterators-and-generators.html
+	- 模块 https://www.tslang.cn/docs/handbook/modules.html
+	- 命名空间 https://www.tslang.cn/docs/handbook/namespaces.html
+	- 命名空间和模块 https://www.tslang.cn/docs/handbook/namespaces-and-modules.html
+
+- Git介绍:https://www.cnblogs.com/syp172654682/p/7689328.html
+

+ 486 - 0
操作系统.md

@@ -0,0 +1,486 @@
+小牛和同学开发的八股文网站http://interviewtop.top 持续更新BAT面试题。
+
+
+![](interviewtop.jpg)
+
+
+## 什么是操作系统?请简要概述一下
+
+操作系统是管理计算机硬件和软件资源的计算机程序,提供一个计算机用户与计算机硬件系统之间的接口。
+
+向上对用户程序提供接口,向下接管硬件资源。
+
+操作系统本质上也是一个软件,作为最接近硬件的系统软件,负责处理器管理、存储器管理、设备管理、文件管理和提供用户接口。
+
+## 操作系统有哪些分类?
+
+操作系统常规可分为批处理操作系统、分时操作系统、实时操作系统。
+
+若一个操作系统兼顾批操作和分时的功能,则称该系统为通用操作系统。
+
+常见的通用操作系统有:Windows、Linux、MacOS等。
+
+## 什么是内核态和用户态?
+
+为了避免操作系统和关键数据被用户程序破坏,将处理器的执行状态分为内核态和用户态。
+
+内核态是操作系统管理程序执行时所处的状态,能够执行包含特权指令在内的一切指令,能够访问系统内所有的存储空间。
+
+用户态是用户程序执行时处理器所处的状态,不能执行特权指令,只能访问用户地址空间。
+
+用户程序运行在用户态,操作系统内核运行在内核态。
+
+## 如何实现内核态和用户态的切换?
+
+处理器从用户态切换到内核态的方法有三种:系统调用、异常和外部中断。
+
+1. 系统调用是操作系统的最小功能单位,是操作系统提供的用户接口,系统调用本身是一种软中断。
+
+2. 异常,也叫做内中断,是由错误引起的,如文件损坏、缺页故障等。
+
+3. 外部中断,是通过两根信号线来通知处理器外设的状态变化,是硬中断。
+
+## 并发和并行的区别
+
+1. 并发(concurrency):指宏观上看起来两个程序在同时运行,比如说在单核cpu上的多任务。但是从微观上看两个程序的指令是交织着运行的,指令之间交错执行,在单个周期内只运行了一个指令。这种并发并不能提高计算机的性能,只能提高效率(如降低某个进程的相应时间)。
+
+2. 并行(parallelism):指严格物理意义上的同时运行,比如多核cpu,两个程序分别运行在两个核上,两者之间互不影响,单个周期内每个程序都运行了自己的指令,也就是运行了两条指令。这样说来并行的确提高了计算机的效率。所以现在的cpu都是往多核方面发展。
+
+## 什么是进程?
+
+进程是操作系统中最重要的抽象概念之一,是资源分配的基本单位,是独立运行的基本单位。
+
+进程的经典定义就是一个执行中程序的实例。系统中的每个程序都运行在某个进程的上下文(context)中。
+
+上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。
+
+进程一般由以下的部分组成:
+
+1. 进程控制块PCB,是进程存在的唯一标志,包含进程标识符PID,进程当前状态,程序和数据地址,进程优先级、CPU现场保护区(用于进程切换),占有的资源清单等。
+
+2. 程序段
+
+3. 数据段
+
+## 进程的基本操作
+
+以Unix系统举例:
+
+1. 进程的创建:fork()。新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,这就意味着当父进程调用 fork 时,子进程可以读写父进程中打开的任何文件。父进程和新创建的子进程之间最大的区别在于它们有不同的 PID。fork函数是有趣的(也常常令人迷惑), 因为它只被调用一次,却会返回两次:一次是在调用进程(父进程)中,一次是在新创建的子进程中。在父进程中,fork 返回子进程的 PID。在子进程中,fork 返回 0。因为子进程的 PID 总是为非零,返回值就提供一个明 确的方法来分辨程序是在父进程还是在子进程中执行。
+
+    ```c
+    pid_t fork(void);
+    ```
+
+2. 回收子进程:当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除。相反,进程被保持在一种已终止的状态中,直到被它的父进程回收(reaped)。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程。一个进程可以通过调用 waitpid 函数来等待它的子进程终止或者停止。
+
+    ```c
+    pid_t waitpid(pid_t pid, int *statusp, int options);
+    ```
+
+3. 加载并运行程序:execve 函数在当前进程的上下文中加载并运行一个新程序。
+
+    ```c
+    int execve(const char *filename, const char *argv[], const char *envp[]);
+    ```
+
+4. 进程终止:
+
+    ```c
+    void exit(int status);
+    ```
+
+## 简述进程间通信方法
+
+每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。
+
+不同进程间的通信本质:进程之间可以看到一份公共资源;而提供这份资源的形式或者提供者不同,造成了通信方式不同。
+
+进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket。
+
+## 进程如何通过管道进行通信
+
+管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
+
+1. 其本质是一个伪文件(实为内核缓冲区)
+
+2. 由两个文件描述符引用,一个表示读端,一个表示写端。
+
+3. 规定数据从管道的写端流入管道,从读端流出。
+
+管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区实现。
+
+管道的局限性:
+
+1. 数据自己读不能自己写。
+
+2. 数据一旦被读走,便不在管道中存在,不可反复读取。
+
+3. 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
+
+4. 只能在有公共祖先的进程间使用管道。
+
+## 进程如何通过共享内存通信?
+
+它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。
+
+特点:
+
+1. 共享内存是最快的一种IPC,因为进程是直接对内存进行操作来实现通信,避免了数据在用户空间和内核空间来回拷贝。
+
+2. 因为多个进程可以同时操作,所以需要进行同步处理。
+
+3. 信号量和共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
+
+## 什么是信号
+
+一个信号就是一条小消息,它通知进程系统中发生了一个某种类型的事件。 Linux 系统上支持的30 种不同类型的信号。 每种信号类型都对应于某种系统事件。低层的硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程而言是不可见的。信号提供了一种机制,通知用户进程发生了这些异常。
+
+1. 发送信号:内核通过更新目的进程上下文中的某个状态,发送(递送)一个信号给目的进程。发送信号可以有如下两种原因:
+
+    + 内核检测到一个系统事件,比如除零错误或者子进程终止。
+
+    + —个进程调用了kill 函数, 显式地要求内核发送一个信号给目的进程。一个进程可以发送信号给它自己。
+
+2. 接收信号:当目的进程被内核强迫以某种方式对信号的发送做出反应时,它就接收了信号。进程可以忽略这个信号,终止或者通过执行一个称为信号处理程序(signal handler)的用户层函数捕获这个信号。
+
+## 如何编写正确且安全的信号处理函数
+
+1. 处理程序要尽可能简单。 避免麻烦的最好方法是保持处理程序尽可能小和简单。例如,处理程序可能只是简单地设置全局标志并立即返回;所有与接收信号相关的处理都由主程序执行,它周期性地检查(并重置)这个标志。
+
+2. 在处理程序中只调用异步信号安全的函数。 所谓异步信号安全的函数(或简称安全的函数)能够被信号处理程序安全地调用,原因有二:要么它是可重入的(例如只访问局部变量),要么它不能被信号处理程序中断。
+
+3. 保存和恢复errno。 许多Linux 异步信号安全的函数都会在出错返回时设置errno在处理程序中调用这样的函数可能会干扰主程序中其他依赖于分。解决方法是在进人处理程序时把errno 保存在一个局部变量中,在处理程序返回前恢复它。注意,只有在处理程序要返回时才有此必要。如果处理程序调用_exit终止该进程,那么就不需要这样做了。
+
+4. 阻塞所有的信号,保护对共享全局数据结构的访问。 如果处理程序和主程序或其他处理程序共享一个全局数据结构,那么在访问(读或者写)该数据结构时,你的处理程序和主程序应该暂时阻塞所有的信号。这条规则的原因是从主程序访问一个数据结构d 通常需要一系列的指令,如果指令序列被访问d 的处理程序中断,那么处理程序可能会发现d 的状态不一致,得到不可预知的结果。在访问d 时暂时阻塞信号保证了处理程序不会中断该指令序列。
+
+5. 用volatile 声明全局变量。 考虑一个处理程序和一个main 函数,它们共享一个全局变量g 。处理程序更新g,main 周期性地读g, 对于一个优化编译器而言,main 中g的值看上去从来没有变化过,因此使用缓存在寄存器中g 的副本来满足对g 的每次引用是很安全的。如果这样,main 函数可能永远都无法看到处理程序更新过的值。可以用volatile 类型限定符来定义一个变量,告诉编译器不要缓存这个变量。例如:volatile 限定符强迫编译器毎次在代码中引用g时,都要从内存中读取g的值。一般来说,和其他所有共享数据结构一样,应该暂时阻塞信号,保护每次对全局变量的访问。
+
+    ```c
+    volatile int g;
+    ```
+
+6. 用sig_atomic_t声明标志。在常见的处理程序设计中,处理程序会写全局标志来记录收到了信号。主程序周期性地读这个标志,响应信号,再清除该标志。对于通过这种方式来共享的标志,C 提供一种整型数据类型sig_atomic_t对它的读和写保证会是原子的(不可中断的)。
+
+7. 信号的一个与直觉不符的方面是未处理的信号是不排队的。因为 pending 位向量中每种类型的信号只对应有一位,所以每种类型最多只能有一个未处理的信号。关键思想是如果存在一个未处理的信号就表明至少有一个信号到达了。
+
+## 进程调度的时机
+
+1. 当前运行的进程运行结束。
+
+2. 当前运行的进程由于某种原因阻塞。
+
+3. 执行完系统调用等系统程序后返回用户进程。
+
+4. 在使用抢占调度的系统中,具有更高优先级的进程就绪时。
+
+5. 分时系统中,分给当前进程的时间片用完。
+
+## 不能进行进程调度的情况
+
+1. 在中断处理程序执行时。
+
+2. 在操作系统的内核程序临界区内。
+
+3. 其它需要完全屏蔽中断的原子操作过程中。
+
+## 进程的调度策略
+
+1. 先到先服务调度算法
+
+2. 短作业优先调度算法
+
+3. 优先级调度算法
+
+4. 时间片轮转调度算法
+
+5. 高响应比优先调度算法
+
+6. 多级队列调度算法
+
+7. 多级反馈队列调度算法
+
+## 进程调度策略的基本设计指标
+
+1. CPU利用率
+
+2. 系统吞吐率,即单位时间内CPU完成的作业的数量。
+
+3. 响应时间。
+
+4. 周转时间。是指作业从提交到完成的时间间隔。从每个作业的角度看,完成每个作业的时间也是很关键
+
+    + 平均周转时间
+
+    + 带权周转时间
+
+    + 平均带权周转时间
+
+## 进程的状态与状态转换
+
+进程在运行时有三种基本状态:就绪态、运行态和阻塞态。
+
+1. 运行(running)态:进程占有处理器正在运行的状态。进程已获得CPU,其程序正在执行。在单处理机系统中,只有一个进程处于执行状态; 在多处理机系统中,则有多个进程处于执行状态。
+
+2.就绪(ready)态:进程具备运行条件,等待系统分配处理器以便运行的状态。  当进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行,进程这时的状态称为就绪状态。在一个系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。
+
+3.阻塞(wait)态:又称等待态或睡眠态,指进程不具备运行条件,正在等待某个时间完成的状态。
+
+各状态之间的转换:
+
+1. 就绪→执行  处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成执行状态。
+
+2. 执行→就绪  处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。
+
+3. 执行→阻塞  正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。
+
+4. 阻塞→就绪  处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
+
+## 什么是孤儿进程?僵尸进程?
+
+1. 孤儿进程: 父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被init进程(1号进程)所收养,并由init进程对他们完成状态收集工作。
+
+2。 僵尸进程: 进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait 获waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的这些进程是僵尸进程。
+
+
+## 什么是线程?
+
+1. 是进程划分的任务,是一个进程内可调度的实体,是CPU调度的基本单位,用于保证程序的实时性,实现进程内部的并发。
+
+2. 线程是操作系统可识别的最小执行和调度单位。每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。
+
+3. 每个线程完成不同的任务,但是属于同一个进程的不同线程之间共享同一地址空间(也就是同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。
+
+## 为什么需要线程?
+
+线程产生的原因:进程可以使多个程序能并发执行,以提高资源的利用率和系统的吞吐量;但是其具有一些缺点:
+
+1. 进程在同一时刻只能做一个任务,很多时候不能充分利用CPU资源。
+
+2. 进程在执行的过程中如果发生阻塞,整个进程就会挂起,即使进程中其它任务不依赖于等待的资源,进程仍会被阻塞。
+
+引入线程就是为了解决以上进程的不足,线程具有以下的优点:
+
+1. 从资源上来讲,开辟一个线程所需要的资源要远小于一个进程。
+
+2. 从切换效率上来讲,运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需时间也远远小于进程间切换所需要的时间(这种时间的差异主要由于缓存的大量未命中导致)。
+
+3. 从通信机制上来讲,线程间方便的通信机制。对不同进程来说,它们具有独立的地址空间,要进行数据的传递只能通过进程间通信的方式进行。线程则不然,属于同一个进程的不同线程之间共享同一地址空间,所以一个线程的数据可以被其它线程感知,线程间可以直接读写进程数据段(如全局变量)来进行通信(需要一些同步措施)。
+
+## 简述线程和进程的区别和联系
+
+1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。
+
+2. 进程在执行过程中拥有独立的地址空间,而多个线程共享进程的地址空间。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。)
+
+3. 进程是资源分配的最小单位,线程是CPU调度的最小单位。
+
+4. 通信:由于同一进程中的多个线程具有相同的地址空间,使它们之间的同步和通信的实现,也变得比较容易。进程间通信`IPC`,线程间可以直接读写进程数据段(如全局变量)来进行通信(需要一些同步方法,以保证数据的一致性)。
+
+5. 进程编程调试简单可靠性高,但是创建销毁开销大;线程正相反,开销小,切换速度快,但是编程调试相对复杂。
+
+6. 进程间不会相互影响;一个进程内某个线程挂掉将导致整个进程挂掉。
+
+7. 进程适应于多核、多机分布;线程适用于多核。
+
+## 进程和线程的基本API
+
+进程API以Unix系统为例,线程相关的API属于Posix线程(Pthreads)标准接口。
+
+| 进程原语  |       线程原语        |             描述             |
+| :-------: | :-------------------: | :--------------------------: |
+|  `fork`   |   `pthread_create`    |        创建新的控制流        |
+|  `exit`   |    `pthread_exit`     |     从现有的控制流中退出     |
+| `waitpid` |    `pthread_join`     |    从控制流中得到退出状态    |
+| `atexit`  | `pthread_cancel_push` | 注册在退出控制流时调用的函数 |
+| `getpid`  |    `pthread_self`     |        获取控制流的ID        |
+|  `abort`  |   `pthread_cancel`    |    请求控制流的非正常退出    |
+
+## 多线程模型
+
+1. 多对一模型。将多个用户级线程映射到一个内核级线程上。该模型下,线程在用户空间进行管理,效率较高。缺点就是一个线程阻塞,整个进程内的所有线程都会阻塞。几乎没有系统继续使用这个模型。
+
+2. 一对一模型。将内核线程与用户线程一一对应。优点是一个线程阻塞时,不会影响到其它线程的执行。该模型具有更好的并发性。缺点是内核线程数量一般有上限,会限制用户线程的数量。更多的内核线程数目也给线程切换带来额外的负担。linux和Windows操作系统家族都是使用一对一模型。
+
+3. 多对多模型。将多个用户级线程映射到多个内核级线程上。结合了多对一模型和一对一模型的特点。
+
+<!-- ## 线程间通信的方式
+
+1. 临界区:通过多线程的串行化来访问公共资源,速度快,适合控制数据访问。
+
+2. 互斥量`Synchronized/Lock`:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
+
+3. 信号量`Semphare`:为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。
+
+4. 事件(信号),Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作 -->
+
+## 进程同步的方法
+
+操作系统中,进程是具有不同的地址空间的,两个进程是不能感知到对方的存在的。有时候,需要多个进程来协同完成一些任务。
+当多个进程需要对同一个内核资源进行操作时,这些进程便是竞争的关系,操作系统必须协调各个进程对资源的占用,进程的互斥是解决进程间竞争关系的方法。 进程互斥指若干个进程要使用同一共享资源时,任何时刻最多允许一个进程去使用,其他要使用该资源的进程必须等待,直到占有资源的进程释放该资源。
+当多个进程协同完成一些任务时,不同进程的执行进度不一致,这便产生了进程的同步问题。需要操作系统干预,在特定的同步点对所有进程进行同步,这种协作进程之间相互等待对方消息或信号的协调关系称为进程同步。进程互斥本质上也是一种进程同步。
+进程的同步方法:
+
+1. 互斥锁
+
+2. 读写锁
+
+3. 条件变量
+
+4. 记录锁(record locking)
+
+5. 信号量
+
+6. 屏障(barrier)
+
+## 线程同步的方法
+
+操作系统中,属于同一进程的线程之间具有相同的地址空间,线程之间共享数据变得简单高效。遇到竞争的线程同时修改同一数据或是协作的线程设置同步点的问题时,需要使用一些线程同步的方法来解决这些问题。
+
+线程同步的方法:
+
+1. 互斥锁
+
+2. 读写锁
+
+3. 条件变量
+
+4. 信号量
+
+5. 自旋锁
+
+6. 屏障(barrier)
+
+## 进程同步与线程同步有什么区别
+
+进程之间地址空间不同,不能感知对方的存在,同步时需要将锁放在多进程共享的空间。而线程之间共享同一地址空间,同步时把锁放在所属的同一进程空间即可。
+
+## 死锁是怎样产生的?
+
+死锁是指两个或两个以上进程在执行过程中,因争夺资源而造成的下相互等待的现象。
+产生死锁需要满足下面四个条件:
+
+1. 互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源。
+
+2. 占有并等待条件:进程获得一定的资源后,又对其他资源发出请求,但是该资源可能被其他进程占有,此时请求阻塞,但该进程不会释放自己已经占有的资源。
+
+3. 非抢占条件:进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用后自己释放。
+
+4. 循环等待条件:进程发生死锁后,必然存在一个进程-资源之间的环形链。
+
+## 如何解决死锁问题?
+
+解决死锁的方法即破坏产生死锁的四个必要条件之一,主要方法如下:
+
+1. 资源一次性分配,这样就不会再有请求了(破坏请求条件)。
+
+2. 只要有一个资源得不到分配,也不给这个进程分配其他的资源(破坏占有并等待条件)。
+
+3. 可抢占资源:即当进程新的资源未得到满足时,释放已占有的资源,从而破坏不可抢占的条件。
+
+4. 资源有序分配法:系统给每类资源赋予一个序号,每个进程按编号递增的请求资源,释放则相反,从而破坏环路等待的条件
+
+## 什么是虚拟地址,什么是物理地址?
+
+地址空间是一个非负整数地址的有序集合。
+
+在一个带虚拟内存的系统中,CPU 从一个有N=pow(2,n)个地址的地址空间中生成虚拟地址,这个地址空间称为虚拟地址空间(virtual address space),现代系统通常支持 32 位或者 64 位虚拟地址空间。
+
+一个系统还有一个物理地址空间(physical address space),对应于系统中物理内存的M 个字节。
+
+地址空间的概念是很重要的,因为它清楚地区分了数据对象(字节)和它们的属性(地址)。
+
+ 一旦认识到了这种区别,那么我们就可以将其推广,允许每个数据对象有多个独立的地址,其中每个地址都选自一个不同的地址空间。这就是虚拟内存的基本思想。
+ 
+ 主存中的每字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。
+
+## 什么是虚拟内存?
+
+为了更加有效地管理内存并且少出错,现代系统提供了一种对主存的抽象概念,叫做虚拟内存(VM)。虚拟内存是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互,它为每个进程提供了一个大的、一致的和私有的地址空间。通过一个很清晰的机制,虚拟内存提供了三个重要的能力:
+
+1. 它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,它高效地使用了主存。
+
+2. 它为每个进程提供了一致的地址空间,从而简化了内存管理。
+
+3. 它保护了每个进程的地址空间不被其他进程破坏。
+
+## 为什么要引入虚拟内存?
+
+1. 虚拟内存作为缓存的工具
+
+    + 虚拟内存被组织为一个由存放在磁盘上的N个连续的字节大小的单元组成的数组。
+    + 虚拟内存利用DRAM缓存来自通常更大的虚拟地址空间的页面。
+
+2. 虚拟内存作为内存管理的工具。操作系统为每个进程提供了一个独立的页表,也就是独立的虚拟地址空间。多个虚拟页面可以映射到同一个物理页面上。
+
+    + **简化链接:** 独立的地址空间允许每个进程的内存映像使用相同的基本格式,而不管代码和数据实际存放在物理内存的何处。
+
+      + 例如:一个给定的`linux`系统上的每个进程都是用类似的内存格式,对于64为地址空间,代码段总是从虚拟地址)`0x400000`开始,数据段,代码段,栈,堆等等。
+
+    + **简化加载:** 虚拟内存还使得容易向内存中加载可执行文件和共享对象文件。要把目标文件中.text和.data节加载到一个新创建的进程中,Linux加载器为代码和数据段分配虚拟页VP,把他们**标记为无效(未被缓存)** ,将页表条目指向目标文件的起始位置。
+
+      + **加载器从不在磁盘到内存实际复制任何数据,在每个页初次被引用时,虚拟内存系统会按照需要自动的调入数据页。**
+
+    + **简化共享:** 独立地址空间为OS提供了一个管理用户进程和操作系统自身之间共享的一致机制。
+        
+        + 一般:每个进程有各自私有的代码,数据,堆栈,是不和其他进程共享的,**这样OS创建页表,将虚拟页映射到不连续的物理页面。**
+        
+        + 某些情况下,需要进程来共享代码和数据。例如每个进程调用相同的操作系统内核代码,或者C标准库函数。**OS会把不同进程中适当的虚拟页面映射到相同的物理页面。**
+
+    + **简化内存分配:** 虚拟内存向用户提供一个简单的分配额外内存的机制。当一个运行在用户进程中的程序要求额外的堆空间时(如`malloc`),OS分配一个适当k大小个连续的虚拟内存页面,并且将他们映射到物理内存中任意位置的k个任意物理页面,**因此操作系统没有必要分配k个连续的物理内存页面,页面可以随机的分散在物理内存中**。
+
++ 虚拟内存作为内存保护的工具。不应该允许一个用户进程修改它的只读段,也不允许它修改任何内核代码和数据结构,不允许读写其他进程的私有内存,不允许修改任何与其他进程共享的虚拟页面。每次CPU生成一个地址时,`MMU`会读一个`PTE`,通过在`PTE`上添加一些额外的许可位来控制对一个虚拟页面内容的访问十分简单。
+
+## 常见的页面置换算法
+
+当访问一个内存中不存在的页,并且内存已满,则需要从内存中调出一个页或将数据送至磁盘对换区,替换一个页,这种现象叫做缺页置换。当前操作系统最常采用的缺页置换算法如下:
+
++ 先进先出(FIFO)算法:
+
+  + 思路:置换最先调入内存的页面,即置换在内存中驻留时间最久的页面。
+
+  + 实现:按照进入内存的先后次序排列成队列,从队尾进入,从队首删除。
+
+  + 特点:实现简单;性能较差,调出的页面可能是经常访问的
+
++ 最近最少使用(`LRU`)算法:
+
+  + 思路: 置换最近一段时间以来最长时间未访问过的页面。根据程序局部性原理,刚被访问的页面,可能马上又要被访问;而较长时间内没有被访问的页面,可能最近不会被访问。
+
+  + 实现:缺页时,计算内存中每个逻辑页面的上一次访问时间,选择上一次使用到当前时间最长的页面
+
+  + 特点:可能达到最优的效果,维护这样的访问链表开销比较大
+
+当前最常采用的就是`LRU`算法。
+
++ 最不常用算法(`Least Frequently Used, LFU`)
+
+  + 思路:缺页时,置换访问次数最少的页面
+
+  + 实现:每个页面设置一个访问计数,访问页面时,访问计数加1,缺页时,置换计数最小的页面
+
+  + 特点:算法开销大,开始时频繁使用,但以后不使用的页面很难置换
+
+## 请说一下什么是写时复制?
+
++ 如果有多个进程要读取它们自己的那部门资源的副本,那么复制是不必要的。每个进程只要保存一个指向这个资源的指针就可以了。只要没有进程要去修改自己的“副本”,就存在着这样的幻觉:每个进程好像独占那个资源。从而就避免了复制带来的负担。如果一个进程要修改自己的那份资源“副本”,那么就会复制那份资源,并把复制的那份提供给进程。不过其中的复制对进程来说是透明的。这个进程就可以修改复制后的资源了,同时其他的进程仍然共享那份没有修改过的资源。所以这就是名称的由来:在写入时进行复制。
+
++ 写时复制的主要好处在于:如果进程从来就不需要修改资源,则不需要进行复制。惰性算法的好处就在于它们尽量推迟代价高昂的操作,直到必要的时刻才会去执行。
+
++ 在使用虚拟内存的情况下,写时复制(Copy-On-Write)是以页为基础进行的。所以,只要进程不修改它全部的地址空间,那么就不必复制整个地址空间。在fork()调用结束后,父进程和子进程都相信它们有一个自己的地址空间,但实际上它们共享父进程的原始页,接下来这些页又可以被其他的父进程或子进程共享。
+
+## 实时操作系统的概念
+
+实时操作系统(Real-time operating system, RTOS),又称即时操作系统,它会按照排序运行、管理系统资源,并为开发应用程序提供一致的基础。 实时操作系统与一般的操作系统相比,最大的特色就是“实时性”,如果有一个任务需要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。这种特性保证了各个任务的及时执行。
+
+## 优先级反转是什么?如何解决
+
+由于多进程共享资源,具有最高优先权的进程被低优先级进程阻塞,反而使具有中优先级的进程先于高优先级的进程执行,导致系统的崩溃。这就是所谓的优先级反转(Priority Inversion)。其实,优先级反转是在高优级(假设为A)的任务要访问一个被低优先级任务(假设为C)占有的资源时,被阻塞.而此时又有优先级高于占有资源的任务(C)而低于被阻塞的任务(A)的优先级的任务(假设为B)时,于是,占有资源的任务就被挂起(占有的资源仍为它占有),因为占有资源的任务优先级很低,所以,它可能一直被另外的任务挂起.而它占有的资源也就一直不能释放,这样,引起任务A一直没办法执行.而比它优先低的任务却可以执行。
+
+目前解决优先级反转有许多种方法。其中普遍使用的有2种方法:一种被称作优先级继承(priority inheritance);另一种被称作优先级极限(priority ceilings)。
+
+1. 优先级继承(priority inheritance) 优先级继承是指将低优先级任务的优先级提升到等待它所占有的资源的最高优先级任务的优先级.当高优先级任务由于等待资源而被阻塞时,此时资源的拥有者的优先级将会自动被提升。
+
+2. 优先级天花板(priority ceilings)优先级天花板是指将申请某资源的任务的优先级提升到可能访问该资源的所有任务中最高优先级任务的优先级.(这个优先级称为该资源的优先级天花板)。
+
+![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)

+ 262 - 0
数据库.md

@@ -0,0 +1,262 @@
+小牛和同学开发的八股文网站http://interviewtop.top 持续更新BAT面试题。
+
+
+![](interviewtop.jpg)
+
+
+## 简述数据库三大范式
+第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。
+
+数据库第二范式:关系模式必须满足第一范式,并且所有非主属性都完全依赖于主码。注意,符合第二范式的关系模型可能还存在数据冗余、更新异常等问题。关系模型(学号,姓名,专业编号,专业名称)中,学号->姓名,而专业编号->专业名称,不满足数据库第二范式
+
+数据库第三范式:关系模型满足第二范式,所有非主属性对任何候选关键字都不存在传递依赖。即每个属性都跟主键有直接关系而不是间接关系。接着以学生表举例,对于关系模型(学号,姓名,年龄,性别,所在院校,院校地址,院校电话)院校地址,院校电话和学号不存在直接关系,因此不满足第三范式。
+
+
+## 简述MySQL的架构
+MySQL可以分为应用层,逻辑层,数据库引擎层,物理层。
+
+应用层:负责和客户端,响应客户端请求,建立连接,返回数据。
+
+逻辑层:包括SQK接口,解析器,优化器,Cache与buffer。
+
+数据库引擎层:有常见的MyISAM,InnoDB等等。
+
+物理层:负责文件存储,日志等等。
+
+## 简述执行SQL语言的过程
+1. 客户端首先通过连接器进行身份认证和权限相关
+2. 如果是执行查询语句的时候,会先查询缓存,但MySQL 8.0 版本后该步骤移除。
+3. 没有命中缓存的话,SQL 语句就会经过解析器,分析语句,包括语法检查等等。
+4. 通过优化器,将用户的SQL语句按照 MySQL 认为最优的方案去执行。
+5. 执行语句,并从存储引擎返回数据。
+
+## 简述MySQL的共享锁排它锁
+共享锁也称为读锁,相互不阻塞,多个客户在同一时刻可以同时读取同一个资源而不相互干扰。排他锁也称为写锁,会阻塞其他的写锁和读锁,确保在给定时间内只有一个用户能执行写入并防止其他用户读取正在写入的同一资源。
+
+## 简述MySQL中的按粒度的锁分类
+表级锁: 对当前操作的整张表加锁,实现简单,加锁快,但并发能力低。
+
+行锁: 锁住某一行,如果表存在索引,那么记录锁是锁在索引上的,如果表没有索引,那么 InnoDB 会创建一个隐藏的聚簇索引加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
+
+Gap 锁:也称为间隙锁: 锁定一个范围但不包括记录本身。其目的是为了防止同一事物的两次当前读出现幻读的情况。
+
+Next-key Lock: 行锁+gap锁。
+
+## 如何解决数据库死锁
+1. 预先检测到死锁的循环依赖,并立即返回一个错误。
+2. 当查询的时间达到锁等待超时的设定后放弃锁请求。
+
+## 简述乐观锁和悲观锁
+乐观锁:对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁,只有到数据提交的时候才通过一种机制来验证数据是否存在冲突。
+
+悲观锁:对于数据冲突保持一种悲观态度,在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的。
+
+## 简述InnoDB存储引擎
+InnoDB 是 MySQL 的默认事务型引擎,支持事务,表是基于聚簇索引建立的。支持表级锁和行级锁,支持外键,适合数据增删改查都频繁的情况。
+
+InnoDB 采用 MVCC 来支持高并发,并且实现了四个标准的隔离级别。其默认级别是 REPEATABLE READ,并通过间隙锁策略防止幻读,间隙锁使 InnoDB 不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定防止幻行的插入。
+
+## 简述MyISAM存储引擎
+MySQL5.1及之前,MyISAM 是默认存储引擎。MyISAM不支持事务,Myisam支持表级锁,不支持行级锁,表不支持外键,该存储引擎存有表的行数,count运算会更快。适合查询频繁,不适合对于增删改要求高的情况
+
+## 简述Memory存储引擎
+Memory存储引擎将所有数据都保存在内存,不需要磁盘 IO。支持哈希索引,因此查找速度极快。Memory 表使用表级锁,因此并发写入的性能较低。
+
+
+## 索引是什么?
+索引是存储引擎中用于快速找到记录的一种数据结构。在关系型数据库中,索引具体是一种对数据库中一列或多列的值进行排序的存储结构。
+
+## 为什么引入索引?
+为了提高数据查询的效率。索引对数据库查询良好的性能非常关键,当表中数据量越来越大,索引对性能的影响越重要。
+
+## Mysql有哪些常见索引类型?
+* 数据结构角度
+  > B-Tree索引
+  > 哈希索引
+  > R-Tree索引
+  > 全文索引
+
+* 物理存储角度
+  > 主键索引(聚簇索引):叶子节点存的是整行的数据
+  > 非主键索引(二级索引):叶子节点存的主键的值
+
+## 简述B-Tree与B+树
+B-Tree 是一种自平衡的多叉树。每个节点都存储关键字值。其左子节点的关键字值小于该节点关键字值,且右子节点的关键字值大于或等于该节点关键字值。
+
+B+树也是是一种自平衡的多叉树。其基本定义与B树相同,不同点在于数据只出现在叶子节点,所有叶子节点增加了一个链指针,方便进行范围查询。
+
+B+树中间节点不存放数据,所以同样大小的磁盘页上可以容纳更多节点元素,访问叶子节点上关联的数据也具有更好的缓存命中率。并且数据顺序排列并且相连,所以便于区间查找和搜索。
+
+B树每一个节点都包含key和value,查询效率比B+树高。
+
+## 简述Hash索引
+哈希索引对于每一行数据计算一个哈希码,并将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。只有 Memory 引擎显式支持哈希索引。
+
+Hash索引不支持范围查询,无法用于排序,也不支持部分索引列匹配查找。
+
+## 简述自适应Hash索引
+InnoDB对于频繁使用的某些索引值,会在内存中基于 B-Tree 索引之上再创键一个哈希索引,这也被称为自适应Hash索引。
+
+## 简述聚集索引和稀疏索引
+聚集索引按每张表的主键构建一棵B+树,数据库中的每个搜索键值都有一个索引记录,每个数据页通过双向链表连接。表数据访问更快,但表更新代价高。
+
+稀疏索引不会为每个搜索关键字创建索引记录。搜索过程需要,我们首先按索引记录进行操作,并按顺序搜索,直到找到所需的数据为止。
+
+## 简述辅助索引与回表查询
+辅助索引是非聚集索引,叶子节点不包含记录的全部数据,包含了一个书签用来告诉InnoDB哪里可以找到与索引相对应的行数据。
+
+通过辅助索引查询,先通过书签查到聚集索引,再根据聚集索引查对应的值,需要两次,也称为回表查询。
+
+## 简述联合索引和最左匹配原则
+联合索引是指对表上的多个列的关键词进行索引。
+
+对于联合索引的查询,如果精确匹配联合索引的左边连续一列或者多列,则mysql会一直向右匹配直到遇到范围查询(>,<,between,like)就停止匹配。Mysql会对第一个索引字段数据进行排序,在第一个字段基础上,再对第二个字段排序。
+
+## 简述覆盖索引
+覆盖索引指一个索引包含或覆盖了所有需要查询的字段的值,不需要回表查询,即索引本身存了对应的值。
+
+## 为什么数据库不用红黑树用B+树
+红黑树的出度为 2,而 B Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B Tree 大非常多,IO次数很多,导致会比较慢,因此检索的次数也就更多。
+
+B+Tree 相比于 B-Tree 更适合外存索引,拥有更大的出度,IO次数较少,检索效率会更高。
+
+## 基于主键索引的查询和非主键索引的查询有什么区别?
+对于select * from 主键=XX,基于主键的普通查询仅查找主键这棵树,对于select * from 非主键=XX,基于非主键的查询有可能存在回表过程(回到主键索引树搜索的过程称为回表),因为非主键索引叶子节点仅存主键值,无整行全部信息。
+
+## 非主键索引的查询一定会回表吗?
+不一定,当查询语句的要求字段全部命中索引,不用回表查询。如select 主键 from 非主键=XX,此时非主键索引叶子节点即可拿到主键信息,不用回表。
+
+## 简述MySQL使用EXPLAIN 的关键字段
+explain关键字用于分析sql语句的执行情况,可以通过他进行sql语句的性能分析。
+
+type:表示连接类型,从好到差的类型排序为
+- system:系统表,数据已经加载到内存里。
+- const:常量连接,通过索引一次就找到。
+- eq_ref:唯一性索引扫描,返回所有匹配某个单独值的行。
+- ref:非主键非唯一索引等值扫描,const或eq_ref改为普通非唯一索引。
+- range:范围扫描,在索引上扫码特定范围内的值。 
+- index:索引树扫描,扫描索引上的全部数据。
+- all:全表扫描。
+
+key:显示MySQL实际决定使用的键。
+
+key_len:显示MySQL决定使用的键长度,长度越短越好
+
+Extra:额外信息
+- Using filesort:MySQL使用外部的索引排序,很慢需要优化。
+- Using temporary:使用了临时表保存中间结果,很慢需要优化。
+- Using index:使用了覆盖索引。
+- Using where:使用了where。
+
+## 简述MySQL优化流程
+1. 通过慢日志定位执行较慢的SQL语句
+2. 利用explain对这些关键字段进行分析
+3. 根据分析结果进行优化
+
+## 简述MySQL中的日志log
+redo log: 存储引擎级别的log(InnoDB有,MyISAM没有),该log关注于事务的恢复.在重启mysql服务的时候,根据redo log进行重做,从而使事务有持久性。
+
+undo log:是存储引擎级别的log(InnoDB有,MyISAM没有)保证数据的原子性,该log保存了事务发生之前的数据的一个版本,可以用于回滚,是MVCC的重要实现方法之一。
+
+bin log:数据库级别的log,关注恢复数据库的数据。
+
+
+## 简述事务
+事务内的语句要么全部执行成功,要么全部执行失败。
+
+事务满足如下几个特性:
+
+* 原子性(Atomicity):
+  
+  >一个事务中的所有操作要么全部完成,要么全部不完成。
+* 一致性(Consistency):
+  
+  >事务执行前后数据库的状态保存一致。
+* 隔离性(Isolation)
+  
+  >多个并发事务对数据库进行操作,事务间互不干扰。
+* 持久性(Durability)
+  
+  >事务执行完毕,对数据的修改是永久的,即使系统故障也不会丢失
+
+## 数据库中多个事务同时进行可能会出现什么问题?
+* 丢失修改
+* 脏读:当前事务可以查看到别的事务未提交的数据。
+* 不可重读:在同一事务中,使用相同的查询语句,同一数据资源莫名改变了。
+* 幻读:在同一事务中,使用相同的查询语句,莫名多出了一些之前不存在的数据,或莫名少了一些原先存在的数据。
+
+
+## SQL的事务隔离级别有哪些?
+* 读未提交:
+  
+  >一个事务还没提交,它做的变更就能被别的事务看到。
+* 读提交:
+  
+  >一个事务提交后,它做的变更才能被别的事务看到。
+* 可重复读:
+  
+  >一个事务执行过程中看到的数据总是和事务启动时看到的数据是一致的。在这个级别下事务未提交,做出的变更其它事务也看不到。
+* 串行化:
+  
+  >对于同一行记录进行读写会分别加读写锁,当发生读写锁冲突,后面执行的事务需等前面执行的事务完成才能继续执行。
+
+## 什么是MVCC?
+MVCC为多版本并发控制,即同一条记录在系统中存在多个版本。其存在目的是在保证数据一致性的前提下提供一种高并发的访问性能。对数据读写在不加读写锁的情况下实现互不干扰,从而实现数据库的隔离性,在事务隔离级别为读提交和可重复读中使用到。
+
+在InnoDB中,事务在开始前会向事务系统申请一个事务ID,该ID是按申请顺序严格递增的。每行数据具有多个版本,每次事务更新数据都会生成新的数据版本,而不会直接覆盖旧的数据版本。数据的行结构中包含多个信息字段。其中实现MVCC的主要涉及最近更改该行数据的事务ID(DB_TRX_ID)和可以找到历史数据版本的指针(DB_ROLL_PTR)。InnoDB在每个事务开启瞬间会为其构造一个记录当前已经开启但未提交的事务ID的视图数组。通过比较链表中的事务ID与该行数据的值与对应的DB_TRX_ID,并通过DB_ROLL_PTR找到历史数据的值以及对应的DB_TRX_ID来决定当前版本的数据是否应该被当前事务所见。最终实现在不加锁的情况下保证数据的一致性。
+
+## 读提交和可重复读都基于MVCC实现,有什么区别?
+在可重复读级别下,只会在事务开始前创建视图,事务中后续的查询共用一个视图。而读提交级别下每个语句执行前都会创建新的视图。因此对于可重复读,查询只能看到事务创建前就已经提交的数据。而对于读提交,查询能看到每个语句启动前已经提交的数据。
+
+## InnoDB如何保证事务的原子性、持久性和一致性?
+利用undo log保障原子性。该log保存了事务发生之前的数据的一个版本,可以用于回滚,从而保证事务原子性。
+
+利用redo log保证事务的持久性,该log关注于事务的恢复.在重启mysql服务的时候,根据redo log进行重做,从而使事务有持久性。
+
+利用undo log+redo log保障一致性。事务中的执行需要redo log,如果执行失败,需要undo log 回滚。
+
+## MySQL是如何保证主备一致的?
+MySQL通过binlog(二进制日志)实现主备一致。binlog记录了所有修改了数据库或可能修改数据库的语句,而不会记录select、show这种不会修改数据库的语句。在备份的过程中,主库A会有一个专门的线程将主库A的binlog发送给
+备库B进行备份。其中binlog有三种记录格式:
+1. statement:记录对数据库进行修改的语句本身,有可能会记录一些额外的相关信息。优点是binlog日志量少,IO压力小,性能较高。缺点是由于记录的信息相对较少,在不同库执行时由于上下文的环境不同可能导致主备不一致。
+2. row:记录对数据库做出修改的语句所影响到的数据行以及对这些行的修改。比如当修改涉及多行数据,会把涉及的每行数据都记录到binlog。优点是能够完全的还原或者复制日志被记录时的操作。缺点是日志量占用空间较大,IO压力大,性能消耗较大。
+3. mixed:混合使用上述两种模式,一般的语句使用statment方式进行保存,如果遇到一些特殊的函数,则使用row模式进行记录。MySQL自己会判断这条SQL语句是否可能引起主备不一致,如果有可能,就用row格式,
+否则就用statement格式。但是在生产环境中,一般会使用row模式。
+
+## redo log与binlog的区别?
+1. redo log是InnoDB引擎特有的,只记录该引擎中表的修改记录。binlog是MySQL的Server层实现的,会记录所有引擎对数据库的修改。
+2. redo log是物理日志,记录的是在具体某个数据页上做了什么修改;binlog是逻辑日志,记录的是这个语句的原始逻辑。
+3. redo log是循环写的,空间固定会用完;binlog是可以追加写入的,binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
+
+
+## crash-safe能力是什么?
+InnoDB通过redo log保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
+
+## WAL技术是什么?
+WAL的全称是Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。事务在提交写入磁盘前,会先写到redo log里面去。如果直接写入磁盘涉及磁盘的随机I/O访问,涉及磁盘随机I/O访问是非常消耗时间的一个过程,相比之下先写入redo log,后面再找合适的时机批量刷盘能提升性能。
+
+## 两阶段提交是什么?
+为了保证binlog和redo log两份日志的逻辑一致,最终保证恢复到主备数据库的数据是一致的,采用两阶段提交的机制。
+1. 执行器调用存储引擎接口,存储引擎将修改更新到内存中后,将修改操作记录redo log中,此时redo log处于prepare状态。
+2. 存储引擎告知执行器执行完毕,执行器生成这个操作对应的binlog,并把binlog写入磁盘。
+3. 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交commit状态,更新完成。
+
+
+## 只靠binlog可以支持数据库崩溃恢复吗?
+不可以。
+历史原因:
+1. InnoDB在作为MySQL的插件加入MySQL引擎家族之前,就已经是一个提供了崩溃恢复和事务支持的引擎了。InnoDB接入了MySQL后,发现既然binlog没有崩溃恢复的能力,那引入InnoDB原有的redo log来保证崩溃恢复能力。
+实现原因:
+2. binlog没有记录数据页修改的详细信息,不具备恢复数据页的能力。binlog记录着数据行的增删改,但是不记录事务对数据页的改动,这样细致的改动只记录在redo log中。当一个事务做增删改时,其实涉及到的数据页改动非常细致和复杂,包括行的字段改动以及行头部以及数据页头部的改动,甚至b+tree会因为插入一行而发生若干次页面分裂,那么事务也会把所有这些改动记录下来到redo log中。因为数据库系统进程crash时刻,磁盘上面页面镜像可以非常混乱,其中有些页面含有一些正在运行着的事务的改动,而一些已提交的事务的改动并没有刷上磁盘。事务恢复过程可以理解为是要把没有提交的事务的页面改动都去掉,并把已经提交的事务的页面改动都加上去这样一个过程。这些信息,都是binlog中没有记录的,只记录在了存储引擎的redo log中。
+3. 操作写入binlog可细分为write和fsync两个过程,write指的就是指把日志写入到文件系统的page cache,并没有把数据持久化到磁盘,fsync才是将数据持久化到磁盘的操作。通过参数设置sync_binlog为0的时候,表示每次提交事务都只write,不fsync。此时数据库崩溃可能导致部分提交的事务以及binlog日志由于没有持久化而丢失。
+
+## 简述MySQL主从复制
+MySQL提供主从复制功能,可以方便的实现数据的多处自动备份,不仅能增加数据库的安全性,还能进行读写分离,提升数据库负载性能。
+
+主从复制流程:
+1. 在事务完成之前,主库在binlog上记录这些改变,完成binlog写入过程后,主库通知存储引擎提交事物
+2. 从库将主库的binlog复制到对应的中继日志,即开辟一个I/O工作线程,I/O线程在主库上打开一个普通的连接,然后开始binlog dump process,将这些事件写入中继日志。从主库的binlog中读取事件,如果已经读到最新了,线程进入睡眠并等待ma主库产生新的事件。
+
+读写分离:即只在MySQL主库上写,只在MySQL从库上读,以减少数据库压力,提高性能。
+
+![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)

+ 154 - 0
数据结构与算法.md

@@ -0,0 +1,154 @@
+小牛和同学开发的八股文网站http://interviewtop.top 持续更新BAT面试题。
+
+
+![](interviewtop.jpg)
+
+
+## 简述数据结构栈
+栈是一种线性表,其限制只能在表尾进行插入或删除操作。由于该特性又称为后进先出的线性表。
+
+## 简述数据结构队列
+队列是一种先进先出的线性表。其限制只能在线性表的一端进行插入,而在另一端删除元素。
+
+## 简述二叉树
+二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成。
+
+
+## 简述满二叉树
+一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
+
+## 简述完全二叉树
+一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
+
+## 简述二叉树的前中后序遍历算法
+前序遍历:若二叉树为空树,则执行空逻辑,否则:
+1. 访问根节点
+2. 递归前序遍历左子树
+3. 递归前序遍历右子树
+
+中序遍历:若二叉树为空树,则执行空逻辑,否则:
+1. 递归中序遍历左子树
+2. 访问根节点
+3. 递归中序遍历右子树
+
+后序遍历:若二叉树为空树,则执行空逻辑,否则:
+1. 递归后序遍历左子树
+2. 递归后序遍历右子树
+3. 访问根节点
+
+## 简述解决Hash冲突的方法
+开放定址法:当发生哈希冲突时,如果哈希表未被装满,那么可以把这个值存放到冲突位置中的下一个空位置中去
+
+链地址法:对相同的哈希地址,设置一个单链表,单链表内放的都是哈希冲突元素。
+
+
+## 简述AVL树
+AVL树是一种改进版的搜索二叉树,其引入平衡因子(左子支高度与右子支高度之差的绝对值),通过旋转使其尽量保持平衡。
+任何一个节点的左子支高度与右子支高度之差的绝对值不超过1。
+
+## 简述红黑树
+红黑树本身是有2-3树发展而来,红黑树是保持黑平衡的二叉树,其查找会比AVL树慢一点,添加和删除元素会比AVL树快一点。增删改查统计性能上讲,红黑树更优。
+红黑树主要特征是在每个节点上增加一个属性表示节点颜色,可以红色或黑色。红黑树和 AVL 树类似,都是在进行插入和删除时通过旋转保持自身平衡,从而获得较高的查找性能。红黑树保证从根节点到叶尾的最长路径不超过最短路径的 2 倍,所以最差时间复杂度是 O(logn)。红黑树通过重新着色和左右旋转,更加高效地完成了插入和删除之后的自平衡调整。
+
+## 简述稳定排序和非稳定排序的区别
+稳定排序:排序前后两个相等的数相对位置不变,则算法稳定
+非稳定排序:排序前后两个相等的数相对位置发生了变化,则算法不稳定
+
+## 常见的稳定排序算法有哪些
+插入排序、冒泡排序、归并排序
+
+## 常见的不稳定排序算法有哪些
+希尔排序、直接选择排序、堆排序、快速排序
+
+## 简述插入排序
+插入排序:每一趟将一个待排序记录按其关键字的大小插入到已排好序的一组记录的适当位置上,直到所有待排序记录全部插入为止。
+
+排序算法稳定。时间复杂度 O(n²),空间复杂度 O(1)。
+
+## 简述希尔排序
+希尔排序:把记录按下标的一定增量分组,对每组进行直接插入排序,每次排序后减小增量,当增量减至 1 时排序完毕。
+
+排序算法不稳定。时间复杂度 O(nlogn),空间复杂度 O(1)。
+
+## 简述直接选择排序
+直接选择排序:每次在未排序序列中找到最小元素,和未排序序列的第一个元素交换位置,再在剩余未排序序列中重复该操作直到所有元素排序完毕。
+
+排序算法不稳定。时间复杂度 O(n²),空间复杂度 O(1)。
+
+## 简述堆排序
+堆排序:将待排序数组看作一个树状数组,建立一个二叉树堆。通过对这种数据结构进行每个元素的插入,完成排序工作。
+
+排序算法不稳定,时间复杂度 O(nlogn),空间复杂度 O(1)。
+
+## 简述冒泡排序
+冒泡排序:比较相邻的元素,如果第一个比第二个大就进行交换,对每一对相邻元素做同样的工作。
+
+排序算法稳定,时间复杂度 O(n²),空间复杂度 O(1)。
+
+## 简述快速排序
+快速排序:随机选择一个基准元素,通过一趟排序将要排序的数据分割成独立的两部分,一部分全部小于等于基准元素,一部分全部大于等于基准元素,再按此方法递归对这两部分数据进行快速排序。
+
+排序算法不稳定,时间复杂度 O(nlogn),空间复杂度 O(logn)。
+
+## 简述归并排序
+归并排序:将待排序序列分成两部分,然后对两部分分别递归排序,最后进行合并。
+排序算法稳定,时间复杂度都为 O(nlogn),空间复杂度为 O(n)。
+
+## 简述图
+图是由顶点集合和顶点之间的边集合组成的一种数据结构,分为有向图和无向图。
+
+有向图:边具有方向性
+
+无向图:边不具有方向性
+
+## 简述邻接矩阵
+用一个二维数组存放图顶点间关系的数据,这个二维数组称为邻接矩阵。
+对于无向图,邻接矩阵是对称矩阵
+
+## 简述邻接表
+邻接表是通过链表表示图连接关系的一种方。对于表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。
+
+## 简述图的深度优先搜索DFS
+将图中每个顶点的访问标志设为 FALSE,  之后搜索图中每个顶点,如果未被访问,则以该顶点V0为起始点出发,访问此顶点,然后依次从V0的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和V0有路径相通的顶点都被访问到。
+
+## 简述图的广度优先搜索
+从图中的某个顶点V0出发,并在访问此顶点之后依次访问V0的所有未被访问过的邻接点,之后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和V0有路径相通的顶点都被访问到。
+
+## 简述最小生成树和其对应的算法
+对于有 n 个结点的原图,生成原图的极小连通子图,其包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
+
+普里姆算法:取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w。在添加的顶点 w 和已经在生成树上的顶点v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有 n-1 个顶点为止。
+
+克鲁斯卡尔算法:先构造一个只含 n 个顶点的子图 SG,然后从权值最小的边开始,若它的添加不使 SG 中产生回路,则在 SG 上加上这条边,如此重复,直至加上 n-1 条边为止。
+
+## 简述最短路径算法
+Dijkstral算法为求解一个点到其余各点最小路径的方法,其算法为:
+
+假设我们求解的是顶点v到其余各个点的最短距离。n次循环至n个顶点全部遍历:
+1. 从权值数组中找到权值最小的,标记该边端点k
+2. 打印该路径及权值
+3. 如果存在经过顶点k到顶点i的边比v->i的权值小
+4. 更新权值数组及对应路径
+
+## 简述堆
+堆是一种完全二叉树形式,其可分为最大值堆和最小值堆。
+
+最大值堆:子节点均小于父节点,根节点是树中最大的节点。
+
+最小值堆:子节点均大于父节点,根节点是树中最小的节点。
+
+## 简述set
+Set是一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。
+
+![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)
+
+
+
+
+
+
+
+
+
+
+

+ 349 - 0
游戏开发精粹.md

@@ -0,0 +1,349 @@
+<!--
+ * @Author: lc
+ * @Date: 2021-09-02 10:10:43
+ * @LastEditTime: 2021-09-02 19:49:17
+ * @LastEditors: Please set LastEditors
+ * @Description: 游戏开发精粹
+ * @FilePath: \undefinedd:\Projects\markdown\游戏开发精粹.md
+-->
+# 游戏开发精粹1
+
+## 通用编程技术
+
+### 数据驱动
+
+逻辑部分定义游戏引擎的核心原则和算法,数据部分则提供其内容和行为的具体细节。
+游戏数据应该从文件载入,而不应该内嵌在代码中。
+
+1. 创建一个能按需解析文本文件的系统(能在不修改代码的情况下,进行各种数据尝试)
+2. 不要硬编码常量,把常量放进文本中,这样进行改变时就不用重新编译代码
+3. 假定任何东西都可能改变,通过配置文件的神奇作用可以对所有元素进行改变
+4. 将控制流写成脚本
+5. 避免重复数据,**绝对不要复制代码**,同理真实数据也是
+6. 开发工具来生成数据:游戏编辑器、关卡编辑器、脚本编辑器等
+
+> 你的游戏应该有能力应对规则、角色、种族、武器、关卡、控制方案以及对象的改变进行处理
+> 没有这种灵活性,改变的代价很大,并且每一个改变都需要有程序员参与——这完全是资源浪费
+
+### 面向对象编程
+
+1. 代码风格,比如**匈牙利标注法**,比如`私有整型变量`可以使用`m_iSomeVar`等
+
+| 类型 | 描述 |
+| --- | --- |
+| I | Interger |
+| F | Float |
+| D | Double(float) |
+| L | Long(nteger) |
+| C | Character |
+| B | Boolean |
+| Dw | Double wond |
+| W | Wond |
+| by 或 byte | Byte |
+| Sz | C-style(null-terminated) string |
+
+| 常用扩展名 | 描述 |
+| --- | --- |
+| Str | C++字符串对象 |
+| H | 句柄(自定义类型) |
+| V | 向量(自定义类) |
+| Rgb | RGB三元组(自定义结构或类型) |
+| P | 指针 |
+| R | 索引 |
+| U | 非符号 |
+| a或ary | 数组 |
+
+| 范围 | 描述 |
+| --- | --- | 
+| m_ | 成员变量 |
+| g_ | 全局变量 |
+| s_ | 静态变量 |
+
+> 常用的标注类型(对象通常不加任何前缀)
+> 不要再代码规范上做的太过,如果写代码要查规范严重影响效率
+> 类名的设计也应该易于阅读和维护,用功能作为类名前缀也是很有用的
+
+2. 类设计
+
+```cpp
+class Sample{
+    public:
+        Sample() {Clear();}
+        ~Sample() {Destroy();}
+
+        void Clear();   // 清空所有内部成员变量
+
+        bool Create();  // 动态创建对象,返回true/false表示创建成功或失败
+        void Update();  
+        void Destroy(); // 销毁对象
+}
+```
+
+动态分配内存的开销很大,所以只要可能都尽量避免
+无论表示什么对象,`Create()`和`Destroy()`成员函数真正完成创建和销毁对象的工作
+
+> `Create()`方法也可以返回错误代码类型,通常为符号整形
+> `Destroy()`方法我们希望既有自动清理的便利,又有按需创建和删除的灵活性,所以`Destroy()`函数能安全地多次调用,或者没有调用`Create()`的情况下也能安全调用
+
+
+3. 类层次结构设计
+
+通过继承来复用类,是面向对象编程的一大要素
+扩展类之间的合作只要是两种方法:继承和分层
+
+分层:从一个类派生出另一个类
+分层:一个对象作为成员包含于另一个对象,也称组合
+
+4. 设计模式
+
+- 单例模式:当大量的类或模块需要访问一个全局对象时,使用单例模式
+- Facade界面模式(外观模式):尽量避免子系统内部类对外的暴露
+- State(状态)模式:简单的使用对象来表示逻辑状态是一种更为优雅的面向对象解决方案
+  - 状态得到了更好的封装
+  - 状态可以在他们的基类中逻辑的共享代码,并且新的状态可以通过继承轻易地从现有状态中派生出来
+  - 减少了在离散的状态间进行剪切、粘贴代码所带来的典型问题
+  - 还可以用于AI系统,甚至可以用于在一个游戏中表示不同的游戏模式类型
+- Factory(工程)模式:用于组织对象的创建,他的一种形式定义为一种方法,允许抽象接口类指定何时创建具体的派生实现类。把对象分配聚集到一个位置的好处是尤其值得注意的
+  - 动态内存分配很昂贵,所以需要对分配进行仔细监控,在一个中央区域分配所有的对象是的分配监控变得更为容易
+  - 通常全体对象的公共初始化或创建方法应该在一个类层次结构中。如果把对象的分配放在一个中央区域,就使得基于对象的操作变得更加容易
+  - 工厂具有可扩展性,它允许新的对象从现有工厂中派生出来。通过传递新的类ID,就可以在运行时扩展新的的类而不用改变现有的基代码
+
+### 使用模板元编程的快速数学方法
+
+[浅谈元编程](https://zhuanlan.zhihu.com/p/87917516)
+
+1. 斐波那契额
+
+```cpp
+#include <iostream>
+
+using namespace std;
+
+template< unsigned N > 
+struct Fib{
+    enum{
+        // 递归定义 不常见但合法
+        Val = Fib<N-1>::Val + Fib<N-2>::Val
+    };
+};
+
+// 基于特殊情况的模板特化
+template<> struct Fib< 0 > {enum{Val = 0};};
+template<> struct Fib< 1 > {enum{Val = 1};};
+
+#define FibT( n ) Fib< n >::Val
+
+int main()
+{
+    std::cout << FibT(4) << std::endl;
+    return 0;
+}
+```
+
+> 模板函数并不是真正的函数——他是叫做Val的枚举整形,在**编译期**递归生成
+> 模板参数N用于指定函数的输入,以简化编辑。在默认情况下结构数据是公用的
+> 要终止递归,需要正确地处理结束条件。对于斐波那契数来说,终止条件就是0和1
+
+2. 阶乘
+
+```cpp
+template< unsigned N >
+struct Fact{
+    enum {
+        Val = N * Fact< N - 1 >::Val
+    };
+}
+
+template<> struct Fact<1>{
+    enum {Val = 1};
+};
+
+#define FactT( n ) Fact< n >::Val
+```
+
+3. 三角函数
+
+通过泰勒公式可以直接用上面类似的方法展开
+
+4. 模板与标准C++
+
+并非所有编译程序都能完全与C++标准兼容,尤其是在它们处理模板的时候更是如此
+模板是如此灵活而且强大,以至于为了让他们能够正确运行,编译程序的开发者需要付出大量辛苦的劳动
+
+
+### 资源和内存管理
+
+游戏通常有多种类型的数据库,每种数据库常常将各种不同的情况硬编码,以保证速度。
+游戏数据库的例子有:文件系统、纹理管理器、字体管理器、游戏角色管理器等。在此之上有大量基于不同游戏风格和内容的专用数据库
+
+建立在所有游戏中的一个资源数据库就是基本对象内存管理器。程序员调用new创建对象、不用时delete将资源返还给系统,但这种做法不适用共享资源
+
+游戏中的不同系统:开发控制台和GUI中的文本控制都要使用字体对象,但不能让每个系统都创建自己的本地字体对象拷贝,因为这样做的运行速度很慢并且会消耗大量内存。这个时候使用一个单例的字体管理器`FontManager`对象,使用它获得字体指针、在系统空闲时加载、将其高速缓存直到不再需要位置、全局范围有效,负责系统中的所有字体对象。但对于何时创建、如何释放等问题单纯的单例模式并不能给出完美的解决方案
+
+资源管理器的工作是按需创建资源,将它提供给需要使用的对象,并最终将其删除
+
+- 如果使用指针
+  - 不安全
+  - 指针可能变成空指针,系统的某部分可能让资源管理器删除某资源,而这会让正在使用这资源的所有指针无效
+  - 底层的数据组织方式不能改变,任何对缓冲区的再分配都会使得所有相应的指针无效
+
+> 因此用指针的方式提供资源,对安全、灵活的资源管理器来说并不是个好主意
+
+可以不必编写高智能、过于复杂的智能指针,而是加一层抽象,使用`句柄`,把重任抓交给管理器类
+
+> Win32文件系统中`CreateFile()`调用返回的HANDLE类型就是句柄
+> 句柄很适合单CPU寄存器,因为它能以集合方式搞笑存储,并且能作为参数传递给函数,易于检查有效性,而且不是直接指向资源所以改变底层的数据组织方式而不会产生无效句柄,句柄还可以直接存储
+
+```cpp
+#include <vector>
+#include <map>
+#include <casset>
+
+//...[平台相关的句柄类型]
+typedef LPDIRECTDRAWSURFACR7 OsHandle;
+
+struct tagTexture {};
+typedef Handle <tagTexture> HTexture;
+
+class TextureMgr
+{
+    //材质对象数据
+    struct Texture
+    {
+        typedef std::vector <OsHandle> HandleVec;
+
+        std::string m_Name; //重构造时使用
+        unsigned int m_Width;    //宽度
+        unsigned int m_Height;  //高度
+        HandleVec m_Handles;    //surface句柄
+
+        OsHandle GetOsHandle(unsigned int mip) const
+        {
+            assert(mip < m_Handles.size());
+            return { m_Handles[mip]};
+        }
+
+        bool Load (const std::string& name);
+        void Unload(void);
+    };
+
+    typedef HandleMgr <Texture, HTexture> HTextureMgr;
+
+    //数据库以名字为索引
+
+    //大小写不敏感的字符串比较谓词
+    struct istring_less
+    {
+        bool operator() (const std::string& l, const std::string& r) const {
+            return ( ::stricmp(1.c_str(), r.c_str()) < 0 );
+        }
+    };
+
+    typedef std::map <std::string, HTexture, istring_less> NameIndex;
+    typedef std::pair <NameIndex::iterator, bool> NameIndexInsertRc;
+
+    //私有数据
+    HTextureMgr m_Texture;
+    NameIndex m_NameIndex;
+
+public:
+
+    //生命期
+    TextureMgr(void) { /*...*/ }
+    ~TextureMgr(void);
+
+    //材质管理
+    HTexture GetTexture(const char* name);
+    void DeleteTexture(HTexture htex);
+
+    //材质查询
+    const std::string& GetName(HTexture htex) const{
+        return m_Texture.Dereference(htex)->m_Name;
+    }
+    int GetWidth(HTexture htex) const{
+        return m_Texture.Dereference(htex)->m_Width;
+    }
+    int GetHeight(HTexture htex) const{
+        return m_Texture.Dereference(htex)->m_Height;
+    }
+    OsHandle GetTexture(HTexture htex, unsigned int mip = 0) const{
+        return m_Texture.Dereference(htex)->GetOsHandle(mip);
+    }
+};
+
+TextureMgr::~TextureMgr(void)
+{
+    //在析构前释放索引剩余材质
+    NameIndex::iterator i, begin = m_NameIndex.begin(), end = m_NameIndex.end();
+    for(i = begin; i != end; ++i)
+    {
+        m_Textures.Dereference(i->second)->Unload();
+    }
+}
+
+HTexture TextureMgr::GetTexture(const char* name)
+{
+    //插入和查找
+    NameIndexInsertRc rc = m_NameIndex.insert(std::make_pair(name, HTexture()));
+    if(rc.second)
+    {
+        //这是一个新的插入操作
+        Texture* tex = m_Textures.Acquire(rc.first->second);
+        if(!tex->Load(rc.first->first))
+        {
+            DeleteTexture(rc.first->second);
+            rc.first->second=HTexture();
+        }
+    }
+    return rc.first->second;
+}
+
+void TextureMgr::DeleteTexture(HTexture htex)
+{
+    Texture* tex = m_Textures.Dereference(htex);
+    if(tex != 0)
+    {
+        //使用索引删除
+        m_NameIndex.erase(m_NameIndex.find(tex->m_Name));
+
+        //从数据库删除
+        tex->Unload();
+        m_Texture.Release(htex);
+    }
+}
+
+bool TextureMgr::Texture::Load(const std::string& name)
+{
+    m_Name = name;
+    //...[从文件系统载入材质,失败时返回false]
+    return true;
+}
+
+void TextureMgr::Texture::UnLoad(void)
+{
+    m_Name.erase();
+    //...[释放surfaces]
+    m_Handles.clear();
+}
+```
+
+### 快速数据载入技巧
+
+
+###  基于帧的内存分配
+
+
+### 简单快速的位数组
+
+
+### 在线游戏的网络协议
+
+
+### 最大限度的利用Assert
+
+
+### Stats:事实统计和游戏内调试
+
+
+### 实时的游戏内建刨析
+

+ 458 - 0
虚幻.md

@@ -0,0 +1,458 @@
+<!--
+ * @Author: lc
+ * @Date: 2021-09-03 21:41:18
+ * @LastEditTime: 2021-09-09 19:15:30
+ * @LastEditors: Please set LastEditors
+ * @Description: 虚幻学习
+ * @FilePath: D:\Projects\markdown\虚幻.md
+-->
+****
+# 编辑器
+
+虚幻提供许多编辑器
+
+1. 关卡编辑器
+2. 材质编辑器
+3. 蓝图编辑器
+4. 行为树编辑器
+5. Persona编辑器
+6. 粒子编辑器(程序一般不管,美术会用)
+7. UMG编辑器
+8. Matinee编辑器(影视编辑器 一般做CG)
+9. SoudCue编辑器
+10. Paper2D编辑器
+11. Paper2D Flipbook 编辑器
+12. Physics Asset工具编辑器
+13. 静态网格编辑器
+14. 媒体播放编辑器
+
+- 蓝图与C++
+  - 可视化蓝图脚本是对游戏实时逻辑和序列型的事件的理想制作手段。这个系统对策划、美术以及面向可视化编程的程序员是非常好的,它能够可视化的管理游戏内的物体,并且容易访问
+  - C++编程可以为更大体谅的任务工作,比如构建一个游戏体系,制作复杂的AI行为,或者新增引擎功能
+  - 大部分项目建议使用蓝图和C++进行混编。由C++构建逻辑框架,产品原型,由蓝图进行编辑扩展,提供给美术或者策划使用更加便捷
+
+- 虚幻支持的文件内容
+
+| 分类 | 类型 |
+| --- | --- |
+| 3D | fbx/obj |
+| 贴图 | png/jpge/bmp/tga/dds/exr/psd/hdr |
+| 声音 | wav |
+| 字体 | ttf/otf |
+| 视频 | mov/mp4/wmv/avi |
+
+> 常规使用的文件格式一般都支持
+
+![显示UE的资产](img/UE_Asset1.png)
+
+> 显示UE的资产
+
+![显示UE的资产](img/UE_Asset2.png)
+
+> UE推荐的文件分类文件夹命名
+
+![显示UE的资产](img/UE_Asset3.png)
+
+> 设置文件夹颜色,帮助分类
+
+![光源类型](img/UE_Asset4.png)
+
+## 光照
+
+- 光源
+  - 定向光源:无位置信息,有照射方向信息。所有的阴影照射均是平行效果。一般用来模拟阳光
+  - 点光源:如同真实世界中的灯泡,发光模式向各个方向均匀放光。随着距离出现衰减。一般可用来制作无照射方向的光源。例如:灯泡、烛光、篝火、炮弹爆炸光等
+  - 聚光源:以锥形空间为照射模型,朝着锥桶方向进行均匀照射,随着距离进行衰减。用作:手电筒、探照灯、汽车灯光等带有光束特殊点灯光
+  - 矩形光源:从一个定好宽度和高度的矩形平面向场景发出光线。可以用来模拟有用矩形面积的任意类型光源。例如:电视机、显示器、吊顶灯具或壁灯
+  - 天空光照:获取场景中一定距离以外的部分将他们作为光照应用于场景中。例如:远处的大气、云、群杀、天空光需要重建光照信息,才会进行捕捉计算
+
+- 光源的移动特性
+  - 可移动光源:产生完全动态的光源和阴影,消耗较大!运行时光源的大部分信息均可被修改(不会产生间接光照)
+  - 静态光源:指在运行时态不可以做任何改变或移动光源。静态光源效果只应用于光照贴图计算(预光照计算,即烘焙),只影响静态物体。静态光源消耗最低。一般应用于移动平添,用途非常有限(不与动态物体产生交互,所以不产生阴影)
+  - 固定光源:固定位置不变的光源,但是光源其他信息可以被变更(颜色、亮度等)。固定光源会产生阴影
+
+- 光照模式
+  - 直接光照:光源本身发射,直接照射到物体表面,体现的光照结果
+  - 间接光照:光源照射到物体表面,通过反射关系产生的光照则为间接光照
+
+> 间接光照是真实世界的光照重要组成部分
+
+- 构建光照(烘焙)
+  - 构建光照,在场景运行前期将场景中的光照关系进行提前计算,以防止在运行时态再计算光照关系,从而提升运行时的效率。构建光照的目的时**计算间接光照关系,产生间接光照缓存**
+  - 优点:在运行时获得更加真实的光照关系,免去了运行时计算光照的开销。虚幻中静态光和固定光都需要构建光照
+
+> **构建光照可以获得间接光照缓存(光的颜色贴图)**,这样运行时直接使用该贴图即可
+
+- 间接光照缓存
+  - 全局光照可以生成静态物体上的间接光照信息,但是场景中的动态对象,也需要一种接受间接光照的方法,这种方法就是**间接光照缓存**
+  - Unity中叫**光照探头**
+
+当你对场景进行构建的时候,会进行光照收集(光量子收集),收集这片区域每个点上的光亮强度,记录光的颜色。当移动物体在区域内移动的时候会把颜色贴到物体表面上从而产生照亮效果。
+
+当物体在场景中移动的时候,其实就是在光照贴图中找对应区域的颜色,然后把颜色贴到物体表面上去(给人感觉像是照亮罢了)
+
+![LIV](img/UE_Asset5.png)
+****
+- 重要光照体积(Lightmass Importance Volume,简称LIV)
+  - 可以将光照信息进行精密化计算
+  - 人物在场景中可以运行到的或者移动的地方是有限的,所以只需要关心人物可以到达的地方有高质量的光照,其他区域不要进行精密光照计算
+  - LIV可以帮助我们调整发射光量子范围,允许我们集中在需要详细间接光照的区域进行收集,而在重要体积外的地方我们获取低质量的间接光照即可
+
+![LIV](img/UE_Asset6.png)
+
+- 固定光源的光照缓存与灯光在一起,当光源删除时光照缓存删除
+- 静态光源不在运行时态产生光照信息,所以删除灯光后光照缓存依旧会存在
+
+## 蓝图基础
+
+### 简介
+
+蓝图时虚幻4中的一个可视化编程语言。具备传统编程语言的特点,同时具备简单、易用、易理解的特性
+
+- 种类
+  - 关卡蓝图
+  - 对象蓝图
+  - 动画蓝图
+  - 组件蓝图
+  - UMG蓝图
+  - GameMode蓝图(属于Actor对象蓝图,用于逻辑)
+  - GameState蓝图(属于Actor对象蓝图,用于状态)
+  - PlayerController蓝图(属于Actor对象蓝图,玩家控制)
+  - …
+
+- Actor(演员)
+  - 是一个基本类型的对象
+  - 虚幻中将场景的所有对象体均定义为是一个Actor,他是虚幻引擎的重要组成概念
+  - 虚幻中规定,能存在于场景中的物体需要从Actor进行扩展衍生
+  - 可以将Actor直接放到场景中,也可以在世界中生成Actor
+  - 虚幻中常见的Actor:摄像机、触发器、各种体积、静态网格、骨骼网格、Player Start
+
+![蓝图编辑器结构](img/UE_Asset7.png)
+
+> 在目标文件夹(一般是新建名为BluePrints的文件夹)中右键,新建蓝图即可  
+> 在编辑器视口中,按住鼠标右键拖动  
+> 在编辑器是口中,按住鼠标左键拖动可拖出节点或执行线  
+> 按住alt键再点击连接线,可删除连接线  
+> 安卓ctrl键再点击连接线,可重新选择目标节点  
+
+### 蓝图事件
+
+![名字](img/UE_Asset8.png)
+
+- 图中对应节点名称
+  1. **蓝图节点**(逻辑节点、函数节点)
+  2. 节点右边叫**逻辑输出阵脚**,左边叫**逻辑输入针脚**
+  3. **逻辑执行线**或**执行线**
+  4. **输出参数针脚**
+
+读取蓝图的时候,从白色执行线的起点开始,顺着执行线一个蓝图节点一个蓝图节点的读取即可
+
+![蓝图注释](img/UE_Asset14.png)
+
+> 事件类型
+
+![蓝图注释](img/UE_Asset15.png)
+
+创建的蓝图对象直接拖入到场景中并不会被渲染出来,需要添加网格组件才可以被被渲染出来
+
+- 网格
+  - 静态网格
+  - 骨骼网格
+  - ……
+
+![显示物体](img/UE_Asset16.png)
+
+1. 点击添加组件,在搜索框中输入 静态网格/static mesh
+2. 即为我们添加的静态网格组件
+3. 设置静态网格组件的材质
+
+> 关闭每帧执行的tick
+> Begin Play(事件开始运行的执行 **先于** Tick每帧调用)
+
+想创建一个朝固定方向移动的球体
+
+1. 创建蓝图
+2. 添加static mesh,设置static mesh贴图
+3. 在事件图表中添加`AddActorWorldOffest`节点,设置参数
+4. 将每帧执行的Tick连线到添加的节点上
+5. 将蓝图实例化(拖动)到场景中
+6. 运行场景
+
+![显示物体](img/UE_Asset17.png)
+
+![显示物体](img/UE_Asset18.png)
+
+### 蓝图日志
+
+日志可以提供运行时态的程序信息,并进行输出
+通过查看日志来查看对象状态,以反馈数据内容
+
+> 比如查看玩家具体速度数值、根据数值异常查找bug、判断逻辑是否执行等
+
+![蓝图日志节点](img/UE_Asset9.png)
+
+- In String:日志文本内容
+- Print to screen:是否输出到屏幕
+- Print to Log:是否输出到控制台(是否输出到日志文件)
+- Text Color:屏幕输出颜色
+- Duration:屏幕持续时间
+
+![打开日志窗口](img/UE_Asset10.png)
+
+> 打开日志窗口  
+> 在日志窗口中,可以右键清除日志
+
+### 蓝图注释
+
+1. 框选中你要注释的一个或多个蓝图节点
+2. 单击键盘字母**C**键
+3. 设置注释内容
+
+![蓝图注释](img/UE_Asset11.png)
+
+> 选中注释,点击键盘**del**即可删除
+
+1. 鼠标悬停在单个目标蓝图节点上的标题上
+2. 单击白色省略号按钮
+3. 设置文本即可完成单个节点的注释
+
+![蓝图注释](img/UE_Asset12.png)
+
+### Construction Scrip
+
+功能类似构造函数
+只要蓝图对象被构建、调用,则该函数就会执行
+
+当你在编辑器窗口拖动时,虚幻会认为你要重新构建该物体,所以物体的构造函数会被执行
+
+![蓝图注释](img/UE_Asset13.png)
+
+> 通过图片上的设置,可以关闭拖动时重新构建调用构造函数
+
+一般将调整信息逻辑放入到Construction图表中,只要在场景中物体被移动,Construction就会执行,物体会自己重新设置自己
+
+在编辑器模式中拖拽蓝图对象会调用Construction,但是在真正的独立发布软件上使用代码控制对象不会执行Construction,也即发布后的软件上只在最开始的时候调用一次Construction
+
+### 添加变量
+
+![添加和设置变量](img/UE_Asset19.png)
+
+1. 添加变量
+2. 设置变量名
+3. 设置变量名
+4. 设置变量类型
+5. 设置变量的默认值(需要点击工具栏左边的编译之后才可以进行设置)
+
+- 命名规则:有规则可以增加代码的可读性
+  - 帕斯卡命名法:单字之间不以空格、下划线连接欸,第一个单字首字母大写,后续单字首字母也大写(UE内部使用的就是帕斯卡命名法)
+  - 大驼峰命名法 = 帕斯卡命名法
+  - 小驼峰命名法
+
+选中目标变量,将其拖入到蓝图的视口中,可以选择使用get/set方法
+
+![拖入到蓝图中](img/UE_Asset20.png)
+
+### 基本运算
+
+直接在视口中敲击 +、-、*、/、%即可进行对应运算
+
+基本数据类型中,字节型、整型、浮点型均支持常规数学运算,即加减乘除
+
+执行顺序是*事件开始*->*设置Age的值*->*Print1*->*Print2*
+
+Age的初始值为5
+
+当执行到*设置Age的值*时,根据针脚指针往回推导,开始计算,最后设置Age的值为40
+
+当执行到*Print1*时,直接获得setAge的值为40
+
+当执行到*Print2*时,根据针脚指针往回推导,开始计算,最后获得输出值为215
+
+![计算顺序](img/UE_Asset21.png)
+
+> Print2时,Age的值已经被改变了 所以Print1和Print2的输出并不相同
+
+![取余](img/UE_Asset22.png)
+
+> 整型数据的自增自减
+
+### 流程控制
+
+![常见流程控制](img/UE_Asset23.png)
+
+> 常见流程控制
+
+![常见流程控制](img/UE_Asset24.png)
+
+> 判断是否成年
+
+# 蓝图
+
+## 流程控制
+
+- **序列 Sequence**
+
+允许逻辑执行先进行分割,但不是并行关系,而是次序关系,最上层位于运行上端,以此类推
+序列支持多个次序执行线并存,通过增加引脚来增加次序执行线
+序列中的逻辑执行针脚是按照次序一次执行
+**目的是来帮助开发者将逻辑进行划分,可以将逻辑分成几块,而不必非要串联到一条逻辑线上,达到清晰逻辑结构的目的**
+
+![序列](img/UE_Asset25.png)
+
+----
+
+- **Do N & Do once**
+
+1. Do Once只可以进行一次执行权通过
+  - 通过调用Reset可以重置通过限制
+  - 设置节点开始是关闭的,无法使用,必须通过调用Reset针脚激活节点
+2. Do N可以根据给定的输入参数N来决定执行权通过次数
+  - 输入参数针脚Connter将当前次数进行返回
+  - 如果想要重置节点,需要调用Reset针脚进行重置
+
+Do节点用来控制当前执行逻辑线进行连接后,视口可以通过输出逻辑针脚将逻辑执行权向下进行传递,**主要用来控制执行权通过次数**
+
+> Do N可以用来制作开枪,比如枪管只有20发子弹,只能发射20发;有的武器只能发射一次,则使用Do once
+
+![Do](img/UE_Asset26.png)
+
+> `Do Once`节点中的 `Start Closed`表示初始是否是非激活状态,勾中表示一开始`Do Once`节点就是失效的不会执行后续操作
+
+1. 点击数字键盘1,开始执行`Do Once`,最开始是激活状态,所以会输出字符串`1`
+2. 再次点击数字见1,因为`Do Once`之前已经执行过一次了,所以不会执行后续操作
+3. 点击数字2,Reset`Do Once`节点,使之重新进入激活状态
+4. 点击数字3,开始执行`Do N`,设置`Do N`节点的N为5,所以点击五次3,才会使`Do N`节点失效
+5. 点击数字4,Reset`Do N`节点的次数
+
+----
+
+- **Do Once MultiInput**
+
+与`DoOnce`节点基本相同,但是可以通过添加新的引脚,增加独赢的输入逻辑,只要任意逻辑输入针脚有调用,则整个节点被关闭  
+通过调用重置输入针脚重置整个控制节点
+
+![Do Once MultiInput](img/UE_Asset27.png)
+
+> 这里A、B、C中任意一个被执行了,另外两个都不会再次执行,除非重置整个`Do Once MultiInput`节点
+
+---
+
+- **FlipFlop 反复循环**
+
+节点取入执行输出并在两个执行输出间切换  
+节点同时有bool变量输出,可以用来判断A何时被调用(执行A返回true)  
+
+例如:第一次被调用执行了A,则第二次被调用执行B,然后再是A,然后再是B  
+
+> 实际运用:开关灯
+
+----
+
+- **ForLoop**
+
+按照从`First Index`开始,到`Last Index`结束,调用逻辑输出引脚`Loop Body`,调用次数为`First Index`到`Last Index`差值  
+`Index`输出参数引脚将输出从开始到结束的所有整数,当整个节点执行完毕后,调用逻辑输出引脚`Completed`
+
+> 常用于叠加计算,或多次执行某个动作
+> 等价C++代码中的`for`关键则
+
+![ForLoop](img/UE_Asset28.png)
+
+> 输出5 6 7 8 9 10 Complete
+
+----
+
+- **ForLoopWIthBreak 循环中断**
+
+与`ForLoop`相同,用于将单词执行逻辑进行循环  
+但添加了`Break`输出引脚,如果被打断则直接停止循环,跳到输出逻辑引脚`Completed`
+
+![ForLoopWIthBreak](img/UE_Asset29.png)
+
+> 使用for循环计算1~100的和,当和超过1000的时候停止计算  
+> 当和超过1000时,分支语句`Branch`为True的针脚向后执行,`Break`掉`ForLoopWIthBreak`,随后直接输出计算和1035  
+
+----
+
+- **Gate 门**
+
+用来控制开启和关闭的执行逻辑
+
+Enter输入逻辑进入  
+Open引脚被调用后,Enter引脚流入的执行逻辑才会从Exit引脚流出  
+Close引脚被调用后,Enter引脚流入的执行逻辑将被阻挠,无法流出  
+Toggle从开和关之间进行往返  
+Start Close设置节点开始状态,勾选后开始状态为关闭
+
+![Gate](img/UE_Asset30.png)
+
+> 最开始`Start Closed`为选中状态,所以`Gate`处于关闭状态,点0无反应  
+> 点按钮键盘1之后,`Gate`打开,再点击0执行输出`Toggle`  
+> 点按钮键盘2之后,`Gate`关闭,再点击0,无输出  
+> 点按钮键盘3之后,`Gate`如果原本关闭则变为打开,如果原本是打开则变为关闭,再点击0,根据状态判断是否输出  
+
+----
+
+- **MultiGate**
+
+输入逻辑节点将按照顺序或随机方式从右侧输出引脚进行输出执行逻辑
+
+Reset 重置整个节点,使之回复到起始状态  
+Is Random 勾选引脚,按照随机的方式从右侧输出引脚选择输出执行逻辑  
+Loop  勾选引脚,按照循环方式从右侧输出引脚输出执行逻辑  
+Start Index 设置第一调节用节点从右侧那个引脚进行输出
+
+> 只要不勾选`Loop`,则每次执行`MultiGate`会把逻辑引脚依次全部进行**一次**输出(不会重复输出)  
+
+![MultiGate](img/UE_Asset31.png)
+
+> 如果`Is Random`和`Loop`都没勾选,则多次点击键盘按钮会输出一次`Out 0`,一次`Out 1`,然后`MultiGate`关闭  
+> 如果只勾选了`Is Random`,则多次点击键盘按钮1会随机输出一次`Out 0`或`Out 1`  
+> 如果只勾选了`Loop`,则多次点击键盘按钮1会循环输出`Out 0`,`Out 1`  
+
+----
+
+- **WhileLoop**
+
+只要特定值`Condition`为true,则`whileloop`节点将会输出一个结果。在循环的每个迭代中,他会查看器输入bool值的当前状态,一旦为false,则停止循环
+
+> 等价C++的while
+
+![WhileLoop](img/UE_Asset32.png)
+
+> SumValue初始值为0,每次都SumValue = (SumValue + 1) * 2,当SumValue小于100的时候执行循环体,大于100就跳出循环,最后输出SumValue的值为126
+
+----
+
+- **Switch 切换**
+
+当逻辑输入针脚被调用时,将会按照给定的值`Selection`进行右侧选择引脚输出
+
+> 功能等价于C++的switch  
+
+![WhileLoop](img/UE_Asset33.png)
+
+> 如果值为1,打印执行值为0对应的第一个Print  
+> 如果值为2,打印执行值为1对应的第二个Print  
+> 如果值为3,打印执行值为2对应的第三个Print  
+> 如果不为1、2、3,打印执行Default对应的Print  
+
+----
+
+- **Delay 延时操作**
+
+Delay节点,延时操作  
+当延时任务未完成时,再次调用节点,**忽略调用**  
+尽量不要将Delay节点让如Tick中  
+Delay只能在事件表中使用  
+
+`Duration`延时动作持续时间  
+
+`Retriggerable Delayer`与上面的`Delay`的节点一致,只是如果延时任务没有完成,再次调用节点,则延迟动作**重新计时**
+
+![WhileLoop](img/UE_Asset34.png)
+
+> 不停按键盘按钮4和键盘按钮5可体会`Delay`和`Retriggerable Delayer`的差别
+
+----
+

+ 284 - 0
计算机网络.md

@@ -0,0 +1,284 @@
+
+## 简述OSI七层协议
+
+OSI七层协议包括:物理层,数据链路层,网络层,运输层,会话层,表示层, 应用层
+
+## 简述TCP/IP五层协议
+
+TCP/IP五层协议包括:物理层,数据链路层,网络层,运输层,应用层
+
+## 物理层有什么作用
+
+主要解决两台物理机之间的通信,通过二进制比特流的传输来实现,二进制数据表现为电流电压上的强弱,到达目的地再转化为二进制机器码。网卡、集线器工作在这一层。
+
+## 数据链路层有什么作用
+
+在不可靠的物理介质上提供可靠的传输,接收来自物理层的位流形式的数据,并封装成帧,传送到上一层;同样,也将来自上层的数据帧,拆装为位流形式的数据转发到物理层。这一层在物理层提供的比特流的基础上,通过差错控制、流量控制方法,使有差错的物理线路变为无差错的数据链路。提供物理地址寻址功能。交换机工作在这一层。
+
+## 网络层有什么作用
+
+将网络地址翻译成对应的物理地址,并决定如何将数据从发送方路由到接收方,通过路由选择算法为分组通过通信子网选择最佳路径。路由器工作在这一层。
+
+## 传输层有什么作用
+
+传输层提供了进程间的逻辑通信,传输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个传输层实体之间有一条端到端的逻辑通信信道。
+
+## 会话层有什么作用
+
+建立会话:身份验证,权限鉴定等;
+保持会话:对该会话进行维护,在会话维持期间两者可以随时使用这条会话传输局;
+断开会话:当应用程序或应用层规定的超时时间到期后,OSI会话层才会释放这条会话。
+
+## 表示层有什么作用
+
+对数据格式进行编译,对收到或发出的数据根据应用层的特征进行处理,如处理为文字、图片、音频、视频、文档等,还可以对压缩文件进行解压缩、对加密文件进行解密等。
+
+## 应用层有什么作用
+
+提供应用层协议,如HTTP协议,FTP协议等等,方便应用程序之间进行通信。
+
+## TCP与UDP区别
+
+TCP作为面向流的协议,提供可靠的、面向连接的运输服务,并且提供点对点通信
+UDP作为面向报文的协议,不提供可靠交付,并且不需要连接,不仅仅对点对点,也支持多播和广播
+
+## 为何TCP可靠
+
+TCP有三次握手建立连接,四次挥手关闭连接的机制。
+除此之外还有滑动窗口和拥塞控制算法。最最关键的是还保留超时重传的机制。
+对于每份报文也存在校验,保证每份报文可靠性。
+
+## 为何UDP不可靠
+
+UDP面向数据报无连接的,数据报发出去,就不保留数据备份了。
+仅仅在IP数据报头部加入校验和复用。
+UDP没有服务器和客户端的概念。
+UDP报文过长的话是交给IP切成小段,如果某段报废报文就废了。
+
+## 简述TCP粘包现象
+
+TCP是面向流协议,发送的单位是字节流,因此会将多个小尺寸数据被封装在一个tcp报文中发出去的可能性。
+可以简单的理解成客户端调用了两次send,服务器端一个recv就把信息都读出来了。
+
+## TCP粘包现象处理方法
+
+固定发送信息长度,或在两个信息之间加入分隔符。
+
+## 简述TCP协议的滑动窗口
+
+滑动窗口是传输层进行流量控制的一种措施,接收方通过通告发
+送方自己的窗口大小,从而控制发送方的发送速度,防止发送方发送速度过快而导致自己被淹没。
+
+## 简述TCP协议的拥塞控制
+
+拥塞是指一个或者多个交换点的数据报超载,TCP又会有重传机制,导致过载。
+为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量.
+
+当cwnd < ssthresh 时,使用慢开始算法。
+当cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
+当cwnd = ssthresh 时,即可使用慢开始算法,也可使用拥塞避免算法。
+
+慢开始:由小到大逐渐增加拥塞窗口的大小,每接一次报文,cwnd指数增加。
+
+拥塞避免:cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1。
+
+快恢复之前的策略:发送方判断网络出现拥塞,就把ssthresh设置为出现拥塞时发送方窗口值的一半,继续执行慢开始,之后进行拥塞避免。
+
+快恢复:发送方判断网络出现拥塞,就把ssthresh设置为出现拥塞时发送方窗口值的一半,并把cwnd设置为ssthresh的一半,之后进行拥塞避免。
+
+## 简述快重传
+
+如果在超时重传定时器溢出之前,接收到连续的三个重复冗余ACK,发送端便知晓哪个报文段在传输过程中丢失了,于是重发该报文段,不需要等待超时重传定时器溢出再发送该报文。
+
+## TCP三次握手过程
+
+1. 第一次握手:客户端将标志位SYN置为1,随机产生一个值序列号seq=x,并将该数据包发送给服务端,客户端
+进入syn_sent状态,等待服务端确认。
+2. 第二次握手:服务端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务端将标志位SYN和
+ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给客户端以确认连接请求,服务端进入syn_rcvd状态。
+3. 第三次握手:客户端收到确认后检查,如果正确则将标志位ACK为1,ack=y+1,并将该数据包发送给服务端,服务端进行检查如果正确则连接建立成功,客户端和服务端进入established状态,完成三次握手,随后客户端和服务端之间可以开始传输
+数据了
+
+## 为什么TCP握手需要三次,两次行不行?
+
+不行。TCP进行可靠传输的关键就在于维护一个序列号,三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值。
+
+如果只是两次握手, 至多只有客户端的起始序列号能被确认, 服务器端的序列号则得不到确认。
+
+
+## 简述半连接队列
+
+TCP握手中,当服务器处于SYN_RCVD 状态,服务器会把此种状态下请求连接放在一个队列里,该队列称为半连接队列。
+
+## 简述SYN攻击
+
+SYN攻击即利用TCP协议缺陷,通过发送大量的半连接请求,占用半连接队列,耗费CPU和内存资源。
+
+优化方式:
+1. 缩短SYN Timeout时间 
+2. 记录IP,若连续受到某个IP的重复SYN报文,从这个IP地址来的包会被一概丢弃。
+
+## TCP四次挥手过程
+
+1. 第一次挥手:客户端发送一个FIN,用来关闭客户端到服务端的数据传送,客户端进入fin_wait_1状态。
+2. 第二次挥手:服务端收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1,服务端进入Close_wait状态。此时TCP连接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收。
+3. 第三次挥手:服务端发送一个FIN,用来关闭服务端到客户端的数据传送,服务端进入Last_ack状态。
+4. 第四次挥手:客户端收到FIN后,客户端进入Time_wait状态,接着发送一个ACK给服务端,确认后,服务端进入Closed状态,完成四次挥手。
+
+## 为什么TCP挥手需要4次
+
+主要原因是当服务端收到客户端的 FIN 数据包后,服务端可能还有数据没发完,不会立即close。
+
+所以服务端会先将 ACK 发过去告诉客户端我收到你的断开请求了,但请再给我一点时间,这段时间用来发送剩下的数据报文,发完之后再将 FIN 包发给客户端表示现在可以断了。之后客户端需要收到 FIN 包后发送 ACK 确认断开信息给服务端。
+
+## 为什么四次挥手释放连接时需要等待2MSL
+
+MSL即报文最大生存时间。设置2MSL可以保证上一次连接的报文已经在网络中消失,不会出现与新TCP连接报文冲突的情况。
+
+
+## 简述DNS协议
+
+DNS协议是基于UDP的应用层协议,它的功能是根据用户输入的域名,解析出该域名对应的IP地址,从而给客户端进行访问。
+
+## 简述DNS解析过程
+
+1、客户机发出查询请求,在本地计算机缓存查找,若没有找到,就会将请求发送给dns服务器
+
+2、本地dns服务器会在自己的区域里面查找,找到即根据此记录进行解析,若没有找到,就会在本地的缓存里面查找
+
+3、本地服务器没有找到客户机查询的信息,就会将此请求发送到根域名dns服务器
+
+4、根域名服务器解析客户机请求的根域部分,它把包含的下一级的dns服务器的地址返回到客户机的dns服务器地址
+
+5、客户机的dns服务器根据返回的信息接着访问下一级的dns服务器
+
+6、这样递归的方法一级一级接近查询的目标,最后在有目标域名的服务器上面得到相应的IP信息
+
+7、客户机的本地的dns服务器会将查询结果返回给我们的客户机
+
+8、客户机根据得到的ip信息访问目标主机,完成解析过程
+
+## 简述HTTP协议
+http协议是超文本传输协议。它是基于TCP协议的应用层传输协议,即客户端和服务端进行数据传输的一种规则。该协议本身HTTP 是一种无状态的协议。
+
+## 简述cookie
+
+HTTP 协议本身是无状态的,为了使其能处理更加复杂的逻辑,HTTP/1.1 引入 Cookie 来保存状态信息。
+
+Cookie是由服务端产生的,再发送给客户端保存,当客户端再次访问的时候,服务器可根据cookie辨识客户端是哪个,以此可以做个性化推送,免账号密码登录等等。
+
+## 简述session
+
+session用于标记特定客户端信息,存在在服务器的一个文件里。
+一般客户端带Cookie对服务器进行访问,可通过cookie中的session id从整个session中查询到服务器记录的关于客户端的信息。
+
+
+## 简述http状态码和对应的信息
+
+1XX:接收的信息正在处理
+
+2XX:请求正常处理完毕
+
+3XX:重定向
+
+4XX:客户端错误
+
+5XX:服务端错误
+
+常见错误码:
+301:永久重定向
+302:临时重定向
+304:资源没修改,用之前缓存就行
+400:客户端请求的报文有错误
+403:表示服务器禁止访问资源
+404:表示请求的资源在服务器上不存在或未找到
+
+## 转发和重定向的区别
+
+转发是服务器行为。服务器直接向目标地址访问URL,将相应内容读取之后发给浏览器,用户浏览器地址栏URL不变,转发页面和转发到的页面可以共享request里面的数据。
+
+重定向是利用服务器返回的状态码来实现的,如果服务器返回301或者302,浏览器收到新的消息后自动跳转到新的网址重新请求资源。用户的地址栏url会发生改变,而且不能共享数据。
+
+## 简述http1.0
+
+规定了请求头和请求尾,响应头和响应尾(get post)
+
+每一个请求都是一个单独的连接,做不到连接的复用
+
+## 简述http1.1的改进
+
+HTTP1.1默认开启长连接,在一个TCP连接上可以传送多个HTTP请求和响应。使用 TCP 长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
+
+支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
+
+服务端无法主动push
+
+## 简述HTTP短连接与长连接区别
+
+HTTP中的长连接短连接指HTTP底层TCP的连接。
+
+短连接: 客户端与服务器进行一次HTTP连接操作,就进行一次TCP连接,连接结束TCP关闭连接。
+
+长连接:如果HTTP头部带有参数keep-alive,即开启长连接网页完成打开后,底层用于传输数据的TCP连接不会直接关闭,会根据服务器设置的保持时间保持连接,保持时间过后连接关闭。
+
+## 简述http2.0的改进
+
+提出多路复用。多路复用前,文件时串行传输的,请求a文件,b文件只能等待,并且连接数过多。引入多路复用,a文件b文件可以同时传输。
+
+引入了二进制数据帧。其中帧对数据进行顺序标识,有了序列id,服务器就可以进行并行传输数据。
+
+
+## http与https的区别
+
+http所有传输的内容都是明文,并且客户端和服务器端都无法验证对方的身份。
+https具有安全性的ssl加密传输协议,加密采用对称加密,
+https协议需要到ca申请证书,一般免费证书很少,需要交费。
+
+## 简述TLS/SSL, HTTP, HTTPS的关系
+
+SSL全称为Secure Sockets Layer即安全套接层,其继任为TLSTransport Layer Security传输层安全协议,均用于在传输层为数据通讯提供安全支持。
+
+可以将HTTPS协议简单理解为HTTP协议+TLS/SSL
+
+
+## https的连接过程
+
+1. 浏览器将支持的加密算法信息发给服务器
+2. 服务器选择一套浏览器支持的加密算法,以证书的形式回发给浏览器
+3. 客户端(SSL/TLS)解析证书验证证书合法性,生成对称加密的密钥,我们将该密钥称之为client key,即客户端密钥,用服务器的公钥对客户端密钥进行非对称加密。
+4. 客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端对称密钥发送给服务器
+5. 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文。
+6. 服务器将加密后的密文发送给客户端
+7. 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成
+
+## Get与Post区别
+
+Get:指定资源请求数据,刷新无害,Get请求的数据会附加到URL中,传输数据的大小受到url的限制。
+
+Post:向指定资源提交要被处理的数据。刷新会使数据会被重复提交。post在发送数据前会先将请求头发送给服务器进行确认,然后才真正发送数据。
+
+## Get方法参数有大小限制吗
+
+一般HTTP协议里并不限制参数大小限制。但一般由于get请求是直接附加到地址栏里面的,由于浏览器地址栏有长度限制,因此使GET请求在浏览器实现层面上看会有长度限制。
+
+## 了解REST API吗
+
+REST API全称为表述性状态转移(Representational State Transfer,REST)即利用HTTP中get、post、put、delete以及其他的HTTP方法构成REST中数据资源的增删改查操作:
+- Create : POST
+- Read : GET
+- Update : PUT/PATCH
+- Delete: DELETE
+
+
+## 浏览器中输入一个网址后,具体发生了什么
+
+1. 进行DNS解析操作,根据DNS解析的结果查到服务器IP地址
+2. 通过ip寻址和arp,找到服务器,并利用三次握手建立TCP连接
+3. 浏览器生成HTTP报文,发送HTTP请求,等待服务器响应
+4. 服务器处理请求,并返回给浏览器
+5. 根据HTTP是否开启长连接,进行TCP的挥手过程
+6. 浏览器根据收到的静态资源进行页面渲染
+
+
+
+![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)

+ 96 - 0
设计模式.md

@@ -0,0 +1,96 @@
+小牛和同学开发的八股文网站http://interviewtop.top 持续更新BAT面试题。
+
+
+![](interviewtop.jpg)
+
+
+## 简述设计模式七大原则
+开放封闭原则:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。
+
+单一职责原则:一个类、接口或方法只负责一个职责,降低代码复杂度以及变更引起的风险。
+
+依赖倒置原则:针对接口编程,依赖于抽象类或接口而不依赖于具体实现类。
+
+接口隔离原则:将不同功能定义在不同接口中实现接口隔离。
+
+里氏替换原则:任何基类可以出现的地方,子类一定可以出现。
+
+迪米特原则:每个模块对其他模块都要尽可能少地了解和依赖,降低代码耦合度。
+
+合成复用原则:尽量使用组合(has-a)/聚合(contains-a)而不是继承(is-a)达到软件复用的目的。
+
+## 简述设计模式的分类
+创建型模式:在创建对象的同时隐藏创建逻辑,不使用 new 直接实例化对象。有工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
+
+结构型模式:通过类和接口间的继承和引用实现创建复杂结构的对象。有适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
+
+行为型模式:通过类之间不同通信方式实现不同行为。有策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
+
+## 简述简单工厂模式
+简单工厂模式指由一个工厂对象来创建实例,适用于工厂类负责创建对象较少的情况。例子:Spring 中的 BeanFactory 使用简单工厂模式,产生 Bean 对象。
+
+## 简述工厂模式
+工厂方法模式指定义一个创建对象的接口,让接口的实现类决定创建哪种对象,让类的实例化推迟到子类中进行。例子:Spring 的 FactoryBean 接口的 getObject 方法也是工厂方法。
+
+## 简述抽象工厂模式
+抽象工厂模式指提供一个创建一系列相关或相互依赖对象的接口,无需指定它们的具体类。例子:java.sql.Connection 接口。
+
+## 简述单例模式
+一个单例类在任何情况下都只存在一个实例。
+饿汉式实现
+```java
+public class Singleton {
+    private Singleton(){}
+    private static Singleton instance 
+        = new Singleton();
+ 
+    public static Singleton getInstance() {
+        return instance;
+    }
+}
+```
+懒汉式实现
+```java
+public class Singleton {
+    private DoubleCheckSingleton(){}
+    private volatile static 
+    Singleton instance;
+
+    public static Singleton getInstance() {
+        if(instance == null) {
+            synchronized (Singleton.class) {
+                if (instance == null) {
+                    instance = new Singleton();
+                }
+            }
+        }
+        return instance;
+    }
+}
+```
+## 简述代理模式
+
+代理模式为其他对象提供一种代理以控制对这个对象的访问。优点是可以增强目标对象的功能,降低代码耦合度,扩展性好。缺点是在客户端和目标对象之间增加代理对象会导致请求处理速度变慢,增加系统复杂度。
+
+静态代理:在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
+
+动态代理:程序运行期间动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
+
+## 简述适配器模式
+适配器模式将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作。
+
+## 简述模板模式
+模板模式定义了一个操作中的算法的骨架,并将一些步骤延迟到子类,适用于抽取子类重复代码到公共父类。
+可以封装固定不变的部分,扩展可变的部分。但每一个不同实现都需要一个子类维护,会增加类的数量。
+
+## 简述装饰器模式
+装饰者模式可以动态地给对象添加一些额外的属性或行为,即需要修改原有的功能,但又不愿直接去修改原有的代码时,设计一个Decorator套在原有代码外面。
+## 简述观察者模式
+观察者模式表示的是一种对象与对象之间具有依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
+
+
+
+
+>>![](https://cdn.jsdelivr.net/gh/emmett-xd/AutoencoderMagazine/201229/xnBroadcast.jpg)
+
+---