malloc和new的区别
new/delete 是 C++关键字,需要编译器支持。malloc/free 是库函数,需要头文件支持 使用 new 操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而 malloc 则需要显式地指出所需内存的尺寸 new 操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故 new 是符合类型安全性的操作符。而 malloc 内存分配成功则是返回 void * ,需要通过强制类型转换将 void*指针转换成我们需要的类型 new 内存分配失败时,会抛出 bad_alloc 异常。malloc 分配内存失败时返回 NULL new 会先调用 operator new 函数,申请足够的内存(通常底层使用 malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete 先调用析构函数,然后调用 operator delete 函数释放内存(通常底层使用 free 实现)。malloc/free 是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作
排序算法时间复杂度
如何用Linux shell命令统计一个文本中各个单词的个数
more log.txt | tr ' ' '\n' | sort | uniq -c
Linux下需要打开或者查看大文件
查看文件的几行到几行 sed -n '10,10000p' log # 查看第10到10000行的数据
linux 怎么查看进程,怎么结束进程?原理是什么?
top, kill
ps -e 查看进程详细信息
Linux怎么查看当前的负载情况
uptime命令主要用于获取主机运行时间和查询linux系统负载等信息
cat /proc/loadavg
tload
top
Http Code
MySQL的底层索引结构,InnoDB里面的B+Tree
不同引擎索引的区别
i++是否原子操作
锁的底层实现
B Tree 和 B+ Tree的区别
b树和b+树
B树(B-tree)是一种树状数据结构,它能够存储数据、对其进行排序并允许以O(log n)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构。B树,概括来说是一个节点可以拥有多于2个子节点的二叉查找树。与自平衡二叉查找树不同,B-树为系统最优化大块数据的读和写操作。B-tree算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统。 B 树可以看作是对2-3查找树的一种扩展,即他允许每个节点有M-1个子节点。M阶B树具有以下特征:
B+树 B+树是对B树的一种变形树,它与B树的差异在于:
B树与B+树的区别:
B树的优点:
B+树的优点:
B树与B+树的共同优点:
MySQL索引的发展过程?是一来就是B+Tree的么?从 没有索引、hash、二叉排序树、AVL树、B树、B+树
MySQL里面的事务,说说什么是事务
数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的 ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由 DBMS 中的事务管理子系统负责事务的处理。
MySQL里面有那些事务级别,并且不同的事务级别会出现什么问题
读未提交 (脏读):最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生 读提交 (读旧数据,不可重复读问题):只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。 可重复读 (解决了脏读但是有幻影读):在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。可以解决脏读、不可重复读。 串行化:事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。
不可重复读和幻读的区别
数据库持久性是怎么实现的?
详细 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。 MySQL采用了一种叫WAL(Write Ahead Logging)提前写日志的技术。意思就是说,发生了数据修改操作先写日志记录下来,等不忙的时候再持久化到磁盘。这里提到的日志就是redo log。 redo log称为重做日志,当有一条记录需要修改的时候,InnoDB引擎会先把这条记录写到redo log里面。redo log是物理格式日志,它记录的是对于每个页的修改。 redo log是由两部分组成的:一是内存中的重做日志缓冲(redo log buffer);二是用来持久化的重做日志文件(redo log file)。为了消耗不必要的IO操作,事务再执行过程中产生的redo log首先会redo log buffer中,之后再统一存入redo log file刷盘进行持久化,这个动作称为fsync binlog记录了mysql执行更改了所有操作,但不包含select和show这类本对数据本身没有更改的操作。但是不是说对数据本身没有修改就不会记录binlog日志。
更新一条语句的流程
相关视频推荐
【mysql数据库】C++程序员眼中MySQL的索引和事务
内存泄漏的3个解决方案与原理实现,知道一个可以轻松应用开发工作
10道网络八股文,每道都很经典,让你在面试中逼格满满
需要C/C++ Linux服务器架构师学习资料及大厂面试题加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
数据库回表是什么?
详细 Innodb的索引存在两类,一类是聚簇索引一类是非聚簇索引,Innodb有且仅有一个聚簇索引。1. 如果表定义了PK,则PK就是聚集索引;2. 如果表没有定义PK,则第一个not NULL unique列是聚集索引;否则,InnoDB会创建一个隐藏的row-id作为聚集索引; InnoDB 聚集索引 的叶子节点存储行记录,而普通索引的叶子节点存储主键值 在使用聚簇索引时,可以一步直接获取到记录值,而使用普通索引时,会首先获取记录的PK,然后再从聚簇索引中查找对应的记录,这个过程叫做回表
索引覆盖
理解方式一:就是select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖 理解方式二:索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据;当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引 理解方式三:是非聚集复合索引的一种形式,它包括在查询里的Select、Join和Where子句用到的所有列(即建索引的字段正好是覆盖查询条件中所涉及的字段,也即,索引包含了查询正在查找的数据)。 总结:要查找的数据可以都在索引中出现,而不需要再去查表获取完整记录 为了实现索引覆盖可以将被查询的字段,建立到联合索引里去
数据库读写锁发生死锁的情景
详细
为什么推荐主键使用自增的整型,MySQL为什么主键自增好
为什么推荐主键:Innodb底层是B+树,数据和索引放在一起,因此需要一个主键作为索引,从而存储数据 为什么要自增:当新存储一条数据时,只需要向B+树后面的叶子节点插入即可,而不需要B+ 树为保持有序而进行旋转 为什么要整型:整形作为索引,容易直接判断大小而保持有序,使用String,相对于整数而言,不易判断大小
vector的底层实现,扩容机制
详细 使用一段连续的内存来存储数据,同时在数据结构中保存了三个指针来标记内存地址,首先是指向vector中数据的起始位置的_Myfirst指针,最后一个数据的位置的_Mylst指针,以及一个指向连续内存空间末尾的_Myend指针 当 vector 的大小和容量相等(size==capacity)也就是满载时,如果再向其添加元素,那么 vector 就需要扩容。vector 容器扩容的过程需要经历以下 3 步:
MySQL中如果使用like进行模糊匹配的时候,是否会使用索引
mysql在使用like查询的时候只有使用后面的%时,才会使用到索引
Volatile的作用,Volatile如何保证可见性的?以及如何实现可见性的机制
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。声明时语法:int volatile vInt; 当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存 volatile 用在如下的几个地方:
如果大量的使用Volatile存在什么问题
操作系统的线程,以及它的状态
线程的基本状态: 1.新建 new语句创建的线程对象处于新建状态,此时它和其他java对象一样,仅被分配了内存。 2.等待 当线程在new之后,并且在调用start方法前,线程处于等待状态。 3.就绪 当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态。处于这个状态的线程位于Java虚拟机的可运行池中,等待cpu的使用权。 4.运行状态 处于这个状态的线程占用CPU,执行程序代码。在并发运行环境中,如果计算机只有一个CPU,那么任何时刻只会有一个线程处于这个状态。只有处于就绪状态的线程才有机会转到运行状态。 5. 阻塞状态 阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU,直到线程重新进入就绪状态,它才会有机会获得运行状态。 6.死亡状态 当线程执行完run()方法中的代码,或者遇到了未捕获的异常,就会退出run()方法,此时就进入死亡状态,该线程结束生命周期。
进程和线程(进程与线程)的区别以及使用场景
线程产生的原因:进程可以使多个程序能并发执行,以提高资源的利用率和系统的吞吐量;但是其具有一些缺点:
进程是资源分配的最小单位,线程是操作系统进行执行和调度的最小单位
使用场景
线程私有:线程栈,寄存器,程序寄存器 共享:堆,地址空间,全局变量,静态变量 进程私有:地址空间,堆,全局变量,栈,寄存器 共享:代码段,公共数据,进程目录,进程 ID
为什么线程创建和撤销开销大
当从一个线程切换到另一个线程时,不仅会发生线程上下文切换,还会发生特权模式切换。 既然是线程切换,那么一定涉及线程状态的保存和恢复,包括寄存器、栈等私有数据。另外,线程的调度是需要内核级别的权限的(操作CPU和内存),也就是说线程的调度工作是在内核态完成的,因此会有一个从用户态到内核态的切换。而且,不管是线程本身的切换还是特权模式的切换,都要进行CPU的上下文切换
线程和协程的由来和作用
协程,又称微线程。协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。 和多线程比,协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显 第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多 在协程上利用多核 CPU —— 多进程+协程,既充分利用多核,又充分发挥协程的高效率, 可获得极高的性能
多线程的通信和同步,多线程访问同一个对象怎么办
查看端口号或者进程号,使用什么命令
查看程序对应的进程号: ps -ef | grep 进程名字 查看进程号所占用的端口号: netstat -nltp | grep 进程号 查看端口号所使用的进程号: lsof -i:端口号
信号量与mutex和自旋锁的区别
信号量(semaphore)用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作。而互斥锁(Mutual exclusion,缩写 Mutex)是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这个资源 自旋锁与前两者的区别是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁
非对称加密与对称加密
对称加密算法:加密效率高,速度快,适合大数据量加密。DES/AES 非对称加密算法:算法复杂,加密速度慢,安全性更高。结合对称加密使用。RSA、DH
在两列属性上分别建索引,则某个查询语句使用 where att1 = * and att2 = * 会怎么使用索引
一次查询只使用一个索引,因为每个索引都代表了一颗树,使用多个索引所带来的增益远小于增加的性能消耗。 对于联合索引来说会使用最左匹配 -- 在MySQL的user表中,对a,b,c三个字段建立联合索引,根据查询字段的位置不同来决定,如查询a, a,b a,b,c a,c 都可以走索引,其他条件的查询不能走索引 对于多个单列索引来说,MySQL会试图选择一个限制最严格的索引。但是,即使是限制最严格的单列索引,它的限制能力也肯定远远低于在多列上的多列索引
通过两个索引查询出来的结果,会进行什么要的操作?交集,并集
MySQL中遇到一些慢查询,有什么解决方法
根据慢查询日志定位慢查询sql
优化数据库结构 分解关联查询: 很多高性能的应用都会对关联查询进行分解,就是可以对每一个表进行一次单表查询,然后将查询结果在应用程序中进行关联,很多场景下这样会更高效。 增加索引 建立视图 优化查询语句 添加存储过程 冗余保存数据
有了解过IO多路复用技术是个什么样的原理
I/O多路复用,I/O就是指的我们网络I/O,多路指多个TCP连接(或多个Channel),复用指复用一个或少量线程。串起来理解就是很多个网络I/O复用一个或少量的线程来处理这些连接。
通过一个线程,同时连接多个线程会不会存在多个线程切换
在操作系统中,有高速缓存,主存,虚拟内存,外存,有知道它们之间有什么样的关系,以及它们的作用是啥
缓存: 在CPU同时处理很多数据,而又不可能同时进行所有数据的传输的情况,把优先级低的数据暂时放入缓存中,等优先级高的数据处理完毕后再把它们从缓存中拿出来进行处理 主存:主存就是内存,是直接与CPU交换信息的存储器,指CPU能够通过指令中的地址码直接访问的存储器,常用于存放处于活动状态的程序和数据 虚拟内存:当运行数据超过内存限度,部分数据自动“溢出”,这时系统会将硬盘上的部分空间模拟成内存——虚拟内存,并且将暂时不运行的程序或不使用的数据存放到虚拟内存中等待需要时调用 辅存就是外存: 硬盘与磁盘、光盘、软盘、U盘等
缺页的产生和换页算法
缺页中断:进程线性地址空间里的页面不必常驻内存,在执行一条指令时,如果发现他要访问的页没有在内存中(存在位为0),那么停止该指令的执行,并产生一个页不存在异常,对应的故障处理程序可通过从外存加载该页到内存的方法来排除故障,之后,原先引起的异常的指令就可以继续执行,而不再产生异常 页面置换算法:将新页面调入内存时,如果内存中所有的物理页都已经分配出去,就要按某种策略来废弃某个页面,将其所占据的物理页释放出来,好的算法,让缺页率降低。
struct DLinkedNode {
int key, value;
DLinkedNode* prev;
DLinkedNode* next;
DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
unordered_map cache;
DLinkedNode* head;
DLinkedNode* tail;
int size;
int capacity;
public:
LRUCache(int _capacity): capacity(_capacity), size(0) {
// 使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head->next = tail;
tail->prev = head;
}
int get(int key) {
if (!cache.count(key)) {
return -1;
}
// 如果 key 存在,先通过哈希表定位,再移到头部
DLinkedNode* node = cache[key];
moveToHead(node);
return node->value;
}
void put(int key, int value) {
if (!cache.count(key)) {
// 如果 key 不存在,创建一个新的节点
DLinkedNode* node = new DLinkedNode(key, value);
// 添加进哈希表
cache[key] = node;
// 添加至双向链表的头部
addToHead(node);
++size;
if (size > capacity) {
// 如果超出容量,删除双向链表的尾部节点
DLinkedNode* removed = removeTail();
// 删除哈希表中对应的项
cache.erase(removed->key);
// 防止内存泄漏
delete removed;
--size;
}
}
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
DLinkedNode* node = cache[key];
node->value = value;
moveToHead(node);
}
}
void addToHead(DLinkedNode* node) {
node->prev = head;
node->next = head->next;
head->next->prev = node;
head->next = node;
}
void removeNode(DLinkedNode* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
}
void moveToHead(DLinkedNode* node) {
removeNode(node);
addToHead(node);
}
DLinkedNode* removeTail() {
DLinkedNode* node = tail->prev;
removeNode(node);
return node;
}
};
面向对象采用的设计模式有哪些
单例模式(有的叫单元素模式,单态模式) 工厂模式 观察者模式 命令链模式 策略模式
设计模式的六大原则
为什么要有补码? (为了更方便的实现减法运算)
一致性哈希
面向对象有哪些设计原则
OCP原则(也叫开闭原则): 开闭原则就是说对扩展开放,对修改关闭。 SRP原则(职责单一原则): 一个类只负责一项职责,可以降低类的复杂度,提高类的可读性,提高系统的可维护性,当修改一个功能时,可以显著降低对其他功能的影响。 OCP原则(里氏替换原则):任何基类可以出现的地方,子类一定可以出现。通俗的理解即为子类可以扩展父类的功能,但不能改变父类原有的功能。 DIP原则(依赖倒置原则):高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。通俗点说:要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。 LoD法则(迪米特法则):一个对象应该对其他对象保持最少的了解。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。 接口隔离原则:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
说说你对于 “不要用共享内存来通信,而应该用通信来共享内存” 的理解
在锁模式中,一块内存可以被多个线程同时看到,所以叫共享内存。线程之间通过改变内存中的数据来通知其他线程发生了什么,所以是通过共享内存来通信。锁是为了保护一个线程对内存操作的逻辑完整性而引入的一种约定,注意是一种约定而不是规则(一个线程可以不获取锁就操作内存,也可以解锁其他线程加的锁从而破坏保护,这种错误很难发现)。这种约定要每个线程的编写人员自觉遵守,否则就会出现多线程问题,如数据被破坏,死锁,饥饿等。 在go模式中,一块内存同一时间只能被一个线程看到,另外一个线程要操作这块内存,需要当前线程让渡所有权,这个所有权的让渡过程是“通信”。通信的原子性由channel封装好了,内存同一时间只能被同一线程使用,所以这种模式下不需要显示的锁。然而go模式也有约定,如果传递的是内存的指针,或者是控制消息,还是等于共享了内存,还是要保证将所有权让渡后, 不能再操作这块内存。
什么是双向链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
操作系统内存管理(分页、分段、段页式)
页式内存管理,内存分成固定长度的一个个页片。操作系统为每一个进程维护了一个从虚拟地址到物理地址的映射关系的数据结构,叫页表,页表的内容就是该进程的虚拟地址到物理地址的一个映射。页表中的每一项都记录了这个页的基地址。通过页表,由逻辑地址的高位部分先找到逻辑地址对应的页基地址,再由页基地址偏移一定长度就得到最后的物理地址,偏移的长度由逻辑地址的低位部分决定。一般情况下,这个过程都可以由硬件完成,所以效率还是比较高的。 页式内存管理的优点就是比较灵活,内存管理以较小的页为单位,方便内存换入换出和扩充地址空间。 分段存储管理方式的目的,主要是为了满足用户(程序员)在编程和使用上多方面的要求,其中有些要求是其他几种存储管理方式所难以满足的。因此,这种存储管理方式已成为当今所有存储管理方式的基础
段页式系统的基本原理,是分段和分页原理的结合,即先将用户程序分成若干个段,再把每个段分成若干个页,并为每一个段赋予一个段名。在段页式系统中,地址结构由段号、段内页号和页内地址三部分所组成。
浏览器输入网址到渲染的过程
详细
DNS解析URL地址、生成HTTP请求报文、构建TCP连接、使用IP协议选择传输路线、数据链路层保证数据的可靠传输、物理层将数据转换成电子、光学或微波信号进行传输
DNS解析过程
https讲一下?密钥怎么交换的?私钥存储在哪里
HTTPs 是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版,即HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL HTTPs的握手过程包含五步
http的流程
每个万维网的网点都有一个服务器进程,它不断的监听TCP端口80,以便发现是否有浏览器向它发出连接请求,一旦监听到连接建立请求,就通过三次握手建立TCP连接,然后浏览器会向服务器发出浏览某个页面的请求,服务器接着返回所请求的页面作为响应,然后TCP连接就被释放了。 这些响应和请求报文都遵循一定的格式,这就是HTTP协议所规定的。
SSL加密
session和cookie
http和https的区别
https的安全外壳是怎么实现的
HTTPS就是在原HTTP的基础上加上一层用于数据加密、解密、校验、身份认证的安全层SSL/TSL,用于解决HTTP存在的安全隐患 信息加密:所有信息都是加密传播,第三方无法窃听;内容经过对称加密,每个连接生成一个唯一的加密密钥; 身份认证:配备了身份认证,第三方无法伪造服务端(客户端)的身份 数据完整性校验:内容传输经过完整性校验,一旦报文被篡改,通信双方会立刻发现
TCP TIMEWAIT讲一下?为啥需要这个?
当断开连接时,客户端发送完ACK将处于TIME WAIT状态,保持2MSL,之后完全断开意义在于:
说一下TCP/IP
TCP/IP协议是包含TCP协议和IP协议,UDP(User Datagram Protocol)协议、ICMP(Internet Control Message Protocol) 协议和其他一些的协议的协议组 TCP/IP定义了电子设备(如计算机)如何连入因特网,以及数据如何在它们之间传输的标准.它是互联网中的基本通信语言或协议,在私网中它也被用作通信协议,当用户直接网络连接时,计算机应提供一个TCP/IP程序的标准实现,而且接受所发送的信息的计算机也应只有一个TCP/IP程序的标准实现 TCP/IP协议并不完全符合OSI 标准定制的七层参考模型,它采取了四层的层级结构 网络接口层:接收IP数据包并进行传输,从网络上接收物理帧,抽取IP 转交给下一层,对实际网络的网络媒体的管理,定义如何使用物理网络 ,如以太网。 网际层IP: 负责提供基本的数据封包传送功能,让每一块数据包都能打到目的主机,但不检查是否被正确接收,主要表现为IP协议 传输层:在此层中,它提供了节点的数据传送,应用程序之间的通信服务,主要是数据格式化,数据确认和丢失重传等。主要协议包括TCP和UDP 应用层:应用程序间沟通单层,如万维网(WWW)、简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等
fread和read的区别
read/write 操作文件描述符 (int型) fread/fwrite 操作文件流 (FILE*型) fread/fwrite 调用 read/write read/write是系统调用,要自己分配缓存,也就是说效率要自己根据实际情况来控制。 fread/fwrite是标准输入/输出函数,不需要自己分配缓存,对于一般情况具有较高的效率。
什么是内存栅栏
内存栅栏(Memory Barrier)就是从本地或工作内存到主存之间的拷贝动作。 仅当写操作线程先跨越内存栅栏而读线程后跨越内存栅栏的情况下,写操作线程所做的变更才对其他线程可见。关键字 synchronized 和 volatile 都强制规定了所有的变更必须全局可见,该特性有助于跨越内存边界动作的发生,无论是有意为之还是无心插柳。 在程序运行过程中,所有的变更会先在寄存器或本地 cache 中完成,然后才会被拷贝到主存以跨越内存栅栏。此种跨越序列或顺序称为 happens-before。 写操作必须要 happens-before 读操作,即写线程需要在所有读线程跨越内存栅栏之前完成自己的跨越动作,其所做的变更才能对其他线程可见
TCP拥塞控制
拥塞控制的最终受控变量是发送端向网络一次连续写入的数据量(收到其中第一个数据报的确认之前),称之为发送窗口(SWND),SWND 受接收方接受窗口(RWND)的影响。同时也受控于发送方的拥塞窗口(CWND)。SWND = min(RWND, CWND )
TCP为什么要三次握手、但是要四次挥手,握手第二步拆开行不行,挥手2 3步合并行不行,第三次握手确认的是什么能力
三次握手是为了避免僵尸连接,四次挥手是为了确保被断开方的数据能够全部完成传输,握手的第二部可以分开,不过需要增加一下状态。如果服务端没有数据要发送,挥手的2,3步可以合并,因为TCP是全双工的。第三次握手确认的是客户端时真实IP
TCP和UDP的区别
TCP 是面向连接的传输层协议,即传输数据之前必须先建立好连接, UDP 无连接 TCP 是点对点的两点间服务,即一条 TCP 连接只能有两个端点;UDP 支持一对一,一对多,多对一,多对多的交互通信 TCP 是可靠交付:无差错,不丢失,不重复,按序到达;UDP 是尽最大努力交付,不保证可靠交付 TCP 有拥塞控制和流量控制保证数据传输的安全性;UDP 没有拥塞控制,网络拥塞不会影响源主机的发送效率 TCP 是动态报文长度,即 TCP 报文长度是根据接收方的窗口大小和当前网络拥塞情况决定的。UDP 面向报文,不合并,不拆分,保留上面传下来报文的边界 TCP 首部开销大,首部 20 个字节;UDP 首部开销小,8 字节 如果数据完整性更重要,如文件传输、重要状态的更新等,应该选用 TCP 协议。如果通信的实时性较重要,如视频传输、实时通信等,则使用 UDP 协议
用udp会有什么问题
不可靠,不稳定 因为本身没有重传的控制机制,所以丢包率的可能是其最主要的问题
TCP是可靠的,为什么UDP还要去实现可靠连接
Linux查看网络连接的命令
使用netstat查看存在的网络连接 使用ping判断主机间联通情况
tcp可靠性传输怎么实现
序列号、确认应答、超时重传 窗口控制与高速重发控制/快速重传(重复确认应答) 拥塞控制 流量控制
虚函数的实现原理,继承的时候怎么实现的
在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(.text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。
局部变量分配在哪
分配在栈区
进程的栈有多大
32位Windows,一个进程栈的默认大小是1M,在vs的编译属性可以修改程序运行时进程的栈大小 inux下进程栈的默认大小是10M,可以通过 ulimit -s查看并修改默认栈大小 默认一个线程要预留1M左右的栈大小,所以进程中有N个线程时,Windows下大概有N M的栈大小 堆的大小理论上大概等于进程虚拟空间大小-内核虚拟内存大小。windows下,进程的高位2G留给内核,低位2G留给用户,所以进程堆的大小小于2G。Linux下,进程的高位1G留给内核,低位3G留给用户,所以进程堆大小小于3G
进程的最大线程数
32位windows下,一个进程空间4G,内核占2G,留给用户只有2G,一个线程默认栈是1M,所以一个进程最大开2048个线程。当然内存不会完全拿来做线程的栈,所以最大线程数实际值要小于2048,大概2000个 32位Linux下,一个进程空间4G,内核占1G,用户留3G,一个线程默认8M,所以最多380个左右线程(ps:ulimit -a 查看电脑的最大进程数,大概7000多个)
怎么快速把进程的栈用完
对函数进行递归调用 在函数中定义大对象
C++内存对齐
为什么要内存对齐:
如何进行内存对齐
class A {
int a, b;
char c;
};
class B {
int a, b;
double c;
};
class C {
char a;
int b;
double c;
};
class D {
int a;
double b;
int c;
};
class E {
int a;
double b;
int c;
char d;
};
int main() {
cout << sizeof(A) << " " << sizeof(B) << " "
<< sizeof(C) << " " << sizeof(D) << " " << sizeof(E) << endl;
return 0;
}
// output
// 12 16 16 24 24
引用和指针的区别?对const型常量可以取引用吗
首先引用可以视作对象的别名,指针拥有自己的地址空间,其中保存着所指对象的地址 区别:
http请求格式
HTTP 请求报文由请求行、请求头部、空行 和 请求包体 4 个部分组成
get post的区别 put delete 知道吗 put和post
http 1.X 2.0区别 ( 帧 流 推送 头部压缩 安全性等等
联合索引:b+树是什么状态
HTTP头部字段
详细
http头部可以包含二进制吗
http2支持二进制,http1.x不支持
MYSQL的事务
ACID特性,原子性、一致性、隔离性、持久性
多级缓存的由来和使用
计算机结构中CPU和内存之间一般都配有一级缓存、二级缓存来增加交换速度,这样当CPU调用大量数据时,就可避开内存直接从CPU缓存中调用,加快读取速度。 根据CPU缓存得出多级缓存的特点:
项目文件传输时怎么限速
在客户端进行文件传输时,每当上传限制大小数据,就sleep一下
用户态和内核态,为啥这样做,好处是什么
用户态和内核态是操作系统的两种运行级别,两者最大的区别就是特权级不同。用户态拥有最低的特权级,内核态拥有较高的特权级。运行在用户态的程序不能直接访问操作系统内核数据结构和程序。内核态和用户态之间的转换方式主要包括:系统调用,异常和中断进程
堆和栈的区别
堆是由低地址向高地址扩展;栈是由高地址向低地址扩展 堆中的内存需要手动申请和手动释放;栈中内存是由 OS 自动申请和自动释放,存放着参数、局部变量等内存 堆中频繁调用 malloc 和 free,会产生内存碎片,降低程序效率;而栈由于其先进后出的特性,不会产生内存碎片 堆的分配效率较低,而栈的分配效率较高 栈是操作系统提供的数据结构,计算机底层对栈提供了一系列支持:分配专门的寄存器存储栈的地址,压栈和入栈有专门的指令执行;而堆是由 C/C++函数库提供的,机制复杂,需要一些列分配内存、合并内存和释放内存的算法,因此效率较低
五种IO模型
bio nio aio 区别
详细 BIO (Blocking I/O):同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。 NIO (New I/O):同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。 AIO ( Asynchronous I/O):异步非阻塞I/O模型。异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
Select poll epoll
I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回 I/O 多路复用和阻塞 I/O 其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个 system call (select 和 recvfrom),而 blocking IO 只调用了一个 system call (recvfrom)。但是,用 select 的优势在于它可以同时处理多个 connection。 所以,如果处理的连接数不是很高的话,使用 select/epoll 的 web server 不一定比使用 multi-threading + blocking IO 的 web server 性能更好,可能延迟还更大。select/epoll 的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。 select 是最初解决 IO 阻塞问题的方法。用结构体 fd_set 来告诉内核监听多个文件描述符,该结构体被称为描述符集。由数组来维持哪些描述符被置位了。对结构体的操作封装在三个宏定义中。通过轮寻来查找是否有描述符要被处理 存在的问题:
poll 与 select 相比,poll 使用链表保存文件描述符,一没有了监视文件数量的限制,但其他三个缺点依然存在 epoll epoll 使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的 copy 只需一次。Epoll 是事件触发的,不是轮询查询的。没有最大的并发连接限制,内存拷贝,利用 mmap() 文件映射内存加速与内核空间的消息传递。 epoll 对文件描述符的操作有两种模式:LT(level trigger)和 ET(edge trigger LT 模式是默认模式 LT(level triggered)是缺省的工作方式,并且同时支持 block 和 no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的 fd 进行 IO 操作。如果你不作任何操作,内核还是会继续通知你的 ET 模式 ET(edge-triggered)是高速工作方式,只支持 no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过 epoll 告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个 EWOULDBLOCK 错误)。但是请注意,如果一直不对这个 fd 作 IO 操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once) ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll 工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死 LT 模式与 ET 模式的区别如下: LT 模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用 epoll_wait 时,会再次响应应用程序并通知此事件。 ET 模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用 epoll_wait 时,不会再次响应应用程序并通知此事件。
epoll的底层实现
epoll发展于介绍 epoll中就绪列表引用着就绪的socket,所以它应能够快速的插入数据。程序可能随时调用epoll_ctl添加监视socket,也可能随时删除。当删除时,若该socket已经存放在就绪列表中,它也应该被移除。所以就绪列表应是一种能够快速插入和删除的数据结构。双向链表就是这样一种数据结构,epoll使用双向链表来实现就绪队列。 既然epoll将“维护监视队列”和“进程阻塞”分离,也意味着需要有个数据结构来保存监视的socket。至少要方便的添加和移除,还要便于搜索,以避免重复添加。红黑树是一种自平衡二叉查找树,搜索、插入和删除时间复杂度都是O(log(N)),效率较好。epoll使用了红黑树作为索引结构
Linux的阻塞和非阻塞怎么体现
阻塞(休眠)调用是没有获得资源则挂起进程,被挂起的进程进入休眠状态,调用的函数只有在得到结果之后才返回,进程继续。 非阻塞(休眠)是不能进行设备操作时不挂起,或返回,或反复查询,直到可以进行操作为止,被调用的函数不会阻塞当前进程,而会立刻返回
进程间通信
管道 具名管道 消息队列 信号 信号量 共享内存 socket
进程上下文切换
1 保存当前进程的上下文 2 恢复某个先前被抢占的进程的上下文 3 将控制传递给这个新恢复的进程 详细 保存处理器上下文环境即 PSW、PC 等寄存器和堆栈内容,保存到内核堆栈中 调整被中断进程的 PCB 进程控制块信息,改变进程状态和其它信息 将进程控制块移到相应队列即阻塞队列或就绪队列 选择另一个进程执行 更新所选择进程的 PCB 进程控制块 更新内存管理的数据结构 恢复第二个进程的上下文环境
中断是什么
中断定义:指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。 硬件中断是由外设引发的, 软中断是执行中断指令产生的。 硬件中断的中断号是由中断控制器提供的, 软中断的中断号由指令直接指出, 无需使用中断控制器。 硬件中断是可屏蔽的, 软中断不可屏蔽。
中断处理过程
线程的上下文是什么、进程的上下文是什么
Linux文件系统,inode讲一讲?inode里存文件名称吗?
inode,中文名为索引结点,引进索引结点是为了在物理内存上找到文件块,所以 inode 中包含文件的相关基本信息,比如文件位置、文件创建者、创建日期、文件大小等,输入 stat 指令可以查看某个文件的 inode 信息 硬盘格式化的时候,操作系统自动将硬盘分成两个区域,一个是数据区,一个是 inode 区,存放 inode 所包含的信息,查看每个硬盘分区的 inode 总数和已经使用的数量,可以用 df 命令 在 linux 系统中,系统内部并不是采用文件名查找文件,而是使用 inode 编号来识别文件。查找文件分为三个过程:系统找到这个文件名对应的inode 号码,通过 inode 号码获得 inode 信息,根据 inode 信息找到文件数据所在的 block 读取数据 除了文件名之外的所有文件信息,都存储在 inode 之中
Linux chmod讲一讲,为啥有9位,分别对应什么
详细 Linux chmod(英文全拼:change mode)命令是控制用户对文件的权限的命令 Linux/Unix 的文件调用权限分为三级 : 文件所有者(Owner)、用户组(Group)、其它用户(Other Users) 9为代表三种用户的权限: rwxrwxrwx,前三位rwx表示文件所有者的权限,中间三位表示用户组的权限,最后三位表示其它用户的权限
STL中的map和unordered_map的实现
map实现使用红黑树,unordered_map使用hash表 简述
红黑树,B+树,跳表
怎么查看linux下哪个进程打开了哪些文件
详细 lsof
虚拟地址怎么转换成物理地址
对于段页式系统来说,首先是查找段号,在对应段内找到页号,在页内找到页内偏移,从而 程序地址:段号+页号+页内偏移
进程的调度算法
怎么解锁死锁
死锁四条件:
解决死锁的方案:
死锁避免:银行家算法
OSI七层模型
ARP协议
ARP(地址解析)协议是一种解析协议,本来主机是完全不知道这个 IP 对应的是哪个主机的哪个接口,当主机要发送一个 IP 包的时候,会首先查一下自己的 ARP 高速缓存表(最近数据传递更新的 IP-MAC 地址对应表),如果查询的 IP-MAC 值对不存在,那么主机就向网络广播一个 ARP 请求包,这个包里面就有待查询的 IP 地址,而直接收到这份广播的包的所有主机都会查询自己的 IP 地址,如果收到广播包的某一个主机发现自己符合条件,那么就回应一个 ARP 应答包(将自己对应的 IP-MAC 对应地址发回主机),源主机拿到 ARP 应答包后会更新自己的 ARP 缓存表。源主机根据新的 ARP 缓存表准备好数据链路层的的数据包发送工作
malloc底层的实现
Malloc 函数用于动态分配内存。为了减少内存碎片和系统调用的开销,malloc 其采用内存池的方式,先申请大块内存作为堆区,然后将堆区分为多个内存块,以块作为内存管理的基本单位。当用户申请内存时,直接从堆区分配一块合适的空闲块。Malloc 采用隐式链表结构将堆区分成连续的、大小不一的块,包含已分配块和未分配块;同时 malloc 采用显示链表结构来管理所有的空闲块,即使用一个双向链表将空闲块连接起来,每一个空闲块记录了一个连续的、未分配的地址 当进行内存分配时,Malloc 会通过隐式链表遍历所有的空闲块,选择满足要求的块进行分配;当进行内存合并时,malloc 采用边界标记法,根据每个块的前后块是否已经分配来决定是否进行块合并 Malloc 在申请内存时,一般会通过 brk 或者 mmap 系统调用进行申请。其中当申请内存小于128K 时,会使用系统函数 brk 在堆区中分配;而当申请内存大于 128K 时,会使用系统函数 mmap在映射区分配
逻辑地址---(分段硬件)>>> 线型地址 --- (分页硬件)>>> 物理地址 的过程,虚拟内存的实现
为什么引入虚拟内存
为了防止不同进程同一时刻在物理内存中运行而对物理内存的争夺和践踏,采用了虚拟内存。
虚拟内存技术使得不同进程在运行过程中,它所看到的是自己独自占有了当前系统的 4G 内存。所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。
虚拟内存的好处:
虚拟内存的代价:
64位操作系统下,实现一个链接式的hash map,保存n组(key, value) 对,假设key, value各占8字节,问一共需要占多少字节
64位操作系统,指针8字节,假设hash函数有m个值,则8*m + (8+8+8)*n = 8m + 24n 8m表示开始的m个指针大小,(8+8+8)分别表示指针、key和value
延时队列怎么实现
详细 什么是延时队列?顾名思义:首先它要具有队列的特性,再给它附加一个延迟消费队列消息的功能,也就是说可以指定队列中的消息在哪个时间点被消费。 对于C++来说,可以直接使用优先队列,如在队列中存储消息id以及过期时间,队列自动按照时间排序,每次从队列中拿第一个消息进行消费。
怎么解决缓存击穿?怎么解决缓存雪崩?
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大且不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。 解决方案:
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力 解决方案: 设置热点数据永远不过期 加互斥锁降低从数据库中读取数据频率
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库 解决方案:
10M带宽,下载速度大约有多少
10 M 带宽单位是bps,而我们常使用的是Byte,因此约为10/8=1.25M。上行/上传速度会更慢,大约只有256K
客户端和服务端建立socket连接的过程,相关的方法
路由器和交换机有什么区别,分别工作在哪一层
交换机,工作在 OSI 第二层(数据链路层),根据 MAC 地址进行数据转发。 路由器,工作在 OSI 第三次(网络层),根据 IP 进行寻址转发数据包
fork 与 vfork
fork与vfork都是创建进程,vfork创建的子进程是与父进程共享地址空间,而fork创建的子进程是父进程的副本,它们的区别如下
vfork为什么需要exit()而不用return
如果你在vfork中return了,那么,这就意味main()函数return了,注意因为函数栈父子进程共享,所以整个程序的栈就跪了。
#include
#include
#include
using namespace std;
int gdata = 1;
int main(){
pid_t pid;
pid = fork(); //更改为vfork
int tmp = 10;
if(pid == -1) cout << "fork error"<< endl;
else if(pid == 0){
cout << "son process, my parent is: " << getppid() << endl;
gdata = 11;
cout << "in son: " << gdata << endl;
tmp = 100;
cout << "son tmp: " << tmp << endl;
exit(0);
}else {
cout << "parent process, my pid is " << getpid() << endl;
cout << "in parent: " << gdata << endl;
cout << "in parent: " << tmp << endl;
}
return 0;
}
/*
当前输出:
son process, my parent is: 9743
in son: 11
son tmp: 100
parent process, my pid is 9743
in parent: 1
in parent: 10
将fork改为vfork
son process, my parent is: 9735
in son: 11
son tmp: 100
parent process, my pid is 9735
in parent: 11
in parent: 10
*/
客户端怎么校验https的证书是否合法
详细 数字证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA 的信息、有效时间、证书序列号等信息的明文,同时包含一个签名 客户端在对服务器say hello之后,服务器将公开密钥证书发送给客户端,注意这个证书里面包含了公钥+各种信息+签名(私钥对各种信息加密后生成签名),客户端收到公开密钥证书后,相当于收到了一个包裹里面有公钥+各种信息+签名,怎么样使用这三个数据来校验呢,很简单,公钥加密,私钥解,私钥加密公钥也可以解,只要利用公钥对签名进行解密,然后最和各种信息做比较就可以校验出证书的合法性。
两个进程某变量用gdb调试打印出的地址是否会一样
如果使用指针访问一个区域,指针+1 、指针-1可能会访问到什么?为什么
一致性哈希
详细
通过hash环来实现负载均衡,将不同的服务器hash映射到一致性hash环上,当服务请求到来时,使用hash将其映射到hash环上,然后可以采用如顺时针寻找的方法选择距其最近的服务器进行服务。 当服务器较少或hash公式不够好时,可能出现大多数请求都会落在同一个服务器上,这就是数据倾斜,可以采用添加服务器、虚拟节点、更换一致性hash的方法进行解决。
Tcp: 拔网线之后连接是否存在 为什么 (记得tcp的长连接是有一个类似心跳检测的机制,忘了叫啥了,面试官问我心跳检测是在传输层吗还是应用层 ,我说应用层有心跳检测,但tcp那层也有类似的,后来回来看了下tcp的保活,
操作系统如何识别tcp连接
C++锁
互斥锁(Mutex) -- 互斥锁用于控制多个线程对他们之间共享资源互斥访问的一个信号量。 条件锁 -- 条件锁就是所谓的条件变量 自旋锁 读写锁
vector、stack、queue这些容器是怎么实现的
stack 底层一般用 deque 实现,封闭头部即可,不用 vector 的原因应该是容量大小有限制,扩容耗时 queue 底层一般用 deque 实现,封闭头部的出口和前端的入口即可 priority_queue 的底层数据结构一般为 vector 为底层容器,堆 heap 为处理规则来管理底层容器实现
并发编程三要素
原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行 可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 有序性:即程序执行的顺序按照代码的先后顺序执行,首先什么是指令重排序,一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。处理器在进行重排序时也是会考虑指令之间的数据依赖性。指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。
短网址服务 -- 将很长的网址连接设计成短网址
如果用了 301,Google、百度等搜索引擎,搜索的时候会直接展示真实地址,那我们就无法统计到短地址被点击的次数了,也无法收集用户的 Cookie、User Agent 等信息,这些信息可以用来做很多有意思的大数据分析,也是短网址服务商的主要盈利来源 详细
零拷贝
“零拷贝”:在整个发送数据过程中,数据的复制是必不可少的,这里数据复制分两种类型,一种是CPU参与的一个字节一个字节处理的数据复制,一个是CPU不用参与,通过专有硬件DMA参与的,批量数据复制。自然,不用CPU参与的数据复制性能高。而“零拷贝”所说的拷贝,其实指的是,减少CPU参与的数据拷贝,最好减少到零次,但是各种实现方式里,很多种都只是减少一次两次,并没有直接让CPU参与的数据复制数归零
通过mmap实现的零拷贝I/O
发出mmap系统调用,导致用户空间到内核空间的上下文切换(第一次上下文切换)。通过DMA引擎将磁盘文件中的内容拷贝到内核空间缓冲区中(第一次拷贝: hard drive ——> kernel buffer)。 mmap系统调用返回,导致内核空间到用户空间的上下文切换(第二次上下文切换)。接着用户空间和内核空间共享这个缓冲区,而不需要将数据从内核空间拷贝到用户空间。因为用户空间和内核空间共享了这个缓冲区数据,所以用户空间就可以像在操作自己缓冲区中数据一般操作这个由内核空间共享的缓冲区数据 发出write系统调用,导致用户空间到内核空间的上下文切换(第三次上下文切换)。将数据从内核空间缓冲区拷贝到内核空间socket相关联的缓冲区(第二次拷贝: kernel buffer ——> socket buffer)。 write系统调用返回,导致内核空间到用户空间的上下文切换(第四次上下文切换)。通过DMA引擎将内核空间socket缓冲区中的数据传递到协议引擎(第三次拷贝: socket buffer ——> protocol engine 通过mmap实现的零拷贝I/O进行了4次用户空间与内核空间的上下文切换,以及3次数据拷贝。其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝
快照读在读提交和可重复读(RR和RC)模式下的问题
事务总能够读取到,自己写入(update /insert /delete)的行记录 RC下,快照读总是能读到最新的行数据快照,当然,必须是已提交事务写入的 RR下,某个事务首次read记录的时间为T,未来不会读取到T时间之后已提交事务写入的记录,以保证连续相同的read读到相同的结果集 在RC级别下每次都是读取最新的快照版本,在RR级别下是事务开启时生成一个全局快照,后续的快照读都读取这个快照
索引失效
1.有or必全有索引; 2.复合索引未用左列字段; 3.like以%开头; 4.需要类型转换; 5.where中索引列有运算; 6.where中索引列使用了函数; 7.如果mysql觉得全表扫描更快时(数据少);
内存泄漏
内存泄漏是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制。
检查、定位内存泄漏:
检查方法: 在 main 函数最后面一行,加上一句_CrtDumpMemoryLeaks()。调试程序,自然关闭程序让其退出,查看输出: 被{}包围的 数字x 就是我们需要的内存泄漏定位值 定位代码位置: 在 main 函数第一行加上_CrtSetBreakAlloc(x);意思就是在申请 x这块内存的位置中断。然后调试程序,程序中断了,查看调用堆栈。加上头文件#include
算法题
由于篇幅有限,后续再更新