数据结构知识

数据结构 数据结构知识 具体内容 数组:数组的内存空间是连续的,随机访问的时间复杂度是O1,适用于需要按索引访问元素的场景,但是插入和删除元素较慢,时间复杂度是On 链表:链表是由节点组成,节点之间是分散存储的,内存不连续,每个节点存储数据和指向下一个节点的指针。适用于频繁插入和删除元素的场景,随机访问元素较慢 栈:栈是一种后进先出的数据结构,只允许在栈顶进行插入和删除操作 队列:队列是一种先进先出(FIFO)的数据结构,允许在队尾插入元素,在队首删除元素 树:树是一种非线性数据结构,由节点和边组成,每个节点可以有多个子节点。树适用于表示层次关系的场景,例如文件系统、组织结构等 数组与链表区别? 访问效率,插入和删除操作效率,缓存命中率 应用场景:数组适合静态大小、频繁访问元素的场景,而链表适合动态大小、频繁插入、删除操作的场景 平衡二叉树 二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质: 非空左子树的所有键值小于其根结点的键值。 非空右子树的所有键值大于其根结点的键值。 左、右子树都是二叉搜索树。 二叉树搜索树的目的:缩短插入、删除、修改和查找节点的时间 一棵理想的二叉搜索树所有操作的时间可以缩短到 O(logn) 一棵每个结点只有右孩子的二叉搜索树,那么性质就和链表一样 平衡树将二叉查找树平衡均匀地分布,好处就是可以减少二叉查找树的深度 平衡二叉树平衡的特性: 左右两个子树的高度差(平衡因子)的绝对值不超过1 左右两个子树都是一棵平衡二叉树 红黑树 每个节点要么是红色,要么是黑色。 根节点是黑色。 每个叶子节点(NIL节点)是黑色。 如果一个节点是红色,则其子节点必须是黑色。 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。 二叉查找树 当每次插入的元素都是二叉查找树中最大的元素,二叉查找树就会退化成了一条链表,查找数据的时间复杂度变成了 O(n) 平衡二叉查找树 每个节点的左子树和右子树的高度差不能超过 1。也就是说节点的左子树和右子树仍然为平衡二叉树,这样查询操作的时间复杂度就会一直维持在 O(logn) B+树 B+树是一种自平衡的多路查找树,所有叶节点都位于同一层,保证了树的平衡,使得搜索、插入和删除操作的时间复杂度为对数级别的 非叶节点仅包含索引信息,不存储具体的数据记录,用来引导搜索到正确的叶节点 所有数据记录都存储在叶节点中,且叶节点中的数据是按关键字排序的。叶节点包含实际的数据和关键字,是数据存储和检索的实体单元。叶节点之间通过指针相互链接,形成一个链表,便于范围查询和顺序遍历 B+树与B-树 检索路径:B树在查找数据时,可能在非叶子节点找到目标数据,路径长度不固定。B+树中所有数据都在叶子节点,查找数据时必须走到叶子节点,路径长度固定(均等)。即查找总是要到叶子节点结束 叶子节点结构:B树中叶子节点之间没有特别的链接,彼此独立。B+树中叶子节点通过指针连接,形成一个有序链表,便于范围查询和顺序访问 非叶子节点内容:B树中非叶子节点存储数据和索引。B+树中非叶子节点只存储索引,不存储实际数据。当数据量比较大时,相对于B树,B+树的层高更少,查找效率也就更高。 **高效地范围查询:**B+树叶子节点采用的是双链表连接,适合 MySQL 中常见的基于范围的顺序查找,而 B 树在进行范围查询时需要进行中序遍历,性能较差 堆 堆是一颗完全二叉树,实现的堆也被称为二叉堆。堆中节点的值都大于等于(或小于等于)其子节点的值,堆中如果节点的值都大于等于其子节点的值,我们把它称为大顶堆,如果都小于等于其子节点的值,我们将其称为小顶堆 排序算法 冒泡排序:通过相邻元素的比较和交换,每次将最大(或最小)的元素逐步“冒泡”到最后(或最前)。时间复杂度:最好情况下O(n),最坏情况下O(n^2),平均情况下O(n^2)。,空间复杂度:O(1) //冒泡排序 public static void bubbleSort(int[] arr) { int temp;//临时变量 boolean flag;//是否交换的标志 for (int i = 0; i < arr....

Mysql事务日志锁

事务 事务是由 MySQL 的引擎来实现,常见的 InnoDB 引擎它是支持事务的 并不是所有的引擎都能支持事务,比如 MySQL 原生的 MyISAM 引擎就不支持事务 特性 具体特性 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节;事务在执行过程中发生错误,会被回滚到事务开始前的状态 一致性:事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态 隔离性:允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失 如何保证特性? 持久性是通过 redo log (重做日志)来保证的 原子性是通过 undo log(回滚日志) 来保证的 隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的 一致性则是通过持久性+原子性+隔离性来保证 隔离级别 MySQL服务端允许多个客户端连接,意味着 MySQL 会出现同时处理多个事务的情况 问题: 脏读:如果一个事务「读到」了另一个「未提交事务修改过的数据」 事务 A 是还没提交事务的,也就是它随时可能发生回滚操作,被读 不可重复读:一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况 事务A两次读取过程中,事务B进行修改且提交 幻读:一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况 事务A两次读取过程中,事务B进行修改且提交 隔离级别 读未提交:一个事务还没提交时,它做的变更就能被其他事务看到 读提交:一个事务提交之后,它做的变更才能被其他事务看到 可重复读:一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别 串行化:会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行 「可重复读」但是很大程度上避免幻读现象 针对快照读(普通 select 语句),通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的 针对当前读(select … for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,当执行 select … for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那这个插入语句就会被阻塞,无法成功插入 四种隔级别实现 「读未提交」可以读到未提交事务修改的数据,所以直接读取最新的数据就好 「串行化」通过加读写锁的方式来避免并行访问; 「读提交」和「可重复读」通过 Read View 来实现的,它们的区别在于创建 Read View 的时机不同,大家可以把 Read View 理解成一个数据快照,就像相机拍照那样,定格某一时刻的风景。「读提交」隔离级别是在「每个语句执行前」都会重新生成一个 Read View,而「可重复读」隔离级别是「启动事务时」生成一个 Read View,然后整个事务期间都在用这个 Read View 执行「开始事务」begin/start transaction 命令 执行了 begin/start transaction 命令后,并不代表事务启动了 只有在执行这个命令后,执行了第一条 select 语句,才是事务真正启动的时机 Read View在MVCC工作 Read View 有四个重要的字段: m_ids :在创建 Read View 时,当前数据库中「活跃事务」的事务 id 列表,注意是一个列表,“活跃事务”指的就是,启动了但还没提交的事务 min_trx_id :在创建 Read View 时,当前数据库中「活跃事务」中事务 id 最小的事务,也就是 m_ids 的最小值 max_trx_id :这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值,就是全局事务中最大的事务 id 值 + 1 creator_trx_id :创建该 Read View 的事务的事务 id InnoDB 存储引擎的数据库表,聚簇索引记录中包含下面两个隐藏列: trx_id:一个事务对某条聚簇索引记录进行改动时,会把该事务的事务 id 记录在 trx_id 隐藏列里 roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧版本的记录写入到 undo 日志中,这个隐藏列是个指针,指向每一个旧版本记录,于是就可以通过它找到修改前的记录 通过「版本链」控制并发事务访问同一个记录时的行为叫 MVCC(多版本并发控制) 可重复读和读提交如何工作 可重复读隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View 读提交隔离级别是在每次读取数据时,都会生成一个新的 Read View 日志 undo log(回滚日志):是 Innodb 存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和 MVCC...

Mysql索引查询

MySQL查询过程 MySQL 的架构共分为两层:Server 层和存储引擎层 Server 层负责建立连接、分析和执行 SQL。主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数(如日期、时间、数学和加密函数等)和所有跨存储引擎的功能(如存储过程、触发器、视图等。)都在 Server 层实现 存储引擎层负责数据的存储和提取。支持 InnoDB、MyISAM、Memory 等多个存储引擎,不同的存储引擎共用一个 Server 层。从 MySQL 5.5 版本开始, InnoDB 成为了 MySQL 的默认存储引擎。索引数据结构就是由存储引擎层实现,不同的存储引擎支持的索引类型也不相同,比如 InnoDB 支持索引类型是 B+树 ,且是默认使用,在数据表中创建的主键索引和二级索引默认使用的是 B+ 树索引 第一步:连接器 启动Mysql服务,连接 MySQL 服务 # -h 指定 MySQL 服务得 IP 地址,如果是连接本地的 MySQL服务,可以不用这个参数; # -u 指定用户名,管理员角色名为 root; # -p 指定密码,如果命令行中不填写密码(为了密码安全,建议不要在命令行写密码),就需要在交互对话里面输入密码 mysql -h$ip -u$user -p 查看 MySQL 服务被多少客户端连接 执行 show processlist 命令进行查看 MySQL 定义空闲连接的最大空闲时长,由 wait_timeout 参数控制的,如果空闲连接超过了这个时间,连接器就会自动将它断开 mysql> show variables like 'wait_timeout'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wait_timeout | 28800 | +---------------+-------+ 1 row in set (0....

OS调度网络

调度算法 进程调度 总论 当CPU空闲,操作系统就选择内存中的某个「就绪状态」的进程,并给其分配 CPU 什么时候会发生 CPU 调度 当进程从运行状态转到等待状态 当进程从运行状态转到就绪状态 当进程从等待状态转到就绪状态 当进程从运行状态转到终止状态 其中发生在 1 和 4 两种情况下的调度称为「非抢占式调度」, 2 和 3 两种情况下发生的调度称为「抢占式调度」 非抢占式:当进程正在运行时,会一直运行,直到该进程完成或发生某个事件而被阻塞时,才会把 CPU 让给其他进程 抢占式:进程正在运行的时,可以被打断,使其把 CPU 让给其他进程 先来先服务调度 先来后到,每次从就绪队列选择最先进入队列的进程,然后一直运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行 对长作业有利,适用 CPU 繁忙型作业的系统,而不适用 I/O 繁忙型作业的系统 最短作业优先调度 会优先选择运行时间最短的进程来运行,这有助于提高系统的吞吐量 显然对长作业不利,很容易造成一种极端现象 高响应比优先调度 每次进行进程调度时,先计算「响应比优先级」,然后把「响应比优先级」最高的进程投入运行 时间片轮转调度 每个进程被分配一个时间段,称为时间片,即允许该进程在该时间段中运行 最高优先级调度 对多用户计算机系统就有不同的看法,它们希望调度是有优先级的,即希望调度程序能从就绪队列中选择最高优先级的进程进行运行,这称为最高优先级调度 优先级可分为: 静态优先级:创建进程时候,就已经确定了优先级了,然后整个运行时间优先级都不会变化 动态优先级:根据进程的动态变化调整优先级,比如如果进程运行时间增加,则降低其优先级,就随着时间的推移增加等待进程的优先级 多级反馈队列调度 「时间片轮转算法」和「最高优先级算法」的综合和发展 「多级」有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。 「反馈」如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列; 内存页面置换 缺页异常(缺页中断) 当 CPU 访问的页面不在物理内存,便产生一个缺页中断,请求操作系统将所缺页调入到物理内存。与一般中断区别在于: 缺页中断在指令执行「期间」产生和处理中断信号,而一般中断在一条指令执行「完成」后检查和处理中断信号 缺页中断返回到该指令的开始重新执行「该指令」,而一般中断返回回到该指令的「下一个指令」执行 页面置换算法:当出现缺页异常,需调入新页面但内存已满,选择被置换的物理页面,也就选择一个物理页面换出到磁盘,然后把需要访问的页面换入到物理页 最佳页面置换 置换在「未来」最长时间不访问的页面,但无法实现 先进先出置换 选择在内存驻留时间很长的页面进行中置换,就是「先进先出置换」算法的思想 最近最久未使用置换 发生缺页时,选择最长时间没有被访问的页面进行置换,假设已经很久没有使用的页面很有可能在未来较长的一段时间内仍然不会被使用 时钟页面置换 把所有的页面都保存在一个类似钟面的「环形链表」中,一个表针指向最老的页面 缺页中断时,首先检查表针指向的页面: 如果它的访问位位是 0 就淘汰该页面,并把新的页面插入这个位置,然后把表针前移一个位置; 如果访问位是 1 就清除访问位,并把表针前移一个位置,重复这个过程直到找到了一个访问位为 0 的页面为止 网络 I/O多路复用 服务用户数量...

OS内存进程

总论 现代操作系统,内核一般会提供 4 个基本能力: 管理进程、线程,决定哪个进程、线程使用 CPU,也是进程调度的能力; 管理内存,决定内存的分配和回收,也是内存管理的能力; 管理硬件设备,为进程与硬件设备之间提供通信能力,也是硬件通信能力; 提供系统调用,如果应用程序要运行更高权限运行的服务,就需要有系统调用,是用户程序与操作系统之间的接口 内核具有很高的权限,可控制 cpu、内存、硬盘等硬件,而应用程序具有的权限很小。 因此大多数操作系统,把内存分成了两个区域 内核空间:这个内存空间只有内核程序可以访问 用户空间:这个内存空间专门给应用程序使用 用户空间的代码只能访问一个局部的内存空间,而内核空间的代码可以访问所有内存空间 当程序使用用户空间时,我们常说该程序在用户态执行 当程序使内核空间时,程序则在内核态执行 应用程序如果需要进入内核空间,就需要通过系统调用 内核程序执行在内核态,用户程序执行在用户态 应用程序使用系统调用时,会产生一个中断。发生中断后, CPU 会中断当前在执行的用户程序,转而跳转到中断处理程序(开始执行内核程序) 内核处理完后,主动触发中断,把 CPU 执行权限交回给用户程序,回到用户态继续工作 现代操作系统,对于内存管理,采用的是虚拟内存技术,也就是每个进程都有自己独立的虚拟内存空间,不同进程的虚拟内存映射到不同的物理内存中。所以,即使进程 A 和 进程 B 的虚拟地址是一样的,其实访问的是不同的物理内存地址,对于数据的增删查改互不影响 内存管理 虚拟内存 单片机的 CPU 是直接操作内存的「物理地址」 想在内存中同时运行两个程序是不可能的。如果第一个程序在 2000 的位置写入一个新的值,将会擦掉第二个程序存放在相同位置上的所有内容,所以同时运行两个程序是根本行不通的 解决方法? 把进程所使用的地址「隔离」开来,即让操作系统为每个进程分配独立的一套「虚拟地址」,人人都有,大家自己玩自己的地址就行,互不干涉 有个前提每个进程都不能访问物理地址,至于虚拟地址最终怎么落到物理内存里,对进程来说是透明的 理解:之所以不能同时运行,就是因为两个程序引用相同物理地址,也就是不能让其使用相同的物理地址,加一层映射过去 操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来 程序要访问虚拟地址的时候,由操作系统转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,这样就不会冲突了 引出两种地址概念: 我们程序所使用的内存地址叫做虚拟内存地址 实际存在硬件里面的空间地址叫物理内存地址 操作系统引入了虚拟内存,进程持有的虚拟地址会通过 CPU 芯片中的内存管理单元(MMU)的映射关系,来转换变成物理地址,然后再通过物理地址访问内存 虚拟内存作用: 第一,虚拟内存使得进程对运行内存超过物理内存大小,因为程序运行符合局部性原理,CPU 访问内存会有很明显的重复访问的倾向性,对于那些没有被经常使用到的内存,我们可以把它换出到物理内存之外,比如硬盘上的 swap 区域 理解:加载一个很大的存储,原本只能一次性,现在可分按需批 第二,由于每个进程都有自己的页表,所以每个进程的虚拟内存空间就是相互独立的。进程也没有办法访问其他进程的页表,所以这些页表是私有的,这就解决了多进程之间地址冲突的问题 第三,页表里的页表项中除了物理地址之外,还有一些标记属性的比特,比如控制一个页的读写权限,标记该页是否存在等。在内存访问方面,操作系统提供了更好的安全性 内存分段 程序是由若干个逻辑分段组成的,如可由代码分段、数据分段、栈段、堆段组成。不同的段是有不同的属性的,所以就用分段(Segmentation)的形式把这些段分离出来 分段机制下,虚拟地址的物理地址的映射 分段机制下的虚拟地址由两部分:段选择因子和段内偏移量 段选择子就保存在段寄存器里面。段选择子里面最重要的是段号,用作段表的索引。段表里面保存的是这个段的基地址、段的界限和特权等级等。 虚拟地址中的段内偏移量应该位于 0 和段界限之间,如果段内偏移量是合法的,就将段基地址加上段内偏移量得到物理内存地址 虚拟地址是通过段表与物理地址进行映射的,分段机制会把程序的虚拟地址分成 4 个段,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量,于是就能找到物理内存中的地址 问题: 第一个是内存碎片 内存分段管理可以做到段根据实际需求分配内存,有多少需求就分配多大的段,所以不会出现内部内存碎片,但是由于每个段的长度不固定,所以多个段未必能恰好使用所有的内存空间,会产生了多个不连续的小物理内存,导致新的程序无法被装载,所以会出现外部内存碎片 解决「外部内存碎片」的是内存交换 内存交换空间,在Linux中,是我们常看到的 Swap 空间,这块空间是从硬盘划分出来的,用于内存与硬盘的空间交换 第二个是内存交换的效率低 如果内存交换的时候,交换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿 内存分页 分段的好处就是能产生连续的内存空间 但会出现「外部内存碎片和内存交换的空间太大」的问题 解决这些问题,那么就要想出能少出现一些内存碎片的办法 分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小,这样一个连续并且尺寸固定的内存空间,我们叫页(Page)。在 Linux 下,每一页的大小为 4KB 虚拟地址与物理地址之间通过页表来映射 页表存储在内存里,内存管理单元 (MMU)将虚拟内存地址转换成物理地址工作 当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行 解决分段问题 内存分页由于内存空间都是预先划分好的,采用了分页,页与页之间是紧密排列的,所以不会有外部碎片 内存分页机制分配内存的最小单位是一页,即程序不足一页大小,我们最少只能分配一个页,所以页内会出现内存浪费,所以针对内存分页机制会有内部内存碎片 内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为换出(Swap Out)。一旦需要的时候,再加载进来,称为换入(Swap In)一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高 分页的方式使得我们在加载程序的时候,不再需要一次性都把程序加载到物理内存中,只有在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去 分页机制,虚拟地址与物理地址的映射 分页机制下,虚拟地址分为两部分,页号和页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,这个基地址与页内偏移的组合就形成了物理内存地址 总结一下,对于一个内存地址转换,就是这样三个步骤: 把虚拟内存地址,切分成页号和偏移量; 根据页号,从页表里面,查询对应的物理页号; 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址 缺陷: 操作系统是可以同时运行非常多的进程的,那这不就意味着页表会非常的庞大 在 32 位的环境,虚拟地址空间共 4GB,假设一个页大小是 4KB(2^12),就需要大约 100 万 (2^20) 个页,每个「页表项」需要 4 个字节(4B)来存储,那整个 4GB 空间的映射就需要 4MB 的内存来存储页表 100 个进程的话,就需要 400MB 的内存来存储页表,这是非常大的内存 多级页表 解决分页的缺陷 把这 100 多万个「页表项」的单级页表再分页,将页表(一级页表)分为 1024 页表(二级页表),每个表(二级页表)中包含 1024 个「页表项」,形成二级分页 算机组成原理里面无处不在的局部性原理 每个进程都有 4GB 的虚拟地址空间,而显然对于大多数程序来说,其使用到的空间远未达到 4GB,因为会存在部分对应的页表项都是空的,根本没有分配,对于已分配的页表项,如果存在最近一定时间未访问的页表,在物理内存紧张的情况下,操作系统会将页面换出到硬盘,也就是说不会占用物理内存 一级页表就可以覆盖整个 4GB 虚拟地址空间,但如果某个一级页表的页表项没有被用到,也就不需要创建这个页表项对应的二级页表了,即可以在需要时才创建二级页表 深入理解思想: 从页表的性质来看,保存在内存中的页表承担的职责是将虚拟地址翻译成物理地址 页表一定要覆盖全部虚拟地址空间,不分级的页表就需要有 100 多万个页表项来映射,而二级分页则只需要 1024 个页表项 此时一级页表覆盖到了全部虚拟地址空间,二级页表在需要时创建 TLB 多级页表虽然解决了空间上的问题,但是虚拟地址到物理地址的转换就多了几道转换的工序,这显然就降低了这俩地址转换的速度,带来了时间上的开销 程序是有局部性的,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域 最常访问的几个页表项存储到访问速度更快的硬件,于是计算机科学家们,就在 CPU 芯片中,加入了一个专门存放程序最常访问的页表项的 Cache,这个 Cache 就是 TLB 有TLB 后,那么 CPU 在寻址时,先查 TLB,如果没找到,才会继续查常规的页表 TLB 的命中率其实是很高的,因为程序最常访问的页就那么几个 段页式内存管理 内存分段和内存分页并不是对立的,它们是可以组合起来在同一个系统中使用的,那么组合起来后,通常称为段页式内存管理 段页式内存管理实现方式: 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制 再把每个段划分为多个页,也就对分段划分出来的连续空间,再划分固定大小的页 这样,地址结构就由段号、段内页号和页内位移三部分组成 段页式地址变换中要得到物理地址须经过三次内存访问: 第一次访问段表,得到页表起始地址; 第二次访问页表,得到物理页号; 第三次将物理页号与页内位移组合,得到物理地址 Linux内存结构 Linux 系统中的每个段都是从 0 地址开始的整个 4GB 虚拟空间(32 位环境下),也就是所有的段的起始地址都是一样的。这意味着,Linux 系统中的代码,包括操作系统本身的代码和应用程序代码,所面对的地址空间都是线性地址空间(虚拟地址),这种做法相当于屏蔽了处理器中的逻辑地址概念,段只被用于访问控制和内存保护 Linux 内存主要采用的是页式内存管理,但同时也不可避免地涉及了段机制 每个进程都各自有独立的虚拟内存,但是每个虚拟内存中的内核地址,其实关联的都是相同的物理内存。这样,进程切换到内核态后,就可以很方便地访问内核空间内存 分配内存 应用程序通过 申请内存的时候,实际上申请的是虚拟内存,此时并不会分配物理内存...

Network(TCP)

TCP握手挥手 TCP TCP 是面向连接的、可靠的、基于字节流的传输层通信协议 面向连接:一定是「一对一」才能连接,不能像 UDP 协议一个主机同时向多个主机发送消息 可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端 字节流:用户消息通过 TCP 协议传输时,消息可能会被操作系统「分组」成多个的 TCP 报文,如果接收方的程序不知道「消息的边界」,无法读出一个有效的用户消息的。并且 TCP 报文是「有序的」,当「前一个」TCP 报文没有收到的时候,即使它先收到了后面的 TCP 报文,那么也不能扔给应用层去处理,同时对「重复」的 TCP 报文会自动丢弃 TCP连接 用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括 Socket、序列号和窗口大小称为连接 Socket:由 IP 地址和端口号组成 序列号:用来解决乱序问题等 窗口大小:用来做流量控制 TCP与UDP区别 1. 连接 TCP 是面向连接的传输层协议,传输数据前先要建立连接。 UDP 是不需要连接,即刻传输数据。 2. 服务对象 TCP 是一对一的两点服务,即一条连接只有两个端点。 UDP 支持一对一、一对多、多对多的交互通信 3. 可靠性 TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按序到达。 UDP 是尽最大努力交付,不保证可靠交付数据。但是可以基于 UDP 传输协议实现一个可靠的传输协议,比如 QUIC 协议 4. 拥塞控制、流量控制 TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。 UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。 5. 传输方式 TCP 是流式传输,没有边界,但保证顺序和可靠。 UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。 6. 分片不同 TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。 UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层 传输层的「端口号」是为了区分同一个主机上不同应用程序的数据包 传输层两个传输协议 TCP 和 UDP,在内核中是两个完全独立的软件模块...

网络基础

网络模型 同一台设备上的进程间通信,有很多种方式,比如有管道、消息队列、共享内存、信号等方式 不同设备上的进程间通信,需要网络通信,而设备是多样性的,协商出了一套通用的网络协议 TCP/IP网络模型 应用层 最上层的,也是我们能直接接触到的,应用就把应用数据传给下一层,就是传输层 应用层为用户提供应用功能,比如 HTTP、FTP等 传输层 传输层会有两个传输协议,分别是 TCP 和 UDP TCP:大部分应用使用的正是 TCP 传输层协议,比如HTTP。TCP有流量控制、超时重传、拥塞控制等,为保证数据包能可靠地传输给对方 UDP:只负责发送数据包,不保证数据包是否能抵达对方,它实时性相对更好,传输效率也高 应用需要传输的数据可能会非常大,当传输层的数据包大小超过 MSS(TCP 最大报文段长度),将数据包分块,这样即使中途有一个分块丢失或损坏了,只需要重新发送这一个分块 MTU:一个网络包最大长度,以太网中一般为 1500 字节。 MSS:除去 IP 和 TCP 头部后,一个网络包所能容纳的 TCP 数据的最大长度。 传输层负责把数据包传给应用,但一台设备上会有很多应用接收或者传输数据,用编号将应用区分开来,这个编号是端口 80 端口通常是 Web 服务器用的,22 端口通常是远程登录服务器用的 对于浏览器(客户端)中的每个标签栏都是一个独立的进程,操作系统会为这些进程分配临时的端口号 网络层 传输层协议处理太多的事情,只需要服务好应用即可;而实际的传输功能就交给下一层,也就网络层,负责将数据从一个设备传输到另一个设备 IP 协议将传输层的报文作为数据部分,再加上 IP 包头组装成 IP 报文;如果 IP 报文大小超过 MTU(以太网中一般为 1500 字节)就会再次进行分片,得到一个即将发送到网络的 IP 报文 IP 地址给设备进行编号 IPv4 协议, IP 地址共 32 位,分成了四段(比如192.168.100.1),每段是8位 将 IP 地址分成两种意义: 网络号,负责标识该 IP 地址是属于哪个「子网」的; 主机号,负责标识同一「子网」下的不同主机 配合子网掩码才能算出 IP 地址 的网络号和主机号 比如 10....