TCP 为什么要三次握手?两次握手会有什么问题?、

三次握手可以让服务器和客户端确定序列号seq,并且因为是全双工通信,如果两次握手客户端没办法确认和服务器的连接,因为客户端也要发送信息。

Redis 缓存穿透、击穿、雪崩分别是什么?怎么解决?

1.缓存穿透:查了不存在的数据。用户请求一个数据库里根本没有的数据。

        缓存空值:查不到就把null存入缓存,设置短过期时间 

        布隆过滤器:把所有存在的key放入布隆过滤器,不存在的直接返回,不查数据库。

2.缓存击穿:是查热点Key过期,一个访问量极高的 Key,刚好过期了。一瞬间大量请求同时打到数据库。

        1.设置热点key永不过期。

        2.设置互斥锁,只让一个线程去查数据库,其他等待。

3.缓存雪崩:大量key同时过期大量缓存同一时间全部失效,所有请求一起打到数据库。

        1.过期时间加随机值

        2.搭建redis集群

        3.读物降级/熔断机制

        4.缓存永不过期

进程和线程的核心区别是什么?什么时候用多进程、什么时候用多线程?

进程是程序执行的最小单位,线程是cpu调度的最小单位,线程间资源可以共享,并且一个线程出现异常,其他线程会收到影响,进程是独立资源,一个进程出现问题其他进程不会受影响。进程切换开销大,线程切换小。

多进程:需要稳定性高,一个崩了不能拖垮整体,需要强隔离性,安全需求高,程序之间基本不通信。Nginx,浏览器多标签等。

多线程:需要频繁通信,追求高性能,低开小,并发处理等。

MySQL 索引什么时候会失效?至少说 3 种情况。

违反了最左前缀原则,对索引列进行了计算,采用了!=,<>,IS NOT NULL等运算符,使用了模糊查询,隐式类型转换,采用or连接非索引。

死锁的 4 个必要条件是什么?如何预防死锁?

互斥条件,循环等待,持有并保持,不可剥夺。

破环其中一个条件就行,采用银行家算法,采用鸵鸟思想认为不会发生死锁,

  • 破坏请求与保持一次性申请所有需要的资源,要么全分配,要么全不分配。

  • 破坏不可剥夺进程申请新资源得不到时,主动释放已持有资源;或系统强行剥夺占用的资源。

  • 破坏环路等待统一资源编号,按固定顺序申请资源,所有进程都按相同顺序获取锁 / 资源,杜绝循环等待。

  • 破坏互斥尽量减少独占互斥使用,能共享的资源设计为共享访问

HTTP 和 HTTPS 的区别?HTTPS 是怎么保证安全的?

 HTTP      HTTPS
端口         80端口 443端口
安全性 明文传输,无加密   加密传输安全
协议层级 应用层协议 HTTP+SLS/TLS组合,在http和tcp中间
证书 无需证书 需要配置CA数字证书
性能

加密,完整性校验,身份验证。保证安全

1.身份验证通过CA数字证书验证服务器身份

2.混合加密防止窃听(对称和非对称加密)

3. 摘要校验(防篡改)

MD5/SHA 哈希摘要

Linux 下查看内存、CPU、端口、进程分别用什么命令?

top free -h  ps -ef netstat -tulnp

epoll 的水平触发 LT 和边缘触发 ET 有什么区别?

水平触发:只要缓冲区有数据未读完,epoll 每次调用都会持续通知该文件描述符就绪,支持阻塞、非阻塞,编程简单、不易出错

边缘触发:只有数据第一次到达的瞬间(状态从无到有)只触发一次;如果没把缓冲区数据读完,后续不会再重复通知。只支持非阻塞,性能高但是容易出错。

数据库事务的四大特性 ACID 分别是什么意思?

A:原子性:要么全成功要么全不成功

C:一致性:事务前后数据完整性约束不变

I:隔离性:事务间互相隔离互不干扰

D:一致性:事务一旦提交,永久生效

智能指针 shared_ptr 和 weak_ptr 的区别?weak_ptr 解决什么问题?

shared_ptr 是共享指针,里面存储了一个引用计数器,每当调用赋值构造或者拷贝构造都会+1,直到他为0时才会释放。

weak_ptr 是弱指针,为了解决共享指针的循环引用问题,每当调用赋值构造或者拷贝构造引用计数不会增加,weak_ptr 不能直接访问,必须调用 .lock() 提升为 shared_ptr 再使用

C++:虚函数和纯虚函数的区别是什么?

虚函数是实现C++动态多态的,纯虚函数是的类是一个抽象类,继承的子类必须重写纯虚函数,

虚函数:基类必须有实现,子类可以重写也可以不重写。

纯虚函数:基类没有实现(或类外实现),子类必须重写,否则子类也成抽象类。

拷贝构造函数为什么必须传引用

如果是普通的值传递,当进行拷贝构造的调用时,会再次调用拷贝构造,会无限循环下去。

指针和引用的区别?

指针:可以不初始化,占用内存,可以指向别的变量。* 解引用->,存在多级指针,支持++--

引用:变量的别名,不占用内存,必须初始化,一旦绑定,终身不变。直接用,& 仅定义时用,不存在多级引用,不支持++--

new/delete 与 malloc/free 的区别?

new/delete:关键字,开辟内存不用给出开辟的大小,自动调用构造函数和析构函数,开辟失败返回异常,返回类型是对象的指针类型,可以重载,类型安全。

malloc/free:库函数,开辟内存需要指出内存大小,释放开辟不调用构造析构,开辟失败返回null,返回对象是void*,不能重载

什么是虚表、虚指针?多态怎么实现?

虚表是一个类中存放虚函数地址的表,每个类只有一个,虚指针是每个对象一个指向所属类的虚表,通过虚指针找到虚表中对应的虚函数地址,进行调用,实现多态。

  • 虚表在编译阶段就构建好,存放在只读数据段,不属于对象内存;
  • 虚指针是对象隐含成员,创建对象时由编译器自动初始化指向本类虚表。

构造函数为什么不能是虚函数?

虚表是在编译阶段就构建好的,构造函数需要初始化时才确定,两者之间矛盾,所以不能是。

并且虚函数是为了让子类重写,运行时选版本,但是构造函数不能被继承,不能被重写,是每个类独有的,两者机制不兼容。

深拷贝和浅拷贝区别?

深拷贝是重新开辟出一块空间,复制进去,修改内容不影响原来的,浅拷贝是让两个指针指向同一片内存,修改任意一个都会影响另一个。

static 和 const 用法与作用?

static修饰全局变量:作用域整个文件内部,生命周期是整个程序运行期间,存放在全局区

修饰局部变量:生生命周期是整个程序运行期间,但是只能在函数内部进行访问,存放在全局区

修饰类成员变量:不属于类,类外定义,生命周期是整个程序运行期间。存放在全局区

修饰类的成员函数:单例,没有this指针,只能访问类的静态成员变量函数,类名:: 直接调用,不用创建对象。存放在全局区

const修饰变量:变量变为只读常量,初始化后不能修改,存在常量区。

修饰函数形参:形参在函数内部不能进行修改。函数内部不能修改该参数,防止误改,const string& s 避免拷贝 + 禁止修改

修饰 this 指针:这个成员函数里,不能修改类的任何普通成员变量

移动语义、右值引用解决什么问题?

解决临时对象产生的无谓拷贝开销,把 “拷贝对象” 变成 “偷资源所有权”,提升性能。

互斥锁、条件变量作用?

保证多线程下临界资源同一时刻只被一个线程访问,解决线程竞争、数据乱序问题。

条件变量:让线程在不满足条件时主动阻塞等待,条件满足后再唤醒,替代轮询,节省 CPU。需要配合互斥锁使用

僵尸进程、孤儿进程是什么?

僵尸进程:子进程先退出,父进程还在运行,但父进程没有调用回收子进程退出状态,子进程进程资源没完全释放,变成僵尸进程

孤儿进程:父进程先退出,子进程还在运行,丢失了父进程,被init进程领养。

线程同步有哪些方式?

mutex互斥锁,条件变量,信号量,原子操作,读写锁,自旋锁

并发和并行区别?

并行:交替执行,看起来一起跑,一个 CPU 核心上时间片轮转交替执行,看起来像是一起执行。

并发:需要多核 CPU,同一时刻多个任务真正同时在不同核心上一起执行。

并发是逻辑上同时,并行是物理上同时。

线程池的好处与原理?

提前创建一批线程放入队列中,任务来了可以取出一个线程使用,任务完成后线程重新放入线程池中,可以避免频繁的销毁和创建带来的开销。

  • 降低资源消耗避免频繁创建、销毁线程的系统开销,复用线程。

  • 提高响应速度任务到达不用等新建线程,直接拿池中空闲线程立刻执行。

  • 方便统一管理可以控制最大并发数、队列排队、拒绝策略,防止无限创建线程把系统拖垮。

  • 可定时 / 延时执行

select/poll/epoll 区别?

selete:最大连接数是1024,每次调用都全量遍历所有 fd,还要拷贝用户态到内核态O (n) ,select只支持水平触发 LT

poll:无固定上限,不用位集,结构优化,但还是全量轮询遍历O (n) 、poll只支持水平触发 LT

epoll:最大连接数无限制,事件驱动,内核只返回已就绪 fd,不用遍历全部O (1),epoll 支持 LT 水平触发 + ET 边缘触发,ET 更高效,适合高并发 Reactor 模型

软链接和硬链接区别?

软连接:软链接是存路径的独立文件,可跨分区、可链目录,原文件删除就失效。

硬链接:同一 inode 多个文件名,不能跨分区、不能链目录,删原文件仍可用

僵尸进程如何避免?

父进程主动调用 wait /waitpid

子进程退出后,父进程主动调用 wait()waitpid()及时回收子进程退出状态,PCB 资源立刻释放,不会产生僵尸进程。

捕获 SIGCHLD 信号

子进程退出会给父进程发 SIGCHLD 信号,父进程注册信号处理函数,在信号函数里循环 waitpid 批量回收所有已退出子进程,高效解决僵尸。

让父进程先退出(托管给 init)

父进程提前退出,子进程变成孤儿进程,被 init 进程(PID=1) 收养,init 会自动回收子进程,不会产生僵尸。

二次 fork(最优雅方案)

父进程 fork 出子进程,子进程再 fork 出孙子进程

    更多推荐