├── README.md ├── 【NO.100】熬夜肝了这一份C++开发详细学习路线.md ├── 【NO.101】什么是DPDK?DPDK的原理及学习路线总结.md ├── 【NO.102】腾讯同事内推的那位Linux CC++后端开发同学面试没过.md ├── 【NO.103】从四线城市程序员,到深圳大厂架构师,我只用了半年.md ├── 【NO.104】详解 epoll 原理【Redis,Netty,Nginx实现高性能IO的核心原理】.md ├── 【NO.105】这篇Redis文章,图灵看了都说好.md ├── 【NO.106】一文深入理解 Kubernetes.md ├── 【NO.107】HTTP 请求之合并与拆分技术详解.md ├── 【NO.108】一文带你理解云原生.md ├── 【NO.109】为什么磁盘存储引擎用 b+树来作为索引结构?.md ├── 【NO.10】CPIP 寻址.md ├── 【NO.110】一致性哈希算法及其在分布式系统中的应用.md ├── 【NO.111】如何实现一个malloc.md ├── 【NO.112】Linux网络设计之异步IO机制与io_uring.md ├── 【NO.113】glibc malloc源码分析.md ├── 【NO.114】C++内存管理及内存问题的分析.md ├── 【NO.115】一大波C++进阶知识干货分享,请接收!.md ├── 【NO.116】线程池的优点及其原理,简单、明了。.md ├── 【NO.117】2022年腾讯C++研发类笔试面试试题及答案.md ├── 【NO.118】C++后台开发,以我之见.md ├── 【NO.119】ETCD介绍—etcd概念及原理方面分析.md ├── 【NO.11】CPIP 协议.md ├── 【NO.120】十个问题理解Linux epoll工作原理.md ├── 【NO.121】GPU虚拟化,算力隔离,和qGPU.md ├── 【NO.122】一文入门 Kafka.md ├── 【NO.123】浏览器性能优化实战.md ├── 【NO.124】一文掌握 Linux 内存管理.md ├── 【NO.125】Linux网络编程 零拷贝 :sendfile、mmap、splice、tee.md ├── 【NO.126】TCP将成为历史?看看谷歌的QUIC协议都做了些什么你就知道了.md ├── 【NO.127】MySQL 的锁机制,那么多的锁,该怎么区分?.md ├── 【NO.128】详解 MySQL 的事务以及隔离级别.md ├── 【NO.129】glibc内存管理那些事儿.md ├── 【NO.12】TCPIP 邮件.md ├── 【NO.130】红黑树 与 B+树区别和应用场景.md ├── 【NO.131】CC++ Linux 后台服务器开发高级架构师学习知识路线总结.md ├── 【NO.132】C++后台服务器开发必备技能——数据库连接池.md ├── 【NO.133】深入理解异步IO+epoll+协程.md ├── 【NO.134】redis 数据类型详解 以及 redis适用场景场合(详细).md ├── 【NO.136】OSI 七层模型和TCPIP模型及对应协议(详解).md ├── 【NO.137】腾讯T9T3.1级别的后台服务器开发技术大佬是怎样炼成的?.md ├── 【NO.138】TCP和UDP详解.md ├── 【NO.139】网络IO模型的介绍引出nginx的网络IO模型.md ├── 【NO.13】Nginx负载均衡原理与实战经典案例.md ├── 【NO.140】Nginx底层原理:解析Nginx为什么并发数可以达到3w!.md ├── 【NO.141】常见C++面试题及基本知识点总结.md ├── 【NO.142】百度面试题(C++方向).md ├── 【NO.143】C++面试集锦( 面试被问到的问题).md ├── 【NO.144】cc++常见面试题精选.md ├── 【NO.145】分布式事务解决方案.md ├── 【NO.146】一个故事看懂CPU的SIMD技术.md ├── 【NO.147】聊一聊数据库中的锁.md ├── 【NO.148】如何写代码 —— 编程内功心法.md ├── 【NO.149】性能优化知多少.md ├── 【NO.14】TCP 的封包格式:TCP 为什么要粘包和拆包?.md ├── 【NO.150】Linux 江湖系列阶段性总结.md ├── 【NO.151】p2p之网络穿透NAT,NAT、穿透的原理.md ├── 【NO.152】同步与异步,回调与协程.md ├── 【NO.153】Linux专家谈如何学习Linux,以及Linux的职业发展.md ├── 【NO.154】理解MySQL——索引与优化篇.md ├── 【NO.155】skynet源码分析:服务,Actor模型,lua接口编程,demo演示Actor编程思维.md ├── 【NO.156】 设计模式之工厂设计模式.md ├── 【NO.157】 Nginx 防盗链.md ├── 【NO.158】一文读懂数据库优化之分库分表.md ├── 【NO.159】深入剖析虚拟内存工作原理.md ├── 【NO.15】网络收发与Nginx事件间的对应关系.md ├── 【NO.160】Redis vs Tendis:冷热混合存储版架构揭秘.md ├── 【NO.161】深入理解TCPIP 协议栈.md ├── 【NO.162】作为C++后端开发程序员,应该彻底理解Epoll实现原理.md ├── 【NO.163】协程的原理和应用,C++现实协程.md ├── 【NO.164】redis源码分析——内存布局.md ├── 【NO.165】服务器开发必备-【数据库】Redis集群篇.md ├── 【NO.166】深入解读无服务器架构下的数据库.md ├── 【NO.167】Facebook、谷歌、微软和亚马逊的网络架构揭秘.md ├── 【NO.168】Nginx 架构浅析.md ├── 【NO.169】内核调试技巧--systemtap定位丢包原因.md ├── 【NO.16】Nginx源码分析与实践---编写一个简单的Http模块.md ├── 【NO.170】五分钟了解互联网Web技术发展史.md ├── 【NO.171】快速实现一个分布式定时器.md ├── 【NO.172】MySQL 深入学习总结.md ├── 【NO.173】QUIC 协议原理浅解.md ├── 【NO.174】微信直播聊天室架构演进.md ├── 【NO.175】2021 有哪些不容错过的后端技术趋势.md ├── 【NO.176】Redis 多线程网络模型全面揭秘.md ├── 【NO.177】网络 IO 演变发展过程和模型介绍.md ├── 【NO.178】操作系统与存储:解析Linux内核全新异步IO引擎io_uring设计与实现.md ├── 【NO.179】云时代,我们需要怎样的数据库?.md ├── 【NO.17】三次握手时,客户端发送的 SYN 报文为什么会被丢弃?.md ├── 【NO.180】STGW 下一代互联网标准传输协议QUIC大规模运营之路.md ├── 【NO.181】linux:零拷贝( zero-copy )技术原理详解.md ├── 【NO.182】【源码剖析】MemoryPool —— 简单高效的内存池 allocator 实现.md ├── 【NO.183】STL 红黑树源码分析.md ├── 【NO.184】C++数据结构与算法:布隆过滤器(Bloom Filter)原理与实现.md ├── 【NO.185】ringbuffer 消息队列 内存池 性能优化利器.md ├── 【NO.186】ZeroMQ无锁队列的原理与实现.md ├── 【NO.187】网络不通?服务丢包?这篇 TCP 连接状态详解及故障排查,收好了.md ├── 【NO.188】Linux编程:一个异步信号处理引起死锁问题的思考.md ├── 【NO.189】手把手教你纯c实现异常捕获try-catch组件.md ├── 【NO.18】为什么要使用 TCP keepalive?CC++代码实现TCP keepalive.md ├── 【NO.190】skynet源码结构、启动流程以及多线程工作原理.md ├── 【NO.191】P2P通信原理与实现(C++).md ├── 【NO.192】linux:线程池的作用、应用场景、工作原理与纯C实现.md ├── 【NO.193】[ C++ ] 一篇带你了解C++中动态内存管理.md ├── 【NO.194】我们常说的短连接长连接和socket和http到底有什么关系.md ├── 【NO.195】一文掌握谷歌 C++ 单元测试框架 GoogleTest.md ├── 【NO.196】redis、nginx、memcached等网络编程模型详解.md ├── 【NO.197】【底层原理】一层层剥开文件系统的面纱,彻底理解Linux文件系统.md ├── 【NO.198】互斥锁、自旋锁、原子操作的原理、区别及应用场景.md ├── 【NO.199】DNS异步请求池原理与实现.md ├── 【NO.19】C++中STL用法超详细总结(收藏级).md ├── 【NO.1】【数据结构】史上最好理解的红黑树讲解,让你彻底搞懂红黑树.md ├── 【NO.200】[底层原理]Socket 究竟是什么 为啥网络离不开 Socket?.md ├── 【NO.201】后端开发-MySQL数据库相关流程图原理图.md ├── 【NO.202】HTTP—TCPIP—SOCKET理解及浅析.md ├── 【NO.203】C++后端程序员必须彻底搞懂Nginx,从原理到实战详解.md ├── 【NO.204】图文详解Linux的IO模型和相关技术.md ├── 【NO.205】详解从网络IO到IO多路复用.md ├── 【NO.206】C++后端程序员必须彻底搞懂Nginx,从原理到实战(高级篇).md ├── 【NO.207】详解内存池设计与实现.md ├── 【NO.208】C++ 中的多线程的使用和线程池建设.md ├── 【NO.209】mysql锁机制详解.md ├── 【NO.20】一文掌握google开源单元测试框架Google Test.md ├── 【NO.210】TCPIP协议详解.md ├── 【NO.211】【网络】【操作系统】详解select、poll、epoll.md ├── 【NO.212】亿级流量架构之网关设计思路、常见网关对比.md ├── 【NO.213】【Redis】利用 Redis 实现分布式锁.md ├── 【NO.214】Linux 的 IO 通信 以及 Reactor 线程模型详解.md ├── 【NO.215】MySQL数据库的性能的影响分析及优化.md ├── 【NO.216】Nginx-接入层Nginx架构及模块介绍.md ├── 【NO.217】深入Linux CC++ Timer定时器的实现核心原理.md ├── 【NO.218】深入理解MySQL索引和优化丨MySQL的核心原理.md ├── 【NO.219】perf-网络协议栈性能分析.md ├── 【NO.21】高性能异步io机制:io_uring.md ├── 【NO.220】【epoll】epoll多路复用和Reactor设计思想.md ├── 【NO.221】实例分析Linux内存泄漏检测方法.md ├── 【NO.222】Linux基础组件之无锁消息队列ypipeyqueue详解.md ├── 【NO.223】Nginx 的异步非阻塞体现在哪里?从理论分析到源码验证.md ├── 【NO.224】【NO.224】Redis:6.0多线程无锁(lock-free)设计和多线程Reactor模式助力Redis QPS更上一层楼.md ├── 【NO.225】从Reactor模式俯瞰Nginx,你会发现你与高手的差距就在设计模式上.md ├── 【NO.226】Redis 多线程网络模型全面揭秘.md ├── 【NO.227】网络 IO 演变发展过程和模型介绍.md ├── 【NO.228】操作系统与存储:解析Linux内核全新异步IO引擎io_uring设计与实现.md ├── 【NO.229】云时代,我们需要怎样的数据库?.md ├── 【NO.22】「查缺补漏」巩固你的Nginx知识体系.md ├── 【NO.230】STGW 下一代互联网标准传输协议QUIC大规模运营之路.md ├── 【NO.231】Redis 多线程网络模型全面揭秘.md ├── 【NO.232】网络 IO 演变发展过程和模型介绍.md ├── 【NO.233】编译原理初学者入门指南.md ├── 【NO.234】分布式之系统底层原理.md ├── 【NO.235】Kubernetes 入门&进阶实战.md ├── 【NO.236】万字详解:腾讯如何自研大规模知识图谱 Topbase.md ├── 【NO.237】浅谈如何搭建知识体系.md ├── 【NO.238】为什么微信推荐这么快?.md ├── 【NO.239】万字详文告诉你如何做 Code Review.md ├── 【NO.23】一篇文章彻底搞懂websocket协议的原理与应用(一).md ├── 【NO.240】HTTP3 原理实战.md ├── 【NO.241】协程及c++ 20原生协程研究报告.md ├── 【NO.242】从零开始学架构(上篇).md ├── 【NO.243】c++异步从理论到实践 -总览篇.md ├── 【NO.244】大数据组件选型对比及架构.md ├── 【NO.245】由CPU高负载引发内核探索之旅.md ├── 【NO.246】(建议收藏)万字长文总结分布式事务,总有一款适合你.md ├── 【NO.247】MySQL WriteSet并行复制分析.md ├── 【NO.248】从一道数据库面试题彻谈MySQL加锁机制.md ├── 【NO.249】从零开始学架构(下篇).md ├── 【NO.24】Makefile入门(超详细一文读懂).md ├── 【NO.250】C++一行代码实现任意系统函数 Hook.md ├── 【NO.251】如何优雅地记录操作日志?.md ├── 【NO.252】美团基于知识图谱的剧本杀标准化建设与应用.md ├── 【NO.253】美团商品知识图谱的构建及应用.md ├── 【NO.254】GraphQL及元数据驱动架构在后端BFF中的实践.md ├── 【NO.255】美团外卖实时数仓建设实践.md ├── 【NO.256】FlutterWeb性能优化探索与实践.md ├── 【NO.257】百亿规模API网关服务Shepherd的设计与实现.md ├── 【NO.258】多业务建模在美团搜索排序中的实践.md ├── 【NO.259】Spock单元测试框架介绍以及在美团优选的实践.md ├── 【NO.25】从CPU架构开始,讲清楚Linux进程调度和管理.md ├── 【NO.260】一款可以让大型iOS工程编译速度提升50%的工具.md ├── 【NO.261】CMake基础 第1节 初识CMake.md ├── 【NO.262】CMake基础 第2节 分离编译.md ├── 【NO.263】CMake基础 第3节 静态库.md ├── 【NO.264】CMake基础 第4节 动态库.md ├── 【NO.265】CMake基础 第5节 安装项目.md ├── 【NO.266】CMake基础 第6节 生成类型.md ├── 【NO.267】CMake基础 第7节 编译标志.md ├── 【NO.268】CMake基础 第8节 包含第三方库.md ├── 【NO.269】CMake基础 第9节 使用Clang编译.md ├── 【NO.26】【引路者】学习DPDK,须知多队列网卡的原理.md ├── 【NO.270】CMake基础 第10节 使用ninja构建.md ├── 【NO.271】CMake基础 第9节 使用Clang编译.md ├── 【NO.272】CMake基础 第10节 使用ninja构建.md ├── 【NO.273】CMake基础 第11节 导入目标.md ├── 【NO.274】CMake基础 第12节 设置C++标准.md ├── 【NO.275】CMake基础 第13节 构建子项目.md ├── 【NO.276】CMake基础 第14节 在文件中进行变量替换.md ├── 【NO.277】CMake基础 第15节 使用Protobuf生成源文件.md ├── 【NO.278】CMake基础 第16节 创建deb文件.md ├── 【NO.279】CMake基础 第17节 Clang分析器.md ├── 【NO.27】Netmap一个用于快速数据包IO的新框架.md ├── 【NO.280】CMake基础 第18节 Boost单元测试框架.md ├── 【NO.281】五种网络IO模型详解.md ├── 【NO.282】Redis不是一直号称单线程效率也很高吗,为什么又采用多线程了?.md ├── 【NO.283】C++ 协程的近况、设计与实现中的细节和决策.md ├── 【NO.284】网络四层、七层负载均衡的区别.md ├── 【NO.285】Redis源码分析.md ├── 【NO.286】后端开发程序员须彻底搞懂的 IO 底层原理.md ├── 【NO.287】Linux网络编程-UDP和TCP协议详解.md ├── 【NO.288】从底层原理出发,了解Linux内核之内存管理.md ├── 【NO.289】图解 epoll 是如何工作的及epoll实现原理.md ├── 【NO.28】【涨知识】腾讯、京东、爱奇艺都在用DPDK,看看用它做了什么?.md ├── 【NO.290】现在后端开发都在用什么数据库存储数据?.md ├── 【NO.291】Redis多线程原理详解.md ├── 【NO.292】工作年限、成长路线、进阶技术。怎样才能成为架构师?.md ├── 【NO.293】malloc函数背后的实现原理——内存池.md ├── 【NO.294】深入理解MYSQL索引优化:多列索引.md ├── 【NO.295】C++高性能服务器框架——日志系统详解.md ├── 【NO.296】熬夜肝了这一份C++开发详细学习路线.md ├── 【NO.297】什么是DPDK?DPDK的原理及学习路线总结.md ├── 【NO.298】腾讯同事内推的那位Linux CC++后端开发同学面试没过.md ├── 【NO.299】从四线城市程序员,到深圳大厂架构师,我只用了半年.md ├── 【NO.29】一文让你看懂内存与CPU之间的关系.md ├── 【NO.2】红黑树(一)之 原理和算法详细介绍.md ├── 【NO.300】详解 epoll 原理【Redis,Netty,Nginx实现高性能IO的核心原理】.md ├── 【NO.301】关于TCP的CLOSING状态和CLOSE_WAIT状态浅析.md ├── 【NO.302】Linux 网络性能优化-C10K、C1000K、C10M 问题总结.md ├── 【NO.303】C语言回调函数到底是什么?如何使用回调函数?.md ├── 【NO.304】腾讯面试题:十亿数据如何去重?红黑树到hash再到布隆过滤器.md ├── 【NO.305】从进入内核态看linux内存管理.md ├── 【NO.306】从6种IO模式谈谈协程的作用.md ├── 【NO.307】数据从应用层的应用进程到最后的网络包是怎么一步步封装的?TCP怎么拆分?IP怎么分片?.md ├── 【NO.308】谈谈QUIC协议原理.md ├── 【NO.309】Redis基本数据结构及底层实现原理.md ├── 【NO.30】为什么要使用 TCP keepalive?CC++代码实现TCP keepalive.md ├── 【NO.310】linux后端开发-定时器设计详解.md ├── 【NO.311】C++高性能大规模服务器开发实践.md ├── 【NO.312】gRPC C++开发环境搭建.md ├── 【NO.313】TCP 三次握手的性能优化.md ├── 【NO.314】redis7.0源码阅读:Redis中的IO多线程(线程池).md ├── 【NO.315】linux cc++开发:多线程并发锁:互斥锁、自旋锁、原子操作、CAS.md ├── 【NO.316】作为程序员,如何彻底理解高并发中的协程.md ├── 【NO.317】Redis 这么强?该如何进行性能优化呢?.md ├── 【NO.318】腾讯面试官用「B+树」虐哭我了.md ├── 【NO.319】超专业解析linux文件系统的底层架构及其工作原理.md ├── 【NO.31】最浅显易懂的 NAT 原理解析,看不懂来打我.md ├── 【NO.320】Linux 高性能服务 epoll 的本质,真的不简单(含实例源码).md ├── 【NO.321】c++开发工作中常见的几种内存泄漏场景汇总.md ├── 【NO.322】手写线程池与性能分析.md ├── 【NO.323】Redis6.0多线程模型总结.md ├── 【NO.324】进程的同步、互斥、通信的区别,进程与线程同步的区别.md ├── 【NO.325】通过Redis学习事件驱动设计.md ├── 【NO.326】TCP通信接收数据不完整的解决方法.md ├── 【NO.327】图解|揭开协程的神秘面纱.md ├── 【NO.328】文件的 io 栈,你真的知道了吗.md ├── 【NO.329】如何用300行代码实现一个完整的线程池.md ├── 【NO.32】谈谈 CC++ 和 JAVA 哪个更有前景!.md ├── 【NO.33】一文掌握google开源单元测试框架Google Test.md ├── 【NO.340】从一次线上问题说起,详解 TCP 半连接队列、全连接队列.md ├── 【NO.341】浅析进程间通信的几种方式(含实例源码).md ├── 【NO.342】超详细的网络抓包神器 tcpdump 使用指南.md ├── 【NO.343】原来 mmap 这么简单.md ├── 【NO.344】深入理解 http 反向代理(nginx).md ├── 【NO.345】C++使用protobuf实现序列化与反序列化.md ├── 【NO.346】Redis原理和机制详解.md ├── 【NO.347】网络丢包故障如何定位?如何解决?.md ├── 【NO.348】Linux进程地址空间与进程内存布局详解.md ├── 【NO.349】浅谈有栈协程与无栈协程.md ├── 【NO.34】深入linux操作系统-malloc到底如何分配内存?.md ├── 【NO.350】Nginx 性能优化(吐血总结).md ├── 【NO.351】TCP通信过程详解以及tcp长连接和短连接.md ├── 【NO.352】Linux系统编程之进程间通信:共享内存.md ├── 【NO.353】内存泄漏的原因,内存泄漏如何避免?内存泄漏如何定位?.md ├── 【NO.354】线上大量CLOSE_WAIT的原因深入分析.md ├── 【NO.355】一文弄懂tcp连接中各种状态及故障排查.md ├── 【NO.356】QQ音乐高可用架构体系.md ├── 【NO.357】QQ 浏览器搜索相关性实践.md ├── 【NO.358】ClickHouse 查询优化详细介绍.md ├── 【NO.359】腾讯云OCR性能是如何提升2倍的.md ├── 【NO.35】TCP收发数据“丢失”问题的排查与解决.md ├── 【NO.360】一文读懂数据库优化之分库分表.md ├── 【NO.361】Linux下跨语言调用C++实践.md ├── 【NO.362】数据库异常智能分析与诊断.md ├── 【NO.363】标准化思想及组装式架构在后端BFF中的实践.md ├── 【NO.364】基于代价的慢查询优化建议.md ├── 【NO.365】设计模式二三事.md ├── 【NO.366】即时通信IM核心能力及应用场景.md ├── 【NO.367】新知 腾讯云视立方播放器技术实现与应用.md ├── 【NO.368】AXP-QUIC:自适应X路QUIC网络传输加速.md ├── 【NO.369】SRS5优化:如何将DVR性能提升一倍.md ├── 【NO.36】一条TCP连接时占用内存空间多少.md ├── 【NO.370】SRS配置升级,云原生友好的配置能力.md ├── 【NO.371】linux服务器网络编程之线程模型.md ├── 【NO.372】腾讯跟阿里两位王者之间的对比.md ├── 【NO.373】IM(即时通讯)服务端.md ├── 【NO.374】一文带你了解大厂亿级并发下高性能服务器是如何实现的!.md ├── 【NO.375】你真的懂Redis与MySQL双写一致性如何保证吗?.md ├── 【NO.376】基于后端开发Redisson实现分布式锁源码分析解读.md ├── 【NO.377】聊聊对不同IO模型的理解 (阻塞非阻塞IO,同步异步IO).md ├── 【NO.378】Redis与Memcache对比.md ├── 【NO.379】Nginx高效的原因,你都了解了吗.md ├── 【NO.37】300行代码带你实现一个Linux文件系统.md ├── 【NO.380】深入理解 ProtoBuf 原理与工程实践.md ├── 【NO.381】从抖音到火山引擎——看流媒体技术演进和机会.md ├── 【NO.382】阿里云全球实时传输网络GRTN—QOE优化实践.md ├── 【NO.383】腾讯云实时音视频出海技术实践及落地.md ├── 【NO.384】MPEG音频编码三十年.md ├── 【NO.385】利用WebTransport进行现场视频流注入.md ├── 【NO.386】WEBRTC 笔记.md ├── 【NO.387】想学习音视频开发,感觉网上的资料很少?.md ├── 【NO.388】WebRTC开源项目-手把手教你搭建AppRTC.md ├── 【NO.389】最全的MSVC编译参数,收藏备用,MinGW与MSVC编译的区别.md ├── 【NO.38】一次解决Linux内核内存泄漏实战全过程.md ├── 【NO.390】针对初学者的 20 多个 FFmpeg 命令.md ├── 【NO.391】谷歌开源、高性能RPC框架:gRPC 使用体验.md ├── 【NO.392】C++音视频开发的技术要点.md ├── 【NO.393】FFmpeg使用小结.md ├── 【NO.394】2022技术展望|开源十年,WebRTC 的现状与未来.md ├── 【NO.395】FFmpeg入门宝典,音视频流媒体开发学习,一篇看到就要收藏的文章(附20个视频资料).md ├── 【NO.396】FFmpeg的结构和命令行工具(在线介绍).md ├── 【NO.397】音视频编解码常用知识点.md ├── 【NO.398】WebRTC 发送方码率预估实现解析.md ├── 【NO.399】C++ 开发者的机会在哪里?盘点 2022 好的CC++ 就业方向.md ├── 【NO.39】图解通用网络IO底层原理、Socket、epoll、用户态内核态······.md ├── 【NO.3】红黑树(二)之 C语言的实现.md ├── 【NO.400】WebRTC 源码分析 -- RTC_CHECK.md ├── 【NO.401】RTMP推流及协议学习(全代码).md ├── 【NO.402】【网络通信 -- 直播】流媒体协议中的时间戳理解与音视频同步,RTPRTCPRTMP推流拉流音视频同步.md ├── 【NO.403】webrtc搭建视频通话、视频会议 (亲测半个小时搭建成功).md ├── 【NO.404】x264码率控制.md ├── 【NO.405】FFmpeg源码分析:内存管理系统.md ├── 【NO.406】WebRTC 传输安全机制第二话:深入显出 SRTP 协议.md ├── 【NO.407】WebRTC能给我带来什么?.md ├── 【NO.408】FFmpeg学习笔记——重采样demo解析.md ├── 【NO.409】Linux ubuntu FFmpeg开发环境搭建(保姆式搭建教程).md ├── 【NO.40】C++开发中使用协程需要注意的问题.md ├── 【NO.410】音视频开发技术的基本知识.md ├── 【NO.411】Linux操作系统原理—内核网络协议栈.md ├── 【NO.412】从linux内核出发彻底弄懂socket底层的来龙去脉.md ├── 【NO.413】深入理解epoll背后的原理.md ├── 【NO.414】Linux下全新的异步IO:io_uring详解.md ├── 【NO.415】epoll源码剖析:为什么使用红黑树以及如何使用红黑树.md ├── 【NO.416】盘点腾讯linux C++后台开发面试题.md ├── 【NO.417】60道30K+C++工程师面试必问面试题.md ├── 【NO.418】C++高并发内存池的设计和实现.md ├── 【NO.419】不懂并行和并发?一文彻底搞懂并行和并发的区.md ├── 【NO.41】深入源码理解TCP建立连接过程(3次握手).md ├── 【NO.420】etcd:etcd的原理和应用场景全面解析.md ├── 【NO.421】腾讯面试:linux内存性能优化总结.md ├── 【NO.422】linux内核vmalloc原理与实现.md ├── 【NO.423】高性能服务器开发十大必须掌握的核心技术.md ├── 【NO.424】百行代码实现基于C++11的线程池threadpool , 简洁且可带任意多参数.md ├── 【NO.425】全网最透彻的五种linux IO模型分析「值得收藏」.md ├── 【NO.426】TCP连接中TIME_WAIT状态的作用及优化.md ├── 【NO.427】深入浅出DPDK学习笔记——认识DPDK.md ├── 【NO.428】最强阿里巴巴历年经典面试题汇总:C++研发岗.md ├── 【NO.429】超硬核,进程在内存中的样子!以及进程的一生.md ├── 【NO.42】这是我见过最详细的Nginx 内存池分析.md ├── 【NO.430】Linux原生异步IO原理与实现(Native AIO).md ├── 【NO.431】如何减少频繁分配内存(malloc或new)造成的内存碎片.md ├── 【NO.432】深入理解Linux 的Page Cache.md ├── 【NO.433】高并发高吞吐IO秘密武器——epoll池化技术.md ├── 【NO.434】面试必备:计算机网络常问的六十二个问题(建议收藏).md ├── 【NO.435】深入剖析阻塞式socket的timeout.md ├── 【NO.436】深入理解 Linux 的 epoll 机制及epoll原理.md ├── 【NO.437】Linux中的消息队列、共享内存,你确定都掌握了吗?.md ├── 【NO.438】关于高性能服务器底层网络通信模块的设计方法.md ├── 【NO.439】你真的了解Redis单线程为什么如此之快吗?.md ├── 【NO.43】红黑树的原理以及实现.md ├── 【NO.440】并发与多线程之线程安全篇.md ├── 【NO.441】设计模式—代理模式以及动态代理的实现.md ├── 【NO.442】后端开发—一文详解网络IO模型.md ├── 【NO.443】【技术干货分享】一文了解Nginx反向代理与conf原理.md ├── 【NO.444】Linux环境,CC++语言手写代码实现线程池.md ├── 【NO.445】一文掌握tcp服务器epoll的多种实现.md ├── 【NO.446】后端开发【一大波干货知识】tcpip定时器与滑动窗口详解.md ├── 【NO.447】网络IO管理-简单一问一答、多线程方式.md ├── 【NO.448】后端开发【一大波干货知识】定时器方案红黑树,时间轮,最小堆.md ├── 【NO.449】后端开发【一大波干货知识】—Redis,Memcached,Nginx网络组件.md ├── 【NO.44】浅谈NIO和Epoll实现原理.md ├── 【NO.450】手写实现分布式锁.md ├── 【NO.451】后端开发【一大波干货知识】定时器方案红黑树,时间轮,最小堆.md ├── 【NO.452】Reactor实现http服务器,附完整代码.md ├── 【NO.453】hash,bloomfilter,分布式一致性hash.md ├── 【NO.454】DPDK技术系统学习一(接收,发送,arp,icmp功能测试).md ├── 【NO.455】后端开发【一大波干货知识】网络通信模型和网络IO管理.md ├── 【NO.456】音视频开发技术的基本知识.md ├── 【NO.457】用WinDbg断点调试FFmpeg.md ├── 【NO.458】FFplay源码分析-nobuffer.md ├── 【NO.459】RTSP直播延时的深度优化(干货).md ├── 【NO.45】Linux性能优化—内存实战篇.md ├── 【NO.460】H264解码之FFmepg解码ES数据.md ├── 【NO.461】YUV与RGB的格式采样方式存储方式.md ├── 【NO.462】【音视频技术】播放器架构设计.md ├── 【NO.463】Nginx搭建RTMP推拉流服务器.md ├── 【NO.464】FFMPEG 之 AVDevice.md ├── 【NO.465】【网络通信 -- WebRTC】WebRTC 源码分析 -- 线程相关(线程切换分析 -- MethodCall ConstMethodCall 类分析).md ├── 【NO.466】【网络通信 -- WebRTC】WebRTC 基础知识 -- 基础知识总结【1】WebRTC 简介.md ├── 【NO.467】神器 ffmpeg——操作视频,极度舒适.md ├── 【NO.468】音视频面试问题面试技巧.md ├── 【NO.469】什么是码率控制 在视频编码中,码率控制的概念是什么,它是通过什么实现的.md ├── 【NO.46】一文读懂网关中间件-Nginx.md ├── 【NO.470】FFmpeg命令行格式和转码过程.md ├── 【NO.471】进程原理及系统调用.md ├── 【NO.472】posix API与网络协议栈的实现原理.md ├── 【NO.473】常使用的网络IO管理.md ├── 【NO.474】服务器模型reactor.md ├── 【NO.475】nginx 中数据结构讲解.md ├── 【NO.476】nginx自定义实现一个计量模块.md ├── 【NO.477】协程的调度实现与性能测试.md ├── 【NO.478】tcp服务器epoll的多种实现.md ├── 【NO.479】C++面试常问基础总结梳理.md ├── 【NO.47】Redis面试题:基本数据类型与底层存储结构.md ├── 【NO.480】Nginx数据结构.md ├── 【NO.481】Linux服务器开发,libeventlibev框架实战那些坑.md ├── 【NO.482】tcp支持浏览器websocket协议.md ├── 【NO.483】Linux服务器开发,手写分布式锁.md ├── 【NO.484】Linux服务器开发,手写内存检测组件.md ├── 【NO.485】Linux服务器开发,mysql连接池的实现.md ├── 【NO.486】后端开发【一大波干货知识】数据库之mysql索引原理详解.md ├── 【NO.487】TCP三次握手、四次挥手以及TIME_WAIT详解.md ├── 【NO.488】Linux内核必懂知识—调度器分析及完全公平调度器CFS.md ├── 【NO.489】一文彻底掌握用户态协议栈,一看就懂的.md ├── 【NO.48】一文来了解关于分布式锁的那些事儿.md ├── 【NO.490】分布式缓存--缓存与数据库强一致场景下的方案.md ├── 【NO.491】手写内存池以及原理代码分析【C语言】.md ├── 【NO.492】tcp协议栈实现,tcp定时器与滑动窗口实现.md ├── 【NO.493】如何更有效的使用 Redis 缓存.md ├── 【NO.494】Redis之最细命令介绍.md ├── 【NO.495】Linux CC++ 并发下的技术方案(互斥锁、自旋锁、原子操作).md ├── 【NO.496】Linux服务器开发【干货知识】—MySQL事务原理分析.md ├── 【NO.497】UDP的可靠性传输详解.md ├── 【NO.498】DPDK系统学习—DPDK的虚拟交换机框架 OvS.md ├── 【NO.499】后台开发【一大波干货知识】Nginx数据结构剖析.md ├── 【NO.49】2022年作为一个中年程序员写给35岁的自己.md ├── 【NO.4】红黑树(三)之 Linux内核中红黑树的经典实现.md ├── 【NO.500】后端开发【一大波干货知识】Redis的线程模型和异步机制.md ├── 【NO.501】Linux的虚拟内存详解(MMU、页表结构).md ├── 【NO.502】各大厂c++ linux后端开发岗位要求汇总.md ├── 【NO.503】内存优化-如何使用tcmalloc来提升内存性能?提升的结果太不可思议.md ├── 【NO.504】一文搞懂Linux进程调度原理.md ├── 【NO.505】盘点后端开发那些值得学习的优秀开源项目.md ├── 【NO.506】关于linux进程间的close-on-exec机制.md ├── 【NO.507】网络编程手绘TCP状态机.md ├── 【NO.508】从进程和线程的创建过程来看进程和线程的区别.md ├── 【NO.509】超详细讲解Linux中的基础IO.md ├── 【NO.50】让人秒懂的Redis的事件处理机制.md ├── 【NO.510】操作系统:文件系统的实现.md ├── 【NO.511】Linux网络分析必备技能:tcpdump实战详解.md ├── 【NO.512】大厂面试题之计算机网络重点篇(附答案).md ├── 【NO.513】深入 malloc 函数,带你真正理解内存分配!.md ├── 【NO.514】面试必问的epoll技术,从内核源码出发彻底搞懂epoll.md ├── 【NO.515】从进入内核态看内存管理.md ├── 【NO.516】「Linux」多线程详解,一篇文章彻底搞懂多线程中各个难点.md ├── 【NO.517】百度 C++ 工程师的那些极限优化(内存篇).md ├── 【NO.518】malloc内存分配过程详解.md ├── 【NO.519】TCP BBR拥塞控制算法深度解析.md ├── 【NO.51】一文弄懂Linux下五种IO模型.md ├── 【NO.520】Linux完全公平调度算法原理与实现.md ├── 【NO.521】如何快速地进出——C++ 读写数据 IO 性能优化.md ├── 【NO.522】如何解决tcp通信中的粘包问题?.md ├── 【NO.523】多线程还是多进程的选择及区别.md ├── 【NO.524】最常见的linux网络编程面试题【好文收藏】.md ├── 【NO.525】内存优化-使用tcmalloc分析解决内存泄漏和内存暴涨问题.md ├── 【NO.526】Linux服务器开发,fastdfs架构分析和配置.md ├── 【NO.527】用户态协议栈.md ├── 【NO.528】Linux服务器开发,手写死锁检测组件.md ├── 【NO.529】海量数据去重hash与布隆过滤器.md ├── 【NO.52】微信终端自研 C++协程框架的设计与实现.md ├── 【NO.530】Linux服务器开发,内存池原理与实现.md ├── 【NO.531】基础的网络服务器开发.md ├── 【NO.532】实现高并发http 服务器.md ├── 【NO.533】nginx过滤器模块.md ├── 【NO.534】随处可见的红黑树.md ├── 【NO.535】服务器开发,无锁消息队列实现(初步认识).md ├── 【NO.536】Linux系统中的文件操作.md ├── 【NO.537】Linux服务器开发,异步请求池框架实现,协程前传.md ├── 【NO.538】Linux服务器开发,原子操作CAS与锁实现.md ├── 【NO.539】Linux服务器开发,线程池原理与实现.md ├── 【NO.53】高并发系统建设经验总结.md ├── 【NO.540】Linux服务器开发,应用层协议设计ProtoBufThrift.md ├── 【NO.541】Linux服务器开发,stl容器,智能指针,正则表达式(C++STL中的智能指针).md ├── 【NO.542】协程的设计原理与汇编实现.md ├── 【NO.543】redis计数,布隆过滤器,hyperloglog.md ├── 【NO.544】Linux服务器开发,Makefilecmakeconfigure.md ├── 【NO.545】磁盘存储链式的 B 树与 B+树.md ├── 【NO.546】互斥锁、读写锁、自旋锁,以及原子操作指令xaddl、cmpxchg的使用场景剖析.md ├── 【NO.547】网络通信模型和网络IO管理.md ├── 【NO.548】MYSQL---服务器配置相关问题.md ├── 【NO.549】Linux服务器开发,定时器方案红黑树,时间轮,最小堆.md ├── 【NO.54】15 年腾讯老兵谈技术人成长之路.md ├── 【NO.550】Posix API 与 网络协议栈 详细介绍.md ├── 【NO.551】Linux服务器百万并发实现与问题排查.md ├── 【NO.552】ZMQ无锁队列的原理与实现.md ├── 【NO.553】redis7.0源码阅读(四):Redis中的IO多线程(线程池).md ├── 【NO.554】SQL之增删改查命令操作详解.md ├── 【NO.555】数据库设计的三范式和反范式.md ├── 【NO.556】基于C++11实现的高效线程池及工作原理.md ├── 【NO.557】Linux内存管理-详解mmap原理.md ├── 【NO.558】通过实战理解CPU上下文切换.md ├── 【NO.559】Linux IO复用中select poll epoll模型的介绍及其优缺点的比较.md ├── 【NO.55】分布式消息队列.md ├── 【NO.560】Linux内核时钟系统和定时器实现.md ├── 【NO.561】linux下C++多线程并发之原子操作与无锁编程.md ├── 【NO.562】Linux网络编程——tcp并发服务器(多线程)实例分享.md ├── 【NO.563】linux下waitwaitpid处理僵死进程详解(SIGCHLD信号).md ├── 【NO.564】从TCP协议到TCP通信的各种异常现象和分析.md ├── 【NO.565】低延迟场景下的性能优化实践.md ├── 【NO.566】万字长文漫谈高可用高并发技术.md ├── 【NO.567】万字长文讲解 linux内核性能调优.md ├── 【NO.568】详解进程的虚拟内存,物理内存,共享内存.md ├── 【NO.569】浅谈TCPIP网络编程中socket的行为.md ├── 【NO.56】MongoDB 基础浅谈.md ├── 【NO.570】内存碎片优化(内存池).md ├── 【NO.571】websocket协议介绍与基于reactor模型的websocket服务器实现.md ├── 【NO.572】redis7.0源码阅读(三):哈希表扩容、缩容以及rehash.md ├── 【NO.573】eBPF学习 - 入门.md ├── 【NO.574】Nginx源码阅读:避免惊群以及负载均衡的原理与具体实现.md ├── 【NO.575】海量数据去重的hash,bitmap与布隆过滤器Bloom Filter.md ├── 【NO.576】锁与原子操作CAS的底层实现.md ├── 【NO.577】httphttps服务器的实现.md ├── 【NO.578】随处可见的红黑树.md ├── 【NO.579】Nginx反向代理与系统参数配置conf原理.md ├── 【NO.57】仅5天注册用户超百万的爆火ChatGPT是什么.md ├── 【NO.580】多线程实践概述.md ├── 【NO.581】C++高性能协程分布式服务框架设计.md ├── 【NO.582】如何能够看懂TCPIP 协议细节?.md ├── 【NO.583】一文搞懂 mmap 涉及的所有内容.md ├── 【NO.584】C++这么难,为什么我们还要学习C++?.md ├── 【NO.585】内存泄露定位手段(c语言hook malloc相关方式).md ├── 【NO.586】linux:孤儿进程与僵尸进程产生及其处理.md ├── 【NO.587】linux异步IO编程实例分析.md ├── 【NO.588】透视Linux内核,BPF 深度分析与案例讲解.md ├── 【NO.589】论fork()函数与Linux中的多线程编程.md ├── 【NO.58】从Linux零拷贝深入了解Linux-IO.md ├── 【NO.590】Linux 直接IO 原理与实现.md ├── 【NO.591】深入了解epoll模型(特别详细).md ├── 【NO.592】内存泄漏-原因、避免和定位.md ├── 【NO.593】一道腾讯面试题目:没有listen,能否建立TCP连接.md ├── 【NO.594】一篇文章读懂dpdk——dpdk原理详解.md ├── 【NO.595】深入理解无锁编程-译自《An Introduction to Lock-Free Programming》.md ├── 【NO.596】网络编程:线上大量CLOSE_WAIT的原因深入分析.md ├── 【NO.597】记录一次腾讯cc++ linux后台开发岗面试经历(面试题含答案).md ├── 【NO.598】如何高效定位网络丢包问题?.md ├── 【NO.599】高并发的socket的高性能设计.md ├── 【NO.59】深入学习IO多路复用 selectpollepoll 实现原理.md ├── 【NO.5】B树详解.md ├── 【NO.600】C++开发常用的设计模式及其实现详解.md ├── 【NO.601】【linux】彻底搞懂零拷贝(Zero-Copy)技术.md ├── 【NO.602】Linux C++的多线程编程(新手最全教程).md ├── 【NO.603】TCP协议之Send和Recv原理及常见问题分析.md ├── 【NO.604】MySQL 死锁案例解析,能让你彻底理解死锁的原因!.md ├── 【NO.605】C++之内存管理:申请与释放.md ├── 【NO.606】计算机操作系统知识点总结(有这一篇就够了!!!).md ├── 【NO.607】UDP的可靠性传输.md ├── 【NO.608】Linux 进程间通信:管道、共享内存、消息队列、信号量.md ├── 【NO.609】深入操作系统,一文搞懂Socket到底是什么.md ├── 【NO.60】十多年前祖传代码重构——从25万到5万行.md ├── 【NO.610】C++多线程详解(全网最全).md ├── 【NO.611】linux多线程--双buffer “无锁” 设计.md ├── 【NO.612】一篇文章教你,Linux内存管理原理.md ├── 【NO.613】一篇文章助你了解dpdk所有技术点.md ├── 【NO.614】C++多线程编程,线程互斥和同步通信,死锁问题分析解决.md ├── 【NO.615】linux服务器性能调优之tcpip性能调优.md ├── 【NO.616】国内顶级网络大神对TCP的深刻理解.md ├── 【NO.617】Linux性能优化-CPU性能优化思路.md ├── 【NO.618】浅谈linux定时器时间轮算法.md ├── 【NO.619】一文彻底揭秘linux操作系统之「零拷贝」!.md ├── 【NO.61】字节跳动面试题汇总 -- C++后端(含答案).md ├── 【NO.620】c++ 协程_关于协程的实现与原理,多年程序员深度总结.md ├── 【NO.621】深度剖析linux socket的epollinepollout是何时触发的.md ├── 【NO.622】Linux中的各种锁及其基本原理.md ├── 【NO.623】redis IO多路复用原理:高性能IO之Reactor模式.md ├── 【NO.624】【进程管理】fork之后子进程到底复制了父进程什么?.md ├── 【NO.625】Linux内核进程上下文切换深入理解.md ├── 【NO.62】百度C++研发工程师面试题(最新整理).md ├── 【NO.63】linux 零拷贝( zero-copy )技术原理详解.md ├── 【NO.64】C++数据结构与算法:布隆过滤器(Bloom Filter)原理与实现.md ├── 【NO.65】底层原理:epoll源码分析,还搞不懂epoll的看过来.md ├── 【NO.66】游戏服务器框架-skynet的设计原理和使用.md ├── 【NO.67】面试题Linux网络编程中可靠UDP,KCP协议快在哪.md ├── 【NO.68】Socket 面对的挑战?.md ├── 【NO.69】TCP收发数据“丢失”问题的排查与解决.md ├── 【NO.6】B树、B-树、B+树、B树之间的关系.md ├── 【NO.70】用红黑树封装map和set.md ├── 【NO.71】linux操作系统中线程是怎么切换的(用户态的内部切换).md ├── 【NO.72】Linux下系统 IO 性能分析的套路.md ├── 【NO.73】linux性能优化之网络篇.md ├── 【NO.74】linux操作系统是如何管理tcp连接的?.md ├── 【NO.75】资深工程师带你探秘C++内存管理(理论篇).md ├── 【NO.76】杂谈代码整洁.md ├── 【NO.77】微信 libco 协程库原理剖析.md ├── 【NO.78】如何做一款面向企业客户的商用级 SDK.md ├── 【NO.79】消息队列背后的设计思想.md ├── 【NO.7】什么是B+树?(详细图解).md ├── 【NO.80】一文读懂 @Decorator 装饰器——理解 VS Code 源码的基础.md ├── 【NO.81】学C++的以后都能从事哪些岗位?.md ├── 【NO.82】耗时1个月,万字干货,详解腾讯面试(T1-T9)核心技术点,面试题整理.md ├── 【NO.83】关于【零声教育】2022年12代CC++Linux服务器开发高级架构师课程学习心得,资源分享.md ├── 【NO.84】2022全网最详细的音视频开发学习路线,零基础到项目实战,从小白到音视频专家.md ├── 【NO.85】高性能库DPDK精简理解.md ├── 【NO.86】带你快速了解 Docker 和 Kubernetes.md ├── 【NO.87】浅谈 Protobuf 编码.md ├── 【NO.88】gRPC 基础概念详解.md ├── 【NO.89】深入浅出 Linux 惊群:现象、原因和解决方案.md ├── 【NO.8】b+树详解.md ├── 【NO.90】Nginx 最全操作总结.md ├── 【NO.91】带你快速了解 Docker 和 Kubernetes.md ├── 【NO.92】浅谈 Protobuf 编码.md ├── 【NO.93】gRPC 基础概念详解.md ├── 【NO.94】深入浅出 Linux 惊群:现象、原因和解决方案.md ├── 【NO.95】Nginx 最全操作总结.md ├── 【NO.96】基于libco的c++协程实现(时间轮定时器).md ├── 【NO.97】Linux文件系统、磁盘IO是怎么工作的.md ├── 【NO.98】linux性能优化实战之cpu篇.md ├── 【NO.99】Linux服务器检查性能瓶颈.md └── 【NO.9】TCPIP 介绍.md /【NO.101】什么是DPDK?DPDK的原理及学习路线总结.md: -------------------------------------------------------------------------------- 1 | # 【NO.101】什么是DPDK?DPDK的原理及学习路线总结 2 | 3 | ## 1.什么是DPDK 4 | 5 |   对于用户来说,它可能是一个性能出色的包数据处 理加速软件库;对于开发者来说,它可能是一个实践包处理新想法的创 新工场;对于性能调优者来说,它可能又是一个绝佳的成果分享平台。  6 | 7 |   DPDK用软件的方式在通用多核处理器上演绎着数据包处理的新篇 章,而对于数据包处理,多核处理器显然不是唯一的平台。支撑包处理 的主流硬件平台大致可分为三个方向。 8 |   ·硬件加速器 9 |   ·网络处理器 10 |   ·多核处理器 11 | 12 |   在类似 IA(Intel Architecture)多核处理器为目标的平台上,网络数据包处理远早于DPDK而存在。从商业版的 Windows到开源的Linux操作系统,所有跨主机通信几乎都会涉及网络 协议栈以及底层网卡驱动对于数据包的处理。然而,低速网络与高速网 络处理对系统的要求完全不一样。 13 | 14 | ## 2.DPDK原理 15 | 16 | 网络设备(路由器、交换机、媒体网关、SBC、PS网关等)需要在瞬间进行大量的报文收发,因此在传统的网络设备上,往往能够看到专门的NP(Network Process)处理器,有的用FPGA,有的用ASIC。这些专用器件通过内置的硬件电路(或通过编程形成的硬件电路)高效转发报文,只有需要对报文进行深度处理的时候才需要CPU干涉。 17 | 18 | 但在公有云、NFV等应用场景下,基础设施以CPU为运算核心,往往不具备专用的NP处理器,操作系统也以通用Linux为主,网络数据包的收发处理路径如下图所示: 19 | 20 | ![img](https://pic4.zhimg.com/80/v2-39d9c5742815718ffc3e616342c75c9b_720w.webp) 21 | 22 | 在虚拟化环境中,路径则会更长: 23 | 24 | ![img](https://pic3.zhimg.com/80/v2-ee0175b746bbf5eed76de9f29bb4bbda_720w.webp) 25 | 26 | 由于包处理任务存在内核态与用户态的切换,以及多次的内存拷贝,系统消耗变大,以CPU为核心的系统存在很大的处理瓶颈。为了提升在通用服务器(COTS)的数据包处理效能,Intel推出了服务于IA(Intel Architecture)系统的DPDK技术。 27 | 28 | DPDK是Data Plane Development Kit的缩写。简单说,DPDK应用程序运行在操作系统的User Space,利用自身提供的数据面库进行收发包处理,绕过了Linux内核态协议栈,以提升报文处理效率。 29 | 30 | ## 3.DPDK源码目录结构  31 | 32 |   lib/ : DPDK的库源代码 33 |   drivers/ : DPDK轮询模式驱动程序源代码 34 |   app/ : DPDK应用程序源代码 35 |   examples/ : DPDK的一些应用程序例子源代码 36 |   config/ : DPDK关于arm和x86平台的一些编译配置 37 |   buildtools/ : DPDK一些编译配置的脚本 38 |   mk/ : DPDK的Makefile 39 |   usertools/ : DPDK提供给用户的一些实用工具 40 | 41 | ## 4.常用术语及缩写 42 | 43 |   ACL:Access Control List,访问控制列表,是路由器和交换机接口的指令列表,用来控制端口进出的数据包;简而言之就是用来控制数据流。 44 |   SSL:Secure Sockets Layer,安全套接层,是为网络通信提供安全及数据完整性的一种安全协议,在传输层对网络连接进行加密。 45 |   RSS:Receive Side Scaling,是一种能够在多处理器系统下使接收报文在多个CPU之间高效分发的网卡驱动技术。 46 |   NUMA:Non Uniform Memory Access Architecture,非统一内存访问架构; 47 |   QOS:Quality of Service,服务质量,指一个网络能够利用各种基础技术,为指定的网络通信提供更好的服务能力, 是网络的一种安全机制, 是用来解决网络延迟和阻塞等问题的一种技术。 48 |   NIC:Network Interface Card,网卡,网卡是局域网中最基本的部件之一,它是连接计算机与网络的硬件设备。 49 |   PCI:Peripheral Component Interconnect,计算机一种标准总线,NIC就是使用的这种总线方式。 50 |   PMD:Poll Mode Drive,轮询模式驱动,DPDK就是采用的这种模式。 51 |   RTE:Run Time Environment,通过PMD实现快速分组处理数据的一个框架。 52 |   MPLS:Multi-Protocol Label Switching,多协议标签交换,是一种用于快速数据包交换和路由的体系,它为网络数据流量提供了目标、路由地址、转发和交换等能力。更特殊的是,它具有管理各种不同形式通信流的机制。 53 | 54 | ## 5.DPDK框架简介 55 | 56 |   DPDK为IA上的高速包处理而设计。 57 | 58 |   图1-6所示的DPDK主要模块分 解展示了以基础软件库的形式,为上层应用的开发提供一个高性能的基 础I/O开发包。它大量利用了有助于包处理的软硬件特性,如大页、缓 存行对齐、线程绑定、预取、NUMA、IA最新指令的利用、Intel DDIO、内存交叉访问等。 59 |   核心库Core Libs,提供系统抽象、大页内存、缓存池、定时器及无 锁环等基础组件。 60 |   PMD库,提供全用户态的驱动,以便通过轮询和线程绑定得到极高 的网络吞吐,支持各种本地和虚拟的网卡。 61 |   Classify库,支持精确匹配(Exact Match)、最长匹配(LPM)和 通配符匹配(ACL),提供常用包处理的查表操作。 62 |   QoS库,提供网络服务质量相关组件,如限速(Meter)和调度 (Sched)。 63 | 64 | ![img](https://pic1.zhimg.com/80/v2-617431c4136778d6ccc9462016a76f10_720w.webp) 65 | 66 | ## 6.DPDK的轮询模式 67 | 68 |   DPDK采用了轮询或者轮询混杂中断的模式来进行收包和发包,此 前主流运行在操作系统内核态的网卡驱动程序基本都是基于异步中断处 理模式。 69 | 70 | ##   1、异步中断模式 71 | 72 |   当有包进入网卡收包队列后,网卡会产生硬件 (MSIX/MSI/INTX)中断,进而触发CPU中断,进入中断服务程序,在 中断服务程序(包含下半部)来完成收包的处理。当然为了改善包处理 性能,也可以在中断处理过程中加入轮询,来避免过多的中断响应次 数。总体而言,基于异步中断信号模式的收包,是不断地在做中断处 理,上下文切换,每次处理这种开销是固定的,累加带来的负荷显而易 见。在CPU比I/O速率高很多时,这个负荷可以被相对忽略,问题不 大,但如果连接的是高速网卡且I/O频繁,大量数据进出系统,开销累 加就被充分放大。中断是异步方式,因此CPU无需阻塞等待,有效利用 率较高,特别是在收包吞吐率比较低或者没有包进入收包队列的时候, CPU可以用于其他任务处理。 73 | 当有包需要发送出去的时候,基于异步中断信号的驱动程序会准备 好要发送的包,配置好发送队列的各个描述符。在包被真正发送完成 时,网卡同样会产生硬件中断信号,进而触发CPU中断,进入中断服务 程序,来完成发包后的处理,例如释放缓存等。与收包一样,发送过程 也会包含不断地做中断处理,上下文切换,每次中断都带来CPU开销; 同上,CPU有效利用率高,特别是在发包吞吐率比较低或者完全没有发 包的情况。 74 | 75 | ##   2、轮询模式 76 | 77 |   DPDK起初的纯轮询模式是指收发包完全不使用任何中断,集中所 有运算资源用于报文处理。但这不是意味着DPDK不可以支持任何中 断。根据应用场景需要,中断可以被支持,最典型的就是链路层状态发 生变化的中断触发与处理。 78 | 79 | 原文链接:https://zhuanlan.zhihu.com/p/397919872 80 | 81 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.103】从四线城市程序员,到深圳大厂架构师,我只用了半年.md: -------------------------------------------------------------------------------- 1 | # 【NO.103】从四线城市程序员,到深圳大厂架构师,我只用了半年 2 | 3 | 从苦逼的程序员,到现在的**Linux高级互联网架构师,**要问身份的转变给我带来了什么实质上的利益,那肯定是**薪水**了。除此之外就是**面子**,毕竟在大厂比在不知名小公司要长脸的多。 4 | 5 | 主要还是去年在家上班那段时间,感觉到了小公司的种种不便,最让人难以忍受的就是在家996随时待命,还只发底薪,真是令人窒息的操作,让我只想赶紧逃离这个公司。 6 | 7 | ![img](https://pic1.zhimg.com/80/v2-510e73918b0cd1069f60d0b2f1f7639c_720w.webp) 8 | 9 | 但成年人的世界从来就不是可以任性的,我也自知我的水平没办法胜任更好的岗位,于是我决定**工作之余提升自己**。 10 | 11 | 边上班边学习其实挺苦的,幸好时间不长,我也熬了过来。现在每个月看到工资卡上比之前涨了几倍的数字,觉得**当时的努力是很值**的。 12 | 13 | 咳咳,扯远了啊,言归正传,就跟大家分享一下,我是**怎样进阶**的。 14 | 15 | ## **1.** **学好C语言** 16 | 17 | 作为一名程序员,C++的难度在我看来是top级的,多少次被这玩意折腾的怀疑人生。但是!不入虎穴焉得虎子,它的确很难,但是它的**含金量和竞争力**同样也是没话说的。 18 | 19 | 会与不会,很多时候就是薪资高低的决定因素。 20 | 21 | ![img](https://pic2.zhimg.com/80/v2-a6a9b09b56496a588782fb33806eb029_720w.webp) 22 | 23 | 要学习C++,那就一定要先打牢C语言的基础,这是至关重要的前提条件。 24 | 25 | ## **2.** **看书** 26 | 27 | 有了扎实的C语言基础之后,可以开始学习C++了。 28 | 29 | 给大伙推荐一些我觉得不错的书。 30 | 31 | ▪ **《C++ Primer》 及习题册** 32 | 33 | 如果只想看一本教材,那我强烈推荐这本。《C++ Primer》 非常全面,方方面面都考虑到了,可读性也很强,很适合初学者。它的习题册也一定要买,搭配使用事半功倍。 34 | 35 | ![img](https://pic1.zhimg.com/80/v2-4f9573d37434595aef464cd0a4b5627c_720w.webp) 36 | 37 | **▪ 《21天学通C++》** 38 | 39 | 听这个名字就知道,这也是一本适合初学者的书,难度没有上一本那样大,但基本知识都交代了,适合作为学习C++的第一本书。 40 | 41 | ![img](https://pic3.zhimg.com/80/v2-580d5740d933ea60dda9f01e9320ea4e_720w.webp) 42 | 43 | ▪ **《Effective C++》、《More Effective C++》** 44 | 45 | 这两本是初学者看完、练完《C++ Primer》后,用来提升的教材。告诉程序员在使用C++时应该怎么解决问题、要注意什么、避免什么,进阶之路必备好书。 46 | 47 | ![img](https://pic4.zhimg.com/80/v2-b1faf8d048e7b5c9662049e917f69fb3_720w.webp) 48 | 49 | ![img](https://pic1.zhimg.com/80/v2-35ccf2c850b39c5d2370a378021783c8_720w.webp) 50 | 51 | ## **3.** **看教程** 52 | 53 | 根据我自己的学习经验,其实单纯看书挺枯燥的,很多时候就是看不下去,所以我会**结合一些教程来**看。 54 | 55 | ![img](https://pic3.zhimg.com/80/v2-c71979939e6b6a7f6d608098bf103062_720w.webp) 56 | 57 | 也有很多一样在学习C++的同仁,可以**相互交流每天打卡**,有队友学起来才更有激情嘛。而且会在群里聊聊**行情**什么的,也有**项目实操**,是锻炼的好机会。 58 | 59 | ## **4.** **学习资料** 60 | 61 | 来点干货,这是群里大牛整理的**腾讯核心技术学习路线(T1-T9)** 62 | 63 | ![img](https://pic1.zhimg.com/80/v2-806d7ae9d425ebf381e284802f7f1118_720w.webp) 64 | 65 | **腾讯职级技术学习提升路线详情版**: 66 | 67 | ![img](https://pic1.zhimg.com/80/v2-806d7ae9d425ebf381e284802f7f1118_720w.webp) 68 | 69 | 相比很多人学C++学的怀疑人生,我学习的过程其实**没有走多少弯路**,毕竟一开始就找到了优质的教程和学习资料,而且大牛带飞嘛,结果自然不同凡响。 70 | 71 | 我是感觉学习任何一门技术都不能闭门造车,因为学习过程中很多问题不是你一个人遇到过,**多和同仁交流**,钻牛角尖的概率低很多。 72 | 73 | 另外,作为一个过来人,也想提醒大家:想要学习C++,一定要努力且有耐心,不可能一天就能走到罗马,唯一可以做的,就是**立刻出发**。 74 | 75 | 原文链接:https://zhuanlan.zhihu.com/p/356387701 76 | 77 | 作者:Linux服务器研究 -------------------------------------------------------------------------------- /【NO.10】CPIP 寻址.md: -------------------------------------------------------------------------------- 1 | # 【NO.10】CP/IP 寻址 2 | 3 | TCP/IP 使用 32 个比特或者 4 组 0 到 255 之间的数字来为计算机编址。 4 | 5 | ## 1.IP地址 6 | 7 | 每个计算机必须有一个 IP 地址才能够连入因特网。 8 | 9 | 每个 IP 包必须有一个地址才能够发送到另一台计算机。 10 | 11 | 在本教程下一节,您会学习到更多关于 IP 地址和 IP 名称的知识。 12 | 13 | ## 2.IP 地址包含 4 组数字: 14 | 15 | TCP/IP 使用 4 组数字来为计算机编址。每个计算机必须有一个唯一的 4 组数字的地址。 16 | 17 | 每组数字必须在 0 到 255 之间,并由点号隔开,比如:192.168.1.60。 18 | 19 | ## 3.32 比特 = 4 字节 20 | 21 | TCP/IP 使用 32 个比特来编址。一个计算机字节是 8 比特。所以 TCP/IP 使用了 4 个字节。 22 | 23 | 一个计算机字节可以包含 256 个不同的值: 24 | 25 | 00000000、00000001、00000010、00000011、00000100、00000101、00000110、00000111、00001000 ....... 直到 11111111。 26 | 27 | 现在,您应该知道了为什么 TCP/IP 地址是介于 0 到 255 之间的 4 组数字。 28 | 29 | ## 4.IP V6 30 | 31 | IPv6 是 "Internet Protocol Version 6" 的缩写,也被称作下一代互联网协议,它是由 IETF 小组(Internet 工程任务组Internet Engineering Task Force)设计的用来替代现行的 IPv4(现行的)协议的一种新的 IP 协议。 32 | 33 | 我们知道,Internet 的主机都有一个唯一的 IP 地址,IP 地址用一个 32 位二进制的数表示一个主机号码,但 32 位地址资源有限,已经不能满足用户的需求了,因此 Internet 研究组织发布新的主机标识方法,即 IPv6。 34 | 35 | 在 RFC1884 中(RFC 是 Request for Comments document 的缩写。RFC 实际上就是 Internet 有关服务的一些标准),规定的标准语法建议把 IPv6 地址的 128 位(16 个字节)写成 8 个 16 位的无符号整数,每个整数用 4 个十六进制位表示,这些数之间用冒号(:)分开,例如: 36 | 37 | ``` 38 | 686E:8C64:FFFF:FFFF:0:1180:96A:FFFF 39 | ``` 40 | 41 | 冒号十六进制记法允许零压缩,即一串连续的0可以用一对冒号取代,例如: 42 | 43 | ``` 44 | FF05:0:0:0:0:0:0:B3可以定成:FF05::B3 45 | ``` 46 | 47 | 为了保证零压缩有一个清晰的解释,建议中规定,在任一地址中,只能使用一次零压缩。该技术对已建议的分配策略特别有用,因为会有许多地址包含连续的零串。 48 | 49 | 冒号十六进制记法结合有点十进制记法的后缀。这种结合在IPv4向IPv6换阶段特别有用。例如,下面的串是一个合法的冒号十六进制记法: 50 | 51 | ``` 52 | 0:0:0:0:0:0:128.10.1.1 53 | ``` 54 | 55 | 这种记法中,虽然冒号所分隔的每一个值是一个16位的量,但每个分点十进制部分的值则指明一个字节的值。再使用零压缩即可得出: 56 | 57 | ``` 58 | ::128.10.1.1 59 | ``` 60 | 61 | ## 5.域名 62 | 63 | 12 个阿拉伯数字很难记忆。使用一个名称更容易。 64 | 65 | 用于 TCP/IP 地址的名字被称为域名。runoob.com 就是一个域名。 66 | 67 | 当你键入一个像 http://www.runoob.com 这样的域名,域名会被一种 DNS 程序翻译为数字。 68 | 69 | 在全世界,数量庞大的 DNS 服务器被连入因特网。DNS 服务器负责将域名翻译为 TCP/IP 地址,同时负责使用新的域名信息更新彼此的系统。 70 | 71 | 当一个新的域名连同其 TCP/IP 地址一起注册后,全世界的 DNS 服务器都会对此信息进行更新。 72 | 73 | 原文链接:https://www.runoob.com/tcpip/tcpip-addressing.html -------------------------------------------------------------------------------- /【NO.110】一致性哈希算法及其在分布式系统中的应用.md: -------------------------------------------------------------------------------- 1 | # 【NO.110】一致性哈希算法及其在分布式系统中的应用 2 | 3 | ## 1.摘要 4 | 5 | 本文将会从实际应用场景出发,介绍一致性哈希算法(Consistent Hashing)及其在分布式系统中的应用。首先本文会描述一个在日常开发中经常会遇到的问题场景,借此介绍一致性哈希算法以及这个算法如何解决此问题;接下来会对这个算法进行相对详细的描述,并讨论一些如虚拟节点等与此算法应用相关的话题。 6 | 7 | ## 2.分布式缓存问题 8 | 9 | 假设我们有一个网站,最近发现随着流量增加,服务器压力越来越大,之前直接读写数据库的方式不太给力了,于是我们想引入Memcached作为缓存机制。现在我们一共有三台机器可以作为Memcached服务器,如下图所示。 10 | 11 | ![img](https://pic3.zhimg.com/80/v2-84a49b3095bc9498af19c29eb0117d06_720w.webp) 12 | 13 | 很显然,最简单的策略是将每一次Memcached请求随机发送到一台Memcached服务器,但是这种策略可能会带来两个问题:一是同一份数据可能被存在不同的机器上而造成数据冗余,二是有可能某数据已经被缓存但是访问却没有命中,因为无法保证对相同key的所有访问都被发送到相同的服务器。因此,随机策略无论是时间效率还是空间效率都非常不好。 14 | 15 | 要解决上述问题只需做到如下一点:保证对相同key的访问会被发送到相同的服务器。很多方法可以实现这一点,最常用的方法是计算哈希。例如对于每次访问,可以按如下算法计算其哈希值: 16 | 17 | h = Hash(key) % 3 18 | 19 | 其中Hash是一个从字符串到正整数的哈希映射函数。这样,如果我们将Memcached Server分别编号为0、1、2,那么就可以根据上式和key计算出服务器编号h,然后去访问。 20 | 21 | 这个方法虽然解决了上面提到的两个问题,但是存在一些其它的问题。如果将上述方法抽象,可以认为通过: 22 | 23 | h = Hash(key) % N 24 | 25 | 这个算式计算每个key的请求应该被发送到哪台服务器,其中N为服务器的台数,并且服务器按照0 – (N-1)编号。 26 | 27 | 这个算法的问题在于容错性和扩展性不好。所谓容错性是指当系统中某一个或几个服务器变得不可用时,整个系统是否可以正确高效运行;而扩展性是指当加入新的服务器后,整个系统是否可以正确高效运行。 28 | 29 | 现假设有一台服务器宕机了,那么为了填补空缺,要将宕机的服务器从编号列表中移除,后面的服务器按顺序前移一位并将其编号值减一,此时每个key就要按h = Hash(key) % (N-1)重新计算;同样,如果新增了一台服务器,虽然原有服务器编号不用改变,但是要按h = Hash(key) % (N+1)重新计算哈希值。因此系统中一旦有服务器变更,大量的key会被重定位到不同的服务器从而造成大量的缓存不命中。而这种情况在分布式系统中是非常糟糕的。 30 | 31 | 一个设计良好的分布式哈希方案应该具有良好的单调性,即服务节点的增减不会造成大量哈希重定位。一致性哈希算法就是这样一种哈希方案。 32 | 33 | ## 3.一致性哈希算法 34 | 35 | ### 3.1算法简述 36 | 37 | 一致性哈希算法(Consistent Hashing)最早在论文《[Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web](https://link.zhihu.com/?target=http%3A//www.akamai.com/dl/technical_publications/ConsistenHashingandRandomTreesDistributedCachingprotocolsforrelievingHotSpotsontheworldwideweb.pdf)》中被提出。简单来说,一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0 - 232-1(即哈希值是一个32位无符号整形),整个哈希空间环如下: 38 | 39 | ![img](https://pic4.zhimg.com/80/v2-405f0a2eab8b49e24b83a21c75695c57_720w.webp) 40 | 41 | 整个空间按顺时针方向组织。0和232-1在零点中方向重合。 42 | 43 | 下一步将各个服务器使用H进行一个哈希,具体可以选择服务器的ip或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置,这里假设将上文中三台服务器使用ip地址哈希后在环空间的位置如下: 44 | 45 | ![img](https://pic2.zhimg.com/80/v2-dbc599469147a9ec7881b6a5fc19c361_720w.webp) 46 | 47 | 接下来使用如下算法定位数据访问到相应服务器:将数据key使用相同的函数H计算出哈希值h,通根据h确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器。 48 | 49 | 例如我们有A、B、C、D四个数据对象,经过哈希计算后,在环空间上的位置如下: 50 | 51 | ![img](https://pic3.zhimg.com/80/v2-2e5b64d7c60a521670c1a3308ee41f0a_720w.webp) 52 | 53 | 根据一致性哈希算法,数据A会被定为到Server 1上,D被定为到Server 3上,而B、C分别被定为到Server 2上。 54 | 55 | ### 3.2容错性与可扩展性分析 56 | 57 | 下面分析一致性哈希算法的容错性和可扩展性。现假设Server 3宕机了: 58 | 59 | ![img](https://pic3.zhimg.com/80/v2-869803fe1377a9ddf0a050607f43e3c2_720w.webp) 60 | 61 | 可以看到此时A、C、B不会受到影响,只有D节点被重定位到Server 2。一般的,在一致性哈希算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即顺着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。 62 | 63 | 下面考虑另外一种情况,如果我们在系统中增加一台服务器Memcached Server 4: 64 | 65 | ![img](https://pic3.zhimg.com/80/v2-330fbba0d628ce5cfbc6c3c4c6491076_720w.webp) 66 | 67 | 此时A、D、C不受影响,只有B需要重定位到新的Server 4。一般的,在一致性哈希算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器(即顺着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。 68 | 69 | 综上所述,一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。 70 | 71 | ### 3.3虚拟节点 72 | 73 | 一致性哈希算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜问题。例如我们的系统中有两台服务器,其环分布如下: 74 | 75 | ![img](https://pic3.zhimg.com/80/v2-0eee2ab0fe933a1d827c4c66e3205a26_720w.webp) 76 | 77 | 此时必然造成大量数据集中到Server 1上,而只有极少量会定位到Server 2上。为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器ip或主机名的后面增加编号来实现。例如上面的情况,我们决定为每台服务器计算三个虚拟节点,于是可以分别计算“Memcached Server 1#1”、“Memcached Server 1#2”、“Memcached Server 1#3”、“Memcached Server 2#1”、“Memcached Server 2#2”、“Memcached Server 2#3”的哈希值,于是形成六个虚拟节点: 78 | 79 | ![img](https://pic3.zhimg.com/80/v2-451e8e3ceb25c5e43d1766c1627cf16a_720w.webp) 80 | 81 | 同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“Memcached Server 1#1”、“Memcached Server 1#2”、“Memcached Server 1#3”三个虚拟节点的数据均定位到Server 1上。这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。 82 | 83 | ## 4.总结 84 | 85 | 目前一致性哈希基本成为了分布式系统组件的标准配置,例如Memcached的各种客户端都提供内置的一致性哈希支持。本文只是简要介绍了这个算法 86 | 87 | 原文地址:https://zhuanlan.zhihu.com/p/565477967 88 | 89 | 作者:linux -------------------------------------------------------------------------------- /【NO.115】一大波C++进阶知识干货分享,请接收!.md: -------------------------------------------------------------------------------- 1 | # 【NO.115】一大波C++进阶知识干货分享,请接收! 2 | 3 | 2020年是全球IT科技版图震荡和转折之年,系统级软件作为数字世界的核心基础设施,被视为“卡脖子”技术的关键,成为IT产学研“兵家必争之地”。C++语言一直被誉为系统级编程“皇冠上的明珠”。 4 | 5 | 在疫情放假那段时间,我就专心钻研C++进阶知识,终于功夫不负有心人,在不断地学习以及练习中,学会了很多知识,也算是学有所成,现在是一名月收入上万的Linux高级互联网架构师,觉得这么长时间的努力还是没有白费。 6 | 7 | ![img](https://pic1.zhimg.com/80/v2-9b95855affa78bfede6b505f254443e4_720w.webp) 8 | 9 | ## 1.源码分析专栏 10 | 11 | 关于这一部分的内容还是非常广阔的,主要讲一下关于nginx方面的知识。 12 | 13 | ### 1.1.Nginx基础架构 14 | 15 | core :Nginx的核心源代码,包括常用数据结构的以及Nginx 内核实现的核心代码; 16 | 17 | event:Nginx事件驱动模型,以及定时器的实现相关代码; 18 | 19 | http :Nginx 实现http 服务器相关的代码; 20 | 21 | mail :Nginx 实现邮件代理服务器相关的代码; 22 | 23 | misc :辅助代码,测试C++头 的兼容性,以及对Google_PerfTools 的支持; 24 | 25 | os :不同体系统结构所提供的系统函数的封装,提供对外统一的系统调用接口; 26 | 27 | stream:nginx(tcp/udp)反向代理及与上游通信的基础模块 28 | 29 | ### 1.2.HTTP架构 30 | 31 | 它的架构主要有七层,包括Controller、View、Application、Business、Component、Datadriver、Systemdriver。 32 | 33 | 这方面知识非常广阔,我主要拿两个重点讲,可能下面也有些内容讲得不够全面,大家都可以进交流群详细了解,免费领取相关学习资料。 34 | 35 | 1、Application 36 | 37 | 应用层在最上面,其针对实际中的单个页面或则单个接口, Controller通过HTTP请求地址中的参数找到对应的Application,然后执行中指定的公共方法,比如main(), 然后应用就开始启动。 38 | 39 | 应用层的职责包括接受HTTP参数(- 般是间接接受,比如从request对象中获取) ,调用Business层的特定业务,保存业务执行结果,这些结果最终会由View显示出来,当然是通过Controller协调。应用层是M层分解成五层之后最高的层,Controller会 与此层直接通信。 40 | 41 | 2、Business 42 | 43 | 业务层在应用层之下,通常一个应用实例对应一个业务实例, 而一个业务有可能为多个应用服务,业务是一个执行流, 它通过执行一系列的操作来完成应用的需求。 44 | 45 | 这些操作来自下层的组件层Component,可能一个业务需 要一个或则多 个组件来完成-个完整的需求。因为一个业务实例通常只对应-个功能,所以只有-个固定的方法会被上层的应用调用,比如flow()。 业务层的职责是帮应用层执行业务流并且有必要的时候返回数据给应用层,它会调用下层Component的方法。 46 | 47 | ### 1.3.进程间的通信机制 48 | 49 | 进程通常被定义为一个正在运行的程序的实例,它由两个部分组成: 50 | 一个是操作系统用来管理进程的内核对象,它是系统用来存放关于进程的统计信息的地方。 51 | 另一个是地址空间,它包含所有的可执行模块或DLL模块的代码和数据,还包含动态分配的空间,如线程堆栈和堆分配空间。 52 | 53 | 每个进程被赋予它自己的虚拟地址空间,当进程中的一个线程正在运行时,该线程可以访问只属于它的进程的内存。属于其它进程的内存则是隐藏的,并不能被正在运行的线程访问。 54 | 55 | ### 1.4.Nginx高级数据结构 56 | 57 | ![img](https://pic2.zhimg.com/80/v2-c21d86c61bb6a3d5756d945a3fde65ed_720w.webp) 58 | 59 | ### 1.5.Slab共享共存 60 | 61 | 使用共享内存,需要在配置文件里加上该共享内存的相关配置信息,而 Nginx 在进行配置解析的过程中,根据这些配置信息就会创建对应的共享内存,不过此时的创建仅仅只是代表共享内存的结构体 ngx_shm_zone_t 变量的创建。具体实现在函数 ngx_shared_memory_add 内。 62 | 63 | ### 1.6.upstream机制设计 64 | 65 | 1、轮询(weight) 66 | 67 | 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。默认当weight不指定时,各服务器weight相同,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 68 | 69 | 2、ip_hash 70 | 71 | 每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session不能跨服务器的问题。如果后端服务器down掉,要手工down掉。 72 | 73 | 3、fair(第三方插件) 74 | 75 | 按后端服务器的响应时间来分配请求,响应时间短的优先分配。 76 | 77 | 4、url_hash(第三方插件) 78 | 79 | 按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存服务器时比较有效。在upstream中加入hash语句,hash_method是使用的hash算法。 80 | 81 | 设备的状态有: 82 | 83 | (1)down:表示单前的server暂时不参与负载 84 | 85 | (2)weight:权重,默认为1, weight越大,负载的权重就越大。 86 | 87 | (3)max_fails:允许请求失败的次数默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。 88 | 89 | (4)fail_timeout:max_fails次失败后,暂停的时间。 90 | 91 | (5)backup:备用服务器, 其它所有的非backup机器down或者忙的时候,请求backup机器,所以这台机器压力会最轻。 92 | 93 | 94 | 95 | ## 2.对标腾讯T1-T9学习技术路线图谱 96 | 97 | ![img](https://pic4.zhimg.com/80/v2-0136174b9a47c53154bd90972b8b93b3_720w.webp) 98 | 99 | ## **3. 数十位大佬大厂面经视频及2021面试题资源分享** 100 | 101 | ![img](https://pic1.zhimg.com/80/v2-238077f1b5caa91c38999aac3cd24bfc_720w.webp) 102 | 103 | ## **4.金三银四”程序员26套求职简历模板** 104 | 105 | ![img](https://pic4.zhimg.com/80/v2-8eea469345276a43b1f78991b5d4e8f3_720w.webp) 106 | 107 | 作为一个过来人给大家提个醒,C++的学习是一个漫长的过程,特别是进阶需要花很多时间,学习过程也困难重重,所以最好是找学习的小伙伴,相互帮助一起学,学起来会感觉轻松一些,学习效果也不言而喻。 108 | 109 | 原文链接:https://linuxcpp.0voice.com/?id=107 110 | 111 | 作者:[HG](https://linuxcpp.0voice.com/?auth=10) -------------------------------------------------------------------------------- /【NO.116】线程池的优点及其原理,简单、明了。.md: -------------------------------------------------------------------------------- 1 | # 【NO.116】线程池的优点及其原理,简单、明了。 2 | 3 | ## 1.使用线程池的好处 4 | 5 | 池化技术应用:线程池、数据库连接池、http连接池等等。 6 | 7 | 池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。 8 | 9 | **线程池提供了一种限制、管理资源的策略。** 每个**线程池**还维护一些基本统计信息,例如已完成任务的数量。 10 | 11 | **使用线程池的好处:** 12 | 13 | - **降低资源消耗**:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 14 | - **提高响应速度**:当任务到达时,可以不需要等待线程创建就能立即执行。 15 | - **提高线程的可管理性**:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,监控和调优。 16 | 17 | ## 2.Executor框架 18 | 19 | Executor框架不仅包括了线程池的管理,还提供了线程工厂、队列以及拒绝策略等,让并发编程变得更加简单。 20 | 21 | **Executor框架的使用示意图** 22 | 23 | ![img](https://pic2.zhimg.com/80/v2-dff549e683cdef4a7025c42b9f431e5d_720w.webp) 24 | 25 | 1. **主线程首先要创建实现Runnable或Callable接口的任务对象。** 26 | 2. **把创建完成的实现Runnable/Callable接口的对象直接交给ExecutorService执行:** 27 | 28 | ``` 29 | ExecutorService.execute(Runnable command)或者ExecutorService.sumbit(Runnable command)或ExecutorService.sumbit(Callable task). 30 | ``` 31 | 32 | 1. **如果执行ExecutorService.submit(…),ExecutorService将返回一个实现Future接口的对象。最后,主线程可以执行FutureTask.get()方法来等待任务执行完成。主线程也可以执行FutureTask.cancel()来取消次任务的执行。** 33 | 34 | ``` 35 | import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo { private static final int CORE_POOL_SIZE = 5; private static final int MAX_POOL_SIZE = 10; private static final int QUEUE_CAPACITY = 100; private static final Long KEEP_ALIVE_TIME = 1L; public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new ArrayBlockingQueue<>(QUEUE_CAPACITY), new ThreadPoolExecutor.CallerRunsPolicy()); //执行线程代码 executor.shutdown(); }} 36 | ``` 37 | 38 | **CORE_POOL_SIZE:**核心线程数定义了最小可以同时运行的线程数量。 39 | 40 | **MAX_POOL_SIZE:**当队列中存放的任务到达队列容量的时候,当前可以同时运行的线程数量变为最大线程数。 41 | 42 | **QUEUE_CAPACITY:**当新任务加入是会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,任务就会被存放到队列中。 43 | 44 | **KEEP_ALIVE_TIME:**当线程池中的线程数量大于核心线程数时,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过**KEEP_ALIVE_TIME**才会被回收销毁。 45 | 46 | **ThreadPoolExecutor.CallerRunsPolicy():**调用执行自己的线程运行任务,也就是直接在调用execute方法的线程运行(run)被拒绝的任务,如果执行程序已关闭,则会丢弃任务。因此这种策略会降低新任务的提交速度,影响程序的整体性能。另外,这个策略喜欢增加队列容量。如果应用程序可以承受此延迟并且不能任务丢弃一个任务请求的话,可以选择这个策略。 47 | 48 | **线程池分析原理** 49 | 50 | ![img](https://pic1.zhimg.com/80/v2-965f8856f55a687ef34664b2c1a7086c_720w.webp) 51 | 52 | ## 3.线程池大小确定 53 | 54 | 有一个简单且使用面比较广的公式: 55 | 56 | - **CPU密集型任务(N+1):**这种任务消耗的主要是CPU资源,可以将线程数设置为N(CPU核心数)+1,比CPU核心数多出来一个线程是为了防止线程偶发的缺页中断,或者其他原因导致的任务暂停而带来的影响。一旦任务停止,CPU就会处于空闲状态,而这种情况下多出来一个线程就可以充分利用CPU的空闲时间。 57 | - **I/O密集型(2N):**这种任务应用起来,系统大部分时间用来处理I/O交互,而线程在处理I/O的是时间段内不会占用CPU来处理,这时就可以将CPU交出给其他线程使用。因此在I/O密集型任务的应用中,可以配置多一些线程,具体计算方是2N。 58 | 59 | 原文链接:https://zhuanlan.zhihu.com/p/330504183 60 | 61 | 作者:Linux服务器研究 -------------------------------------------------------------------------------- /【NO.118】C++后台开发,以我之见.md: -------------------------------------------------------------------------------- 1 | # 【NO.118】C++后台开发,以我之见 2 | 3 | ![img](https://pic3.zhimg.com/80/v2-7694bcb9180778650c4c8ab710df2562_720w.webp) 4 | 5 | 今天趁着过完春节快要回公司工作之际,也马上进入金三银四的时期,谈谈我个人对后台开发的一些个人见解,希望能够对在校的学生或者刚刚接触C++后台开发的同学有点帮助。 6 | 7 | 还记得自己在学校的时候,一直都比较注重的是:编程语言+数据结构与算法。没错,对于一个在校的计算机专业的学生,这是很重要的方面。但是,这往往不够,或许是因为毕业前一直没有进入企业实习,以至于自己在毕业之前,对自己未来的职业规划做得很不够,不知道自己以后会做什么方向,那时候比较宽泛且迷茫的定位是,只要是软件开发的工作,我都OK。毕业后,主要是从事C++后台开发,工作一段时间后,才知道自己擅长什么,对什么感兴趣。 8 | 9 | ## 1.**前端和后端,你喜欢什么?** 10 | 11 | 一提到前端,大家都会想到html+javascript+css,或许这是web前端的最最基本的东西了吧。我个人会将与用户直接打交道的端称为前端,除了前面所提到的传统意义上的前端,我还会把android和ios开发的app称为前端。现在前端各种框架的迭代速度相当的快,要跟上各种比较NB的框架的步伐,也不是那么简单的事情。虽然工作之后,没有做过前端方面的项目,更多的是与前端工程师FE合作,但是我知道,前端领域也有很多东西要学,而且前端的东西由于能自己直接看到开发结果,或许在工作中会很有成就感,所以永远不要觉得前端工程师做的事情没技术含量,萝卜青菜,各有所爱,任何一个领域,只要深入了,都很有技术含量,关键在于自己喜不喜欢,擅长不擅长。就我个人而言,更喜欢的是后端开发,主要原因是在学校的时候一直学的是C++,工作之后一直做的是后端的项目,没有直接参与前端的项目,既来之,则安之,既然上天给我分配了一个方向,我就应该在这个方向上做深入研究。 12 | 13 | ## 2.**后台开发是什么?** 14 | 15 | 我第一次听说过后台开发这个岗位是在腾讯的招聘网站上,有一个岗位叫后台开发。个人觉得,后台开发也很广,开发语言也很多,如:php,node.js,java,C/C++,go ,每一个公司都有自己主打的语言,如腾讯和百度的后端开发中,C++用的比较多,当然php也用得比较多,阿里和美团,java用得比较多。当然,语言只是一种实现工具而已,不能单一地认为那种语言好那种语言不好,没有最好,只有最适合。后台开发,是相对前端开发而言,个人觉得,所有跟前端直接交互的开发都可以认为是后台开发。企业里面,除了前端开发的岗位,就是后台开发了吗?当然不是。这也是我要说的,希望能够给在校的学生一点思考。在互联网公司里面,有美学功底非常好的UE工程师,他们常常会站在用户的角度进行审美,提高用户体验,能够在产品真正落地之前,做出各种demo;有市场调研和需求分析的产品经理PM,具有严密的逻辑思维和良好的沟通能力;有前面所提到的前端工程师FE,负责向后端发送用户提交的请求,并接收后端返回的结果,进行展示;有软件研发工程师RD,需要具备一定的研发能力和bug定位和修复,系统性能优化等能力;有测试开发工程师QA,上线前的最后把关;又做运维的OP,负责维护和监控线上的稳定;有做运营的,像双十一等大型的购物节,一般都需要强大的运营支持;有做大数据的,hadoop+spark+storm各种大数据框架;有做基础架构的;有做算法分析的。。。还有更多的职位。 16 | 17 | ## 3.**C++后台开发需要掌握什么?** 18 | 19 | 这个话题有点大,而且像我这种小菜,只能抛砖引玉。语言只是基础,不能一味地去研究语法糖。记得我在学校的时候,特别喜欢去研究语法糖,现在想想,浪费了很多时间。当然,作为C++后端的研发工程师,**你首先需要掌握C++的基础语法,需要掌握STL里面常用的库和算法**,如果你觉得这还不够,你可以去系统地学习下**boost库**,里面多STL里面所不具有很备的,看看C++11就知道了,里面很多新增的东西都是来自boost库。当然,仅仅掌握语言还远远不够,C++做后台开发时,模块跟模块直接除了通过lib库或so库的方式相互调用外,还有更多的是采用网络交互,这个时候,你就需要**掌握多线程编程和网络编程的基础知识,**当然,由于开发效率的需要,现在你不需要从零搭建一个网络服务框架,比如:ACE、boost的asio和libevent。当然现在已经有各种开源的RPC框架了,比如google-rpc,你可以通过调用本地函数来完成网络包的发送与接收,so easy!那么网络通信包的格式如何定义呢?客户端和服务端需要提前约定?数据交互格式,常用的包括:js**on、xml和protobuffer**,通常前端后后端交互会采用json,而后端各个模块的交互,你可以随便选择;对于HTTP协议的交互,我用的比较多的是json,而 tcp协议,我用的比较多的是protobuffer。当然,服务端的平台有很重要,国内后台开发,基本都是运行在Linux系统上,**所以你需要掌握Linux系统的常用的命令**,这样你才可以在Linux系统上运用自如,所以,如果你想从事或者即将从事C++后台开发,请暂时抛下VS下的C++学习,从现在开始,转向Linux平台下的C++开发,那里有**你要编译器GCC/G++,调试时用到的gdb,**如果你想依次性一个命令编译所有的文件,**请学习下如何编写makefile。**好了,有了编程语言,有了编译和调试方法,你就可以将你的应用程序放在你的Linux系统上监听客户端的请求了。如果某一天,你的程序出core了怎么办?你必须要学会如果找出bug,除了前面提到的gdb,在大型的应用里面,你必须要学会掌握如何追bug,这个时候,**你就要学会打日志**,并且分等级打印日志,这样**一出问题了你就能够快速定位问题的所在**。日志有了,程序也能正常跑了,那你怎么算你程序的性能或者收益呢?所以,**你需要学会编写脚本语言**,我个人**推荐你去掌握shell脚本和python脚本**,脚本语言能够一边执行一边编译,具有比较高的开发效率,不用你每次执行前编译,掌握了脚本,你不用再那么忙了,哈哈。 20 | 21 | **提高自己的技术硬实力**。**技术的瓶颈是认知的问题,认知不是知其名,还需要知其因,更需要知其原。**这个话题更大,但是适合很多技术岗位。在工作中,你不能只跟项目中的业务逻辑打交道,那样你会觉得自己做的事情越来越没意思,越来越没技术含量。你应该**有一种开源的情怀**,你要找一个比较NB的开源软件,如 redis, zookeeper,nginx等,去阅读其中的源码,当然,你也可以将你写的一些库上传到gitlab上,让大家给你提建议,**相信开源让人进步**;你可以去gitlab上下载和学习各种有意思的开源库,这会给你带来更多的成就感。同时你要**学会利用各种资源来解决你所遇到的各种问题**,如segmentfault,stackoverflow等国外著名的网站。 22 | 23 | 原文链接:https://zhuanlan.zhihu.com/p/352365043 24 | 25 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.11】CPIP 协议.md: -------------------------------------------------------------------------------- 1 | # 【NO.11】CP/IP 协议 2 | 3 | TCP/IP 是不同的通信协议的大集合。 4 | 5 | ## 1.协议族 6 | 7 | TCP/IP 是基于 TCP 和 IP 这两个最初的协议之上的不同的通信协议的大集合。 8 | 9 | ## 2.TCP - 传输控制协议 10 | 11 | TCP 用于从应用程序到网络的数据传输控制。 12 | 13 | TCP 负责在数据传送之前将它们分割为 IP 包,然后在它们到达的时候将它们重组。 14 | 15 | ## 3.IP - 网际协议(Internet Protocol) 16 | 17 | IP 负责计算机之间的通信。 18 | 19 | IP 负责在因特网上发送和接收数据包。 20 | 21 | ## 4.HTTP - 超文本传输协议(Hyper Text Transfer Protocol) 22 | 23 | HTTP 负责 web 服务器与 web 浏览器之间的通信。 24 | 25 | HTTP 用于从 web 客户端(浏览器)向 web 服务器发送请求,并从 web 服务器向 web 客户端返回内容(网页)。 26 | 27 | ## 5.HTTPS - 安全的 HTTP(HTTP Secure) 28 | 29 | HTTPS 负责在 web 服务器和 web 浏览器之间的安全通信。 30 | 31 | 作为有代表性的应用,HTTPS 会用于处理信用卡交易和其他的敏感数据。 32 | 33 | ## 6.SSL - 安全套接字层(Secure Sockets Layer) 34 | 35 | SSL 协议用于为安全数据传输加密数据。 36 | 37 | ## 7.SMTP - 简易邮件传输协议(Simple Mail Transfer Protocol) 38 | 39 | SMTP 用于电子邮件的传输。 40 | 41 | ## 8.MIME - 多用途因特网邮件扩展(Multi-purpose Internet Mail Extensions) 42 | 43 | MIME 协议使 SMTP 有能力通过 TCP/IP 网络传输多媒体文件,包括声音、视频和二进制数据。 44 | 45 | ## 9.IMAP - 因特网消息访问协议(Internet Message Access Protocol) 46 | 47 | IMAP 用于存储和取回电子邮件。 48 | 49 | ## 10.POP - 邮局协议(Post Office Protocol) 50 | 51 | POP 用于从电子邮件服务器向个人电脑下载电子邮件。 52 | 53 | ## 11.FTP - 文件传输协议(File Transfer Protocol) 54 | 55 | FTP 负责计算机之间的文件传输。 56 | 57 | ## 12.NTP - 网络时间协议(Network Time Protocol) 58 | 59 | NTP 用于在计算机之间同步时间(钟)。 60 | 61 | ## 13.DHCP - 动态主机配置协议(Dynamic Host Configuration Protocol) 62 | 63 | DHCP 用于向网络中的计算机分配动态 IP 地址。 64 | 65 | ## 14.SNMP - 简单网络管理协议(Simple Network Management Protocol) 66 | 67 | SNMP 用于计算机网络的管理。 68 | 69 | ## 15.LDAP - 轻量级的目录访问协议(Lightweight Directory Access Protocol) 70 | 71 | LDAP 用于从因特网搜集关于用户和电子邮件地址的信息。 72 | 73 | ## 16.ICMP - 因特网消息控制协议(Internet Control Message Protocol) 74 | 75 | ICMP 负责网络中的错误处理。 76 | 77 | ## 17.ARP - 地址解析协议(Address Resolution Protocol) 78 | 79 | ARP - 用于通过 IP 来查找基于 IP 地址的计算机网卡的硬件地址。 80 | 81 | ## 18.RARP - 反向地址转换协议(Reverse Address Resolution Protocol) 82 | 83 | RARP 用于通过 IP 查找基于硬件地址的计算机网卡的 IP 地址。 84 | 85 | ## 19.BOOTP - 自举协议(Boot Protocol) 86 | 87 | BOOTP 用于从网络启动计算机。 88 | 89 | ## 20.PPTP - 点对点隧道协议(Point to Point Tunneling Protocol) 90 | 91 | PPTP 用于私人网络之间的连接(隧道)。 92 | 93 | 原文链接:https://www.runoob.com/tcpip/tcpip-protocols.html -------------------------------------------------------------------------------- /【NO.12】TCPIP 邮件.md: -------------------------------------------------------------------------------- 1 | # 【NO.12】TCP/IP 邮件 2 | 3 | 电子邮件是 TCP/IP 最重要的应用之一。 4 | 5 | ## 1.您不会用到... 6 | 7 | 当您写邮件时,您不会用到 TCP/IP。 8 | 9 | 当您写邮件时,您用到的是电子邮件程序,例如莲花软件的 Notes,微软公司出品的 Outlook,或者 Netscape Communicator 等等。 10 | 11 | ## 2.邮件程序会用到... 12 | 13 | 您的电子邮件程序使用不同的 TCP/IP 协议: 14 | 15 | - 使用 SMTP 来发送邮件 16 | - 使用 POP 从邮件服务器下载邮件 17 | - 使用 IMAP 连接到邮件服务器 18 | 19 | ## 3.SMTP - 简单邮件传输协议 20 | 21 | SMTP 协议用于传输电子邮件。SMTP 负责把邮件发送到另一台计算机。 22 | 23 | 通常情况下,邮件会被送到一台邮件服务器(SMTP 服务器),然后被送到另一台(或几台)服务器,然后最终被送到它的目的地。 24 | 25 | SMTP 也可以传送纯文本,但是无法传输诸如图片、声音或者电影之类的二进制数据。 26 | 27 | SMTP 使用 MIME 协议通过 TCP/IP 网络来发送二进制数据。MIME 协议会将二进制数据转换为纯文本。 28 | 29 | ## 4.POP - 邮局协议 30 | 31 | POP 协议被邮件程序用来取回邮件服务器上面的邮件。 32 | 33 | 假如您的邮件程序使用 POP,那么一旦它连接上邮件服务器,您的所有的邮件都会被下载到邮件程序中(或者称之为邮件客户端)。 34 | 35 | ## 5.IMAP - 因特网消息访问协议 36 | 37 | 与 POP 类似,IMAP 协议同样被邮件程序使用。 38 | 39 | IMAP 协议与 POP 协议之间的主要差异是:如果 IMAP 连上了邮件服务器,它不会自动地将邮件下载到邮件程序之中。 40 | 41 | IMAP 使您有能力在下载邮件之前先通过邮件服务器端查看他们。通过 IMAP,您可以选择下载这些邮件或者仅仅是删除它们。比方说您需要从不同的位置访问邮件服务器,但是仅仅希望回到办公室的时候再下载邮件,IMAP 在这种情况下会很有用。 42 | 43 | 原文链接:https://www.runoob.com/tcpip/tcpip-email.html -------------------------------------------------------------------------------- /【NO.130】红黑树 与 B+树区别和应用场景.md: -------------------------------------------------------------------------------- 1 | # 【NO.130】红黑树 与 B+树区别和应用场景 2 | 3 | ## **1.红黑树** 4 | 5 | 红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。再二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求: 6 | 7 | \1. 节点是红色或黑色 8 | 9 | \2. 根节点是黑色。 10 | 11 | 3 每个叶节点(NIL节点,空节点)是黑色的。 12 | 13 | 4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点) 14 | 15 | \5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。 16 | 17 | ## **2.红黑树和avl(二叉平衡树)的比较** 18 | 19 | \1. 如果插入一个node引起了树的不平衡,AVL和RB-Tree(红黑树)都是最多只需要2次旋转操作,即两者都是O(1);但是在删除node引起树的不平衡时,最坏情况下,AVL需要维护从被删node到root这条路径上所有node的平衡性,因此需要旋转的量级O(logN),而RB-Tree最多只需3次(因为不需要严格的平衡,从根到叶子的最长的可能路径不多于最短的可能路径的两倍长)旋转以及修改节点的颜色,只需要O(1)的复杂度。 20 | 21 | \2. 其次,AVL的结构相较RB-Tree来说更为平衡,在插入和删除node更容易引起Tree的unbalance,因此在大量数据需要插入或者删除时,AVL需要rebalance的频率会更高。因此,RB-Tree在需要大量插入和删除node的场景下,效率更高。自然,由于AVL高度平衡,因此AVL的search效率更高。 22 | 23 | ## 3.**红黑树实际应用**: 24 | 25 | IO多路复用epoll的实现采用红黑树组织管理sockfd,以支持快速的增删改查. 26 | ngnix中,用红黑树管理timer,因为红黑树是有序的,可以很快的得到距离当前最小的定时器. 27 | java中TreeMap,jdk1.8的hashmap的实现. 28 | 29 | ### 3.1.**B+树** 30 | 31 | B+ 树是一种树数据结构,是一个n叉排序树,每个节点通常有多个孩子,一棵B+树包含根节点、内部节点和叶子节点。根节点可能是一个叶子节点,也可能是一个包含两个或两个以上孩子节点的节点。 32 | 33 | ( ps:举例说明3阶B-树指的是每个结点最多2个关键字,3个孩子) 34 | 35 | **B+**树是对B树的一种变形树,它与B树的差异在于: 36 | 37 | - 有k个子结点的结点必然有k个关键码; 38 | - 非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。 39 | - 树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录,便于区间查找和遍历。 40 | - B+ 树的优点在于:由于B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率。B+树的叶子结点都是相连的,因此对整棵树的便利只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。但是B树也有优点,其优点在于,由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。下面是B 树和B+树的区别图: 41 | 42 | ![img](https://pic2.zhimg.com/80/v2-0b69ce0bbe57d6c35db33c5eb03ecff1_720w.webp) 43 | 44 | ### **3.2.b+树的应用场景:** 45 | 46 | B/B+树是为了磁盘或其它存储设备而设计的一种平衡多路查找树(相对于二叉,B树每个内节点有多个分支),与红黑树相比,在相同的的节点的情况下,一颗B/B+树的高度远远小于红黑树的高度(在下面B/B+树的性能分析中会提到).B/B+树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成,而CPU的速度非常快,所以B树的操作效率取决于访问磁盘的次数,关键字总数相同的情况下B树的高度越小,磁盘I/O所花的时间越少. 47 | 二叉查找树的结构不适合数据库,因为它的查找效率与层数相关。越处在下层的数据,就需要越多次比较。对于数据库来说,每进入一层,就要从硬盘读取一次数据,这非常致命,因为硬盘的读取时间远远大于数据处理时间,数据库读取硬盘的次数越少越好。这种数据结构,非常有利于减少读取硬盘的次数。假定一个节点可以容纳100个值,那么3层的B树可以容纳100万个数据,如果换成二叉查找树,则需要20层!假定操作系统一次读取一个节点,并且根节点保留在内存中,那么B树在100万个数据中查找目标值,只需要读取两次硬盘。 48 | 49 | 原文链接:https://zhuanlan.zhihu.com/p/217875063 50 | 51 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.132】C++后台服务器开发必备技能——数据库连接池.md: -------------------------------------------------------------------------------- 1 | # 【NO.132】C++后台服务器开发必备技能——数据库连接池 2 | 3 | ## 1.数据库连接的 4 | 5 | 前言:在后端开发过程中,有很多技能是必备的,比如我们今天说到的数据库;后端人员与数据库打交道是最多的;当然,数据库连接并不是我们所想的那么简单,只是单纯的连接一下就OK了,其实在这其中还有很多的坑等着我们去踩,我这里提出来几个点,大家可以思考一下: 6 | 1.数据库连接后,怎么防止断开,断开后又是怎么重连的 7 | 2.数据库带宽打满后,数据库超时,我们需要怎么处理 8 | 3.数据库异常,我们又该怎么处理? 9 | 4.数据库连接池连接数越大越好吗? 10 | 这些都是我在做项目中遇到的问题,这些问题大家可以留言一起讨论,这边文章就不作回答了。 11 | 12 | ## **2.数据库连接池** 13 | 14 | 什么是数据库连接池?其实数据库池就是好比我们有很多的玩具放在一个储物柜中,其中玩具就是我们的数据库连接,储物柜就是我们池子;我们需要玩具时就存储物柜中,使用完后会将玩具放入储物柜;这样,我们就会有源源不断的玩具供我们使用,也就是我们会有源源不断的连接供我们使用。 15 | 16 | ## **3.使用连接池的原因** 17 | 18 | 数据库连接是一种关键的有限的昂贵的资源, 一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库连接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。 连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况。 19 | 20 | ## **4.连接池使用的优点** 21 | 22 | ### 1.数据库连接过程 23 | 24 | 执行数据库语句流程 25 | 26 | ![img](https://pic4.zhimg.com/80/v2-b1fcd220075e4b8617bccf9977f79e87_720w.webp) 27 | 28 | 1.TCP建立连接的三次握手 29 | 2.MySQL认证的三次握手 30 | 3.真正的SQL执行 31 | 4.MySQL的关闭 32 | 5.TCP的四次握手关闭 33 | 34 | 缺点: 35 | 网络IO较多 36 | 数据库的负载较高 37 | 响应时间较长及QPS较低 38 | 应用频繁的创建连接和关闭连接,导致临时对象较多 39 | 在关闭连接后,会出现大量TIME_WAIT 的TCP状态(在2个MSL之后关闭) 40 | 41 | ### 2.数据库的连接 42 | 43 | ![img](https://pic2.zhimg.com/80/v2-a8cf302ed0c53994c0fe44ee8fb0ab75_720w.webp) 44 | 45 | 使用数据库连接池的步骤: 46 | 47 | 第一次访问的时候,需要建立连接。 但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。 48 | 49 | 优点: 50 | 51 | 减少了网络开销 52 | 系统的性能会有一个实质的提升 53 | 没了麻烦的TIME_WAIT状态 54 | 55 | ## **5.连接池的工作原理** 56 | 57 | 连接池的工作原理主要由三部分组成,分别为 58 | 59 | 连接池的建立 60 | 连接池中连接的使用管理 61 | 连接池的关闭 62 | 第一、连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等 63 | 第二、连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是: 64 | 当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。 65 | 当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。 66 | 该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。 67 | 第三、连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。 68 | 69 | ## **6.案例** 70 | 71 | 最近在github看到一个数据库连接池,顺便按照这个做一个解析: 72 | 73 | ![img](https://pic2.zhimg.com/80/v2-46ae81bb8e0c21653574b5d5b11cfc81_720w.webp) 74 | 75 | 这个数据库连接池做的比较简单,但是中间是有比较多的问题的: 76 | 1.正常项目中,数据库连接一般会放在配置文件中,避免硬编码 77 | 2.数据库连接应该放在初始化中,建立好固定连接数,如果在程序执行过程中执行的话,速度会比较慢。 78 | 3.对于连接池的维护,更合适的做法是使用线程定时器去不断检测,并且要做好异常措施。 79 | 80 | 原文链接:https://zhuanlan.zhihu.com/p/250542768 81 | 82 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.133】深入理解异步IO+epoll+协程.md: -------------------------------------------------------------------------------- 1 | # 【NO.133】深入理解异步I/O+epoll+协程 2 | 3 | ## 1.前言 4 | 5 | **同步和异步**的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。 6 | **阻塞和非阻塞**的概念描述的是用户线程调用内核IO操作的方式:阻塞是指IO操作需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。 7 | 8 | ## 2.异步I/O 9 | 10 | 在理解异步I/O之前,我们先要知道什么是**同步I/O** 11 | 在**阻塞同步I/O**模型下,用户线程向内核发起 recvfrom 系统调用,当数据没有准备好的时候,用户线程阻塞。 12 | 此外还有一种**非阻塞同步I/O**,此时用户线程不阻塞于 recvfrom,而是反复向系统查询数据状态。当数据准备好了,就对数据进行后续处理。 13 | 14 | ![img](https://pic2.zhimg.com/80/v2-b555a5fbc20f5471f20e87f7d34f9e41_720w.webp) 15 | 16 | Paste_Image.png 17 | 18 | 而在**异步I/O**模式下,用户线程在数据还没有准备好的时候**既不阻塞也不反复查询**,而是**继续干自己该干的事情**。内核会开启一个**内核线程**去读取数据,等到数据准备好了,内核给用户线程一个信号,用户线程中断去执行信号处理函数。 19 | 20 | ![img](https://pic1.zhimg.com/80/v2-369b5305eb19bdb26a628aad2926b72c_720w.webp) 21 | 22 | Paste_Image.png 23 | 24 | **node.js**中的异步回调也是采用开线程的方式实现的 25 | 26 | ## 3.epoll 27 | 28 | epoll/select 是一种**I/O多路复用模型** 29 | 用户线程可以先通过 epoll **注册多个I/O事件** 30 | 然后用户线程反复执行调用 epoll/select 查询是否有准备好的事件 31 | 如果有准备好的I/O事件则进行处理 32 | 关键点是**一个用户线程处理多个I/O事件** 33 | 34 | **epoll/select 的区别在于** 35 | select 的底层原理是遍历所有注册的I/O事件,找出准备好的的I/O事件。 36 | 而 epoll 则是由内核主动通知哪些I/O事件需要处理,不需要用户线程主动去反复查询,因此大大提高了事件处理的效率。 37 | 38 | ![img](https://pic3.zhimg.com/80/v2-e071e80242c1d2d5ade493e9cb7ba532_720w.webp) 39 | 40 | Paste_Image.png 41 | 42 | ## 4.协程 43 | 44 | 协程是一种轻量级的线程 45 | **本质上协程就是用户空间下的线程** 46 | 如果把线程/进程当作虚拟“CPU”,协程即跑在这个“CPU”上的线程。 47 | 48 | **协程的特点** 49 | 50 | 1. 占用的资源更少。 51 | 2. 所有的切换和调度都发生在用户态。 52 | 53 | 不管是进程还是线程,每次阻塞、切换都需要陷入系统调用,先让CPU跑操作系统的调度程序,然后再由调度程序决定该跑哪一个线程。而且由于**抢占式调度执行顺序无法确定**的特点,使用线程时需要非常小心地处理同步问题,而协程完全不存在这个问题。 54 | 因为**协程可以在用户态显示控制切换** 55 | 56 | **例子** 57 | 传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。 58 | 59 | 如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高: 60 | 61 | ``` 62 | import timedef consumer(): r = '' while True: n = yield r if not n: return print('[CONSUMER] Consuming %s...' % n) time.sleep(1) r = '200 OK'def produce(c): c.next() n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) print('[PRODUCER] Consumer return: %s' % r) c.close()if __name__=='__main__': c = consumer() produce(c) 63 | ``` 64 | 65 | 协程的优点是可以用同步的处理方式实现异步回调的性能 66 | 67 | 原文链接:https://zhuanlan.zhihu.com/p/254990291 68 | 69 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.139】网络IO模型的介绍引出nginx的网络IO模型.md: -------------------------------------------------------------------------------- 1 | # 【NO.139】网络IO模型的介绍引出nginx的网络IO模型 2 | 3 | ## 1. 什么是IO? 4 | 5 | 简单来说就是输入输出 6 | 7 | ## 2. 网络IO经历步骤 8 | 9 | - 1. 用户在获取网络资源是在进入网卡,经过网络七层模型将请求交给nginx用户进程 10 | 2. 用户进程无法直接获取磁盘上的资源,会将请求获取什么资源翻译并转发给内核,内核驱动磁盘寻道找到文件(最耗时间的环节) 11 | 3. 文件同样也不能直接交给用户进程,首先磁盘将文件放至内核缓冲区,然后内核告知用户进程请求的资源结果已准备好(耗时比较短) 12 | 4. 内核缓冲区将文件拷贝一份至用户进程缓冲区,用户进程拿到文件 构建响应报文,通过http回传给用户 13 | 14 | - ![img](https://pic4.zhimg.com/80/v2-74c2c051ead8d5e7575b4d620c838c63_720w.webp) 15 | 16 | - 总结: 17 | - IO总共要经历两个阶段 18 | - 1.磁盘读取数据,将数据拷贝至内核缓冲区 19 | - 2.将内核缓冲区数据拷贝至用户进程缓冲区 20 | 21 | ------ 22 | 23 | ## 3. 网络IO模型种类 24 | 25 | ### 3.1.同步阻塞 26 | 27 | - 比如你要去完成这样一件事情,把水烧开,灌入暖瓶 28 | - 现在给你一口普通的锅,你也不知道水什么时候可以烧开,你决定守在旁边等待水开。 29 | - 这个时候的你是什么也做不了的,是不是觉得很浪费时间 30 | - 当水开了就需要你把水灌入暖瓶,你还是做不了其他的事情 31 | 32 | - ![img](https://pic1.zhimg.com/80/v2-8d30ffd63c1dd5e2c539bfec2ada014c_720w.webp) 33 | 34 | ### 3.2.同步非阻塞 35 | 36 | 比如还是烧一锅开水,你还是不知道水什么时候会开,你选择了另外一个策略,去忙其它的事情但是要时不时看下谁开没开 37 | 38 | ### 3.3.改进了什么呢? 39 | 40 | 你可以在空挡时间忙其它的事情 41 | 42 | ### 3.4.增加了什么麻烦呢? 43 | 44 | 你得时不时的确认水开没开(就意味着得不停的调用cpu,占用cpu资源) 45 | 46 | 可能你上一次刚看的时候水没开,等你刚看完水开了,那就得等下一次查看的时候才能发现,增加了延迟,等你知道了水开了,自己去把水灌好 47 | 48 | ![img](https://pic2.zhimg.com/80/v2-d1cbb03b4c24e49aec6527482bc27f39_720w.webp) 49 | 50 | ### 3.5.异步阻塞 51 | 52 | - 这个就比较二了 53 | - 假如给你一个智能热水壶,水开了会自动的提醒你的,但是你还是要傻傻的等 54 | - 完全没有利用好智能热水壶的优势(不做解释) 55 | 56 | - ![img](https://pic2.zhimg.com/80/v2-8409cfe5e921afadeb490dbea39e6221_720w.webp) 57 | 58 | 同步异步:关注的是消息的通知机制* 59 | 60 | 阻塞非阻塞:关注的是在收到结果之前调用者的状态是等待还是忙自己的事情* 61 | 62 | ### 3.6.IO多路复用 63 | 64 | - 还拿烧水的例子 65 | - 不要你来管水开没开,直接交给一个代理,假如代理名叫select,select在那帮你监控水开没开,捎带他还会看饭有没有煮好,你把所有的事情都交代给它,可以去忙自己的事情了在饭还没熟,水还没开的时候select就处于一个等待的状态,假如这个时候水开了select会水开了的结果写到一个小黑板你可以看到并知道水开了,不需要你去查看水开没开,接下来由你将烧开的水灌进暖瓶(用户进程前半部分不阻塞,后半部份阻塞) 66 | 67 | - ![img](https://pic3.zhimg.com/80/v2-3e3c6249511eb75bf6b026f8e5f52386_720w.webp) 68 | 69 | ### 3.7.select、poll、epoll的区别 70 | 71 | 无论是select、poll、epoll都可以面对多个用户进程的请求,它相当于一个代理人,收集很多用户进程的请求,他帮你从磁盘上获取数据,复制到内核中,那么这个数据准备没准备好它的实现机制是不一样的 72 | 73 | 通知方式:假设有一个用户数据准备好了,那么还有很多用户数据还没有准备好,那么如何通知? 74 | 75 | select和poll是遍历扫描,效率低下 76 | 77 | epoll采用回调机制,epoll会主动通知,效率更高(nginx支持) 78 | 79 | ### 3.8.信号驱动IO 80 | 81 | - 举例:比如我们的智能热水壶我么设定好烧水以后,会给你一个反馈结果为设定成功,然后你就可以去忙其他的事情了,等到水烧开了会发给你一条短息,这个时候你再回去把水灌入暖瓶 82 | 83 | - ![img](https://pic1.zhimg.com/80/v2-190efdab6bda949111ca47cec8a527bc_720w.webp) 84 | 85 | - 通知机制: 86 | 水平触发:多次通知 87 | 边缘触发:只通知一次 88 | 89 | - ![img](https://pic3.zhimg.com/80/v2-e94c61f777d1a25e4871637cb10332ce_720w.webp) 90 | 91 | 原文链接:https://zhuanlan.zhihu.com/p/283073309 92 | 93 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.146】一个故事看懂CPU的SIMD技术.md: -------------------------------------------------------------------------------- 1 | # 【NO.146】一个故事看懂CPU的SIMD技术 2 | 3 | 好久不见,我叫阿Q,是CPU一号车间的员工。我所在的CPU有8个车间,也就是8个核心,咱们每个核心都可以同时执行两个线程,就是8核16线程,那速度杠杠滴。 4 | 5 | 我所在的一号车间,除了负责执行指令的我,还有负责读取指令的小A,负责指令译码的小胖和负责结果回写的老K,我们几个各司其职,一起完成执行程序的工作。 6 | 7 | **一个简单的循环** 8 | 9 | 那天,我们遇到了一段代码: 10 | 11 | ``` 12 | void array_add(int data[], int len) { 13 | for (int i = 0; i < len; i++) { 14 | data[i] += 1; 15 | } 16 | } 17 | ``` 18 | 19 | 循环了好几百次之后,才把这段代码执行完成,每次循环都是做简单又重复的工作,把我累得够呛。 20 | 21 | 一旁负责结果回写的老K也是累的满头大汗,吐槽道:“每次都是取出来加1又写回去,要是能一次多取几个数,批量处理就好了” 22 | 23 | 老K的话让我眼前一亮,对啊,能不能批量操作呢? 24 | 25 | 心里一边想着,一边继续干活了。 26 | 27 | 繁忙的一天很快结束了,转眼又到了晚上,计算机关机后,我把大家召集了起来。 28 | 29 | “兄弟们,还记得咱们白天遇到的那个循环吗?” 30 | 31 | “你说哪个循环,咱们这一天可执行了不少循环呢”,小A说到。 32 | 33 | “就是那个把整数数组每个元素都加1的那个循环” 34 | 35 | “我想起来了,那循环怎么了?有什么问题吗?” 36 | 37 | 我看了老K一眼,说道:“我在想今天老K的话,像这种循环,每次都是取出来加1又写回去,一次操作一个数,效率太低了,咱们要是升级改造一下,支持一次取出多个数,批量加1,这样岂不是快很多?” 38 | 39 | ![img](https://img2022.cnblogs.com/blog/659280/202203/659280-20220324095041810-1015126491.png) 40 | 41 | 老K一听来了兴趣,“这敢情好,你打算怎么做?” 42 | 43 | “这我还没想好,大家有什么建议吗?” 44 | 45 | 一旁负责指令译码的小胖说道:“可以新增一条指令,专门用来一次取出多个数据来加1” 46 | 47 | “不行不行,不能限的这么死,今天是加1,万一下次是加2呢?指令里面不能限制为1” 48 | 49 | “那如果每个数据要加的是不一样的怎么办?” 50 | 51 | “你这么一说,那万一不是加法,是减法,乘法怎么办?” 52 | 53 | “还有啊,···” 54 | 55 | 大家开始七嘴八舌讨论了起来,没想到一个小小的加法循环,一下子引出了这么多问题来,这是我们没想到的。 56 | 57 | **并行计算** 58 | 59 | 随着讨论的深入,我觉得已经超出了咱们一号车间能把控的范围,需要上报给领导,组织八个车间代表一起来商讨。 60 | 61 | 领导一听说有提高性能的新技术,马上来了兴趣,很快便开会组织大家一起来商讨方案。 62 | 63 | ![img](https://img2022.cnblogs.com/blog/659280/202203/659280-20220324095048605-964892437.png) 64 | 65 | “都到齐了是吧,阿Q你给大家说一下这个会议的目的”,领导说到。 66 | 67 | 我站了起来,开始把我们遇到的问题和想法跟大家讲了一遍。 68 | 69 | “是这样的,我们一号车间那天遇到了一段循环代码,循环体的内容很简单,就是给数组中的每一个元素加1。我们执行的时候,就是不断取出每一个元素,然后将其执行加法计算后,再写回去。这样一个一个来加1,我们感觉太慢了, 要是可以一次多取几个,并行加1,那一定比一个一个加快上不少。” 70 | 71 | 我刚说完,大家都开始小声议论起来。 72 | 73 | “我看出来了,这其实就是并行计算!”,二号车间小虎一语道出了关键。 74 | 75 | 六号车间小六问道:”阿Q,你们已经有方案了吗?“ 76 | 77 | “还没有,这正是今天开会的目的,因为情况有点复杂,还需要大家一起来出出主意” 78 | 79 | “好像并不复杂嘛” 80 | 81 | “我上面举的例子只是一个简单的情况,并行计算还可能不是固定的数,可能是一个数组和另一个数组相加。还有可能不是整数相加,而是浮点数,甚至,还可能不是加法,而是减法或者乘法,再或者不是算术运算,而是逻辑运算” 82 | 83 | 我刚一说完,大家又开始窃窃私语交流起来。 84 | 85 | “我琢磨着你说的这一系列东西,咱们是要新增一套专门用来并行计算的指令集啊”,小虎说道。 86 | 87 | “这可是大工程啊” 88 | 89 | “是啊···” 90 | 91 | 这时,小六又问道:“咱们的计算的时候,都是把数据读取到寄存器进行的,可这寄存器一次只能装一个数,怎么一次读取多个数据呢?” 92 | 93 | “可能需要新增一些容量大一些的寄存器,比如128bit长度,可以同时容纳4个32位的整数” 94 | 95 | ![img](https://img2022.cnblogs.com/blog/659280/202203/659280-20220324095055595-1269104379.png) 96 | 97 | “有这个必要吗?咱们是通用CPU,又不是专门做数学计算的芯片,搞这些东西干嘛?”,四号车间代表提出了质疑。 98 | 99 | 我也不甘示弱:“那可太有必要了,在图像、视频、音频处理等领域,有大量这样的计算需求,咱们得提升处理这些数据的能力” 100 | 101 | 见我们争执不下,领导拍了拍桌子,会场一下安静了下来。 102 | 103 | “我觉得阿Q说的有道理,咱们确实需要提升处理这类数据运算的能力了。不过不用一下搞那么复杂,先支持整数并行运算就行了。新增寄存器这个也不用着急,可以先借用一下浮点数运算单元FPU的寄存器。这件事先这么定下来,具体的方案你们再继续讨论。”,说完便离开了会议室。 104 | 105 | 领导不愧是领导,几句话就把我们安排的明明白白。 106 | 107 | **SIMD** 108 | 109 | 又经过一阵紧张的讨论,我们终于敲定了方案。 110 | 111 | 我们借用浮点数运算单元的寄存器,还给它们起了新的名字:MM0-MM7。因为是64位的寄存器,所以可以同时存储两个32位的整数或者4个16位整数或者8个8位的整数。 112 | 113 | 我们还新增了一套叫**MMX**的指令集,用来并行执行整数的运算。 114 | 115 | ![img](https://img2022.cnblogs.com/blog/659280/202203/659280-20220324095103206-1244477371.png) 116 | 117 | 我们把这种在一条指令中同时处理多个数据的技术叫做单指令多数据流(`Single Instruction Multiple Data`),简称**SIMD**。 118 | 119 | 有了这套指令集,咱们处理这类整数运算问题的速度快了不少。 120 | 121 | 不过渐渐地发现了两个很麻烦的问题: 122 | 123 | 第一个问题,因为是借用FPU的寄存器,所以当执行SIMD指令的时候,就不能用FPU计算单元,反过来也一样,同时使用的话就会出乱子,所以要经常在不同的模式之间切换,实在是有些麻烦。 124 | 125 | 另一个更重要的问题,咱们这套指令集只能处理整数的并行运算,可现在浮点数的并行运算越来越多,尤其是图像、视频还有深度学习的一些数据处理,浮点数情况越来越多,这时候都派不上用场。 126 | 127 | 我们把这些问题给领导做了汇报,看到我们已经做出的成绩,领导终于同意继续升级。 128 | 129 | 这一次,我们扩展了一套新的SSE指令集出来,新增了XMM0-XMM7总共8个128位的寄存器,再也不用跟FPU共享寄存器了。而且位宽加了一倍,能容纳的数据更多了,能同时处理的数据自然也变多了。 130 | 131 | 后来,我们又不断的修改升级,不仅支持了对浮点数并行处理,还推出了新一代的AVX指令集,把寄存器再一次扩大为256位,现在我们的SIMD技术更加先进,处理数据运算的能力越来越强了! 132 | 133 | 原文作者:[轩辕之风](https://www.cnblogs.com/xuanyuan/) 134 | 135 | 原文链接:https://www.cnblogs.com/xuanyuan/p/16048303.html -------------------------------------------------------------------------------- /【NO.151】p2p之网络穿透NAT,NAT、穿透的原理.md: -------------------------------------------------------------------------------- 1 | # 【NO.151】p2p之网络穿透NAT,NAT、穿透的原理 2 | 3 | ## 1.p2p是什么? 4 | 5 | p2p是对等网络(peer-to-peer networking)其可以定义为:端对端的资源共享,每一端即可是服务端,也可以是客户端。既可以是资源的提供者,也可以是资源的共享者。 6 | 7 | 传统C/S模型需要实现端和端的资源共享, 需要将资源上传到中转服务器。另外一端再去中转服务器下载,如下图: 8 | 9 | ![img](https://pic3.zhimg.com/80/v2-b02447c4cd4ead9cb6169e86dfff507e_720w.webp) 10 | 11 | 传统CS架构,客户端1和客户端2之间是无直接交互.png 12 | 13 | 而P2P则不需要将资源上传到服务器,它是端对端传输,每一个端既可以是服务器,也可以是客户端 14 | 15 | ![img](https://pic4.zhimg.com/80/v2-8b4235fc0df3291ebf4bc91396a594fb_720w.webp) 16 | 17 | p2p架构,无需中转服务器.png 18 | 19 | 优势:实时性最高,流量少,更加安全。在视频直播,在线教育,视频安防行业用的比较多 20 | 劣势:一旦进行p2p传输之后,用户之间的内容将无法监管,浪费用户带宽,频繁进行读写磁盘 21 | 22 | 客户端1和客户端2这样交互是p2p最理想的情况 23 | 图中客户端1和客户端2直接连接, 假如他们处于两个不同的内网呢? 24 | 25 | ## 2.NAT是什么? 26 | 27 | NAT俗称网络地址转换,它是一种把内部私有网络地址(IP地址)转换成公网网络IP地址的技术。比如我们电脑里面网卡地址是192.168.1.100,但是我们再百度搜索“IP”却显示220.112.224.53,这就是NAT的功能。 28 | **NAT主要是部署在路由器或者交换机上。** 29 | 30 | ## 3.为什么需要NAT? 31 | 32 | 主要还是IP地址的不足,使用少量的公有IP 地址代表较多的私有IP 地址的方式,将有助于减缓可用的IP地址空间的枯竭。用大白话:比如你有一个路由器(家用的那种就可以)这个路由器本身连接了公网(被分配到了一个公网的IP地址)。路由器后面有接了N多个设备,每个设备都分配到了一个私有的地址(内网地址),这些地址可以通过这个路由器和外网交互。 33 | 34 | 其次能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。 35 | RFC3489 中将 NAT 的实现分为四大类: 36 | 37 | - Full Cone NAT(完全圆锥型) 38 | - Address Restricted Cone NAT(地址限制圆锥型 ) 39 | - Port Restricted Cone NAT(端口限制圆锥型) 40 | - Symmetric NAT(对称型) 41 | 42 | ## 4.完全圆锥型NAT 43 | 44 | 在完全圆锥型NAT(Full Cone NAT)中,NAT会将客户机地址{X:y}转换成公网地址{A:b}并绑定。任何包都可以通过地址{A:b}送到客户主机的{X:y}地址上。如图所示: 45 | 46 | ![img](https://pic3.zhimg.com/80/v2-b84f4a39e311e786608cf01f7d62cf62_720w.webp) 47 | 48 | RFC3581——完全锥型NAT 49 | 50 | ## 5. 地址限制圆锥型NAT 51 | 52 | 地址限制圆锥型NAT(Address Restricted Cone NAT)会将客户机地址{X:y}转换成公网地址{A:b}并绑定,只有来自主机{P}的包才能和主机{X:y}通信。如下图所示: 53 | 54 | ![img](https://pic4.zhimg.com/80/v2-a329e90e007c0a1ce406349b47819c93_720w.webp) 55 | 56 | RFC3581——地址限制型NAT 57 | 58 | ## 6.端口限制圆锥型NAT 59 | 60 | 端口限制圆锥型NAT(Port Restricted Cone NAT)会将客户机地址{X:y}转换成公网地址{A:b}并绑定,只有来自主机{P,q}的包才能和主机{X:y}通信。如下图所示: 61 | 62 | ![img](https://pic1.zhimg.com/80/v2-baaea3df8eccf4422cd8c688889e4d04_720w.webp) 63 | 64 | RFC3581——端口限制型NAT 65 | 66 | ## 7.对称型NAT 67 | 68 | 对称型NAT(Symmetric NAT)会将客户机地址{X:y}转换成公网地址{A:b}并绑定为{X:y}|{A:b}<->{P:q}。对称型NAT只接受来自{P:q}的连接,将它转给{X:y} ,每次客户机请求一个不同的公网地址和端口,NAT会新分配一个端口号{C,d} 。如下图所示: 69 | 70 | ![img](https://pic4.zhimg.com/80/v2-beb4537f62e9b1897b1ccee19e945747_720w.webp) 71 | 72 | RFC3581——对称型NAT 73 | 74 | **其中完全最上层的完全圆锥形NAT的穿透性最好,而最下层的对称形NAT的安全性最高。** 75 | 76 | ## 8.如何穿透NAT? 77 | 78 | 事实上两个客户端相互通信还需要一个辅助服务器(p2pserver) 来保存两个用户的外网地址端口。 79 | 当用户A连接B时、或者B连接A时, 会向辅助服务器询问对方的外网地址和端口 80 | 81 | ![img](https://pic3.zhimg.com/80/v2-00c8c5a560148ae21565b5f8ee1dc5aa_720w.webp) 82 | 83 | NAT穿透组合情况.png 84 | 85 | 从上面的NAT类型中可以看出,有4种NAT,一共10种组合 86 | 87 | **1. 完全圆锥型NAT和完全圆锥型NAT** 88 | 这种最简单, 只需要B从辅助服务器拿到A的内外网信息, 就可以和A进行连接 89 | **2. 完全圆锥型NAT和地址限制型NAT** 90 | 同上 91 | **3. 完全圆锥型NAT和端口限制性NAT** 92 | 同上 93 | **4. 完全圆锥型NAT和对称型NAT** 94 | 同上 95 | **5.地址限制型NAT和地址限制型NAT** 96 | 97 | - 当B从辅助服务器拿到A的内外网信息, B向A发送连接, 这个时候NAT A设备会丢弃掉B发送过来的连接。 98 | - 这个时候B就向辅助服务器发送请求,让A连接B一次, 连完后B就可以连接到A了,NAT A不再拦截B过来的连接。 99 | 100 | **6.地址限制型NAT和端口限制型NAT** 101 | 同上 102 | **7.地址限制性NAT和对称型NAT** 103 | 同上 104 | **8.端口限制型NAT和端口限制型NAT** 105 | 同上 106 | **9.端口限制型NAT和对称型NAT** 107 | 这种无法穿透, 因为A需要连过B,B才能连到A,但是A无法连接到B,因为B的是对称型NAT,端口一直在变 108 | **10.对称型NAT和对称型NAT** 109 | 这种也无法穿透,因为客户机每次请求一个不同的公网地址和端口, NAT会新分配一个端口号,所以从辅助服务器拿到的端口号是无效的(只是针对和服务器相连的端口号)。 110 | eg:A和辅助服务器相连,NAT A会分配一个端口 8081。 111 | A和B相连, NAT A会分配一个端口号10020,所以B连A并不知道A需要从10020进,所以无法穿透过NAT A。不过也有人通过端口预测算法成功连接, 但是这种并不可靠。 112 | 113 | ![img](https://pic1.zhimg.com/80/v2-80b87d01775cd205828d9a651ab6b48c_720w.webp) 114 | 115 | ## 9.为什么需要保活链路? 116 | 117 | 因为一个连接经过NAT设备之后,在NAT设备上面绑定的端口是有时效性的,一般是30分钟,但是最少的三五分钟就失效了,所以要不停的发送心跳包来保活NAT上的这个“洞”。 118 | 119 | ## 10.移动、联通网络为什么没有电信快? 120 | 121 | 原因是电信拨号之后分配的是公网IP。而联通、移动拨号之后还是内网IP,也就是NAT设备上面还有多层NAT, 多次转发并且最终的出口只有一个,所以总体来说比较慢 122 | 123 | 原文链接:https://zhuanlan.zhihu.com/p/299524798 124 | 125 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.157】 Nginx 防盗链.md: -------------------------------------------------------------------------------- 1 | # 【NO.157】 Nginx 防盗链 2 | 3 | > 本篇主要介绍一下 nginx 中 防盗链的具体配置 , 以及http 的 referer 头 4 | 5 | ![image-20221205135337893](https://img2023.cnblogs.com/other/1898722/202212/1898722-20221213091423499-169252935.png) 6 | 7 | ## 1.概述 8 | 9 | ``` 10 | 防盗链其实就是 防止别的站点来引用你的 资源, 占用你的流量 11 | ``` 12 | 13 | 在了解nginx 防盗链之前 我们先了解一下 什么是 HTTP 的头信息 Referer,当浏览器访问网站的时候,一般会带上Referer,`告诉后端该是从哪个页面过来的` 14 | 15 | nginx的 防盗链'功能基于 HTTP协议的Referer机制,通过判断Referer对来源进行 识别和判断 做出一定的处理 16 | 17 | nginx会通就过`查看referer自动和valid_referers后面的内容进行匹配`,如果匹配到了就将`invalid_referer 变量置位1` , 如果没有匹配到 , 则 将 invalid_referer变量置0 , 匹配的过程中不区分大小写. 18 | 19 | | 语法 | valid_referers none blocked server_names string… | 20 | | ------ | ------------------------------------------------ | 21 | | 默认值 | — | 22 | | 位置 | server、location | 23 | 24 | ## 2.Nginx 防盗链演示 25 | 26 | 这里我拿图片等资源 作为案例演示 27 | 28 | ### 2.1 未配置 防盗链的情况 29 | 30 | > 如果你的图片没有做防盗链的控制 , 像如下配置一样, 那么其他人就可以直接使用你的文件图片等等 31 | 32 | ```properties 33 | http { 34 | include mime.types; 35 | 36 | default_type application/octet-stream; 37 | 38 | sendfile on; 39 | 40 | keepalive_timeout 65; 41 | 42 | 43 | server { 44 | listen 80; 45 | server_name www.testfront.com; 46 | 47 | location ~* .(gif|jpg|png) { 48 | root /www/static; 49 | } 50 | 51 | location / { 52 | proxy_pass http://www.testbackend.com; 53 | } 54 | } 55 | 56 | } 57 | ``` 58 | 59 | 可以看到在其他机器上如 www.testbackend.com 直接引入 www.testfront.com的资源文件 , 也是可以展示的 60 | 61 | ![image-20221205132512265](https://img2023.cnblogs.com/other/1898722/202212/1898722-20221213091423917-1213037371.png) 62 | 63 | ### 2.2 配置nginx防盗链 64 | 65 | 如果配置了valid_referers 66 | 67 | nginx会通就过`查看referer自动和valid_referers后面的内容进行匹配`,如果匹配到了就将`invalid_referer 变量置位1` , 如果没有匹配到 , 则 将 invalid_referer变量置0 , 匹配的过程中不区分大小写. 68 | 69 | ```properties 70 | location ~* .(gif|jpg|png) { 71 | # 配置校验 referer , 意思就是如果referer 是172.16.225.111 或者 www.testfront.com 都通过 72 | valid_referers 172.16.225.111 www.testfront.com; 73 | if ($invalid_referer) { 74 | return 403; 75 | } 76 | root /www/static; 77 | } 78 | ``` 79 | 80 | 此时再访问 www.testbackend.com 去引用 www.testfront.com 的资源 就不能访问了 81 | 82 | ![image-20221205133441066](https://img2023.cnblogs.com/other/1898722/202212/1898722-20221213091424461-1384825207.png) 83 | 84 | ## 3. 防盗链的 具体配置 85 | 86 | 从上面可以看出, 通过配置 valid_referers 后面添加校验的域名和ip , nginx 会自动进行 http的 referer 的匹配 87 | 88 | ``` 89 | 防盗链除了可以配置 ip 域名外, 还能配置 如 none 和 blocked 90 | ``` 91 | 92 | - **none**: 如果Header中的Referer为空,允许访问 93 | 94 | - blocked:在Header中的Referer不为空,但是该值被防火墙或代理进行伪装过,如不带"http://" 、"https://"等协议头的资源允许访问。 95 | 96 | - server_names:指定具体的域名或者IP 97 | 98 | 可以支持正则表达式和*的字符串。如果是正则表达式,需要以~开头表示 99 | 100 | ## 4.扩展Curl 访问 101 | 102 | 可以通过curl 直接进行访问 并且指定 referer 来快速测试 103 | 104 | ``` 105 | curl -i 只看返回头信息 106 | ``` 107 | 108 | ![image-20221205134817848](https://img2023.cnblogs.com/other/1898722/202212/1898722-20221213091424753-2031673148.png) 109 | 110 | ``` 111 | curl -e "" 指定 referer 112 | ``` 113 | 114 | ![image-20221205134908232](https://img2023.cnblogs.com/other/1898722/202212/1898722-20221213091425637-297318403.png) 115 | 116 | ## 5.总结 117 | 118 | 本篇主要介绍了 nginx中如何配置 防盗链, 来限制别人随意的引用你的资源 造成占用你的网络资源情况, 配置还是毕竟简单的. 119 | 120 | 原文作者:[Johnny小屋](https://www.askajohnny.com/) 121 | 122 | 原文链接:https://www.cnblogs.com/askajohnny/p/16977685.html -------------------------------------------------------------------------------- /【NO.185】ringbuffer 消息队列 内存池 性能优化利器.md: -------------------------------------------------------------------------------- 1 | # 【NO.185】ringbuffer 消息队列 内存池 性能优化利器 2 | 3 | ## 1.简约而不简单的ringbuffer 4 | 5 | 最近在研究srsLTE的代码,其中就发现一个有意思的数据结构------ringbuffer。 6 | 7 | 虽然,这是一个很基本的数据结构,但时,它在LTE这种通信协议栈系统中却大行其道,也是很容易被协议开发人员忽略的。在整个通信协议的开发团队中,一般会有一个平台中间件的团队,他们的任务是给业务部门提供高性能、高可靠性的中间件代码,如内存池、线程池、消息通信机制、日志系统等等。这篇文章就来讨论下这个简约而不简单的ringbuffer。 8 | 9 | ## 2.ringbuffer数据结构 10 | 11 | 环形缓冲器(ringr buffer),也称作圆形队列(circular queue),循环缓冲区(cyclic buffer),圆形缓冲区(circula buffer),是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。 12 | 13 | 在通信程序中,经常使用环形缓冲器作为数据结构来存放通信中发送和接收的数据。环形缓冲区是一个先进先出的循环缓冲区,可以向通信程序提供对缓冲区的互斥访问。 14 | 15 | ![img](https://pic4.zhimg.com/80/v2-e62501b9e35913bf4af8340ffde589a3_720w.webp) 16 | 17 | ## 3.用法 18 | 19 | 圆形缓冲区的一个有用特性是:当一个数据元素被用掉后,其余数据元素不需要移动其存储位置。相反,一个非圆形缓冲区(例如一个普通的队列)在用掉一个数据元素后,其余数据元素需要向前搬移。换句话说,圆形缓冲区适合实现先进先出缓冲区,而非圆形缓冲区适合后进先出缓冲区。 20 | 21 | 圆形缓冲区适合于事先明确了缓冲区的最大容量的情形。扩展一个圆形缓冲区的容量,需要搬移其中的数据。因此一个缓冲区如果需要经常调整其容量,用链表实现更为合适。 22 | 23 | 写操作覆盖圆形缓冲区中未被处理的数据在某些情况下是允许的。特别是在多媒体处理时。例如,音频的生产者可以覆盖掉声卡尚未来得及处理的音频数据。 24 | 25 | ## 4.工作机制 26 | 27 | 一般的,圆形缓冲区需要4个指针 : 28 | 29 | - 在内存中实际开始位置; 30 | - 在内存中实际结束位置,也可以用缓冲区长度代替; 31 | - 存储在缓冲区中的有效数据的开始位置(读指针); 32 | - 存储在缓冲区中的有效数据的结尾位置(写指针)。 33 | 读指针、写指针可以用整型值来表示。下例为一个未满的缓冲区的读写指针: 34 | 35 | ![img](https://pic1.zhimg.com/80/v2-386e64896fd987c1c8fae13f7d766f20_720w.webp) 36 | 37 | 下例为一个满的缓冲区的读写指针: 38 | 39 | ![img](https://pic2.zhimg.com/80/v2-54ff4d3d06cbe882d4ecb0d053d1b219_720w.webp) 40 | 41 | ## 5.区分缓冲区满或者空 42 | 43 | 缓冲区是满、或是空,都有可能出现读指针与写指针指向同一位置,有多种策略用于检测缓冲区是满、或是空。常用的做法是总是保持一个存储单元为空,缓冲区中总是有一个存储单元保持未使用状态。缓冲区最多存入 size-1个数据。如果读写指针指向同一位置,则缓冲区为空。如果写指针位于读指针的相邻后一个位置,则缓冲区为满。这种策略的优点是简单、鲁棒;缺点是语义上实际可存数据量与缓冲区容量不一致,测试缓冲区是否满需要做取余数计算。 44 | 45 | 出色的KFIFO 46 | 47 | kfifo是一种"First In First Out “数据结构,它采用了前面提到的环形缓冲区来实现,提供一个无边界的字节流服务。采用环形缓冲区的好处为,当一个数据元素被用掉后,其余数据元素不需要移动其存储位置,从而减少拷贝提高效率。更重要的是,kfifo采用了并行无锁技术,kfifo实现的单生产/单消费模式的共享队列是不需要加锁同步的。 48 | 49 | 熟悉Linux内核的读者应该对kfifo.c和kfifo.h并不陌生.kfifo经过简单改进就可以在用户态进行使用,笔者在实际项目中多次使用,经过实践,代码是稳定、可靠、高效的。 50 | 51 | ## 6.ringbuffer蕴藏的巨大能量 52 | 53 | ### 6.1 **消息队列** 54 | 55 | ringbuffer的一个天生的高性能的消息队列,特别是在单生产/单消费的模式下,它是无锁的,这点非常重要。之前的文章曾介绍过,LTE的协议栈实现对时序是敏感的,这意味着代码的执行不能有阻塞的风险,而线程间的通信几乎是协议栈中必须的基本功能。因此,用ringbuffer去实现一个高性能的消息队列是一种非常理想的方案。当然,由于不同的线程的运行模型不同,例如PDCP线程属于包驱动的线程,大部分时间它是属于阻塞的,当有数据到达,如RRC可以通过消息队列给PDCP发送一个消息,这个时候需要唤醒PDCP进行处理,这个是属于线程同步的技术范畴,可以通过MUTEX、信号量等方案去实现。如果你的系统的Linux(rt-patch),eventfd也是不错的选择,eventfd优势是可以使用poll、select、epoll等操作,这样协议栈的线程实现的方式上较为简洁,关键是eventfd性能也非常的快。 56 | 57 | 当然这里需要划一个重点,不同线程间需要独立的消息队列,来保证FIFO的无锁特性,当然缺点是会浪费一些内容,但是这在协议栈的开发中往往不是什么大的问题,性能和稳定永远是第一位的。由于FIFO通常是固定大小的数据结构不太适合可变消息的发送,这里的技巧是队列里面只放消息的指针,消息的内容通常是在内存池中申请不同大小的结构。 58 | 59 | srsLTE代码的实现PDCP和RLC并不一定是以单独的线程运行的,但是在实际项目中,为了性能的考虑,通常是需要线程化的,且上下行也要线程化,且绑定不同的CPU核,来保证性能。 60 | 61 | 下图是PDCP和RLC线程的消息队列实例: 62 | 63 | ![img](https://pic1.zhimg.com/80/v2-6588c70906a33414dd03cfdd9dc33f20_720w.webp) 64 | 65 | ### 6.2 内存池 66 | 67 | 内存池在通信协议栈和很多的软件中都是常用的技术,它的好处是除了可以避免内存碎片,更重要的一点是,内存是预先申请的,并且自我管理,在申请和释放的效率更快,这对协议栈的实现是十分重要的。 68 | 69 | 内存池的实现在方式都是大同小异的,通常将内存分为8字节、16字节、32字节… 1K等大小不同的内存块,并通过链表的方式进行管理。具体的实现方式可以自行到github上搜索,实现方式都是类似的。 70 | 71 | 那么,ringbuffer和内存池有什么关系呢?实际上,ringbuffer和内存池的实现并无直接的关系,但是内存池在实现上有个至关重要的问题,那就内存的申请和释放可能不是在同一个线程中。简单的说就是,内存的申请和释放可能存在竞争的情况,通常的做法是进行加锁进行保护。但是加锁的操作可能对协议的时序产生不确定的影响,这对时序要求较高的协议实现(如CMAC)是无法接受的。 72 | 73 | ringbuffer的优秀的特性又一次被应用的淋漓尽致,做法也是相当的简单,就是使用ringbuffer单生产/单消费的模式的无锁特性,释放的线程可以将需要释放的地址使用ringbuffer发送给申请的线程,由申请的线程进行内存的释放,这就就不需要加锁的操作,因为同一个线程不会出现并发的链表操作。 74 | 75 | 下图是结合了消息队列和内存池技术的一次应用,该方案是十分经典和有效的,在很多大型的通信系统中都能看到这种方案的影子: 76 | 77 | ![img](https://pic2.zhimg.com/80/v2-72f19fb8b1433a9d9ae3c527f549784d_720w.webp) 78 | 79 | ## 7.总结 80 | 81 | 本文是结合笔者的实际项目经验,介绍了ringbuffer在协议栈软件开发中的一些应用和技巧,主要是ringbuffer单生产/单消费的模式的无锁特性在内存池内存释放和消息队列中的应用技巧。如果读者也有类似的性能方面的系统需求,可以不妨试试 ringbuffer,性能超乎你的想象,且没有特别复杂的算法和CPU指令集的限制。 82 | 83 | 原文地址:https://zhuanlan.zhihu.com/p/556524313 84 | 85 | 作者:linux -------------------------------------------------------------------------------- /【NO.190】skynet源码结构、启动流程以及多线程工作原理.md: -------------------------------------------------------------------------------- 1 | # 【NO.190】skynet源码结构、启动流程以及多线程工作原理 2 | 3 | 本文主要介绍skynet源码目录结构、启动流程以及其多线程工作原理。 4 | 5 | ## **1、skynet目录结构** 6 | 7 | ![img](https://pic3.zhimg.com/80/v2-c98cba27955e00194aa1180e0f3203a2_720w.webp) 8 | 9 | - skynet-src: c语言写的skynet核心代码 10 | - service-src: c语言编写的封装给Lua使用的服务,编译后生成的so文件在cservice中(如gate.so, harbor.so, logger.so, snlua.so) 11 | - lualib-src: c语言编写的封装给lua使用的库,编译后生成的so文件在luaclib中(如bson.so, skynet.so, sproto.so等),提供C层级的api调用,如调用socket模块的api,调用skynet消息发送,注册回调函数的api,甚至是对C服务的调用等,并导出lua接口,供lua层使用,可以视为lua调C的媒介。 12 | - lualib: lua编写的库 13 | - service: lua写服务 14 | - 3rd :第三方库如jemalloc, lua等 15 | - test:lua写的一些测试代码 16 | - examples: 框架示例 17 | 18 | 只允许上层调用下层,而下层不能直接调用上层的api,这样做层次清晰 19 | 20 | ## **2、skynet启动流程** 21 | 22 | 1、启动skynet方式:终端输入./skynet exmaple/config 23 | 24 | 2、启动入口函数为skynet_main.c/main, config作为args[1]参数传入 25 | 26 | ![img](https://pic1.zhimg.com/80/v2-cdfe3c7bce21de12ff6a0f5ee66c590c_720w.webp) 27 | 28 | 3、调用skynet_start.c/skynet_start函数 29 | 30 | ![img](https://pic2.zhimg.com/80/v2-39086a348147da96f15da2854c47aa4d_720w.webp) 31 | 32 | 33 | 34 | ## 3、**skynet多线程工作原理** 35 | 36 | skynet线程创建工作由上述skynet_start.c/start完成,主要有以下四类线程: 37 | 38 | - monitor线程:监测所有的线程是否有卡死在某服务对某条消息的处理 39 | - timer线程:运行定时器 40 | - socket线程: 进行网络数据的收发 41 | - worker线程:负责对消息队列进行调度 42 | 43 | ![img](https://pic2.zhimg.com/80/v2-4a892948c76cf96d19b977f6ab611ed1_720w.webp) 44 | 45 | 1、moniter线程 46 | 47 | 初始化该线程的key对应的私有数据块 48 | 49 | 每5s对所有工作线程进行一次检测 50 | 51 | 调用skynet_monitor_check函数检测线程是否有卡住在某条消息处理 52 | 53 | ![img](https://pic3.zhimg.com/80/v2-ed056e859d5ce9b9b2ef9c56190dd3c6_720w.webp) 54 | 55 | 2、timer定时器线程 56 | 57 | 每隔2500微秒刷新计时、唤醒等待条件触发的工作线程并检查是否有终端关闭的信号,如果有则打开log文件,将log输出至文件中,在刷新计时中会对每个时刻的链表进行相应的处理. 58 | 59 | ![img](https://pic4.zhimg.com/80/v2-45d146ebe0655b1339ffd74d5bfcb66f_720w.webp) 60 | 61 | 3、socket套接字线程 62 | 63 | 处理所有的套接字上的事件,该线程确保所有的工作线程中至少有一条工作线程是处于运行状态的,以便可以处理套接字上的事件。 64 | 65 | ![img](https://pic4.zhimg.com/80/v2-82aad05d895f4f104a53928c88204c27_720w.webp) 66 | 67 | 4、worker工作线程 68 | 69 | 从全局队列中取出服务队列对其消息进行处理,其运行函数thread_worker的工作原理:首先初始化该线程的key对应的私有数据块,然后从全局队列中取出服务队列对其消息进行处理,最后当全局队列中没有服务队列信息时进入等待状态,等待定时器线程或套接字线程触发条件; 70 | 71 | ![img](https://pic2.zhimg.com/80/v2-55008c229c2c83cf16b32d5c4d55c2dd_720w.webp) 72 | 73 | ## **4、skynet消息处理如何保证线程安全?** 74 | 75 | 最后探讨一个问题,消息处理为什么线程安全?解释如下: 76 | 77 | - 每个worker线程,从global_mq取出的次级消息队列都是唯一的,有且只有一个服务与之对应,取出之后,在该worker线程完成所有callback调用之前,不会push回global_mq中,也就是说,在这段时间内,其他worker线程不能拿到这个次级消息队列所对应的服务,并调用callback函数,也就是说一个服务不可能同时在多条worker线程内执行callback函数,从而保证了线程安全。 78 | - 不论是global_mq还是次级消息队列,在入队和出队操作时,都需加上spinlock,这样多个线程同时访问mq的时候,第一个访问者会进入临界区并锁住,其他线程会阻塞等待,直至该锁解除,这样也保证了线程安全。 79 | - 我们在通过handle从skynet_context list里获取skynet_context的过程中(比如派发消息时,要要先获取skynet_context指针,再调用该服务的callback函数),需要加上一个读写锁,因为在skynet运作的过程中,获取skynet_context,比创建skynet_context的情况要多得多,因此这里用了读写锁 80 | 81 | 以上介绍了skynet源码中的目录结构以及各部分功能,接着介绍了skynet的启动流程,最后介绍了skynet的多个线程是如何进行协同工作的。 82 | 83 | 原文地址:https://zhuanlan.zhihu.com/p/549861580 84 | 85 | 作者:linux -------------------------------------------------------------------------------- /【NO.194】我们常说的短连接长连接和socket和http到底有什么关系.md: -------------------------------------------------------------------------------- 1 | # 【NO.194】我们常说的短连接长连接和socket和http到底有什么关系 2 | 3 | **最近有人问我长连接和短连接的问题,顺便写文一篇仅供对这几个概念还不是很清楚的朋友学习** 4 | 5 | **在开始讲之前先来熟悉几个概念** 6 | 7 | ## **1.socket** 8 | 9 | Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议.如今大多数基于网络的软件,如浏览器,即时通讯工具甚至是P2P下载都是基于Socket实现的 10 | 11 | ![img](https://pic1.zhimg.com/80/v2-3c00fe44041391e0d0f3f05795ebb938_720w.webp) 12 | 13 | **Socket通信实例:** 14 | 15 | ![img](https://pic3.zhimg.com/80/v2-dc3c52243f3aa879eed70c84a36e1f2e_720w.webp) 16 | 17 | 主机 A 的应用程序要能和主机 B 的应用程序通信,必须通过 Socket 建立连接,而建立 Socket 连接必须需要底层 TCP/IP 协议来建立 TCP 连接。建立 TCP 连接需要底层 IP 协议来寻址网络中的主机。我们知道网络层使用的 IP 协议可以帮助我们根据 IP 地址来找到目标主机,但是一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通信就要通过 TCP 或 UPD 的地址也就是端口号来指定。这样就可以通过一个 Socket 实例唯一代表一个主机上的一个应用程序的通信链路了。 18 | 19 | ## **2.tcp和udp** 20 | 21 | **UDP**,也就是用户数据报协议。UDP是一种无连接的协议,这就意味着我们每次发送数据报时,需要同时发送本机的socket描述符和接收端的socket描述符。因此,我们在每次通信时都需要发送额外的数据 22 | 23 | **TCP**,也就是传输控制协议。和UDP不同,TCP是一种基于连接的协议。在使用流通信之前,我们必须在通信的一对socket之间建立连接。其中一个socket作为服务器进行监听连接请求。另一个则作为客户端进行连接请求。一旦两个socket建立好了连接,他们可以单向或双向进行数据传输。 24 | 25 | ## **3.tcp/ip** 26 | 27 | TCP/IP是个协议组,可分为三个层次:网络层、传输层和应用层。 28 | 29 | 在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。 30 | 31 | 在传输层中有TCP协议与UDP协议。 32 | 33 | 在应用层有:TCP包括FTP、HTTP、TELNET、SMTP等协议 UDP包括DNS、TFTP等协议 34 | 35 | ## **4.TCP连接的建立和关闭** 36 | 37 | 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需要4次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的 38 | 39 | 经典的三次握手示意图: 40 | 41 | ![img](https://pic2.zhimg.com/80/v2-c4fd5ee79541653872e7fc492f6a0925_720w.webp) 42 | 43 | ## **5.短连接** 44 | 45 | **连接->传输数据->关闭连接** 46 | 47 | **HTTP中的短连接:**是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。 48 | 49 | 也可以这样说----短连接是指SOCKET连接后发送后接收完数据后马上断开连接。 50 | 51 | HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。 52 | 53 | HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码: 54 | 55 | > Connection:keep-alive 56 | 57 | HTTP长连接的过期时间 58 | 59 | ![img](https://pic3.zhimg.com/80/v2-eeb20564044f77a8c236a588444ef43e_720w.webp) 60 | 61 | 上图中的Keep-Alive: timeout=20,表示这个TCP通道可以保持20秒。另外还可能有max=XXX,表示这个长连接最多接收XXX次请求就断开。对于客户端来说,如果服务器没有告诉客户端超时时间也没关系,服务端可能主动发起四次握手断开TCP连接,客户端能够知道该TCP连接已经无效;另外TCP还有心跳包来检测当前连接是否还活着,方法很多,避免浪费资源。 62 | 63 | **tcp中的短连接:**我们模拟一下**TCP**短连接的情况,client向server发起连接请求,server接到请求,然后双方建立连接。client向server发送消息,server回应client,然后一次读写就完成了,这时候双方任何一个都可以发起close操作,不过一般都是client先发起close操作。为什么呢,一般的server不会回复完client后立即关闭连接的,当然不排除有特殊的情况。从上面的描述看,短连接一般只会在client/server间传递一次读写操作 64 | 65 | **短连接的优点是**:管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段 66 | 67 | ## **6.长连接** 68 | 69 | 连接->传输数据->保持连接 -> 传输数据-> 传输中 ->关闭连接。 70 | 71 | 长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。 72 | 73 | **http中的长连接:**HTTP也可以建立长连接的,使用Connection:keep-alive,HTTP 1.1默认进行持久连接。HTTP1.1和HTTP1.0相比较而言,最大的区别就是增加了持久连接支持(貌似最新的 http1.0 可以显示的指定 keep-alive),但还是无状态的,或者说是不可以信任的。 74 | 75 | **tcp中的长连接:**接下来我们再模拟一下长连接的情况,client向server发起连接,server接受client连接,双方建立连接。Client与server完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。 76 | 77 | 首先说一下TCP/IP中的TCP保活功能,保活功能主要为服务器应用提供服务,服务器应用希望知道客户主机是否崩溃,从而可以代表客户使用资源。如果客户已经消失,使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,则服务器将应远等待客户端的数据,保活功能就是试图在服务器端检测到这种半开放的连接。 78 | 79 | 如果一个给定的连接在两小时内没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下4个状态之一: 80 | 81 | 1. 客户主机依然正常运行,并从服务器可达。客户的TCP响应正常,而服务器也知道对方是正常的,服务器在两小时后将保活定时器复位。 82 | 2. 客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的TCP都没有响应。服务端将不能收到对探测的响应,并在75秒后超时。服务器总共发送10个这样的探测 ,每个间隔75秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。 83 | 3. 客户主机崩溃并已经重新启动。服务器将收到一个对其保活探测的响应,这个响应是一个复位,使得服务器终止这个连接。 84 | 4. 客户机正常运行,但是服务器不可达,这种情况与2类似,TCP能发现的就是没有收到探查的响应。 85 | 86 | ## 7.什么时候用长连接,短连接? 87 | 88 | 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。 89 | 90 | 而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。 91 | 92 | ![img](https://pic1.zhimg.com/80/v2-3d3863753676ace3cab9e951f146dd18_720w.webp) 93 | 94 | 原文地址:https://zhuanlan.zhihu.com/p/545256993 95 | 96 | 作者:Linux -------------------------------------------------------------------------------- /【NO.219】perf-网络协议栈性能分析.md: -------------------------------------------------------------------------------- 1 | # 【NO.219】perf-网络协议栈性能分析 2 | 3 | 分析 Linux 网络协议栈性能有多种方式和工具。本文主要通过 Perf 生成 On-CPU 火焰图的方式,分析 Linux 内核网络协议栈在特定场景下的性能瓶颈,从而知晓当前协议栈的网络状况。 4 | 5 | ## 1.关于 On/Off-CPU 6 | 7 | ### 1.1.概念定义 8 | 9 | ![img](https://pic4.zhimg.com/80/v2-140bbf8313007f59c92008d0cf6d87c3_720w.webp) 10 | 11 | ### 1.2.On/Off-CPU 选择 12 | 13 | 在工程实践中,如果是 CPU 消耗型使用 On-CPU 火焰图,如果是 IO 消耗型则使用 Off-CPU 火焰图。如果无法确定, 可以通过压测工具来确认: 通过压测工具看能否让 CPU 使用率趋于饱和,从而判断是否为 CPU 消耗型。 14 | 15 | ### 1.3.分析方法 16 | 17 | Perf 火焰图整个图形看起来就像一团跳动的火焰,这也正是其名字的由来。燃烧在火苗尖部的就是 CPU 正在执行的操作,不过需要说明的是颜色是随机的,本身并没有特殊的含义,纵向表示调用栈的深度,横向表示消耗的时间。因为调用栈在横向会按照字母排序,并且同样的调用栈会做合并,所以一个格子的宽度越大越说明其可能是瓶颈。综上所述,主要就是看那些比较宽大的火苗,特别留意那些类似平顶山的火苗。 18 | 19 | ## 2.火焰图原理 20 | 21 | 火焰图是基于 stack 信息生成图片, 用来展示 CPU 调用栈。 22 | 23 | - y 轴表示调用栈, 每一层都是一个函数。调用栈越深, 火焰就越高, 顶部就是正在执行的函数, 下方都是它的父函数。 24 | - x 轴表示抽样数, 如果一个函数在x轴占据越宽, 则表示它被抽到的次数多, 即执行的时间长。 25 | 26 | 火焰图就是看顶层的哪个函数占据的宽度最大。只要有“平顶”(plateaus),就表示该函数可能存在性能问题。 27 | 28 | ## 3.On-CPU 采集原理 29 | 30 | ![img](https://pic1.zhimg.com/80/v2-a682f2717063b9dc0ca22c4da9989d74_720w.webp) 31 | 32 | ![img](https://pic1.zhimg.com/80/v2-081e26d5bef572c4e585d05ca522990c_720w.webp) 33 | 34 | Linux 内核网络协议栈分析 35 | 36 | server 37 | 38 | ``` 39 | yum install iperfyum install perfgit clone https://github.com/brendangregg/FlameGraph.git 40 | ``` 41 | 42 | 运行 iperf server 43 | 44 | ``` 45 | [root@bogon ~]# iperf -s -u -DRunning Iperf Server as a daemon[root@bogon ~]# 46 | ``` 47 | 48 | 查看 CPU 以及 softirqd 进程号 49 | 50 | ``` 51 | [root@bogon ~]# lscpuArchitecture: aarch64Byte Order: Little EndianCPU(s): 64On-line CPU(s) list: 0-63Thread(s) per core: 1Core(s) per socket: 4Socket(s): 16NUMA node(s): 4Model: 2BogoMIPS: 100.00NUMA node0 CPU(s): 0-15NUMA node1 CPU(s): 16-31NUMA node2 CPU(s): 32-47NUMA node3 CPU(s): 48-63Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid 52 | ``` 53 | 54 | ![img](https://pic4.zhimg.com/80/v2-a4150c37c195fd209c70f582b0959d9b_720w.webp) 55 | 56 | 查看网卡中断亲和性 57 | 58 | ![img](https://pic2.zhimg.com/80/v2-12bd0c929fad16b85392da3063e4f001_720w.webp) 59 | 60 | ![img](https://pic2.zhimg.com/80/v2-a2d31aad24d547a15829d926cb6bbdcd_720w.webp) 61 | 62 | ``` 63 | VM001: 通过如下方式采集信息(采集原理见上)perf record -F 1000 -a -g -p 162 -- sleep 60 64 | ``` 65 | 66 | 发送 UDP 数据报文 67 | 68 | ``` 69 | iperf -c 10.254.2.161 -i 1 -P 10 -t 10 -u -b 1000M 70 | ``` 71 | 72 | ## 4.生成 On-CPU 火焰图 73 | 74 | ![img](https://pic1.zhimg.com/80/v2-b088f3056d212b56812a3b60285b3a04_720w.webp) 75 | 76 | ![img](https://pic1.zhimg.com/80/v2-bddd0d6ad7f632560c3f4ae6dfc8ff48_720w.webp) 77 | 78 | ![img](https://pic1.zhimg.com/80/v2-503a3f2918d0b2e0474fc90de58b6558_720w.webp) 79 | 80 | 原文链接:https://linuxcpp.0voice.com/?id=442 81 | 82 | 作者:[HG ](https://linuxcpp.0voice.com/?auth=10) -------------------------------------------------------------------------------- /【NO.273】CMake基础 第11节 导入目标.md: -------------------------------------------------------------------------------- 1 | # 【NO.273】CMake基础 第11节 导入目标 2 | 3 | ## 1.介绍 4 | 5 | 正如前面在第8节中提到的,较新版本的CMake允许你使用导入的别名目标链接第三方库。 6 | 7 | 本教程中的文件如下: 8 | 9 | ```objectivec 10 | $ tree 11 | . 12 | ├── CMakeLists.txt 13 | ├── main.cpp 14 | ``` 15 | 16 | - [CMakeLists.txt] - 包含要运行的CMake命令 17 | 18 | ```cmake 19 | cmake_minimum_required(VERSION 3.5) 20 | 21 | # Set the project name 22 | project (imported_targets) 23 | 24 | 25 | # find a boost install with the libraries filesystem and system 26 | find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system) 27 | 28 | # check if boost was found 29 | if(Boost_FOUND) 30 | message ("boost found") 31 | else() 32 | message (FATAL_ERROR "Cannot find Boost") 33 | endif() 34 | 35 | # Add an executable 36 | add_executable(imported_targets main.cpp) 37 | 38 | # link against the boost libraries 39 | target_link_libraries( imported_targets 40 | PRIVATE 41 | Boost::filesystem 42 | ) 43 | ``` 44 | 45 | - [main.cpp] - 具有main的源文件 46 | 47 | ```cpp 48 | #include 49 | #include 50 | #include 51 | 52 | int main(int argc, char *argv[]) 53 | { 54 | std::cout << "Hello Third Party Include!" << std::endl; 55 | 56 | // use a shared ptr 57 | boost::shared_ptr isp(new int(4)); 58 | 59 | // trivial use of boost filesystem 60 | boost::filesystem::path path = "/usr/share/cmake/modules"; 61 | if(path.is_relative()) 62 | { 63 | std::cout << "Path is relative" << std::endl; 64 | } 65 | else 66 | { 67 | std::cout << "Path is not relative" << std::endl; 68 | } 69 | 70 | return 0; 71 | } 72 | ``` 73 | 74 | ## 2.要求 75 | 76 | 此示例需要以下条件: 77 | 78 | - CMake v3.5+ 79 | - 安装在默认系统位置的Boost库 80 | 81 | ## 3.概念 82 | 83 | ### 3.1 导入目标 84 | 85 | 导入目标是由FindXXX模块导出的只读目标(例如Boost::filesystem)。 86 | 87 | 要包括Boost文件系统,你可以执行以下操作: 88 | 89 | ```cmake 90 | target_link_libraries( imported_targets 91 | PRIVATE 92 | Boost::filesystem 93 | ) 94 | ``` 95 | 96 | 这将自动链接`Boost::FileSystem`和`Boost::System`库,同时还包括`Boost include`目录(即不必手动添加include目录)。 97 | 98 | ## 4.构建示例 99 | 100 | ```shell 101 | $ mkdir build 102 | 103 | $ cd build/ 104 | 105 | $ cmake .. 106 | -- The C compiler identification is GNU 5.4.0 107 | -- The CXX compiler identification is GNU 5.4.0 108 | -- Check for working C compiler: /usr/bin/cc 109 | -- Check for working C compiler: /usr/bin/cc -- works 110 | -- Detecting C compiler ABI info 111 | -- Detecting C compiler ABI info - done 112 | -- Detecting C compile features 113 | -- Detecting C compile features - done 114 | -- Check for working CXX compiler: /usr/bin/c++ 115 | -- Check for working CXX compiler: /usr/bin/c++ -- works 116 | -- Detecting CXX compiler ABI info 117 | -- Detecting CXX compiler ABI info - done 118 | -- Detecting CXX compile features 119 | -- Detecting CXX compile features - done 120 | -- Boost version: 1.58.0 121 | -- Found the following Boost libraries: 122 | -- filesystem 123 | -- system 124 | boost found 125 | -- Configuring done 126 | -- Generating done 127 | -- Build files have been written to: /data/code/01-basic/K-imported-targets/build 128 | 129 | $ make 130 | Scanning dependencies of target imported_targets 131 | [ 50%] Building CXX object CMakeFiles/imported_targets.dir/main.cpp.o 132 | [100%] Linking CXX executable imported_targets 133 | [100%] Built target imported_targets 134 | 135 | 136 | $ ./imported_targets 137 | Hello Third Party Include! 138 | Path is not relative 139 | ``` 140 | 141 | 142 | 143 | 原文作者:[橘崽崽啊](https://www.cnblogs.com/juzaizai/) 144 | 145 | 原文链接:https://www.cnblogs.com/juzaizai/p/15069682.html -------------------------------------------------------------------------------- /【NO.290】现在后端开发都在用什么数据库存储数据?.md: -------------------------------------------------------------------------------- 1 | # 【NO.290】现在后端开发都在用什么数据库存储数据? 2 | 3 | ## 1.**Oracle:** 4 | 5 | 传统行业,尤其是政府,医疗,学校和大企业,基本上还是Oracle应用最广,其次就是DB2。反而是WebLogic和WebSphere这些中间件基本上随着经典javaee的没落,已经逐步退出历史舞台,被富前端和微服务框架的轻量级组合所替代。 6 | 7 | ![img](https://pic2.zhimg.com/80/v2-c28ba0a898250f1d38646e6b112d9f5d_720w.webp) 8 | 9 | ## **2.MySQL:** 10 | 11 | 传统行业的很多新项目也大量开始应用MySQL,因为轻量级数据库的前期成本很低,可以保证项目预算够用,所以主要是新项目居多,面向互联网连接的项目也居多。这些系统一般不会像Oracle一样承担关键性业务的数据存储,所以选择什么样的数据库都是开发公司自己的选择决定。 12 | 13 | 目前有大量企业都开始上云,大家买云服务以阿里云ecs为主,总体上阿里云还是比较稳定,那么对于云上数据库的稳定有要求的企业一般都会选择阿里云主打的的rds系列,MySQL居多,PostgreSQL也开始逐渐被认可。 14 | 15 | ![img](https://pic4.zhimg.com/80/v2-25d949cfc42546361c4b898b3d611bff_720w.webp) 16 | 17 | ## **3.PostgreSQL:** 18 | 19 | 说到PostgreSQL,的确这两年PG风头正劲,以前我的文章也提到过我做过的互联网医疗产品,其架构设计就选择采用了PostgreSQL,主要就是看中PostgreSQL在生产上的稳定性极高,而且成本很低。尤其是精通Linux服务的架构师,对于PostgreSQL更容易掌握。 20 | 21 | 更具体地说就是使用PostgreSQL的关键因素主要还是业务数据很关键,因为我们当时承载的是互联网医疗数据,医疗数据自身属性就很关键!所以稳定和安全都是刚性要求,同时要平衡成本与互联网方式的灵活性,所以才否定了MySQL方案,坚决执行了PostgreSQL方案。 22 | 23 | ![img](https://pic3.zhimg.com/80/v2-caf72f6736537eeb5dbaeaec90df03fe_720w.webp) 24 | 25 | ## **4.Hadoop HDFS:** 26 | 27 | 大数据类项目的主数据集还是以Hadoop HDFS作为基础存储设施。尽管现在很热的讨论就是Hadoop已经是日落黄昏,可以选择其他更快的NoSQL存储方案。实际上,大数据工程师在最终落地的执行上,还是很诚实的选择了Hadoop,因为其成熟度,稳定性是最终考量的标准。 28 | 29 | ![img](https://pic1.zhimg.com/80/v2-59fec4be130d5d2b463b7371dbe6f10c_720w.webp) 30 | 31 | ## **5.Elasticsearch:** 32 | 33 | ELK家族的Elasticsearch目前被大量作为日志监测分析的主数据集去使用,甚至都忽视了它本身是搜索引擎的这个事实,在电子商务网站,内容发布网站以及社交媒体网站,Elasticsearch作为专业搜索引擎,还是稳坐第一把交椅。 34 | 35 | ![img](https://pic4.zhimg.com/80/v2-bddd1101bb7a2cd70c59e652486f452f_720w.webp) 36 | 37 | ## **6.实时/时序数据库:** 38 | 39 | 工业能源以及其他物联网行业,实时、时序数据库正在逐步采用开源的解决方案,例如[http://Druid.io](https://link.zhihu.com/?target=http%3A//Druid.io)、InfluxDB,OpenTSDB,还是目前存储物联网数据最好的开源选择方案。[http://Druid.io](https://link.zhihu.com/?target=http%3A//Druid.io)是实时与历史一整套实时库解决方案;InfluxDB目前热度非常高的时序数据库,自己独立实现了一套原生的集群存储结构;OpenTSDB主要依赖HBase分布式数据库与HDFS分布式文件系统。另外提一句,清华推出的开源时序数据库IOTDB,目前已经升级成[http://Apache.org](https://link.zhihu.com/?target=http%3A//Apache.org)的顶级项目。 40 | 41 | ![img](https://pic1.zhimg.com/80/v2-c591107988eaa72599469bed52ad0af8_720w.webp) 42 | 43 | ## **7. Hadoop HBase:** 44 | 45 | Hadoop hbase作为列簇存储,也是毫秒级的k-v存储,越来越适应通用场景下的实时数据分析了,可能哪个领域都有能用到它,支撑实时处理的联机分析以及小型批处理业务。它的分布式一致性,存储hdfs的稳定性,都是关键性业务数据进行实时分析的极佳方案。 46 | 47 | ![img](https://pic1.zhimg.com/80/v2-ce2ee6774d8eade80a54e04f52641190_720w.webp) 48 | 49 | ## **8.关系数据库并行能力:** 50 | 51 | 关系数据库也是在不断改进中前进,尤其是轻量级数据库的改进,MySQL8的cluster特性,PostgreSQL11的并行特性,都是不同手段想要达到同一个目的:那就是关系库都在想尽一切办法,不必让用户脱离关系型数据库,非得拥抱NoSQL才能追求到海量数据的并行处理能力,同时也能降低用户替换导致的巨大升级成本。 52 | 53 | ![img](https://pic4.zhimg.com/80/v2-86f5978dc8a84e46656b92f16d5f13cf_720w.webp) 54 | 55 | ## **9.MongoDB:** 56 | 57 | 另一种是关系数据库自身的改进或者引入MongoDB进行部分替代,例如电子商务的订单业务数据,互联网医疗的健康档案数据,内容发布的文章数据,都能实现MongoDB的文档化替代,这不仅更符合业务的文档化模型,而且能保证事务的前提下,实现海量数据的支撑。 58 | 59 | ![img](https://pic4.zhimg.com/80/v2-0c9d43c81aecd876d61a0f0b72ec17f3_720w.webp) 60 | 61 | ## **10.关系数据库并行能力:** 62 | 63 | 关系数据库也是在不断改进中前进,尤其是轻量级数据库的改进,MySQL8的cluster特性,PostgreSQL11的并行特性,都是不同手段想要达到同一个目的:那就是关系库都在想尽一切办法,不必让用户脱离关系型数据库,非得拥抱NoSQL才能追求到海量数据的并行处理能力,同时也能降低用户替换导致的巨大升级成本。 64 | 65 | ![img](https://pic4.zhimg.com/80/v2-41248d3f4ec4e12450acc5bf7c90ceef_720w.webp) 66 | 67 | 非得拥抱NoSQL才能追求到海量数据的并行处理能力,同时也能降低用户替换导致的巨大升级成本。 68 | 69 | ![img](https://pic4.zhimg.com/80/v2-41248d3f4ec4e12450acc5bf7c90ceef_720w.webp) 70 | 71 | 原文链接:https://zhuanlan.zhihu.com/p/382160856 72 | 73 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.292】工作年限、成长路线、进阶技术。怎样才能成为架构师?.md: -------------------------------------------------------------------------------- 1 | # 【NO.292】工作年限、成长路线、进阶技术。怎样才能成为架构师? 2 | 3 | ## 1.前言 4 | 5 | 你只面向工作学习吗? 6 | 7 | 如果说编程只是单纯的承接产品需求开发系统功能,那么基本可以把程序开发简单理解成按照需求PRD,定义属性、创建方法、调用展示,这三个步骤。 8 | 9 | 尤其是在一些大公司中,会有易用的、完善的、标准的架构体系和运维服务,例如:RPC、MQ、Redis集群、分布式任务、配置中心、分库分表组件、网关等搭配出来的系统架构。也因此让程序员做到只关心业务功能开发! 10 | 11 | 让程序员只关心业务开发,有成熟的系统架构、有标准的开发流程、有通用的功能设计,对于团队效能提升来说是非常好的事。但一部分程序员正因为有这样的好事,让日复一日的岁月做着同样的事,最后成为工具人。 12 | 13 | *如果是框架和中间件的存在,是了让程序员只关心业务开发。那为什么你面试的时候会被问到核心组件的设计和原理呢?* 在这个年代,别放弃学习是你几乎唯一的生存途径。 14 | 15 | ## 2.你的成长阶段目标? 16 | 17 | ![img](https://pic4.zhimg.com/80/v2-fe2d27b18865ec50f02fd950b361243b_720w.webp) 18 | 19 | 就编程开发这条道路而言,每一个成长阶段的目标都会有它随着带来的难以攻克的难。 20 | 21 | - 上学阶段,对突如其来的奇怪知识,想把它在自己电脑运行起来,就很难。 22 | - 工作1~3年,以前掌握的都是毛皮,接下来需要有深度的学习,而深入后都将与数学硬碰硬。 23 | - 工作3~5年,看以前理论性的知识也没那么难,但怎么实际要解决一些复杂项目,还是专心脑干。 24 | - 工作5~7年,薪资与职位都会成为这个阶段非常难以突破的瓶颈,积累不足、沉淀不够,现状不满! 25 | - 工作7~10年,以前觉得什么都难学,现在可能让你有空闲时间都难。并不一定年龄到了,本事就到了。 26 | 27 | 随着年龄的增长,每一阶段都有难以跨越的难。而那些看上去突破了瓶颈,达到了你想要的高度的人。其实每一个阶段,他们都跑在前面。 28 | 29 | 但就单纯的技术成长而言,其实理论知识并不难,只要你学就还能会,只是付出的时间成本不同罢了。但过了理论知识这一关后,接下来要面对的是创造能力,也就是为什么你感觉自己会了那么多技术内容,但是实际开发时却总感觉写不出好代码的阶段。 30 | 31 | 会了核心技术但又写不出好代码,就很像是:会汉字但写不出诗词歌赋、懂色彩但绘不出山河大川、能蹦跳但舞不出摇曳生姿。 32 | 33 | 所以,多实战一些项目代码,多看一些设计模式,会让你更好的理解代码该怎么用,也就能提升突破当前的阶段屏障。 34 | 35 | ## 3.怎么成长为架构师? 36 | 37 | ![img](https://pic4.zhimg.com/80/v2-85c6144f048101033160c79de8010fb3_720w.webp) 38 | 39 | 讲到架构师,架构师的成长更多的取决你们的研发组是否需要一个架构师,也同时需要你在这个岗位起到应有的作用。 40 | 41 | **那么上图对于架构师的能力概况,有哪些具体的事项呢?** 42 | 43 | 1. 定得了规范、设计了架构。 44 | 2. 有一定的技术深入和广度,改的了bug、处理得了事故。 45 | 3. 带了了小组推进项目落地,也能协同其他组配合。 46 | 4. 了解运营和业务规划,提前介入产品开发阶段。 47 | 5. 懂得了业务和运营,了解数据指标和各项ROI。 48 | 6. 架构更多的是经验和经历的结合,而不是一个单项内容的单一渠道。 49 | 7. 不是没有架构师就没有架构,有时候是一个公司或者小组承接的项目并没有那么大,使用成型架构模式即可。 50 | 8. 但如果有非常复杂的场景设计,都是十几个系统的分组安排开发,提供服务,支持几万秒杀,几十万日活,在扩展到上百万DAU,就需要有架构师来把控。 51 | 9. 再比如:从下单、到交易、到支付、到结算、到活动、到玩法、怎么支持。这个体量的复杂度才需要有架构权衡。 52 | 10. 没有绝对的对和绝对的错,只是什么时候更适合罢了。多学一些,别给自己设定边界,才更好突围! 53 | 54 | **做好架构,远看是部门效率,近看是解决烂代码!**很多时候的急,可能让整个工程烂掉。烂的越来越多,最终也会影响业务发展。那么这些烂代码都怎么来的呢? 55 | 56 | 1. bug很多时候是接手了的烂代码或者别人的思路没有继续继承。 57 | 2. 业务需求简单开始就写的没有扩展性,后面也不断的堆积。 58 | 3. 没有很好的结构和命名、也从不格式化。 59 | 4. 预期不到将来业务走向,设计不出合理的扩展性系统。 60 | 5. 炫技大于整体规划和设计,一个新技能的引入,但缺少相应的匹配。 61 | 6. 没有设计,功能都是流程式,需要啥就写ifelse。 62 | 7. 总想一把梭,没关系的,心里有抱怨,部门有急功近利,不给你长时间的铺垫,没有有人带,写不出好东西。 63 | 8. 组内缺少相应的流程规范和评审,设计评审、代码评审,也没与标杆项目可以参考。 64 | 9. 懂几个jdk源码从不是写好代码的根本只是基本功。就像老木匠用斧子,新木匠用电锯,但做出来的东西,有的就好,有的就不好。 65 | 10. 没有永远好的代码,如果像代码更好,就需要一直维护,一直改造。 66 | 11. 没有业务对应的体量,不谈QPS、TPS、TP99、TP999,服务健康度,很多空谈都是耍流氓。 67 | 68 | **烂**,来自于很多方面,业务、产品、研发,三方共同努力才能更好的减少烂的出现,而这些也是每一个研发都应该努力的方向,也几乎是你要成为架构师的必经之路。 69 | 70 | ## 4.总结 71 | 72 | 写了这么多主要是想帮助那些和我一样在这条路上持续拼搏的同好,可能大家都会在这些阶段迷茫过:上学时技术怎么学、求职时简历怎么写、工作时个人怎么成长等等。所以很多时候更多的仍然是自己的克制和自己的选择! 73 | 74 | 原文链接:https://zhuanlan.zhihu.com/p/382942954 75 | 76 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.293】malloc函数背后的实现原理——内存池.md: -------------------------------------------------------------------------------- 1 | # 【NO.293】malloc函数背后的实现原理——内存池 2 | 3 | ## 1.前言 4 | 5 | 相对于栈而言,堆这片内存面临着一个稍微复杂的行为模式:在任意时刻,程序可能发出请求,要么申请一段内存,要么释放一段已经申请过的内存,而且申请的大小从几个字节到几个GB都有可能,我们不能假设程序一次申请多少堆空间,因此,堆的管理显得较为复杂。 6 | 7 | 那么,使用 malloc() 在堆上分配内存到底是如何实现的呢? 8 | 9 | 一种做法是把 malloc() 的内存管理交给系统内核去做,既然内核管理着进程的地址空间,那么如果它提供一个系统调用,可以让 malloc() 使用这个系统调用去申请内存,不就可以了吗?当然这是一种理论上的做法,但实际上这样做的性能比较差,因为每次程序申请或者释放堆空间都要进行系统调用。我们知道系统调用的性能开销是比较大的,当程序对堆的操作比较频繁时,这样做的结果会严重影响程序的性能。 10 | 11 | 比较好的做法就是 malloc() 向操作系统申请一块适当大小的堆空间,然后由 malloc() 自己管理这块空间。 12 | 13 | malloc() 相当于向操作系统“批发”了一块较大的内存空间,然后“零售”给程序用。当全部“售完”或程序有大量的内存需求时,再根据实际需求向操作系统“进货”。当然 malloc() 在向程序零售堆空间时,必须管理它批发来的堆空间,不能把同一块地址出售两次,导致地址的冲突。于是 malloc() 需要一个算法来管理堆空间,这个算法就是堆的分配算法。 14 | 15 | ## 2.malloc()和free()的分配算法 16 | 17 | 在程序运行过程中,堆内存从低地址向高地址连续分配,随着内存的释放,会出现不连续的空闲区域,如下图所示: 18 | 19 | ![img](https://pic4.zhimg.com/80/v2-2995b85b5fc71f3479ef6bf611cb46fb_720w.webp) 20 | 21 | 图1:已分配内存和空闲内存相间出现 22 | 23 | 带阴影的方框是已被分配的内存,白色方框是空闲内存或已被释放的内存。程序需要内存时,malloc() 首先遍历空闲区域,看是否有大小合适的内存块,如果有,就分配,如果没有,就向操作系统申请(发生系统调用)。为了保证分配给程序的内存的连续性,malloc() 只会在一个空闲区域中分配,而不能将多个空闲区域联合起来。 24 | 25 | 内存块(包括已分配和空闲的)的结构类似于链表,它们之间通过指针连接在一起。在实际应用中,一个内存块的结构如下图所示: 26 | 27 | ![img](https://pic1.zhimg.com/80/v2-a653b6c31d5ab6f280c47a98bdbea970_720w.webp) 28 | 29 | 图2:内存块的结构 30 | 31 | next 是指针,指向下一个内存块,used 用来表示当前内存块是否已被使用。这样,整个堆区就会形成如下图所示的链表: 32 | 33 | ![img](https://pic2.zhimg.com/80/v2-5160545f625e7bee403ed19df8aff0fd_720w.webp) 34 | 35 | 图3:类似链表的内存管理方式 36 | 37 | 现在假设需要为程序分配100个字节的内存,当搜索到图中第一个空闲区域(大小为200个字节)时,发现满足条件,那么就在这里分配。这时候 malloc() 会把第一个空闲区域拆分成两部分,一部分交给程序使用,剩下的部分任然空闲,如下图所示: 38 | 39 | ![img](https://pic4.zhimg.com/80/v2-b036577488569901d6de9a7cf76162af_720w.webp) 40 | 41 | 图4:为程序分配100个字节的内存 42 | 43 | 仍然以图3为例,当程序释放掉第三个内存块时,就会形成新的空闲区域,free() 会将第二、三、四个连续的空闲区域合并为一个,如下图所示: 44 | 45 | ![img](https://pic4.zhimg.com/80/v2-9f949b9b98f7cd468290c09f7f5097e7_720w.webp) 46 | 47 | 图5:释放第三个内存块 48 | 49 | 可以看到,malloc() 和 free() 所做的工作主要是对已有内存块的分拆和合并,并没有频繁地向操作系统申请内存,这大大提高了内存分配的效率。 50 | 51 | 另外,由于单向链表只能向一个方向搜索,在合并或拆分内存块时不方便,所以大部分 malloc() 实现都会在内存块中增加一个 pre 指针指向上一个内存块,构成双向链表,如下图所示: 52 | 53 | ![img](https://pic1.zhimg.com/80/v2-4ea1602990993e3974b3849cf2b7ec24_720w.webp) 54 | 55 | 链表是一种经典的堆内存管理方式,经常被用在教学中,很多C语言教程都会提到“栈内存的分配类似于数据结构中的栈,而堆内存的分配却类似于数据结构中的链表”就是源于此。 56 | 57 | 链表式内存管理虽然思路简单,容易理解,但存在很多问题,例如: 58 | 一旦链表中的 pre 或 next 指针被破坏,整个堆就无法工作,而这些数据恰恰很容易被越界读写所接触到。 59 | 小的空闲区域往往不容易再次分配,形成很多内存碎片。 60 | 经常分配和释放内存会造成链表过长,增加遍历的时间。 61 | 62 | 针对链表的缺点,后来人们提出了位图和对象池的管理方式,而现在的 malloc() 往往采用多种方式复合而成,不同大小的内存块往往采用不同的措施,以保证内存分配的安全和效率。 63 | 64 | ## 3.内存池 65 | 66 | 不管具体的分配算法是怎样的,为了减少系统调用,减少物理内存碎片,malloc() 的整体思想是先向操作系统申请一块大小适当的内存,然后自己管理,这就是内存池(Memory Pool)。 67 | 68 | 内存池的研究重点不是向操作系统申请内存,而是对已申请到的内存的管理,这涉及到非常复杂的算法,是一个永远也研究不完的课题,除了C标准库自带的 malloc(),还有一些第三方的实现,比如 Goolge 的 tcmalloc 和 jemalloc。 69 | 70 | 我们知道,C/C++是编译型语言,没有内存回收机制,程序员需要自己释放不需要的内存,这在给程序带来了很大灵活性的同时,也带来了不少风险,例如C/C++程序经常会发生内存泄露,程序刚开始运行时占用内存很少,随着时间的推移,内存使用不断增加,导致整个计算机运行缓慢。 71 | 72 | 内存泄露的问题往往难于调试和发现,或者只有在特定条件下才会复现,这给代码修改带来了不少障碍。为了提高程序的稳定性和健壮性,后来的 Java、Python、C#、JavaScript、PHP 等使用了虚拟机机制的非编译型语言都加入了垃圾内存自动回收机制,这样程序员就不需要管理内存了,系统会自动识别不再使用的内存并把它们释放掉,避免内存泄露。可以说,这些高级语言在底层都实现了自己的内存池,也即有自己的内存管理机制。 73 | 74 | ## 4.池化技术 75 | 76 | 在计算机中,有很多使用“池”这种技术的地方,除了内存池,还有连接池、线程池、对象池等。以服务器上的线程池为例,它的主要思想是:先启动若干数量的线程,让它们处于睡眠状态,当接收到客户端的请求时,唤醒池中某个睡眠的线程,让它来处理客户端的请求,当处理完这个请求,线程又进入睡眠状态。 77 | 78 | 所谓“池化技术”,就是程序先向系统申请过量的资源,然后自己管理,以备不时之需。之所以要申请过量的资源,是因为每次申请该资源都有较大的开销,不如提前申请好了,这样使用时就会变得非常快捷,大大提高程序运行效率。 79 | 80 | 原文链接:https://linuxcpp.0voice.com/?id=475 81 | 82 | 作者:[HG](https://linuxcpp.0voice.com/?auth=10) -------------------------------------------------------------------------------- /【NO.297】什么是DPDK?DPDK的原理及学习路线总结.md: -------------------------------------------------------------------------------- 1 | # 【NO.297】什么是DPDK?DPDK的原理及学习路线总结 2 | 3 | ## 1.什么是DPDK 4 | 5 |   对于用户来说,它可能是一个性能出色的包数据处 理加速软件库;对于开发者来说,它可能是一个实践包处理新想法的创 新工场;对于性能调优者来说,它可能又是一个绝佳的成果分享平台。  6 | 7 |   DPDK用软件的方式在通用多核处理器上演绎着数据包处理的新篇 章,而对于数据包处理,多核处理器显然不是唯一的平台。支撑包处理 的主流硬件平台大致可分为三个方向。 8 |   ·硬件加速器 9 |   ·网络处理器 10 |   ·多核处理器 11 | 12 |   在类似 IA(Intel Architecture)多核处理器为目标的平台上,网络数据包处理远早于DPDK而存在。从商业版的 Windows到开源的Linux操作系统,所有跨主机通信几乎都会涉及网络 协议栈以及底层网卡驱动对于数据包的处理。然而,低速网络与高速网 络处理对系统的要求完全不一样。 13 | 14 | ## 2.DPDK原理 15 | 16 | 网络设备(路由器、交换机、媒体网关、SBC、PS网关等)需要在瞬间进行大量的报文收发,因此在传统的网络设备上,往往能够看到专门的NP(Network Process)处理器,有的用FPGA,有的用ASIC。这些专用器件通过内置的硬件电路(或通过编程形成的硬件电路)高效转发报文,只有需要对报文进行深度处理的时候才需要CPU干涉。 17 | 18 | 但在公有云、NFV等应用场景下,基础设施以CPU为运算核心,往往不具备专用的NP处理器,操作系统也以通用Linux为主,网络数据包的收发处理路径如下图所示: 19 | 20 | ![img](https://pic4.zhimg.com/80/v2-39d9c5742815718ffc3e616342c75c9b_720w.webp) 21 | 22 | 在虚拟化环境中,路径则会更长: 23 | 24 | ![img](https://pic3.zhimg.com/80/v2-ee0175b746bbf5eed76de9f29bb4bbda_720w.webp) 25 | 26 | 由于包处理任务存在内核态与用户态的切换,以及多次的内存拷贝,系统消耗变大,以CPU为核心的系统存在很大的处理瓶颈。为了提升在通用服务器(COTS)的数据包处理效能,Intel推出了服务于IA(Intel Architecture)系统的DPDK技术。 27 | 28 | DPDK是Data Plane Development Kit的缩写。简单说,DPDK应用程序运行在操作系统的User Space,利用自身提供的数据面库进行收发包处理,绕过了Linux内核态协议栈,以提升报文处理效率。 29 | 30 | ## 3.DPDK源码目录结构  31 | 32 |   lib/ : DPDK的库源代码 33 |   drivers/ : DPDK轮询模式驱动程序源代码 34 |   app/ : DPDK应用程序源代码 35 |   examples/ : DPDK的一些应用程序例子源代码 36 |   config/ : DPDK关于arm和x86平台的一些编译配置 37 |   buildtools/ : DPDK一些编译配置的脚本 38 |   mk/ : DPDK的Makefile 39 |   usertools/ : DPDK提供给用户的一些实用工具 40 | 41 | ## 4.常用术语及缩写 42 | 43 |   ACL:Access Control List,访问控制列表,是路由器和交换机接口的指令列表,用来控制端口进出的数据包;简而言之就是用来控制数据流。 44 |   SSL:Secure Sockets Layer,安全套接层,是为网络通信提供安全及数据完整性的一种安全协议,在传输层对网络连接进行加密。 45 |   RSS:Receive Side Scaling,是一种能够在多处理器系统下使接收报文在多个CPU之间高效分发的网卡驱动技术。 46 |   NUMA:Non Uniform Memory Access Architecture,非统一内存访问架构; 47 |   QOS:Quality of Service,服务质量,指一个网络能够利用各种基础技术,为指定的网络通信提供更好的服务能力, 是网络的一种安全机制, 是用来解决网络延迟和阻塞等问题的一种技术。 48 |   NIC:Network Interface Card,网卡,网卡是局域网中最基本的部件之一,它是连接计算机与网络的硬件设备。 49 |   PCI:Peripheral Component Interconnect,计算机一种标准总线,NIC就是使用的这种总线方式。 50 |   PMD:Poll Mode Drive,轮询模式驱动,DPDK就是采用的这种模式。 51 |   RTE:Run Time Environment,通过PMD实现快速分组处理数据的一个框架。 52 |   MPLS:Multi-Protocol Label Switching,多协议标签交换,是一种用于快速数据包交换和路由的体系,它为网络数据流量提供了目标、路由地址、转发和交换等能力。更特殊的是,它具有管理各种不同形式通信流的机制。 53 | 54 | ## 5.DPDK框架简介 55 | 56 |   DPDK为IA上的高速包处理而设计。 57 | 58 |   图1-6所示的DPDK主要模块分 解展示了以基础软件库的形式,为上层应用的开发提供一个高性能的基 础I/O开发包。它大量利用了有助于包处理的软硬件特性,如大页、缓 存行对齐、线程绑定、预取、NUMA、IA最新指令的利用、Intel DDIO、内存交叉访问等。 59 |   核心库Core Libs,提供系统抽象、大页内存、缓存池、定时器及无 锁环等基础组件。 60 |   PMD库,提供全用户态的驱动,以便通过轮询和线程绑定得到极高 的网络吞吐,支持各种本地和虚拟的网卡。 61 |   Classify库,支持精确匹配(Exact Match)、最长匹配(LPM)和 通配符匹配(ACL),提供常用包处理的查表操作。 62 |   QoS库,提供网络服务质量相关组件,如限速(Meter)和调度 (Sched)。 63 | 64 | ![img](https://pic1.zhimg.com/80/v2-617431c4136778d6ccc9462016a76f10_720w.webp) 65 | 66 | ## 6.DPDK的轮询模式 67 | 68 |   DPDK采用了轮询或者轮询混杂中断的模式来进行收包和发包,此 前主流运行在操作系统内核态的网卡驱动程序基本都是基于异步中断处 理模式。 69 | 70 | ##   1、异步中断模式 71 | 72 |   当有包进入网卡收包队列后,网卡会产生硬件 (MSIX/MSI/INTX)中断,进而触发CPU中断,进入中断服务程序,在 中断服务程序(包含下半部)来完成收包的处理。当然为了改善包处理 性能,也可以在中断处理过程中加入轮询,来避免过多的中断响应次 数。总体而言,基于异步中断信号模式的收包,是不断地在做中断处 理,上下文切换,每次处理这种开销是固定的,累加带来的负荷显而易 见。在CPU比I/O速率高很多时,这个负荷可以被相对忽略,问题不 大,但如果连接的是高速网卡且I/O频繁,大量数据进出系统,开销累 加就被充分放大。中断是异步方式,因此CPU无需阻塞等待,有效利用 率较高,特别是在收包吞吐率比较低或者没有包进入收包队列的时候, CPU可以用于其他任务处理。 73 | 当有包需要发送出去的时候,基于异步中断信号的驱动程序会准备 好要发送的包,配置好发送队列的各个描述符。在包被真正发送完成 时,网卡同样会产生硬件中断信号,进而触发CPU中断,进入中断服务 程序,来完成发包后的处理,例如释放缓存等。与收包一样,发送过程 也会包含不断地做中断处理,上下文切换,每次中断都带来CPU开销; 同上,CPU有效利用率高,特别是在发包吞吐率比较低或者完全没有发 包的情况。 74 | 75 | ##   2、轮询模式 76 | 77 |   DPDK起初的纯轮询模式是指收发包完全不使用任何中断,集中所 有运算资源用于报文处理。但这不是意味着DPDK不可以支持任何中 断。根据应用场景需要,中断可以被支持,最典型的就是链路层状态发 生变化的中断触发与处理。 78 | 79 | 原文链接:https://zhuanlan.zhihu.com/p/397919872 80 | 81 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.299】从四线城市程序员,到深圳大厂架构师,我只用了半年.md: -------------------------------------------------------------------------------- 1 | # 【NO.299】从四线城市程序员,到深圳大厂架构师,我只用了半年 2 | 3 | 从苦逼的程序员,到现在的**Linux高级互联网架构师,**要问身份的转变给我带来了什么实质上的利益,那肯定是**薪水**了。除此之外就是**面子**,毕竟在大厂比在不知名小公司要长脸的多。 4 | 5 | 主要还是去年在家上班那段时间,感觉到了小公司的种种不便,最让人难以忍受的就是在家996随时待命,还只发底薪,真是令人窒息的操作,让我只想赶紧逃离这个公司。 6 | 7 | ![img](https://pic1.zhimg.com/80/v2-510e73918b0cd1069f60d0b2f1f7639c_720w.webp) 8 | 9 | 但成年人的世界从来就不是可以任性的,我也自知我的水平没办法胜任更好的岗位,于是我决定**工作之余提升自己**。 10 | 11 | 边上班边学习其实挺苦的,幸好时间不长,我也熬了过来。现在每个月看到工资卡上比之前涨了几倍的数字,觉得**当时的努力是很值**的。 12 | 13 | 咳咳,扯远了啊,言归正传,就跟大家分享一下,我是**怎样进阶**的。 14 | 15 | ## **1.** **学好C语言** 16 | 17 | 作为一名程序员,C++的难度在我看来是top级的,多少次被这玩意折腾的怀疑人生。但是!不入虎穴焉得虎子,它的确很难,但是它的**含金量和竞争力**同样也是没话说的。 18 | 19 | 会与不会,很多时候就是薪资高低的决定因素。 20 | 21 | ![img](https://pic2.zhimg.com/80/v2-a6a9b09b56496a588782fb33806eb029_720w.webp) 22 | 23 | 要学习C++,那就一定要先打牢C语言的基础,这是至关重要的前提条件。 24 | 25 | ## **2.** **看书** 26 | 27 | 有了扎实的C语言基础之后,可以开始学习C++了。 28 | 29 | 给大伙推荐一些我觉得不错的书。 30 | 31 | ▪ **《C++ Primer》 及习题册** 32 | 33 | 如果只想看一本教材,那我强烈推荐这本。《C++ Primer》 非常全面,方方面面都考虑到了,可读性也很强,很适合初学者。它的习题册也一定要买,搭配使用事半功倍。 34 | 35 | ![img](https://pic1.zhimg.com/80/v2-4f9573d37434595aef464cd0a4b5627c_720w.webp) 36 | 37 | **▪ 《21天学通C++》** 38 | 39 | 听这个名字就知道,这也是一本适合初学者的书,难度没有上一本那样大,但基本知识都交代了,适合作为学习C++的第一本书。 40 | 41 | ![img](https://pic3.zhimg.com/80/v2-580d5740d933ea60dda9f01e9320ea4e_720w.webp) 42 | 43 | ▪ **《Effective C++》、《More Effective C++》** 44 | 45 | 这两本是初学者看完、练完《C++ Primer》后,用来提升的教材。告诉程序员在使用C++时应该怎么解决问题、要注意什么、避免什么,进阶之路必备好书。 46 | 47 | ![img](https://pic4.zhimg.com/80/v2-b1faf8d048e7b5c9662049e917f69fb3_720w.webp) 48 | 49 | ![img](https://pic1.zhimg.com/80/v2-35ccf2c850b39c5d2370a378021783c8_720w.webp) 50 | 51 | ## **3.** **看教程** 52 | 53 | 根据我自己的学习经验,其实单纯看书挺枯燥的,很多时候就是看不下去,所以我会**结合一些教程来**看。 54 | 55 | ![img](https://pic3.zhimg.com/80/v2-c71979939e6b6a7f6d608098bf103062_720w.webp) 56 | 57 | 也有很多一样在学习C++的同仁,可以**相互交流每天打卡**,有队友学起来才更有激情嘛。而且会在群里聊聊**行情**什么的,也有**项目实操**,是锻炼的好机会。 58 | 59 | ## **4.** **学习资料** 60 | 61 | 来点干货,这是群里大牛整理的**腾讯核心技术学习路线(T1-T9)** 62 | 63 | ![img](https://pic1.zhimg.com/80/v2-806d7ae9d425ebf381e284802f7f1118_720w.webp) 64 | 65 | **腾讯职级技术学习提升路线详情版**: 66 | 67 | ![img](https://pic1.zhimg.com/80/v2-806d7ae9d425ebf381e284802f7f1118_720w.webp) 68 | 69 | 相比很多人学C++学的怀疑人生,我学习的过程其实**没有走多少弯路**,毕竟一开始就找到了优质的教程和学习资料,而且大牛带飞嘛,结果自然不同凡响。 70 | 71 | 我是感觉学习任何一门技术都不能闭门造车,因为学习过程中很多问题不是你一个人遇到过,**多和同仁交流**,钻牛角尖的概率低很多。 72 | 73 | 另外,作为一个过来人,也想提醒大家:想要学习C++,一定要努力且有耐心,不可能一天就能走到罗马,唯一可以做的,就是**立刻出发**。 74 | 75 | 原文链接:https://zhuanlan.zhihu.com/p/356387701 76 | 77 | 作者:Linux服务器研究 -------------------------------------------------------------------------------- /【NO.302】Linux 网络性能优化-C10K、C1000K、C10M 问题总结.md: -------------------------------------------------------------------------------- 1 | # 【NO.302】Linux 网络性能优化-C10K、C1000K、C10M 问题总结 2 | 3 | ## 1.C10K 问题以及优化方法简介 4 | 5 | C10K 表示单机同时处理 1 万个请求 (并发连接 1 万) 的问题; 6 | 7 | **【1.1】主要待解决的问题** 8 | 9 | 问题一,怎样在一个线程内处理多个请求,即要在一个线程内响应多个网络 I/O; 10 | 11 | 问题二,怎么更节省资源地处理客户请求,即要用更少的线程来服务这些请求; 12 | 13 | **【1.2】事件 IO 通知的方式** 14 | 15 | 两种 I/O 事件通知的方式,水平触发和边缘触发; 16 | 17 | - 水平触发,只要文件描述符可以非阻塞地执行 I/O,就会触发通知,即应用程序可以随时检查文件描述符的状态,然后再根据状态,进行 I/O 操作; 18 | - 边缘触发,只有在文件描述符的状态发生改变时,才发送一次通知,此时,应用程序需要尽可能多地执行 I/O,直到无法继续读写,才可以停止;若 I/O 没执行完,或者因为某种原因没来得及处理,那么这次通知也就丢失了; 19 | 20 | **【1.3】优化方案** 21 | 22 | 【1.3.1】I/O 模型优化 23 | 24 | 使用非阻塞 I/O 和水平触发通知 (比如使用 select 或者 poll) 25 | 26 | 根据水平触发的特性,select 和 poll 可以从文件描述符列表中,找出可以执行 I/O 操作的文件描述符,然后进行真正的网络 I/O 操作,由于 I/O 是非阻塞的,一个线程中就可以同时监控一批套接字的文件描述符,从而实现单线程处理多请求的目的; 27 | 28 | **缺陷** 29 | 30 | - 应用程序使用 select 和 poll 时,需要对这些文件描述符列表进行轮询,在请求数多的时候就会比较耗时; 31 | - select 使用固定长度的位向量,表示文件描述符的集合,存在最大描述符数量的限制;并且,在 select 内部,检查套接字状态使用轮询的方法,处理耗时跟描述符数量是 O(N) 的关系; 32 | - 应用程序每次调用 select 和 poll 时,需要把文件描述符的集合,从用户空间传入内核空间,由内核修改后,再传出到用户空间中,内核空间与用户空间切换增加了处理成本; 33 | 34 | 使用非阻塞 I/O 和边缘触发通知 (比如 epoll) 35 | 36 | epoll 使用红黑树,在内核中管理文件描述符的集合,从而就不需要应用程序在每次操作时都传入、传出该集合; 37 | 38 | epoll 使用事件驱动的机制,只关注有 I/O 事件发生的文件描述符,不需要轮询扫描整个集合; 39 | 40 | **缺陷** 41 | 42 | - 边缘触发只在文件描述符可读或可写事件发生时才通知,从而应用程序就需要尽可能多地执行 I/O 并要处理更多的异常事件; 43 | 44 | 使用异步 I/O (Asynchronous I/O,简称为 AIO) 45 | 46 | 异步 I/O 允许应用程序同时发起很多 I/O 操作,而不用等待这些操作完成,而在 I/O 完成后,系统会用事件通知 (比如信号或者回调函数)的方式,通知应用程序;应用程序接到通知才会去查询 I/O 操作的结果; 47 | 48 | **缺陷** 49 | 50 | - 目前该方式不太完善,使用比较困难 51 | 52 | **【1.3.2】工作模型优化** 53 | 54 | \1. 主进程 + 多个 worker 子进程 55 | 56 | - 主进程执行 bind() + listen() 后,创建多个子进程; 57 | - 在每个子进程中,通过 accept() 或 epoll_wait(),来处理相同的套接字; 58 | 59 | ![img](https://pic2.zhimg.com/80/v2-52c9d25906664f38ef89d67a17847149_720w.webp) 60 | 61 | **惊群问题** 62 | 63 | accept() 和 epoll_wait() 调用的惊群问题,即当网络 I/O 事件发生时,多个进程被同时唤醒,但实际上只有一个进程来响应这个事件,其他被唤醒的进程都会重新休眠; 64 | 65 | 解决 66 | 67 | - accept() 的惊群问题,已经在 Linux 2.6 中解决; 68 | - epoll 的问题,在 Linux 4.5 通过 EPOLLEXCLUSIVE 解决; 69 | - Nginx 在每个 worker 进程中,都增加一个了全局锁(accept_mutex),这些 worker 进程需要首先竞争到锁,只有竞争到锁的进程,才会加入到 epoll 中,这样就确保只有一个 worker 子进程被唤醒; 70 | 71 | **2. 监听到相同端口的多进程模型** 72 | 73 | 该方式下,所有的进程都监听相同的端口并且开启 SO_REUSEPORT 选项,由内核负责将请求负载均衡到这些监听进程中 74 | 75 | ![img](https://pic1.zhimg.com/80/v2-cf57e4f185fe6f464a79e0eb9af04380_720w.webp) 76 | 77 | 78 | 79 | ## 2.C1000K 问题与优化 80 | 81 | **物理资源** 82 | 83 | 100 万个请求需要大量的系统资源; 84 | 85 | - 内存,假设每个请求需要 16KB 内存,则总共就需要大约 15 GB 内存; 86 | - 带宽,假设只有 20% 活跃连接,即使每个连接只需要 1KB/s 的吞吐量,总共需要 1.6 Gb/s 的吞吐量;需要配置万兆网卡,或者基于多网卡 Bonding 承载更大的吞吐量; 87 | 88 | **软件资源** 89 | 90 | 大量的连接会占用大量的软件资源; 91 | 92 | - 比如文件描述符的数量、连接状态的跟踪(CONNTRACK)、网络协议栈的缓存大小(比如套接字读写缓存、TCP 读写缓存)等等; 93 | 94 | **大量请求带来的中断处理** 95 | 96 | - 需要多队列网卡、中断负载均衡、CPU 绑定、RPS/RFS (软中断负载均衡到多个 CPU 核上),以及将网络包的处理卸载 (Offload) 到网络设备(如 TSO/GSO、LRO/GRO、VXLAN OFFLOAD) 等各种硬件和软件的优化; 97 | 98 | C1000K 的解决方法,本质上是构建在 epoll 的非阻塞 I/O 模型上,除了 I/O 模型之外,还需要从应用程序到 Linux 内核、再到 CPU、内存和网络等各个层次的深度优化,特别是需要借助硬件,来卸载那些原来通过软件处理的大量功能; 99 | 100 | ## **3.C10M 问题与优化** 101 | 102 | 核心思想是跳过内核协议栈的冗长路径,把网络包直接送到要处理的应用程序中处理,这里有两种常见的机制,DPDK 和 XDP; 103 | 104 | **DPDK** 105 | 106 | - DPDK 是用户态网络的标准,跳过内核协议栈,直接由用户态进程通过轮询的方式处理网络接收; 107 | 108 | ![img](https://pic2.zhimg.com/80/v2-67721fb75c9e824a20f9d50ce1c3a551_720w.webp) 109 | 110 | 在 PPS 非常高的场景中,查询时间比实际工作时间少了很多,绝大部分时间都在处理网络包;而跳过内核协议栈后,就省去了繁杂的硬中断、软中断再到 Linux 网络协议栈逐层处理的过程,应用程序可以针对应用的实际场景,有针对性地优化网络包的处理逻辑,而不需要关注所有的细节;此外,DPDK 通过大页、CPU 绑定、内存对齐、流水线并发等多种机制,优化网络包的处理效率; 111 | 112 | 原文地址:https://zhuanlan.zhihu.com/p/537843366 113 | 114 | 作者:Linux -------------------------------------------------------------------------------- /【NO.307】数据从应用层的应用进程到最后的网络包是怎么一步步封装的?TCP怎么拆分?IP怎么分片?.md: -------------------------------------------------------------------------------- 1 | # 【NO.307】数据从应用层的应用进程到最后的网络包是怎么一步步封装的?TCP怎么拆分?IP怎么分片? 2 | 3 | ![img](https://pic1.zhimg.com/80/v2-82994ab8b40a148b258251469813c26c_720w.webp) 4 | 5 | 上图是一个整体的网络包的结构,可以看到网络包层层封装的结构 6 | 7 | ### 1.因此如果问一句,那数据从应用层的应用进程到最后的网络包是怎么一步步封装的呢? 8 | 9 | 答:比较概括性的回答是:(为便于讨论,假设是一个web服务) 10 | 11 | 首先应用层的应用进程将http请求报文下发到运输层 12 | 13 | 运输层的TCP协议栈给http请求报文添加TCP头部封装成TCP报文段,然后下发到网络层 14 | 15 | 网络层的IP协议栈给TCP报文段添加上IP头部封装成IP包,然后下发到数据链路层 16 | 17 | 数据链路层也有各种协议,假设是以太网协议,那么会给IP包加上帧头和帧尾,形成帧,然后下发到物理层 18 | 19 | 物理层则将帧视为字节流,完全转化为相应的物理信号(电信号或光信号等),在线缆、光纤等媒介中传播 20 | 21 | ### 2.进一步提问:那运输层收到应用层下发的数据后,TCP协议是怎么看待以及怎么打包封装的?什么时候会拆分? 22 | 23 | 1、TCP视应用层下发的http请求报文为字节流,TCP协议会根据规定的MSS(最大报文长度:规定了TCP报文所能携带的数据载荷的大小)判断是否进行拆分 24 | 25 | 2、如果字节流长度大于MSS,则需要进行拆分,拆分成合理的几块,然后为每一块添加上合适的TCP头部 26 | 27 | 3、TCP头部中的序号字段就是为此服务的,最终形成一个个TCP报文段,接收方对应的运输层收到这些TCP报文段后可以根据TCP头部信息进行组装还原原来的http请求报文 28 | 29 | ![img](https://pic2.zhimg.com/80/v2-d032d1c079979626fdc6d5f6c67f29f5_720w.webp) 30 | 31 | ### 3.再进一步提问:那网络层收到运输层下发的TCP报文段后,IP协议是怎么看待以及怎么打包封装的?什么时候会拆分? 32 | 33 | 1、IP协议会根据输出端口(其实也就是被链路层的各种协议类型所规定)的MTU(最大传输单元:它规定了IP网络包的最大长度包括首部和数据载荷)进行判断是否进行分片;例如以太网的MTU=1500字节 34 | 35 | 2、若TCP报文段小于MTU 减 IP头部长度,则IP不需要对TCP报文段分片 36 | 37 | 3、若TCP报文段大于MTU 减 IP头部长度,则IP需要对TCP报文段分片 38 | 39 | 分片是IP对整个TCP报文段一视同仁,也就是不会区分是TCP头部还是数据载荷,如下图所示 40 | 41 | IP头部字段中的标识、标志、片偏移字段就是为此服务的 42 | 43 | 接收端的网络层收到这些分片过的IP数据报,会根据IP头部中的字段信息,对分片进行组装,还原TCP报文段后交给上层也即运输层 44 | 45 | ![img](https://pic3.zhimg.com/80/v2-ff5c9d06d1e634fa0b530375422dce22_720w.webp) 46 | 47 | 注意: 48 | 49 | - IP分片是在TCP拆分后又进行了一次拆分;在接收端刚好相反,先IP组装分片,然后交给运输层TCP,之后再组装TCP数据报文段,最后交给应用层的应用进程 50 | - TCP拆分:依据是MSS(最大报文段长度) 51 | - IP分片:依据是各种转发端口的MTU(最大传输单元) 52 | 53 | ### 4.**补充** 54 | 55 | ![img](https://pic2.zhimg.com/80/v2-ae01c72d5a3acaba7ffb1cbfd4a5e419_720w.webp) 56 | 57 | 以太网帧的最大数据帧长度为1518字节 58 | 59 | 帧头包含目标MAC地址(6个字节)、源MAC地址(6个字节)、上层协议类型(2个字节)共14字节 60 | 61 | 帧的尾部是FCS校验位(4个字节) 62 | 63 | 故以太网帧的MTU = 1518 - 14 - 4 = 1500 字节 64 | 65 | 又因为IP头部最小为20字节,TCP头部最小为20字节,以及在实际的应用中,通常TCP会加一个12字节的时间戳,这些共计52字节 66 | 67 | 所以单个TCP每次能打包的最大数据量为MTU - 52 = 1448字节 68 | 69 | 原文地址:https://zhuanlan.zhihu.com/p/532302700 70 | 71 | 作者:linux -------------------------------------------------------------------------------- /【NO.318】腾讯面试官用「B+树」虐哭我了.md: -------------------------------------------------------------------------------- 1 | # 【NO.318】腾讯面试官用「B+树」虐哭我了 2 | 3 | 我们知道当系统要处理的数据量非常庞大的时候,数据不可能全部存放于内存,需要借助磁盘来完成存储和检索。在数据库中支持很多种索引方式,常见有哈希索引、全文索引和B+树索引。今天将和大家分享使用B+树作为索引的优缺点。 4 | 5 | 面试很多互联网公司,都会问这个问题,也许我们看过太多面经内容,但是基本上答案千篇一律,对于面试官而言也是基本上听腻了,是多么希望能听到不一样的解答,那么今天希望这篇文章可以给你不一样的答案。 6 | 7 | 今天分享的几点如下: 8 | 9 | ![img](https://pic4.zhimg.com/80/v2-104c46a12f7b68d440530dc72551e65f_720w.webp) 10 | 11 | ## 1.**数据从磁盘读写与内存读写有哪些不同** 12 | 13 | > 我们平时接触的有机械硬盘和固态硬盘。内存属于半导体器件,对于内存,我们知道内存地址就可以通过地址拿到数据,也就是内存的随机访问特性。访问速度快但是贵,所以内存空间一般比较小。 14 | 15 | 对于磁盘,属于机械器件。每当磁盘访问数据的时候,都需要等磁盘盘片旋转到磁头,才能读取相应的数据,即使磁盘的转速很快,但是和内存的随机访问相比还是渣渣。 16 | 17 | 所见,如果是随机读写,其性能差距是非常大的。那如果是顺序访问大量数据的时候,磁盘的性能和内存其实差距就不大了,这是为啥? 18 | 19 | 磁盘的最小读写单位是扇区,现在磁盘扇区一般是4k个字节,对于操作系统,一次性会读取多个扇区,至此操作系统的最小读取单位就是块。 20 | 21 | 每当我们从磁盘读取一个数据,操作系统就会一次性读取整个块,那么对于大量的顺序读写来说,磁盘效率会比随机读写高很多。 22 | 23 | 假设现在你有个有序数组,全部以块的方式存放在磁盘中,现在我们通过二分查找的方式查找元素A。首先我们找到中间元素,并从块中取出,将其从磁盘放入内存中,然后再内存中进行二分查找。 24 | 25 | 在进行下一步的时候,如果查找的元素在其他块中,我们需要继续从磁盘读出到内存中。这样反反复复的从磁盘到内存,其效率将非常的低。所以我们需要想办法让访问磁盘的次数尽可能的低。 26 | 27 | ## 2.**数据和索引分离** 28 | 29 | > 我们以公安系统为例。系统中的用户非常多,每个用户除了姓名,年龄等基本信息外,当然还有一个唯一标识的ID,我们拿到这个ID,就可以知道对应的基本信息。但是每个用户的基本信息太多不可能全部存放在内存中,因此考虑存储于磁盘中。 30 | 31 | ![img](https://pic2.zhimg.com/80/v2-5ef83022526f59489d63ee51c0e1b9e9_720w.webp) 32 | 33 | 用户数据 34 | 35 | - 采用有序数组的方式,其中分别存储用户ID和用户信息所在磁盘的位置,这样我只需要存放两个元素,直接存放于内存。如下图所示 36 | 37 | ![img](https://pic4.zhimg.com/80/v2-9347a157b74f20ec993e347482ddade3_720w.webp) 38 | 39 | 有序数组 40 | 41 | 但是在数据频繁变化的场景中,有序数组的弊端就出现了。大部分情况还是考虑使用二叉检索树或者哈希表的方式。但是哈希表又不支持区间查询,因此更多的使用二叉检索树的方式。如下图所示: 42 | 43 | ![img](https://pic3.zhimg.com/80/v2-6c050de8353d625b249050f1348077a6_720w.webp) 44 | 45 | 如果索引太多,依然不能完全存放于内存中,那我们是不是可以考虑将索引也存放于磁盘中?如何高效的在磁盘中组织索引的结构?这就引入了B+树。 46 | 47 | ## **3.B+树** 48 | 49 | - 让节点大小等于块大小 50 | 51 | > 我们知道操作系统在对磁盘进行访问的时候,通常是按照块的方式读取。如果当前你需要读取的数据只有几个字节,但是磁盘依然会将整个块读出来,这样子是不是读写效率就很低呢。在B+树中,大佬采用让一个节点大小等于一个块的大小,节点中存放的不是一个元素,而是一个有序的数组,这样充分利用操作系统的套路,使得读取效率的最大化 52 | 53 | - 内部节点与叶子节点 54 | 55 | > 内部节点和叶子节点虽然是一样的结构,但是其存储的内容有所区别。内部节点存放key以及维持树形结构的指针,它并不存放key对应的数据。而叶子节点存放key和对应的数据,不存放维持树形结构的指针,这样使得节点空间的利用最大化。 56 | 57 | ![img](https://pic2.zhimg.com/80/v2-d43f8d4acc9e1b450f2e5c8301fc9715_720w.webp) 58 | 59 | 内部节点与叶子节点 60 | 61 | - B+树使用双向链表的方式,具有良好的范围查询能力和灵活的调整能力 62 | 63 | 综上三点,B+树是一颗完全平衡的m阶多叉树。 64 | 65 | ![img](https://pic2.zhimg.com/80/v2-e36a724c8884348d30cca34f25932129_720w.webp) 66 | 67 | m阶多叉树 68 | 69 | ## 4.**B+树的检索方案** 70 | 71 | > 刚才吹了一波B+树多么的牛逼,到底是怎么检索的?具体的查找过程是这样的:我们先确认要寻找的查询值,位于数组中哪两个相邻元素中间,然后我们将第一个元素对应的指针读出,获得下一个 block 的位置。读出下一个 block 的节点数据后,我们再对它进行同样处理。这样,B+ 树会逐层访问内部节点,直到读出叶子节点。对于叶子节点中的数组,直接使用二分查找算法,我们就可以判断查找的元素是否存在。如果存在,我们就可以得到该查询值对应的存储数据。如果这个数据是详细信息的位置指针,那我们还需要再访问磁盘一次,将详细信息读出。 72 | 73 | B+树是一个m阶的多叉树,所以B+树中的一个节点可以存放m个元素的数组,ok,这样的话,只需要几层的b+树就可以索引数据量很大的数了。比如1个2k的节点可以存放200个元素,那么一个4层的B+树就能存放200^4,即16亿个元素。 74 | 75 | 如果只有四层,意味着我们最多访问磁盘4次,假设目前每个节点为2k,那么第一层就一个节点也就2k,第二层节点最多200个元素,一共就是0.8M。第三层200^2,也就是40000个节点,一共80M。对于当前的计算机而言,我们完全可以将前面三层存放于内存中,只需要将第四层存放于磁盘中,这样我们只需要和磁盘打一次交道就分手,也就是面试想知道的为什么要分为内部节点与叶子节点。 76 | 77 | ## 5.**B+树如何进行动态的调整** 78 | 79 | > 上面介绍了B+树的结构和查询原理,现在我们看看B+树增加和删除是怎么个情况 80 | 81 | 现在我们以三个元素的B+树 为例,假设目前我们要插入ID为6=5的元素,第一步先查找对应的叶子节点,如果叶子节点没有满,直接插入即可 82 | 83 | ![img](https://pic1.zhimg.com/80/v2-47604db1871b26e7d2e78389652cc1e8_720w.webp) 84 | 85 | 插入元素6 86 | 87 | 如果我们插入的元素是10?按道理我们应该插入到9后面,但是节点已经满了,所以我们需要采取其他的方式。方法是将此叶子节点进行分裂,即生成一个新的节点,然后将数据在两个节点中平分。 88 | 89 | ![img](https://pic3.zhimg.com/80/v2-c076b04a6965196222b27532bf3aa23a_720w.webp) 90 | 91 | 节点分裂 92 | 93 | 很明显,叶子节点的分裂影响到了父节点,如果父节点也是满的,也要进行分裂 94 | 95 | ![img](https://pic4.zhimg.com/80/v2-6572b96960b4c930826f894f0a96153b_720w.webp) 96 | 97 | 节点分裂 98 | 99 | ## 6.**总结** 100 | 101 | > 从大问题拆分为小问题并逐个解决是我们在生活学习重要的本领,比较复杂的B+树其实也就是基本的数据结构(数组,链表,树)组成,其检索过程实际上就是二分查找,所以如果B+树完全载入内存,它的检索效率和有序数组/二叉检索树差不多,但是却更加复杂。B+树最大的优点在于它将索引存放在磁盘,让检索技术摆脱了内存限制,另外通过将索引和数据分离的方式,将索引的数组大小保持在较小范围,这样精简索引。 102 | 103 | 原文地址:https://zhuanlan.zhihu.com/p/522591122 104 | 105 | 作者:Linux -------------------------------------------------------------------------------- /【NO.343】原来 mmap 这么简单.md: -------------------------------------------------------------------------------- 1 | # 【NO.343】原来 mmap 这么简单 2 | 3 | 在本文中,我们主要介绍 `mmap` 的原理。 4 | 5 | ## 1.传统的读写文件 6 | 7 | 一般来说,修改一个文件的内容需要如下3个步骤: 8 | 9 | - 把文件内容读入到内存中。 10 | - 修改内存中的内容。 11 | - 把内存的数据写入到文件中。 12 | 13 | 过程如图 1 所示: 14 | 15 | ![img](https://pic4.zhimg.com/80/v2-eb49f938d177e2c1eff8fd7cb46c10ff_720w.webp) 16 | 17 | 如果使用代码来实现上面的过程,代码如下: 18 | 19 | ```text 20 | read(fd, buf, 1024); // 读取文件的内容到buf 21 | ... // 修改buf的内容 22 | write(fd, buf, 1024); // 把buf的内容写入到文件 23 | ``` 24 | 25 | 从图 1 中可以看出,`页缓存(page cache)` 是读写文件时的中间层,内核使用 `页缓存` 与文件的数据块关联起来。所以应用程序读写文件时,实际操作的是 `页缓存`。 26 | 27 | ## 2.使用 mmap 读写文件 28 | 29 | 从传统读写文件的过程中,我们可以发现有个地方可以优化:如果可以直接在用户空间读写 `页缓存`,那么就可以免去将 `页缓存` 的数据复制到用户空间缓冲区的过程。 30 | 31 | 那么,有没有这样的技术能实现上面所说的方式呢?答案是肯定的,就是 `mmap`。 32 | 33 | 使用 `mmap` 系统调用可以将用户空间的虚拟内存地址与文件进行映射(绑定),对映射后的虚拟内存地址进行读写操作就如同对文件进行读写操作一样。原理如图 2 所示: 34 | 35 | ![img](https://pic1.zhimg.com/80/v2-af305d81ca549ec80da028064d853380_720w.webp) 36 | 37 | 前面我们介绍过,读写文件都需要经过 `页缓存`,所以 `mmap` 映射的正是文件的 `页缓存`,而非磁盘中的文件本身。由于 `mmap` 映射的是文件的 `页缓存`,所以就涉及到同步的问题,即 `页缓存` 会在什么时候把数据同步到磁盘。 38 | 39 | Linux 内核并不会主动把 `mmap` 映射的 `页缓存` 同步到磁盘,而是需要用户主动触发。同步 `mmap` 映射的内存到磁盘有 4 个时机: 40 | 41 | - 调用 `msync` 函数主动进行数据同步(主动)。 42 | - 调用 `munmap` 函数对文件进行解除映射关系时(主动)。 43 | - 进程退出时(被动)。 44 | - 系统关机时(被动)。 45 | 46 | ## 3.mmap的使用方式 47 | 48 | 下面我们介绍一下怎么使用 `mmap`,`mmap` 函数的原型如下: 49 | 50 | ```text 51 | void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 52 | ``` 53 | 54 | 下面介绍一下 `mmap` 函数的各个参数作用: 55 | 56 | - `addr`:指定映射的虚拟内存地址,可以设置为 NULL,让 Linux 内核自动选择合适的虚拟内存地址。 57 | 58 | - `length`:映射的长度。 59 | 60 | - `prot`:映射内存的保护模式,可选值如下: 61 | 62 | - - `PROT_EXEC`:可以被执行。 63 | - `PROT_READ`:可以被读取。 64 | - `PROT_WRITE`:可以被写入。 65 | - `PROT_NONE`:不可访问。 66 | 67 | - `flags`:指定映射的类型,常用的可选值如下: 68 | 69 | - - `MAP_FIXED`:使用指定的起始虚拟内存地址进行映射。 70 | - `MAP_SHARED`:与其它所有映射到这个文件的进程共享映射空间(可实现共享内存)。 71 | - `MAP_PRIVATE`:建立一个写时复制(Copy on Write)的私有映射空间。 72 | - `MAP_LOCKED`:锁定映射区的页面,从而防止页面被交换出内存。 73 | - ... 74 | 75 | - `fd`:进行映射的文件句柄。 76 | 77 | - `offset`:文件偏移量(从文件的何处开始映射)。 78 | 79 | 介绍完 `mmap` 函数的原型后,我们现在通过一个简单的例子介绍怎么使用 `mmap`: 80 | 81 | ```text 82 | int fd = open(filepath, O_RDWR, 0644); // 打开文件 83 | void *addr = mmap(NULL, 8192, PROT_WRITE, MAP_SHARED, fd, 4096); // 对文件进行映射 84 | ``` 85 | 86 | 在上面例子中,我们先通过 `open` 函数以可读写的方式打开文件,然后通过 `mmap` 函数对文件进行映射,映射的方式如下: 87 | 88 | - `addr` 参数设置为 NULL,表示让操作系统自动选择合适的虚拟内存地址进行映射。 89 | - `length` 参数设置为 8192 表示映射的区域为 2 个内存页的大小(一个内存页的大小为 4 KB)。 90 | - `prot` 参数设置为 `PROT_WRITE` 表示映射的内存区为可读写。 91 | - `flags` 参数设置为 `MAP_SHARED` 表示共享映射区。 92 | - `fd` 参数设置打开的文件句柄。 93 | - `offset` 参数设置为 4096 表示从文件的 4096 处开始映射。 94 | 95 | `mmap` 函数会返回映射后的内存地址,我们可以通过此内存地址对文件进行读写操作。我们通过图 3 展示上面例子在内核中的结构: 96 | 97 | ![img](https://pic2.zhimg.com/80/v2-7234375f0ef628e6a38c9c64fe26d991_720w.webp) 98 | 99 | ## 4.总结 100 | 101 | 本文主要介绍了 `mmap` 的原理和使用方式,通过本文我们可以知道,使用 `mmap` 对文件进行读写操作时可以减少内存拷贝的次数,并且可以减少系统调用的次数,从而提高对读写文件操作的效率。 102 | 103 | 由于内核不会主动同步 `mmap` 所映射的内存区中的数据,所以在某些特殊的场景下可能会出现数据丢失的情况(如断电)。为了避免数据丢失,在使用 `mmap` 的时候可以在适当时主动调用 `msync` 函数来同步映射内存区的数据。 104 | 105 | 原文地址:https://zhuanlan.zhihu.com/p/429455424 106 | 107 | 作者:linux -------------------------------------------------------------------------------- /【NO.370】SRS配置升级,云原生友好的配置能力.md: -------------------------------------------------------------------------------- 1 | # 【NO.370】SRS配置升级,云原生友好的配置能力 2 | 3 | 什么才是更好的配置方法?NGINX的conf,还是MySQL的ini,还是新潮的yaml,或者JS友好的json?它们都有各自的问题,最好的方式是conf+环境变量,也就是Grafana的配置方法。 4 | 5 | ## 1.Why Important? 6 | 7 | 为何配置这么重要?因为它是最基本的API,也就是程序和人的接口,也决定了使用体验是否良好。想象下xml的配置文件,想起来都觉得烦躁,这是因为xml并不是对人友好的接口。 8 | 9 | SRS的配置一直都对人挺友好的,因为是使用的NGINX的配置方法,熟悉NGINX的同学可能会觉得很熟悉,比如: 10 | 11 | ``` 12 | listen 1935; 13 | max_connections 1000; 14 | vhost __defaultVhost__ { 15 | } 16 | ``` 17 | 18 | 相当直观和好理解,这些年也就这么过来了,但它并非没有问题,比如: 19 | 20 | - • 程序读写不友好,要用代码修改配置,就需要自己实现这个解析。NGINX是有生态支撑,有工具支持读写,但它只支持NGINX的配置,并不是所有conf格式都能支持。 21 | - • 在文档或Wiki中,或者在给出例子时,总是要给出一个配置文件,而一般还需要修改现有的配置文件,很不方便,也有可能会出错。 22 | - • 在K8s部署时,或者Docker启动时,需要创建文件,并映射到Docker中,哪怕只需要修改某个配置项,也需要这么做,这套机制很麻烦。 23 | 24 | 也正是因为有这些问题,陆陆续续的有同学提出来支持其他的配置方式,详细可以参考#2277[1]中的详细讨论,大概有几种解决方案: 25 | 26 | - • 支持JSON或YAML,这是为了解决程序读写的问题,JSON对JS程序员友好,YAML对DevOps友好。无论哪种方式,都不能对所有人友好,而且还解决不了后面两个问题,还是依赖文件。 27 | - • 支持Redis那种命令行参数的方式,这解决了配置文件的问题,对人也相对比较友好,但若有参数变更,则还是需要依赖文件。 28 | - • 环境变量,方便设置和命令行启动,是基本的传递配置的办法,但多了后不太友好,另外和目前的文件配置方法有差异,导致Reload等机制需要修改。 29 | 30 | > Note: 如果直接换成新的配置方式,都会对目前支持的NGINX的conf文件的方式造成不兼容,影响使用习惯。因此最好的办法不是替代,而是结合现有配置方法,实现配置能力的增强。 31 | 32 | 直到看到了Grafana的配置方式,发现了在目前基础上,可以更好的配置方法。 33 | 34 | ## 2.Solution 35 | 36 | 目前NGINX的conf的配置方法,是对人比较友好的,需要支持的是对程序接口友好的方式,而且是和这种方式是可以配合而不是替代的方案。这就是环境变量的方式,先看Grafana的启动方式: 37 | 38 | ``` 39 | docker run --rm -it --name grafana \ 40 | --env GF_SECURITY_ADMIN_USER=admin \ 41 | --env GF_SECURITY_ADMIN_PASSWORD=admin \ 42 | --env GF_DATABASE_TYPE=mysql \ 43 | --env GF_DATABASE_HOST=host.docker.internal:3306 \ 44 | --env GF_DATABASE_NAME=grafana \ 45 | --env GF_DATABASE_USER=root \ 46 | --env GF_DATABASE_PASSWORD=****** \ 47 | --env GF_INSTALL_PLUGINS=tencentcloud-monitor-app \ 48 | -p 3000:3000 \ 49 | grafana/grafana 50 | ``` 51 | 52 | 这种方式是最方便用Docker启动的方式,在K8s中也是一样的。Grafana所有的配置既可以通过配置文件配置,也可以通过环境变量配置。 53 | 54 | 借鉴这种办法,SRS的配置也可以支持环境变量,比如WebRTC over TCP[2]: 55 | 56 | ``` 57 | docker run --rm -it -p 8080:8080 -p 1985:1985 -p 8000:8000 \ 58 | -e CANDIDATE="192.168.3.82" \ 59 | -e SRS_RTC_SERVER_TCP_ENABLED=on \ 60 | -e SRS_RTC_SERVER_PROTOCOL=tcp \ 61 | registry.cn-hangzhou.aliyuncs.com/ossrs/srs:v5.0.60 62 | ``` 63 | 64 | 无论是命令行启动,还是Docker启动,还是分享命令给其他人,还是K8s启动,这都是最简单直接的办法。 65 | 66 | > Note: 一般我们并不会需要修改特别多的配置,往往只需要修改几个配置,因此这种方式是最便捷的。 67 | 68 | 当然也不是所有SRS的配置都支持环境变量,因为有些配置比如Transcode是数组,就很难支持,具体以full.conf[3]中标记为`Overwrite by env SRS_XXX`为准,比如: 69 | 70 | ``` 71 | # Overwrite by env SRS_LISTEN 72 | listen 1935; 73 | # Overwrite by env SRS_MAX_CONNECTIONS 74 | max_connections 1000; 75 | 76 | rtc_server { 77 | # Overwrite by env SRS_RTC_SERVER_ENABLED 78 | enabled on; 79 | } 80 | 81 | vhost __defaultVhost__ { 82 | rtc { 83 | # Overwrite by env SRS_VHOST_RTC_ENABLED for all vhosts. 84 | enabled on; 85 | } 86 | } 87 | ``` 88 | 89 | 基本上SRS的配置都支持了环境变量的覆盖,非常感谢mapengfei53[4]的贡献。 90 | 91 | ## 3.Next 92 | 93 | 目前SRS支持了环境变量的配置,还需要改进支持Reload。 94 | 95 | 由于Reload依赖配置文件,在收到Reload信号后,重新加载配置文件,对比发现变更后,实现定向的快速Reload。而环境变量的配置,则需要实现对应的变更检测机制,我们会在后续改进和完善。 96 | 97 | 此外,之前Reload的机制过度设计,有些其实没有必要支持Reload,比如侦听的端口,是不会在运行中变化,而且变化会导致很多异常问题。因此,会在未来把很多不常用的Reload功能去掉,只支持必要的Reload。 98 | 99 | 还有,K8s中的配置是通过ConfigMap加载到容器,而通过inotify机制,可以在文件内容修改后,主动加载配置Reload,而不需要发送Reload信号,这样在K8s集群中只需要修改ConfigMap就会自动生效到容器。这个机制同样也需要支持环境变量,如何在环境变量变更后,在K8s集群中生效。 100 | 101 | 最后补充一句:SRS会永远支持配置文件,以及对应的配置变更Reload机制,环境变量的配置方法,是对目前配置能力的完善,并不是替代。 102 | 103 | **引用链接** 104 | 105 | `[1]` #2277: *https://github.com/ossrs/srs/issues/2277* 106 | `[2]` WebRTC over TCP: *https://ossrs.net/lts/zh-cn/docs/v5/doc/webrtc#webrtc-over-tcp* 107 | `[3]` full.conf: *https://github.com/ossrs/srs/blob/develop/trunk/conf/full.conf* 108 | `[4]` mapengfei53: *https://github.com/mapengfei53* 109 | 110 | 原文作者:Written by 马鹏飞, Winlin 111 | 112 | 原文链接:https://mp.weixin.qq.com/s/ABMrvGEer1FYrdR9PrearA -------------------------------------------------------------------------------- /【NO.372】腾讯跟阿里两位王者之间的对比.md: -------------------------------------------------------------------------------- 1 | # 【NO.372】腾讯跟阿里两位王者之间的对比 2 | 3 | ## 1.前言 4 | 5 | 程序员朋友圈有一篇比较火的文章《当下(2018 年)腾讯的技术建设是否处于落后同体量公司的状态?》,虽然网上不乏介绍腾讯与阿里不同之处(包括文化、薪资待遇、公司氛围和技术建设等方面)的文章,今天就来跟大家分析一下,不免具有一定的片面性,不喜勿喷。 6 | 7 | ## 2.关于腾讯 8 | 9 | 腾讯整体上是一家公司,但很多时候各个部门之间各自为政。部门之间的合作更像一个公司与另一个公司之间的合作,在沟通的时候如果不是负责人不能向对方透露任何跟工作有关的细节、进度等。在腾讯某部门的时候由于此还吃过几次亏,以至于来到阿里之后跨部门间合作总是战战兢兢,生怕说错、做错了什么。后来才发现,在阿里大部分情况下都不需要考虑这些。部门之间的合作就是公司内部的合作,就这样我才慢慢晃过神来。 10 | 11 | 微信想必大家都知道,其实当初腾讯做微信这个产品的时候并不是只有广州微信团队在做。成都也有团队在做,当时是谁先做好、谁先推广就能活下去。最后当然是广州的微信做成了,另一个类似“微信”的产品就只能死掉了。 12 | 13 | 还有之前吃鸡游戏比较火的时候。腾讯一下推出了两款吃鸡手游:光子工作室的刺激战场和天美工作室的全军出击来进行瓜分市场。这样的例子还有很多,就我所在的腾讯某部门的一个核心安全产品在公司内都存在竞争对手。可能这就是腾讯的文化,一个个好的产品就这样被竞争、打磨出来了。都说百度的技术、阿里的运营和腾讯的产品,也不是没有道理的。 14 | 15 | ## 3.关于阿里 16 | 17 | 而阿里就完全不一样了,来阿里某 BU 的第一个月。师兄带着我做一款新的产品,产品的功能需要自己去摸索。主管只指定了大的方向,对我们的唯一要求就是不要做别人/别组/别BU已经做过的东西、一定要善于使用公司已有的平台给自己的产品赋能。可见阿里内部更注重合作和创新,同样的事绝不做两遍。 18 | 19 | 在腾讯,正是因为隔离和竞争,所以各个部门之间的交流、合作和共享就很少,特别是在技术方面,每个部门闭门造车,没有或很少有积累,这也是导致腾讯在技术建设方面落后的主因。就我这几个月的亲身体会而言,阿里的技术建设要比腾讯好很多。所以来到阿里后,我常常对那些喊苦的同事说,在阿里上班够幸福了,只需要专心的写业务代码,不用关心一些杂七杂八的破事。 20 | 21 | ## 4.两者对比 22 | 23 | 腾讯更像是在一家创业公司,一个应用从开始到结束所涉及到的所有流程都需要开发参与,包括申请服务器、搭建环境、安装 DB、部署和运维等。也就是说开发要做的事往往包括开发、测试和运维。常常让人叫苦不迭,给我最大的感受就是很忙,但是不知道在忙什么。 当然在阿里也是有这些流程的,但很多时候只需要在页面上点一点就了。这极大的节省了开发的时间、运维的成本和出错的概率。这主要得益于阿里的技术建设的完善,我举两个简单的例子来说明下腾讯和阿里在技术建设方面的差异就更能直观感受两者之间的差异了: 24 | 25 | ### 4.1.统一 DB 管理平台 26 | 27 | 在腾讯某部门的时候,应用要使用 DB 大部分情况下只能在 linux 服务器上自己搭建 mysql 环境,使用本地 DB。然后很多操作都是在 linux shell中进行 CRUD 操作,非常的原始。最痛苦的是对 DB 的运维,出了问题还得自己背锅。在阿里某 BU 的时候,新建应用直接上公司内部平台申请下就好了,而且会自动分配给你两套(测试和正式)。所有的操作都是在页面上,DB 也不需要自己去运维了。读写都做了很好的权限控制,根本不怕误操作。 28 | 29 | ### 4.2.统一配置中心 30 | 31 | 很多应用中的变量都需要在配置文件中进行配置,在腾讯某部门的时候都是以文件的形式和应用一起部署。配置文件的读写都是应用自己实现的,当然这个已经有封装好的库了。但是如果要修改配置文件呢?得手动修改后重启应用,其运维成本可想而知。而在阿里有整个公司公用的统一配置中心中间件:Diamond,其简单易用。使用的时候在页面上修改下,点发布就会自动热更新到应用下,不用重启、不容易出错。 32 | 33 | 可以说阿里在开发、测试、运维和部署等流程都已经有着很成熟的方案和平台了,而腾讯还在使用比较原始的方式。以上两例只是腾讯与阿里在技术建设差距上的一瞥,其他很多方面不想赘述。至于具体原因,知乎上的回答已经有了很好的阐述。所以说平台化、流程化和标准化是多么重要。腾讯每个部门都有自己的一套,各个部门之间很少共享,所以导致很多东西仅限于有而不精。阿里整个公司共用一套,所以慢慢的沉淀出公司级的产品。腾讯可能也已经认识到自己在这方面的不足,不久前刚做了一次组织架构的调整。 34 | 35 | ## 5.总结 36 | 37 | 技术建设完善最大的收益者当然是程序员了,在腾讯和阿里每天上班工作时间差不多。但是在阿里明显轻松多了,只需要一心专注业务代码就好;在腾讯很多时候都在要为环境、运维和部署发愁。写代码的时间很少,还经常被压榨。 38 | 39 | 不过凡事有利有弊,在腾讯由于什么都需要自己亲力亲为,所以员工很多东西都懂一些。特别是底层的一些基础知识、算法和优化方面,但精不精就不知道了。由于历史原因腾讯的主要编程语言是 C++,而阿里则是 Java。所以注定了腾讯偏底层应用开发,而阿里偏上层业务开发。个人觉得腾讯员工在底层技术方面可能要强于阿里的,而阿里员工在面向对象、设计模式和业务沟通方面是要强于腾讯员工的。 40 | 41 | **今天我给大家整理了一下腾讯T1~T9的后端工程师分别需要具备哪些能力以及对应的腾讯核心技术点学习路线总结图。希望可以给各位广大学后端的朋友一面镜子映照自身所学,早日拿到心仪的offer,进入大厂。** 42 | 43 | ![img](https://pic4.zhimg.com/80/v2-3cf60d6bc744aa90b5c61671595ba173_720w.webp) 44 | 45 | 原文链接:[https://zhuanlan.zhihu.com/p/399751135](https://www.zhihu.com/people/huhu520-10) 46 | 47 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.386】WEBRTC 笔记.md: -------------------------------------------------------------------------------- 1 | # 【NO.386】WEBRTC 笔记 2 | 3 | ## 1.WEBRTC 是什么 4 | 5 | 它的全称是WEB Real-time communication。一开始我还觉得是一种通讯技术。这里的communication主要是人与人之间的,因此它解决了在网页视频、音频的播放和获取的问题。它的目标是但愿用户之间直接通讯,而不是经过服务器来进行交互。简单地说就是在浏览器上实现视频通话,并且最好不须要中央服务器。html 6 | 7 | 你们应该仔细看看这个[教程](http://www.noobyard.com/link?url=https://www.html5rocks.com/en/tutorials/webrtc/basics/) ,我但愿这篇笔记能够更快地帮助你们理解,说明一下比较容易困惑的点,少走一些弯路,而不是取代这篇教程。html5 8 | 9 | ## 2.核心技术 10 | 11 | 1. [getUserMedia()](http://www.noobyard.com/link?url=https://webrtc.github.io/samples/src/content/getusermedia/gum/) : 获取视频和音频。git 12 | 2. [MediaRecorder](http://www.noobyard.com/link?url=https://webrtc.github.io/samples/src/content/getusermedia/record/) : 记录视频和音频。github 13 | 3. [RTCPeerConnection](http://www.noobyard.com/link?url=https://webrtc.github.io/samples/src/content/peerconnection/pc1/) : 创建视频流。web 14 | 4. [RTCDataChannel](http://www.noobyard.com/link?url=https://webrtc.github.io/samples/src/content/datachannel/basic/) : 创建数据流。浏览器 15 | 16 | ## 3.实际问题 17 | 18 | 然而在现实中网络是不通畅的,2个浏览器之间没法直接创建链接,甚至都没法发现对方。为此须要额外的技术来完成链接。服务器 19 | 20 | 1. [ICE](http://www.noobyard.com/link?url=https://www.html5rocks.com/en/tutorials/webrtc/basics/#ice) 这个框架应该是嵌入浏览器内部的,咱们并不须要了解太多的细节。网络 21 | 2. [signaling](http://www.noobyard.com/link?url=https://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-signaling) 就个人理解,这个至关于媒人,来帮助2个浏览器来创建链接。框架 22 | 23 | ## 4.创建链接 24 | 25 | [JSEP](http://www.noobyard.com/link?url=http://tools.ietf.org/html/draft-ietf-rtcweb-jsep-00): 26 | ![JavaScript Session Establishment Protocol](https://ewr1.vultrobjects.com/imgur2/000/004/556/742_592_7b5.jpg)socket 27 | 28 | 1. 首先建立 RTCPeerConnection 对象,仅仅是初始化。 29 | 2. 使用 createOffer/createAnswer 来交换[SDP](http://www.noobyard.com/link?url=http://en.wikipedia.org/wiki/Session_Description_Protocol),sdp中包含网络信息,RTCPeerConnection 对象得以创建链接。 30 | 3. 激活onicecandidate完成链接。 31 | 32 | WEBRTC没有规定createOffer/createAnswer时使用的协议,所以signaling server 只要能够与浏览器交换SDP便可。能够用socket.io/wensocket等通讯技术把createOffer/createAnswer中的SDP给送到对方手里就行了。 33 | 34 | ------ 35 | 36 | 下面我将用一个简单的例子来讲明链接是如何创建的。 37 | 为了更好地说明信号服务器的做用,我把它直接给拿掉了。取而代之的是一块公告牌。 38 | 在`sendMessage`和`receiveMsg`中,将要发送的信息写在页面的msg下方。没错,人工复制便可。 39 | 40 | 1. 首先打开2个页面,一个主动方点击call,另外一个被动方点击recv 41 | 2. 将caller的消息复制到receiver的answer按钮边上的文本框内,再点击answer。 42 | 3. 将receiver的消息复制到caller的answer按钮边上的文本框内,再点击answer。 43 | 4. 点击send将send左边的文本发送到对方send右侧的文本框内。 44 | 45 | [demo code,人工信号服务器](http://www.noobyard.com/link?url=https://github.com/erow/webrtc_demo.git) 46 | 47 | ## 5.概述 48 | 49 | 1. 建立对象 。 50 | 51 | 2. 绑定回调函数。 52 | 53 | ``` 54 | peerConn = new RTCPeerConnection(pcConfig); 55 | peerConn.onicecandidate = handleIceCandidate; 56 | 57 | dataChannel = peerConn.createDataChannel('1'); 58 | channel.onopen = function() { 59 | console.log('CHANNEL opened!!!'); 60 | }; 61 | 62 | channel.onmessage = function(event){ 63 | remoteText.innerText = event.data; 64 | }; 65 | ``` 66 | 67 | 3. 提供服务:createOffer。 68 | 69 | ``` 70 | 这期间要发送offer,candidate消息。 71 | `peerConn.createOffer(onLocalSessionCreated, logError);` 72 | 在`onLocalSessionCreated`中调用`sendMessage`。 73 | 随后会触发`handleIceCandidate`调用`sendMessage`。 74 | ``` 75 | 76 | 4. 建立应答: createAnswer。 77 | 78 | ``` 79 | peerConn.setRemoteDescription(new RTCSessionDescription(message), function() {}, 80 | logError); 81 | peerConn.createAnswer(onLocalSessionCreated, logError); 82 | ``` 83 | 84 | ``` 85 | 注意,这一步是在receiver端进行的。 86 | 跟createOffer相似,createAnswer会发送一个answer消息,随后发送candidate消息。 87 | ``` 88 | 89 | 5. 添加candidate 90 | 91 | ``` 92 | peerConn.addIceCandidate(new RTCIceCandidate({ 93 | candidate: message.candidate 94 | })); 95 | ``` 96 | 97 | 6. 链接创建 98 | 99 | 原文作者:[菜鸟学院](http://www.noobyard.com/) 100 | 101 | 原文链接:http://www.noobyard.com/article/p-bvixwrsr-mn.html -------------------------------------------------------------------------------- /【NO.387】想学习音视频开发,感觉网上的资料很少?.md: -------------------------------------------------------------------------------- 1 | # 【NO.387】想学习音视频开发,感觉网上的资料很少? 2 | 3 | 最近有读者留言,说“想转行音视频开发,怎么做”,正巧,前几天我还在本乎上,看到有人在问音视频的学习资料,还是个大一的学生。 4 | 5 | 想说一句:真有眼光。 6 | 7 | ![img](https://pic2.zhimg.com/80/v2-9827214bec98cc58532de80f564ee385_720w.webp) 8 | 9 | 如今这个时代,想赚钱,一个共识是,**得先选对赛道**。有些行业和领域,终其一生的天花板也不过如此。但有的朝阳行业,你一进去就可以获得大量的机会,就是“ROI(投入产出比)”很高。那就聊聊为啥从事音视频技术,未来会很赚钱。要说音视频技术,在这两年迎来爆发期。首先 5G 的发展提供了硬件条件,又受疫情的影响,**生活场景线上化**,大量的线上办公、线上教育、线上娱乐等需求,让几亿人涌入各类线上互动平台。比如: 10 | 11 | - 抖音和快手的**短视频**,需要应用图像处理和视频编码技术,如何在保持高画质的情况下,尽量减少视频文件的大小; 12 | - **连麦直播**需要 RTC 和直播技术,如何能够保证在各种网络状况下实现超低延时、降低卡顿率; 13 | - **视频会议**需要 RTC 和转码合流服务等技术,几十上百人的大型视频会议,如何保证流畅度、卡顿率、画质等指标等; 14 | - 随着线上体验的增加,人们对互动中的**音频体验**要求也在提升。除了听得到、听得清,还得好听、音质还原度高等。例如,Facebook 改名 Meta 进军元宇宙,TWS 耳机支持了空间音频渲染和主动降噪等等。 15 | 16 | 可以说,**音视频技术就如同空气和水**,无处不在,未来充满无限可能。而且这些真实场景都强调**实时互动**,延迟必须控制在毫秒级别内,如果在这个过程中,出现延时高、卡顿、画面模糊、杂音大等情况,你可以想象会出现什么样的体验吗?别说李佳琪双 11 直播带货 100 亿了,正常打一把沟通流畅的王者荣耀都不一定。其实,早在疫情初期,很多 SaaS 平台甚至大厂都出现过卡顿问题,主要对突如其来的流量没有做好充分准备,而如今大家都看到了线上的市场,自然对音视频技术人才的需求就多了。所以不夸张地说,音视频开发是一片**蓝海,人少,钱多**。 17 | 18 | 而且未来,会更加炙手可热。 19 | 20 | ![img](https://pic4.zhimg.com/80/v2-9a4ad28ad35e62dd411fbef69e5bcb37_720w.webp) 21 | 22 | 这张图供参考。但也能看出来,整体薪资待遇比普通开发者要优厚很多。不仅如此,从网上随手一搜,就能看到某某安卓转音视频,真香了、突破就业危机等等。 23 | 24 | ![img](https://pic2.zhimg.com/80/v2-26e1119763afd22a9229e72fc65e7295_720w.webp) 25 | 26 | 如今除了大厂,很多小公司也在寻找音视频的人才,稍微好点的音视频人才可能同时 3~4 家公司抢着要。就是因为从业人才基数低,高端人才缺乏,最重要的是,音视频技术有开发门槛,不好培养,也很难自学。**但这也意味着,你跟别人相比有技术上的核心竞争优势,有分水岭。** 27 | 28 | 快速入门音视频技术的方法,有吗? 29 | 30 | 音视频技术学起来并不容易,要懂的东西太多:音视频的采集、编码、传输、解码、渲染...等等,网上也少见体系化的资料。但就像左耳朵耗子所说,**“要去知识的源头学习”**。对学习者来说,找到优质的**信息源**可以让你事半功倍,不是二手加工的,也不会有信息损失或有误。 31 | 32 | 想学好并从事音视频开发大致需要6个阶段,分别是 33 | 34 | 1.音视频基础→2.ffmpeg实战→3.流媒体客户端→4.SRS流媒体服务器→5.webRTC实战→6.AndroidNDK 35 | 36 | 37 | 38 | ![img](https://pic1.zhimg.com/80/v2-3b5f3ce305df88ff629bd1251b8cb984_720w.webp) 39 | 40 | 41 | 42 | ![img](https://pic4.zhimg.com/80/v2-557eb3e37a4d46b82ce74930c8c2051b_720w.webp) 43 | 44 | ![img](https://pic3.zhimg.com/80/v2-968d41bf822cbf85bfaedd1bef665442_720w.webp) 45 | 46 | ![img](https://pic1.zhimg.com/80/v2-7070fef089d53ad822a9006db2ca9b54_720w.webp) 47 | 48 | ![img](https://pic3.zhimg.com/80/v2-860c658e52a28f8141c6d00926fd4522_720w.webp) 49 | 50 | ![img](https://pic4.zhimg.com/80/v2-3b3be57c41ca17643050d1106dd7f92f_720w.webp) 51 | 52 | ![img](https://pic2.zhimg.com/80/v2-57d781872161a36f4debc88c6c0c7075_720w.webp) 53 | 54 | ![img](https://pic3.zhimg.com/80/v2-5711cc848f8322bf53cc5a05612fa43e_720w.webp) 55 | 56 | ![img](https://pic4.zhimg.com/80/v2-3a7f80e1f93c100b3754a61dc9343a8f_720w.webp) 57 | 58 | ![img](https://pic2.zhimg.com/80/v2-8503b9be650ade46b7432e280e8b845d_720w.webp) 59 | 60 | ![img](https://pic1.zhimg.com/80/v2-dfdc0c221289af4c5bb8c7a8b489f154_720w.webp) 61 | 62 | ![img](https://pic1.zhimg.com/80/v2-728dee14839e77e0f2c26c6dc5fdba8c_720w.webp) 63 | 64 | ![img](https://pic4.zhimg.com/80/v2-63707c58dc8d3b903c8b9f651c5f9bc7_720w.webp) 65 | 66 | ![img](https://pic2.zhimg.com/80/v2-ed81762a6e6f6e4282b921d68db9264d_720w.webp) 67 | 68 | 现如今的音视频技术可以说无处不在。未来,也将作为一种基础技术应用到更广泛的的场景中,音视频技术人才也会成为新宠儿。虽然很难精通,但这个领域知识更新慢,**学的东西不容易淘汰**,积累的经验将会是撬动你更大未来的一个支点。所有的伟大都来源于一个勇敢的开始。 69 | 70 | 无论是现在从事音视频技术,还是后期转岗,都是很多人为数不多的机遇,能不能抓住就看个人了。 71 | 72 | 原文作者:零声音视频开发 73 | 74 | 原文链接:https://zhuanlan.zhihu.com/p/574750538 -------------------------------------------------------------------------------- /【NO.38】一次解决Linux内核内存泄漏实战全过程.md: -------------------------------------------------------------------------------- 1 | # 【NO.38】一次解决Linux内核内存泄漏实战全过程 2 | 3 | 什么是内存泄漏: 4 | 5 | 程序向系统申请内存,使用完不需要之后,不释放内存还给系统回收,造成申请的内存被浪费. 6 | 7 | 发现系统中内存使用量随着时间的流逝,消耗的越来越多,例如下图所示: 8 | 9 | ![img](https://pic2.zhimg.com/80/v2-238d2a09f200f11e145b2bf263456de9_720w.webp) 10 | 11 | 接下来的排查思路是: 12 | 13 | 1.监控系统中每个用户进程消耗的PSS (使用pmap工具(pmap pid)). 14 | 15 | PSS:按比例报告的物理内存,比如进程A占用20M物理内存,进程B和进程A共享5M物理内存,那么进程A的PSS就是(20 - 5) + 5/2 = 17.5M 16 | 17 | 2.监控/proc/meminfo输出,重点观察Slab使用量和slab对应的/proc/slabinfo信息 18 | 19 | 3.参考/proc/meminfo输出,计算系统中未被统计的内存变化,比如内核驱动代码 20 | 21 | 直接调用alloc_page()从buddy中拿走的内存不会被单独统计 22 | 23 | 以上排查思路分别对应下图中的1,2,3 : 24 | 25 | ![img](https://pic1.zhimg.com/80/v2-65fd3146cde9f3843d75b0e49ebf6c9c_720w.webp) 26 | 27 | 在排查的过程中发现系统非常空闲,都没有跑任何用户业务进程。 28 | 29 | 其中在使用slabtop监控slab的使用情况时发现size-4096 不停增长 30 | 31 | ![img](https://pic1.zhimg.com/80/v2-7d36f746609a6582b8a37a0ceb84a4d4_720w.webp) 32 | 33 | 通过监控/proc/slabinfo也发现SReclaimable 的使用量不停增长 34 | 35 | ```text 36 | while true; 37 | do 38 | sleep 1 ; 39 | cat /proc/slabinfo >> /tmp/slabinfo.txt ; 40 | echo "===" >> /tmp/slabinfo.txt ; 41 | done 42 | ``` 43 | 44 | 由此判断很可能是内核空间在使用size-4096 时发生了内存泄漏. 45 | 46 | 接下来使用trace event(tracepoint)功能来监控size-4096的使用和释放过程, 47 | 48 | 主要用来跟踪kmalloc()和kfree()函数对应的trace event, 因为他们的trace event被触发之后会打印kmalloc()和kfree()所申请和释放的内存地址,然后进一步只过滤申请4096字节的情况。 49 | 50 | ```text 51 | #trace-cmd record -e kmalloc 52 | -f 'bytes_alloc==4096' -e kfree -T 53 | ``` 54 | 55 | (-T 打印堆栈) 56 | 57 | 等待几分钟之后… 58 | 59 | \#ctrl ^c 中断trace-cmd 60 | 61 | \#trace-cmd report 62 | 63 | 以上步骤相当于: 64 | 65 | ![img](https://pic1.zhimg.com/80/v2-112f1fb0265b037be2c84bce983cfaa4_720w.webp) 66 | 67 | 等待几分钟之后… 68 | 69 | ```text 70 | #cp /sys/kernel/debug/tracing/trace_pipe /tmp/kmalloc-trace 71 | ``` 72 | 73 | 从trace-cmd report的输出结果来看,很多kmalloc 对应的ptr值都没有kfree与之对应的ptr值 74 | 75 | ![img](https://pic3.zhimg.com/80/v2-d7869c3e5f54a87033e1a79d41a7e3ea_720w.webp) 76 | 77 | 这就说明了cat进程在内核空间使用size-4096之后并没有释放,造成了内存泄漏。 78 | 79 | 80 | 81 | 为了进一步精确定位到是使用哪个内核函数造成的问题,此时手动触发vmcore 82 | 83 | ```text 84 | #echo c > /proc/sysrq-trigger 85 | ``` 86 | 87 | 然后使用crash工具分析vmcore: 88 | 89 | ```text 90 | #crash ./vmcore ./vmlinux.debug 91 | ``` 92 | 93 | 读出上面kmalloc申请的ptr内存信息 94 | 95 | ![img](https://pic3.zhimg.com/80/v2-6df11cad7524f9342bbb729e7240e2b6_720w.webp) 96 | 97 | (读取0xffff880423744000内存开始的4096个字节,并以字符形式显示) 98 | 99 | ![img](https://pic3.zhimg.com/80/v2-bd6f9fe92a846e9c8bf66d9446ac80de_720w.webp) 100 | 101 | 发现从上面几个ptr内存中读出的内容都是非常相似,仔细看一下发现都是/proc/schedstat 的输出内容。 102 | 103 | 通过阅读相关代码发现,当读出/proc/schedstat内容之后,确实没有释放内存 104 | 105 | ![img](https://pic4.zhimg.com/80/v2-c6ea82ba477dcda95180e5f9a530e4c3_720w.webp) 106 | 107 | 然后发现kernel上游已经有patch解决了这个问题: 108 | 109 | commit: 8e0bcc722289 110 | 111 | fix a leak in /proc/schedstats 112 | 113 | 114 | 115 | 原文地址:https://zhuanlan.zhihu.com/p/577973486 116 | 117 | 作者: linux -------------------------------------------------------------------------------- /【NO.396】FFmpeg的结构和命令行工具(在线介绍).md: -------------------------------------------------------------------------------- 1 | # 【NO.396】FFmpeg的结构和命令行工具(在线介绍) 2 | 3 | ## 1.FFmpeg介绍以及编译 4 | 5 | 音视频开发,首先不得不提到FFmpeg。该框架为开发者们提供了非常大的帮助,它是一套可以用来采集、处理、编码、传输的开源框架。可以用在各种PC端(Linux、Windows、macOS)和移动端(iOS、Android)等平台。本节会从编译开始,介绍一下FFmpeg的框架,以及在iOS平台上的使用。 6 | 7 | ## 2.FFmpeg编译选项 8 | 9 | 我们可以到FFmpeg官网下载稳定版本的源码(一般来说4.3的版本比4.3.1的版本稳定)。然后将下载的源码解压,FFmpeg与大部分GNU软件的编译方式类似,都是通过configure脚本来实现编译前定制的,这种方式可以让用户在编译前对软件进行裁剪,同时通过对运行系统以及目标平台的指定,实现满足需求的最小包体积编译。我们可以利用help命令来查看它有哪些编译选项。 10 | 11 | ```text 12 | ./configure -help 13 | 复制代码 14 | ``` 15 | 16 | 1. 标准选项:GNU软件配置项,例如安装路径、-- prefix=...等。 17 | 2. 专利使用协议选项:一些开源协议。 18 | 3. 编译、链接选项:生成静态库还是动态库这些。 19 | 4. 可执行程序选项:决定是否生成FFmpeg、ffplay、ffprobe 和ffserver等。 20 | 5. 文档选项:文档的类型。 21 | 6. 模块选项:需要开启或者关闭里面哪些模块。 22 | 7. Toolchain选项:CPU架构、交叉编译,操作系统。C、C++的一些配置等。 23 | 8. 其他:一些其他深度定制编译选项。 24 | 25 | ## 3.FFmpeg结构 26 | 27 | 默认的编译会生成4个可执行文件和8个静态库。可执行文件分别是用于转码、裁剪文件的ffmpeg、用于播放媒体文件的ffplay、用于获取文件信息的ffprobe、以及作为简单流媒体服务器的ffserver。8个静态库就是FFmpeg的8个模块,具体包括以下内容: 28 | 29 | - AVUtil:核心工具库,该模块是最基础的模块之一,许多其他的模块都会依赖这个工具做一些操作。 30 | - AVFormat:文件格式和协议库,该模块是用来做传输层数据的封装和解封装的,使得协议和格式转换处理更加方便。 31 | - AVCodec:编解码库,FFmpeg有一些默认的编解码库,也可以集成如H264、FDK-AAC等库。其他第三方库以插件的形式添加在FFmpeg中,API调用符合默认接口风格,为开发者提供了统一的接口。 32 | - AVFilter:音视频滤镜库,这个模块提供了音视频中的各种特效处理,非常方便。 33 | - AVDevice:输入输出设备库,获取系统上的输入输出设备,从而实现采集或者播放音视频。 34 | - SWResample:音频重采样,音频的格式转换、混音。 35 | - SWScale:提供图像的裁剪、旋转、格式转换等功能。 36 | - PostProc:视频后期处理。 37 | 38 | ## 4.iOS平台的编译 39 | 40 | 对于iOS平台来说,需要在configure的时候加入以下内容: 41 | 42 | ```text 43 | ./configure \ 44 | --target-os=darwin \ 45 | --arch="arm64" \ 46 | --cc="xcrun -sdk iphoneos clang" \ 47 | --extra-cflags="-arch arm64 -mios-version-min=8.0 -Ixx" \ 48 | --extra-ldflags="-arch arm64 -mios-version-min=8.0 -Lxx" \ 49 | 复制代码 50 | ``` 51 | 52 | 这里推荐一个编译脚本FFmpeg-iOS-build-scriptt,它可以帮我下载指定的FFmpeg版本,选择编译的平台,配置链接的第三方库,非常好用。 53 | 54 | ## 5.在macOS上安装FFmpeg命令行工具 55 | 56 | ```text 57 | brew install ffmpeg 58 | 复制代码 59 | ``` 60 | 61 | 如果安装好没有ffplay工具,那么在安装时加上--with-ffplay就有了。 62 | 63 | ```text 64 | brew install ffmpeg --with-ffplay 65 | ``` 66 | 67 | 原文作者:零声音视频开发 68 | 69 | 原文链接:https://zhuanlan.zhihu.com/p/429550853 -------------------------------------------------------------------------------- /【NO.397】音视频编解码常用知识点.md: -------------------------------------------------------------------------------- 1 | # 【NO.397】音视频编解码常用知识点 2 | 3 | ## 1. 前言 4 | 5 | ffplay是ffmpeg的一个子工具,它具有强大的音视频解码播放能力,目前它广泛被各种流行播放器(QQ影音、暴风影音……)集成应用。作为一款开源软件,ffplay囊括Linux、Windows、Ios、Android等众多主流系统平台,十分适合进行二次开发。这里有必要介绍一下它常用的技巧。首先下载ffmpeg代码包,里面有免编译版、源代码百、静态库版、动态库版,具体怎么下载安装请参考我的博文《FFmpeg简介、功能入门、源码下载安装、常规应用》。接下来以Windows平台为例子讲述一下具体用法。 6 | 7 | ## 2. 使用技巧 8 | 9 | Win+r组合键运行cmd进入Windows命令行控制界面,使用cd命令进入ffplay.exe的可执行目录(当然也可以使用环境变量等手段使ffplay.exe命令全局可用),其他平台如linux的操作也类似。ffplay的基本用法很简单,其一般形式如下: 10 | 11 | ```text 12 | ffplay [option] file 13 | ffplay [option] URL 14 | ``` 15 | 16 | 总结起来ffplay的用法就是option项加上资源路径,option项是用来指定播放时的一些参数的,如指定连接的协议、视频画面的大小,音视频解码器选用、传输码率设定等,一般这些参数我们很少会设置,使用默认就OK,此时option项可以直接忽略,ffplay会帮我们选择,这也是它功能强大的体现,option的更多具体选项可以参考其官方文档;资源路径则包括文件资源路径和网络资源路径,文件资源路径是指定需要播放的音视频文件,如*.mp3、*.mp4、*,avi、*.mkv、*.rmvb等等类型的文件,网络资源路径根据协议可以分为RTSP、RTMP、HTTP流资源,心情好,来个直播,如: 17 | 18 | ```text 19 | ffplay rtmp://live.hkstv.hk.lxdns.com/live/hks 20 | ``` 21 | 22 | 再或者,用http浏览一下视频,如: 23 | 24 | ```text 25 | ffplay http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8 26 | ``` 27 | 28 | RTSP播放也了解一下(对于RTSP播放有个坑,请参考《ffplay播放rtsp网络串流失败问题》),如: 29 | 30 | ```text 31 | rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov 32 | ``` 33 | 34 | 音视频文件指定分辨率播放 35 | 36 | ```text 37 | ffplay -vfscale=1920:1080 xxxx.avi 38 | ``` 39 | 40 | 41 | 42 | ![img](https://pic3.zhimg.com/80/v2-4c1fb9a29803126870077cf5cd729406_720w.webp) 43 | 44 | ## 3. 番外篇 45 | 46 | 在安防等视频流媒体数据处理领域,我们可能更关注的是用ffplay播放RTSP音视频流,其实国内各大厂商的VMS(video management system)平台也是基于此设计的。每每使用它们的IPC、NVR时都需要下载它们,但是有了ffplay神器,一个就够了,它可以播放诸如海康、大华、长视等厂商IPC、NVR的RTSP流,视频监控就变得如此简单。这里很有必要介绍一下RTSP链接的格式。 47 | 48 | RTSP链接格式与HTTP链接格式类似,也是由URL(Uniform Resource Locator)发展继承而来。URL由三部分组成:资源类型、存放资源的主机域名、资源文件名,一般语法格式为(带方括号[]的为可选项): 49 | 50 | ```text 51 | protocol :// hostname[:port] / path / [;parameters][?query]#fragment 52 | ``` 53 | 54 | 这里就不一一解释其各项的含义了,我们重点关注RTSP链接的格式,相比于URL,RTSP由于参数表列是嵌入RTSP报文中的,格式上会少了parameters等参数选项,其一般格式如下: 55 | 56 | ```text 57 | rtsp://[username]:[password]@[ip]:[port]/path 58 | ``` 59 | 60 | ## 4. 总结 61 | 62 | 运用ffplay播放小技巧可以轻松应对各种文件资源和网络资源的播放,特别是在安防监控领域,使用它播放个监控资源那简直太方便了,而且还可以用它来检查验证音视频格式封包是否异常,在调试优化过程ffplay总能带给你惊喜。 63 | 64 | 原文作者:零声音视频开发 65 | 66 | 原文链接:https://zhuanlan.zhihu.com/p/429125576 -------------------------------------------------------------------------------- /【NO.400】WebRTC 源码分析 -- RTC_CHECK.md: -------------------------------------------------------------------------------- 1 | # 【NO.400】WebRTC 源码分析 -- RTC_CHECK 2 | 3 | **【1】RTC_DCHECK(1 != 1) << "hello world " << 100 << 3.14;的执行过程** 4 | 5 | ```text 6 | RTC_DCHECK(1 != 1) << "hello world " << 100 << 3.14;的执行过程 7 | 宏展开结果 8 | while (!(1 != 1)) FatalLogCall("main.cc", 7, "1 != 1") 9 | & LogStreamer<>() << "hello world " << 100 << 3.14; 10 | 注,根据运算符的优先级,<< 运算符比 & 运算符优先级高,所以先算 << 运算符 11 | ``` 12 | 13 | **<< 运算符的运算过程** 14 | 15 | - \1. 先计算 LogStreamer<>() << "hello world ",LogStreamer<>() 生成临时对象,临时对象会调用 operator<<() 函数,operator<<() 函数会把 "hello world" 和临时对象作为参数,生成 LogStreamer 对象,该对象存储着 "hello world" 和临时对象 16 | - \2. 上一步生成的 LogStreamer 对象会继续调用 operator<<() 函数,同时把自己和 100 传入,生成一个新的 LogStreamer 对象,从而递归下去,直到所有的 << 运算符处理完毕 17 | 18 | ![img](https://pic3.zhimg.com/80/v2-4ee47dc77f2d995e55ae78a1c0bb7472_720w.webp) 19 | 20 | **& 运算符的运算过程** 21 | 22 | - \1. 上一步最后会返回 LogStreamer 对象,组成了新的表达式,FatalLogCall(“main.cc”, 7, “1 != 1”) & LogStreamer 23 | - FatalLogCall(“main.cc”, 7, “1 != 1”) 会生成临时对象,临时对象会继续调用 operator&() 函数 24 | - \2. 在 operator&() 函数中会使用 LogStreamer 对象调用 Call() 函数,会产生递归调用,每次调用都会把本类保存的日志数据往下层传递 25 | - \3. 递归到最后,LogStreamer<>() 生成临时对象会调用 FatalLog() 函数,将所有日志数据打印出来 26 | 27 | ![img](https://pic1.zhimg.com/80/v2-ea2a1738bb7d9a287be5478d006e5258_720w.webp) 28 | 29 | 原文作者:零声音视频开发 30 | 31 | 原文链接:https://zhuanlan.zhihu.com/p/485451406 -------------------------------------------------------------------------------- /【NO.404】x264码率控制.md: -------------------------------------------------------------------------------- 1 | # 【NO.404】x264码率控制 2 | 3 | 问题:在做视频编码时,当我们给定编码器一个目标码率的时候,编码器内部是怎么达到码率要求的那? 4 | 5 | 概况:关于码率控制有两个目的,第一:兼容传输,播放条件。第二:获取更高的视频质量。 6 | 7 | 码率控制分为两类:CBR:constant bit rate,固定码率。 VBR:variable bit rate 可变码率。 8 | 9 | VBR:可变码率是一类码率控制算法的统称,他们的特点是局部的码率可变的,常用的可变码率子类包括如下: 10 | 11 | 1:abr:average bit rate,控制整个文件的平均码率。 12 | 13 | 2:crf:constant refactor ,恒定质量。总码率不可控 14 | 15 | 3:cqp:constatnt qp,恒定量化参数。关闭一切码率控制算法,与crf的区别在于,crf允许x264对每一帧,每一个宏块进行选取qp,从而产生一个恒定的质量。 16 | 17 | 对应的x264参数如下: 18 | 19 | ```text 20 | #define X264_RC_CQP 0 21 | #define X264_RC_CRF 1 22 | #define X264_RC_ABR 2 23 | 24 | //恒定QP 25 | int i_qp_constant; /* 0 to (51 + 6*(x264_bit_depth-8)). 0=lossless */ 26 | int i_qp_min; /* min allowed QP value */ 27 | int i_qp_max; /* max allowed QP value */ 28 | int i_qp_step; /* max QP step between frames */ 29 | 30 | //恒定质量 31 | float f_rf_constant; /* 1pass VBR, nominal QP */ 32 | float f_rf_constant_max; /* In CRF mode, maximum CRF as caused by VBV */ 33 | 34 | //平均码率 35 | int i_bitrate; 36 | ``` 37 | 38 | 恒定码率CBR: 39 | 40 | 并不是每个瞬间码率都相同,也不是每一秒码率相同。固定码率指的是固定信道容量。此时就涉及到了VBV(video buffer verifier)视频缓冲区校验器。vbv模型:编码码率通过一个容量受限的信道传输到解码设备,解码设备在解码前有一个缓存,解码器实时从缓存区读取数据解码,保证即不上溢也不下溢(即拿取速度过快或过慢)。 41 | 42 | 对应参数如下:最终生成的mp4文件可以看出码率为147kbps.,buffer_size的带下取决于容忍的延迟以及播放器的硬件内存限制。 43 | 44 | ```text 45 | int i_vbv_max_bitrate;//缓冲区最大填充速度 46 | int i_vbv_buffer_size;//缓冲区大小. 47 | 48 | FFmpeg.exe -i q.mp4 -crf 21 -maxrate 150k -bufsize 450k -codec:v:0 libx264 -s 320x240 -r 15 out. 49 | ``` 50 | 51 | 原文作者:零声音视频开发 52 | 53 | 原文链接:https://zhuanlan.zhihu.com/p/454882492 -------------------------------------------------------------------------------- /【NO.407】WebRTC能给我带来什么?.md: -------------------------------------------------------------------------------- 1 | # 【NO.407】WebRTC能给我带来什么? 2 | 3 | WebRTC现在已经成为了W3C的正式标准,提供具有NAT遍历功能的次秒级的点对点视频和音频流。次秒级延迟已经被广泛应用于视频会议之中,也一直是视频流公司的焦点,如Millicast和Limelight(仅举两个例子),这些公司旨在将这种点对点技术交付给成千上万的人。在不到一秒钟的时间内便实现了交互式视频、游戏流、拍卖和超低延迟的体育运动。 4 | 5 | 针对直接使用其他流媒体协议的用户,Pion的创建者肖恩•杜布瓦(Sean DuBois)在SF Video Tech上谈到了WebRTC带来的RTMP、SRT和RIST等协议。它的核心是WebRTC(如SRT和RIST)创建一个连接,通过它可以发送各种数据。虽然我们期望媒体被发送,但是实际上,文件传输可以很容易地被实现——让我们不要忘记整个SRT是建立在UDT之上的,而UDT是一个专门用于文件传输的实用程序。在可以实现文件传输的地方,实时数据和元数据传输也可以实现 6 | 7 | Sean很快将WebRTC概括为(典型)浏览器之间的协议,这是一种点对点的安全连接,多个音频和视频流可以在其连接上流动。与RIST和其他最新的协议一样,它基于许多已有的协议:SRTP、DTLS、ICE和SDP等技术来提供信令、连接管理、加密和通信。 8 | 9 | ![img](https://pic2.zhimg.com/80/v2-f72b74a2bcb6c4faeefb5c3d1e023511_720w.webp) 10 | 11 | 对于RTMP非常长的改进列表,它们都在视频中被简明扼要地展现出来了,所以我们在这里只突出几个要点。重要的是,低延迟是其中的关键。RTMP在当时是属于低延迟的,但并不是以今天的低延迟标准。Sean解释说,谷歌的Stadia可以为按键提供125毫秒的视频延迟。DTLS和SRTP对于安全性来说是必不可少的,但是它们是众所周知便于理解和可靠的保护数据的方法。DTLS与TLS几乎完全相同,TLS保护您的银行转账,只是将其改为UDP而不是TCP中。但是,WebRTC可以通过交换“指纹”(DTLS-SRTP)而不是支持web上TLS的完全可信的证书基础结构来工作。只要您有信心可以提前安全地交换指纹,那么取消对证书的要求对于灵活性和敏捷性是一个很大的提升。 12 | 13 | NAT遍历也是一大福音,即使两个端点都在防火墙后面,端点也总能找到通信的方法,尽管这确实意味着需要ICE服务器来促进连接。然而,在广播中,你更有可能控制一端,这样就不太需要这样做了。Sean强调了使用WebRTC的“同步广播”功能在同一流中发送多个质量级别的能力。 14 | 15 | 之后Sean着眼于SRT和RIST。这两种协议都是低延迟流协议,它们都可以提供次秒级的流传输,以实现RTT相对较低的良好连接。Sean强调了SRT和RIST在协商使用中的编解码器及其可选安全性方面的不足。由于更注重提供贡献源,它们往往具有更静态的配置,通常是在测试程序之后创建的,以确保其质量能够被广播商/流媒体提供商所接受。 16 | 17 | 最后,Sean重点介绍了WebRTC的一系列有趣的创新用途,从非正式的群组流媒体到无人机、共享在线游戏到文件传输等等 18 | 19 | 原文作者:零声音视频开发 20 | 21 | 原文链接:https://zhuanlan.zhihu.com/p/449083890 -------------------------------------------------------------------------------- /【NO.428】最强阿里巴巴历年经典面试题汇总:C++研发岗.md: -------------------------------------------------------------------------------- 1 | # 【NO.428】最强阿里巴巴历年经典面试题汇总:C++研发岗 2 | 3 | ![img](https://pic1.zhimg.com/80/v2-8c0605f70d3c3cc20b6a6407525943d0_720w.webp) 4 | 5 | (1)、B树、存储模型 6 | 7 | (2)、字典树构造及其优化与应用 8 | 9 | (3)、持久化数据结构,序列化与反序列化时机(4)、在无序数组中找最大的K个数? 10 | 11 | (4)、大规模文本文件,全是单词,求前10词频的单词 12 | 13 | (5)、堆排序与其在求10词频问题中的应用 14 | 15 | (6)、字典树与其在统计词频上的应用 16 | 17 | (7)、红黑树的特性与其在C++ STL中的应用 18 | 19 | (8)、红黑树的调整 20 | 21 | (9)、贪心算法与其弊端 22 | 23 | (10)、能取得全局最优解的算法 24 | 25 | (11)、动态规划的原理与本质 26 | 27 | (12)、01背包问题的详细解释 28 | 29 | (13)、进程间通信方式 30 | 31 | (14)、数据库中join的类型与区别 32 | 33 | (15)、数据库的ACID 34 | 35 | (16)、实现bitmap数据结构,包括数据的存储与插入方式 36 | 37 | (17)、实现unordered_map,键为string,value不限 38 | 39 | (18)、实现unordered_map过程中的冲突解决办法 40 | 41 | (19)、一串int型整数存放磁盘上的压缩存储方式,包括写入与读取及内存无法一次性读取时的解决办法 42 | 43 | (20)、对Java的了解 44 | 45 | (21)、Bloom过滤器处理大规模问题时的持久化,包括内存大小受限、磁盘换入换出问题 46 | 47 | (22)、线程池的了解、优点、调度处理方式和保护任务队列的方式 48 | 49 | (23)、对象复用的了解 50 | 51 | (24)、零拷贝的了解 52 | 53 | (25)、Linux的I/O模型 54 | 55 | (26)、异步I/O的详细解释 56 | 57 | (27)、线程池对线程的管理方式,包括初始化线程的方法、线程创建后的管理、指派任务的方式 58 | 59 | (28)、同步I/O与异步I/O的区别 60 | 61 | (29)、Direct I/O 和其与异步I/O的区别 62 | 63 | (30)、Linux内核如何调用Direct I/O 64 | 65 | (31)、Bloom过滤器的优点与原理 66 | 67 | (32)、字符串hash成状态位的具体实现方式 68 | 69 | (33)、hash函数如何保证冲突最小 70 | 71 | (34)、文件读写使用的系统调用 72 | 73 | (35)、文件读写中涉及的磁盘缓冲区与其手动flush问题 74 | 75 | (36)、数据库join的具体含义 76 | 77 | (37)、struct与class的区别 78 | 79 | (38)、STL库的介绍 80 | 81 | (39)、vector使用的注意点及其原因 82 | 83 | (40)、频繁对vector调用push_back()对性能的影响和原因 84 | 85 | (41)、vector重新分配内存的大小与方式 86 | 87 | (42)、hashmap的实现方式 88 | 89 | (43)、map的实现方式 90 | 91 | (44)、C++虚函数的具体实现原理 92 | 93 | (45)、实现编译器处理虚函数表应该如何处理 94 | 95 | (46)、析构函数一般写成虚函数的原因 96 | 97 | (47)、解释哲学家进餐问题 98 | 99 | (48)、描述银行家算法 100 | 101 | (49)、实现一种算法解决哲学家进餐问题 102 | 103 | (50)、大数量整数的去重问题 104 | 105 | (51)、如果用bitmap解决大数量整数去重问题,计算当全为int型整数时需要消耗的内存 106 | 107 | (52)、算法题:环形公路上加油站算法问题 108 | 109 | 现有一圆环形路,路上有n个加油站,第i个加油站储存有N[i]升容量的油,与下一个加油站之间有一定的距离g[i],一汽车初始无油,假设该车每公里消耗1升油,请问该车从哪个加油站出发可以绕该环形路行驶一圈。 110 | 111 | (53)、多个服务器通信,线程池的设定 112 | 113 | (54)、哈希表的冲突解决方式 114 | 115 | (55)、哈希表在桶固定的情况下,时间复杂度。怎么优化? 116 | 117 | (56)、多线程中哈希表保证线程安全 118 | 119 | (57)、哈希表特别大,桶特别多的时候怎么加锁 120 | 121 | (58)、C语言变量存放位置 122 | 123 | (59)、栈上的分配内存快还是堆上快 124 | 125 | (60)、http的长连接和短连接是什么,各有什么优缺点,然后使用场景 126 | 127 | (61)、在一个浏览器里面输入一个网址,后回车,在这后面发生了什么? 128 | 129 | (62)、进程线程的区别,多进程与多线程的区别 130 | 131 | (63)、什么是生产者消费者模型?如果一个人洗碗,另一个人马上用碗,是生产者消费者模型吗? 132 | 133 | (64)、GET/POST的区别,GET/POST的安全性问题,假如你来实现,你怎么实现GET/POST的安全性 134 | 135 | (65)、你做服务器压力测试时,用什么测试,如何配置参数,吞吐量大小,并发量大小 136 | 137 | (66)、类似Nginx这种web服务器是用什么数据结构实现定时器事件的,四叉堆知道是什么吗,与二叉堆有什么区别? 138 | 139 | (67)、动态规划与贪心算法的区别,什么情况下,动态规划可以转换为贪心算法 140 | 141 | (68)、说一下快排,快排是稳定的吗?为什么?哪些排序算法稳定?哪些不稳定? 142 | 143 | (69)、数据库有哪些索引,你知道哪些索引引擎,这些索引引擎有什么区别 144 | 145 | (70)、epoll与select的区别,epoll在什么情况下吞吐率比较高? 146 | 147 | (71)、非阻塞与异步的区别? 148 | 149 | (72)、HTTP1.0和HTTP1.1的区别,服务器端如何判断是长连接还是短连接? 150 | 151 | (73)、HTTP2.0的 新特性,它是如何实现共用一个长连接? 152 | 153 | (74)、tcp如何连接到服务器,你如何判断tcp连接到服务器,你服务器的输入是什么? 154 | 155 | (75)、epoll的底层实现 156 | 157 | **完整的linux c/c++高级开发技术路线图** 158 | 159 | ![img](https://pic2.zhimg.com/80/v2-dd61a798de3ebe741df54e9eb6da1a7d_720w.webp) 160 | 161 | ![img](https://pic2.zhimg.com/80/v2-b7bf75892f81a3ee5a9ad3b9c0f9f4e1_720w.webp) 162 | 163 | ![img](https://pic1.zhimg.com/80/v2-6a3a9de5f37c74528accdb5b84728034_720w.webp) 164 | 165 | ![img](https://pic4.zhimg.com/80/v2-522e78c886f5121c08990e57e9cd2b3b_720w.webp) 166 | 167 | ![img](https://pic3.zhimg.com/80/v2-82b0ac03ee96fa62235d85bbc6fbb242_720w.webp) 168 | 169 | ![img](https://pic2.zhimg.com/80/v2-d84edf93cf10529900617d85c19eda49_720w.webp) 170 | 171 | ![img](https://pic4.zhimg.com/80/v2-50cc437d64513556a447ad9b64591c83_720w.webp) 172 | 173 | 原文地址:https://zhuanlan.zhihu.com/p/372020083 174 | 175 | 作者:linux -------------------------------------------------------------------------------- /【NO.43】红黑树的原理以及实现.md: -------------------------------------------------------------------------------- 1 | # 【NO.43】红黑树的原理以及实现 2 | 3 | **红黑树** 4 | 5 | **红黑树基于二叉查找树的附加特性** 6 | 7 | 1. 节点是红色或黑色。 8 | 2. 根节点是黑色。 9 | 3. 每个叶子节点都是黑色的空节点(叶子结点指为空的叶子结点)。 10 | 4. 每个红色节点的两个子节点都是黑色的(从每个叶子到根的所有路径上不能有两个连续的红色节点)。 11 | 5. 从任意节点到其每个叶子的所有路径都包含相同数目的黑色节点。 12 | 13 | ## **1. 数据结构** 14 | 15 | ```text 16 | class TreeNode{ 17 | private Boolean color; 18 | private int val; 19 | private TreeNode left; 20 | private TreeNode right; 21 | private TreeNode parent; 22 | get,set... 23 | } 24 | class RBTree{ 25 | public boolean add(int val){...} 26 | public boolean delete(int val){...} 27 | public void display(){...} 28 | } 29 | ``` 30 | 31 | ## **2. 左旋以及右旋** 32 | 33 | ### **2.1 左旋** 34 | 35 | ![img](https://pic2.zhimg.com/80/v2-ee7b35428f94dbbb1a0b77be93b9116d_720w.webp) 36 | 37 | ### **2.2 右旋** 38 | 39 | ![img](https://pic2.zhimg.com/80/v2-4822d2fb2cf7e522a4d19c8a72799511_720w.webp) 40 | 41 | ## **3. 插入** 42 | 43 | - 新插入的节点(newNode)为红色。 44 | - 按照二分查找树插入规则插入。 45 | - **分情况讨论**(**以下情况基本都是为了保持上文所讲的的红黑树特性4和特5**) 46 | 1、若newNode为根节点,则变为黑色,插入完毕,返回 true。 47 | 2、若newNode父节点为黑色,则插入完毕,返回 true。 48 | 3、如下图所示,若newNode父节点为红色,且叔叔节点存在且为红色,则父节点与叔叔节点变为黑色,祖父节点变为红色,newNode = 祖父节点。 49 | 50 | ![img](https://pic2.zhimg.com/80/v2-dbcbfb84ec49083414f92625b1d90bbd_720w.webp) 51 | 52 | 53 | 4、如下图所示,若newNode父节点为红色,叔叔节点不存在或为黑色,且newNode为父节点右孩子,父节点为祖父节点左孩子,则以父节点为轴左旋,进入情况6. 54 | 55 | ![img](https://pic1.zhimg.com/80/v2-3cba7f4e8bd4ca3a568f5450c961be54_720w.webp) 56 | 57 | 5、如下图所示,若newNode父节点为红色,叔叔节点不存在或为黑色,且newNode为父节点左孩子,父节点为祖父节点右孩子,则以父节点为轴右旋,进入情况7 58 | 59 | ![img](https://pic1.zhimg.com/80/v2-c111482e5099c1568ecef70e3de34db8_720w.webp) 60 | 61 | 6、如下图,此时以祖父节点为轴进行右旋,将祖父节点变为红色,newNode变为黑色。 62 | 63 | ![img](https://pic3.zhimg.com/80/v2-59fe561a9ce96ce46e04d5894216dc76_720w.webp) 64 | 65 | 7、如下图,此时以祖父节点为轴进行左旋,将祖父节点变为红色,newNode变为黑色。 66 | 67 | ![img](https://pic1.zhimg.com/80/v2-1030a853056be151c322ad68b31bc0f0_720w.webp) 68 | 69 | ## **4. 删除** 70 | 71 | **分情况讨论**(和插入一样,以下情况基本都是为了保持上文所讲的的红黑树特性4和特5) 72 | 73 | 1. 如下图,如果待删除节点**B**有两个非空的孩子节点,转化成待删除节点只有右孩子(或没有孩子)的情况,习惯性选取待删除节点右子树最小节点**E**替换待删除节点(只是值替换,颜色不变),并将待删除节点变为E。 74 | 75 | ![img](https://pic3.zhimg.com/80/v2-ec68d96519294a04a60240be1bd32546_720w.webp) 76 | 77 | 1. 根据待删除节点和唯一子节点颜色,**分情况处理:** 78 | 79 | 2. 1. 自身O是红色,子节点N是黑色,直接删除。 80 | 2. 自身O是黑色,子节点N是红色,直接删除**并**将子节点N变为黑色。 81 | 3. 自身O是黑色,子节点N不存在(不存在即子节点为空黑色节点,也可以用来判断)**或**也是黑色,较为复杂,**先删除,再分情况讨论:** 82 | 1、N是根节点,则不需要调整。 83 | 2、如下图,N的父亲、兄弟、侄子都是黑色,则将兄弟变为红色,父亲视作N,进行递归处理。 84 | 85 | ![img](https://pic1.zhimg.com/80/v2-5a159ab516c5f352cdc39bd9e04fa238_720w.webp) 86 | 87 | 3、(**存在镜像**)N的兄弟节点是红色,且N为父亲节点左儿子,则以父亲节点为轴左旋(否则右旋),并将旋转后N的祖父节点变为黑色,N的父节点变为红色,进入情况4,5或6. 88 | 89 | ![img](https://pic3.zhimg.com/80/v2-8b2cff6a44151e7a702935f3729daf56_720w.webp) 90 | 91 | 4、N的父亲节点是红色,兄弟和侄子节点是黑色,父亲节点变为黑色,兄弟节点变为红色。 92 | 93 | ![img](https://pic3.zhimg.com/80/v2-8180d41932ed875f0e4ce984d316e43a_720w.webp) 94 | 95 | 5、(**存在镜像**)N的父节点颜色随意,兄弟节点为父节点黑色右孩子,左侄子节点为红色,右侄子节点为黑色,以兄弟节点为轴进行右旋,将旋转后N的兄弟节点变为黑色,N的右侄子节点变为红色,进入情况6 96 | 97 | ![img](https://pic3.zhimg.com/80/v2-5a30f3fd2aac1134fa2e50031c31223a_720w.webp) 98 | 99 | 6、(**存在镜像**)N的父节点随意,兄弟节点为父节点的黑色右儿子,右侄子节点为红色,以N的父节点为轴进行左旋,左旋后的N的祖父节点变为父节点颜色,父节点变黑,叔叔节点变黑。 100 | 101 | ![img](https://pic4.zhimg.com/80/v2-1cc5c141b027e59b1cd78362bef97c23_720w.webp) 102 | 103 | ## 5.**测试** 104 | 105 | **原树(上右下左)** 106 | 107 | ![img](https://pic3.zhimg.com/80/v2-77246a8a5f27c41930382fc8c29a64c2_720w.webp) 108 | 109 | **删除53** 110 | 111 | ![img](https://pic2.zhimg.com/80/v2-1f4bcf75365b8df10a0b5893c047b85d_720w.webp) 112 | 113 | **删除23** 114 | 115 | ![img](https://pic1.zhimg.com/80/v2-79ad4c9fc33586bfe468113ea11affa0_720w.webp) 116 | 117 | **删除54** 118 | 119 | ![img](https://pic2.zhimg.com/80/v2-270cd680a5c46cde236714a42f71e085_720w.webp) 120 | 121 | **添加67** 122 | 123 | ![img](https://pic3.zhimg.com/80/v2-e906fa056465876568af351ce3f55c72_720w.webp) 124 | 125 | 126 | 127 | 原文链接:https://zhuanlan.zhihu.com/p/588861677 128 | 129 | 原文作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.440】并发与多线程之线程安全篇.md: -------------------------------------------------------------------------------- 1 | # 【NO.440】并发与多线程之线程安全篇 2 | 3 | 并发是指某个时间段内,多个任务交替执行的能力。 CPU 把可执行时间均匀地分成若干份,每个进程执行一段时间后,记录当前的工作状态,释放当前的执行资源并进入等待状态,让其他进程抢占 CPU 资源。并行是指同时处理多任务的能力。目前, CPU 已经发展为多核,可以同时执行多个互不依赖的指令及执行块。 4 | 5 | 并发和并行的目标都是尽可能快执行完所有的任务,**两者区别核心在于进程是否同时执行**,并发环境有着以下几个特点: 6 | 7 | 1. 并发程序之间有相互制约的关系。 8 | 2. 并发程序的执行过程是断断续续的。 9 | 3. 当并发设置合理并且 CPU 拥有足够的处理能力时,并发会提高程序的运行效率。 10 | 11 | ## 1.**线程安全** 12 | 13 | > 我们都知道,进程是操作系统进行资源分配的独立单位,而线程是CPU调度和分派的基本单位,为了更充分地利用CPU资源,一般都会使用多线程进行处理。多线程的作用是**提高任务的平均执行速度**。 14 | 15 | 线程可以拥有自己的操作栈、程序计数器、局部变量表等资源,它与同一进程内的其他线程共享该进程的所有资源。线程在生命周期内存在多种状态,分别为**NEW(新建状态)**、**RUNNABLE(就绪状态)**、**RUNNING(运行状态)**、**BLOCKED(堵塞状态)**、**DEAD(死亡状态)** 五种状态。线程状态图如下 16 | 17 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213160114_21905.jpg) 18 | 19 | ## 2.**NEW-新建状态** 20 | 21 | New 是线程被创建且未启动的状态。线程被创建的方式有三种:第一种继承 Thread 类,第二种实现 Runnable 接口,第三种实现 Callable 接口。由于 Java 单继承限制,所以推荐第二种,实现 Runnable 接口可以使编程更加灵活,对外暴露的细节比较少,开发者只需专注于具体的实现,即 run 方法的实现。第三种方式是实现 Callable 接口,代码如下: 22 | 23 | ``` 24 | @FunctionalInterface 25 | public interface Callable { 26 | // Computes a result, or throws an exception if unable to do so. 27 | // Returns:computed result 28 | // Throws:Exception – if unable to compute a result 29 | V call() throws Exception; 30 | } 31 | ``` 32 | 33 | 从注释中看出,当出现无法计算的结果就会抛出异常,并且Callable接口是存在返回值的。这是 Callable 跟 Runnable 的本质区别。 34 | 35 | ## **3.RUNNABLE-就绪状态** 36 | 37 | Runnable 是调用 start() 之后并且在运行之前的状态。线程的 start 方法不能多次调用,否则将抛出 IllegalThreadStateException 异常。 38 | 39 | ## **4.RUNNING-运行状态** 40 | 41 | Running 是 run() 正在执行时线程的状态。线程可能会由于某些原因而退出 RUNNING ,如时间、异常、锁、调度等。 42 | 43 | ## **5.BLOCKED-堵塞状态** 44 | 45 | Blocked 是线程已经发生堵塞的状态,具体发生堵塞的有以下几种情况: 46 | 47 | - 同步堵塞:锁被其他线程占用。 48 | - 主动堵塞:调用 Thread 的某些方法,主动让出 CPU 执行权,比如 sleep()、join() 等。 49 | - 等待堵塞:执行了wait() 。 50 | 51 | ## 6.**DEAD-死亡状态** 52 | 53 | Dead 是线程已经执行完 run 方法,或因异常错误导致退出的状态,该状态无法逆转,即无法回到就绪状态。 54 | 55 | 在计算机的线程处理过程当中,因为每个线程轮流占用 CPU 的计算资源,可能会出现某个线程尚未执行完就不得不中断的情况,容易导致线程不安全。例如,在服务端某个高并发业务共享某用户数据,首先 A 线程执行用户的查询任务,但是查询出来的数据还未返回就退出 CPU 时间片;然后后面进来的 B 线程抢占了 CPU 资源并覆盖了该用户数据,最后 A 线程重新执行,将 B 线程修改过后的数据返回给前端,导致页面出现数据异常。所以,为了保证线程安全,在多个线程并发地竞争共享资源时,通常采用同步机制协调各个线程的执行,确保得到正确的结果。 56 | 57 | 线程安全问题只在多线程环境下出现,单线程串行执行并不会存在此问题。为了保证高并发场景下的线程安全,可以从以下四个维度来探讨: 58 | 59 | (1)**数据单线程内可见**。单线程总是安全的。通过限制数据仅在单线程内可见,可以避免数据被其他线程篡改。最典型的就是线程局部变量,它存储在独立虚拟机栈帧的局部变量表中,与其他线程毫无瓜葛。ThreadLocal 就是采用这种方式来实现线程安全的。 60 | 61 | (2)**只读对象**。只读对象总是安全的。它的特性是允许复制、拒绝写入。最典型的只读对象有 String、Integer 等。一个对象想要拒绝任何写入,必须要满足以下条件:使用 final 关键字修饰类,避免被继承;使用 private final 关键字避免属性被中途修改;没有任何更新方法;返回值不能为可变对象。 62 | 63 | (3)**线程安全类**。某些线程安全类的内部有非常明确的线程安全机制。比如 StringBuffer 就是一个线程安全类,它采用 synchronized 关键字来修饰相关方法。 64 | 65 | (4)**同步与锁机制**。如果想要对某个对象进行并发更新操作,但又不属于上述三类,需要开发者在代码中自定义实现相关的安全同步机制。 66 | 67 | 线程安全的核心理念就是“**要不只读,要不加锁**”。JDK 提供的并发包,主要分成以下几个类族: 68 | 69 | (1)**线程同步类**。这些类使线程间的协调更加容易,支持了更加丰富的线程协调场景,逐步淘汰了使用 Object 的 wait() 和 notify() 进行同步的方式。主要代表有 CountDownLatch、Semaphore、CyclicBarrier 等。 70 | 71 | (2)**并发集合类**。集合并发操作的要求是执行速度快,提取数据准。最典型的莫过于 ConcurrentHashMap ,经过不断的优化,有刚开始的分段式锁到后来的 CAS ,不断的提高并发性能。除此之外,还有 ConcurrentSkipListMap 、 CopyOnWriteArrayList 、BlockingQueue 等。 72 | 73 | (3)**线程管理类**。虽然 Thread 和 ThreadLocal 在 JDK1.0 就已经引入,但是真正把 Thread 的作用发挥到极致的是线程池。根据实际场景的需要,提供了多种创建线程池的快捷方式,如使用 Executors 静态工厂或者使用 ThreadPoolExecutors 等。另外,通过 ScheduledExecutorService 来执行定时任务。 74 | 75 | (4)**锁相关类**。锁以 Lock 接口为核心,派生出一些实际场景中进行互斥操作的锁相关类。最有名的是 ReentrantLock 。 76 | 77 | 原文链接:https://zhuanlan.zhihu.com/p/488001949 78 | 79 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.442】后端开发—一文详解网络IO模型.md: -------------------------------------------------------------------------------- 1 | # 【NO.442】后端开发—一文详解网络IO模型 2 | 3 | ### 1.网络IO: 4 | 5 | 网络IO会涉及到两个系统对象 一个是 用户空间 调用 IO 的进程或者线程,另一个是 内核 6 | 7 | 空间的 内核系统, 比如 发生 IO 操作 read 时,它会经历两个阶段 8 | 9 | 1. 等待数据准备就绪 10 | 11 | 2. 将数据 从内核拷贝到进程 或者线程 中。 12 | 13 | ### 2.服务器模型 Reactor 与 Proactor 14 | 15 |    对高并发编程网络连接上的消息处理,可以分为两个阶段:等待消息准备好、消息 处理。当使用默认的阻塞套接字时(例如上面提到的 1 个线程捆绑处理 1 个连接),往往是把这两个阶段合而为一,这样操作套接字的代码所在的线程就得睡眠来等待消息准备好,这导致了高并发下线程会频繁的睡眠、唤醒,从而影响了 CPU 的使用效率。高并发编程方法当然就是把两个阶段分开处理。即,等待消息准备好的代码段,与处理消息的代码段是分离的。当然,这也要求套接字必须是非阻塞的,否则,处理消息的代码段很容易导致条件不满足时,所在线程又进入了睡眠等待阶段。那 么问题来了,等待消息准备好这个阶段怎么实现?它毕竟还是等待,这意味着线程还是要睡眠的!解决办法就是,线程主动查询,或者让 1 个线程为所有连接而等待!这就是 IO 多路复用了。多路复用就是处理  等待消息准备好这件事的,但它可以同时处理多个连接!它也可能 等待 ””,所以它也会导致线程睡眠,然而这不要紧,因为它一对多、它可以监控所有连接。这样,当我们的线程被唤醒执行时,就一定是有一些连接准备好被我们的代码执行了。 16 | 17 | 作为一个高性能服务器程序通常需要考虑处理三类事件: I/O 事件,定时事件及信号。两种 高效 的事件处理模型: Reactor 和 Proactor。 18 | 19 | ### 3.Reactor模型 20 | 21 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213160712_29194.jpg) 22 | 23 | Reactor 释义 反应堆 ””,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个 API 完成处理,而是恰恰相反, Reactor 逆置了事件处理流程,应用程序需要提供相应的接口并注册到 Reactor 上,如果相应的时间发生, Reactor 将主动调用应用程序注册的接口,这些接口又称为 回调函数 。 24 | 25 | Reactor 模式是处理并发I/O 比较常见的一种模式,用于同步I/O,中心思想是将所有要处理的I/O 事件注册到一个中心I/O 多路复用器上,同时主线程/进程阻塞在多路复用器上;一旦有I/O 事件到来或是准备就绪(文件描述符或socket 可读、写),多路复用器返回并将事先注册的相应I/O 事件分发到对应的处理器中。 26 | 27 | ### 4.Reactor 模型有三个重要的组件: 28 | 29 | 多路复用器:由操作系统提供,在linux 上一般是select, poll, epoll 等系统调用。 30 | 31 | 事件分发器:将多路复用器中返回的就绪事件分到对应的处理函数中。 32 | 33 | 事件处理器:负责处理特定事件的处理函数。 34 | 35 | ### 5.具体流程如下: 36 | 37 | 注册读就绪事件和相应的事件处理器; 38 | 39 | 事件分离器等待事件; 40 | 41 | 事件到来,激活分离器,分离器调用事件对应的处理器; 42 | 43 | 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权; 44 | 45 | ### 6.Reactor 模式是编写高性能网络服务器的必备技术之一,它具有如下的优点: 46 | 47 | 响应快;编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且 避免了多线程进程的切换开销可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源;可复用性, reactor 框架本身与具体事件处理逻辑无关,具有很高的复用性; 48 | 49 | ### 7.实际Reactor 模型可能是这样: 50 | 51 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213160712_74383.jpg) 52 | 53 | ### 8.Proactor模型 54 | 55 | 具体流程如下: 56 | 57 | 处理器发起异步操作,并关注I/O 完成事件 58 | 59 | 事件分离器等待操作完成事件 60 | 61 | 分离器等待过程中,内核并行执行实际的I/O 操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成I/O 完成后,通过事件分离器呼唤处理器事件处理器处理用户自定义缓冲区中的数据 62 | 63 |   我们可以发现proactor 模型最大的特点就是Proactor 最大的特点是使用异步I/O。所有的I/O 操作都交由系统提供的异步I/O 接口去执行。工作线程仅仅负责业务逻辑。在Proactor 中,用户函数启动一个异步的文件操作。同时将这个操作注册到多路复用器上。多路复用器并不关心文件是否可读或可写而是关心这个异步读操作是否完成。异步操作是操作系统完成,用户程序不需要关心。多路复用器等待直到有完成通知到来。当操作系统完成了读文件操作——将读到的数据复制到了用户先前提供的缓冲区之后,通知多路作系统完成了读文件操作——将读到的数据复制到了用户先前提供的缓冲区之后,通知多路复用器相关操作已完成。多路复用器再调用相应的处理程序,处理数据。 64 | 65 |     Proactor 增加了编程的复杂度,但给工作线程带来了更高的效率。Proactor 可以在系统态将读写优化,利用I/O 并行能力,提供一个高性能单线程模型。在windows 上,由于没有epoll 这样的机制,因此提供了IOCP 来支持高并发, 由于操作系统做了较好的优化,windows 较常采用Proactor 的模型利用完成端口来实现服务器。在linux 上,在2.6 内核出现了aio 接口,但aio 实际效果并不理想,它的出现,主要是解决poll 性能不佳的问题,但实际上经过测试,epoll 的性能高于poll+aio,并且aio 不能处理accept,因此linux 主要还是以Reactor 模型为主。 66 | 67 | ### 9.Libevent libev libuv 68 | 69 | libevent 70 | 71 | 名气最大,应用最广泛,历史悠久的跨平台事件库; 72 | 73 | libev : 74 | 75 | 较较libevent而言,设计更简练,性能更好,但对而言,设计更简练,性能更好,但对Windows支持不够好 76 | 77 | libuv : 78 | 79 | 开发nodenode的过程中需要一个跨平台的事件库,他们首选了的过程中需要一个跨平台的事件库,他们首选了libev,但又要支持Windows,故重新封装了一套,linux下用libev实现,Windows下用IOCP实现; 80 | 81 | ### 10.总结: 82 | 83 |   掌握了Reactor原理,为以后学习其他网络框架底层代码,打下了基础。 84 | 85 | 原文链接:https://zhuanlan.zhihu.com/p/489567205 86 | 87 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.446】后端开发【一大波干货知识】tcpip定时器与滑动窗口详解.md: -------------------------------------------------------------------------------- 1 | # 【NO.446】后端开发【一大波干货知识】tcp/ip定时器与滑动窗口详解 2 | 3 | ## 1.为什么udp有包长,而tcp没有包长。 4 | 5 | 首先,send()发送一次发送1k,发送一次缓冲区满了就会返回-1。2k发送出去后缓冲区被清空,send()才会被再次调用。最大传输片会打印四个包发送。而最大传输单元是在数据链路层对网卡的一些限制,如果mss大于mtu时候就会被分割,而mss小于mtu时候就会直接发送。所以udp需要包长,而tcp不需要因为mss+tcp包头就可以完成不需要包长。具体点说,就是帧需要,加上512,可以将包长进行计算出,这里不做重复的事减轻了很多后续工作。 6 | 7 | - 发送1m的文件 8 | - sendbuf 2k 9 | - mss(最大传输片)=512,mss是在包头的option中设置的。 10 | - mtu(最大传输单元)=1500 11 | 12 | ## 2.协议头分析 13 | 14 | - ACK表示确认 15 | - PSH表示应用程序发来数据赶紧处理呀宝贝 16 | - RST表示告诉对端你的数据不合法,重置 17 | - SYN表示同步头 18 | - FIN表示古德拜啦~断开前兆 19 | 20 | ## 3.慢启动的问题 21 | 22 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163738_92368.jpg) 23 | 24 | 对数增长慢启动后,线性增拥塞控制,最后是要快速重传。 25 | 26 | 进入弱网环境,rtt(数据包往返)出现很长时间,这就叫抖动。 27 | 28 | rtt=0.1rtt(new)+0.9rtt(old)这是一个消抖的过程,通信专业知识,有时间应该去学学一下研究通信啦!得出的值就可以判断是否超时。当然了,拥塞避免增长速度肯定是没有慢启动指数增长的快,但是面积大,面积就是传输的数量,所以拥塞避免是为了保证传输的量更加大。 29 | 30 | ## 4.如果接收端缓冲区buffer满了,但是没有丢失数据,还会发吗?会 31 | 32 | 就不会发了呀宝贝。回应上写的windows为0。服务端的recvbuf和回应客户端返回的window完全是两个不同的概念,但是两个值会一直接近。 33 | 34 | ## 5.如果服务器端recvbuf从无到有,如何告诉客户端我有空间了呢? 35 | 36 | 服务器主动告诉客户端,我的recvbuf不为0 37 | 38 | 客户端轮询 39 | 40 | 第一种服务器主动告诉客户端,优点是实时性比较好,但是缺点是发生丢包怎么办?将会陷入死锁的状态。如果做一个定时器会不会好一点?可是客户端关机了怎么办? 41 | 42 | **TCP的做法是什么呢? 43 | 44 | 当服务器recvbuf为0的时候,客户端主动轮训发送探测包,服务器的有空间吗?服务器被动的回。 45 | 46 | ** 47 | 48 | 如果服务器主动发送回复客户端,客户端不回服务器多次发送可以不?做法是可行的,但是违背了原则,毕竟服务器也不愿意当舔狗,主动向全世界客户端宣布爱你。 49 | 50 | ## 6.滑动窗口的运行机制 51 | 52 | 两个指针表示收尾,第三根指针作为成功接收否的标记指针,尾指针后面的接收的指针不处理。 53 | 54 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163738_30203.jpg) 55 | 56 | TCPkeepalive和应用层keepalive,应用层的keepalive可控性更强,而TCPkeepalive很笨,有点像小刘同学嘎嘎幸亏她不知道要不然肯定会打我,应用层你感知不到好难受。 57 | 58 | ## 7.问答环节 59 | 60 | Q1:tcp利用send函数发送数据发现数据丢失了,需要在应用层对丢包重传处理吗? 61 | 62 | 答:利用tcp传输数据,发送数据是一定会发送到对端网卡。如果你发生丢包,那要注意send()返回值是为-1,应用层处理业务逻辑有问题。往往错误都是一些低端的错误。 63 | 64 | Q2:滑动窗口的尾指针后面组织准备接受数据的状态,这是哪里在组织,操作系统吗? 65 | 66 | 答:是tcp协议组织的。 67 | 68 | Q3:如图,当客户端有多个数据包同时发送给服务器,发送中途状态服务器的recvbuf满了,服务器需要立刻通知客户端吗?还是要等一个阶段的数据发送的差不多了再告诉客户端有一部分数据未接收成功?目前看来,这个状况是服务器性能问题,所以不会进入快速重传状态对吧? 69 | 70 | 答:要发送多少,是接收端返回数据。接收端接收数据数据以后,会回ack,ack里面就会确定,还能接受大小。 71 | 72 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163739_22687.jpg) 73 | 74 | 原文链接:https://zhuanlan.zhihu.com/p/494470170 75 | 76 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.449】后端开发【一大波干货知识】—Redis,Memcached,Nginx网络组件.md: -------------------------------------------------------------------------------- 1 | # 【NO.449】后端开发【一大波干货知识】—Redis,Memcached,Nginx网络组件 2 | 3 | ## 1.reator网络编程 4 | 5 | epoll被称为事件管理器,利用管理器去管理多个连接。 6 | 7 | ``` 8 | int clientfd=accept(listenfd,addr,sz); 9 | clientfd ==-1 && erro==EWOLDBLOCK //表示全连接中连接为空 10 | int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); 11 | error == EINPROGRESS //正在建立连接 12 | error == EISCONN //连接建立成功 13 | ``` 14 | 15 | - 关闭读端 read = 0 16 | - 关闭写端 write = -1 && errno =EPIPE 17 | 18 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163416_21695.jpg) 19 | 20 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163417_68931.jpg) 21 | 22 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163418_80973.jpg) 23 | 24 | - io函数像read只能检测一个fd对应的状态,可以检测具体的状态。 25 | - io多路复用可以检测多个fd对应的状态,只能检测可读,可写,错误,断开笼统等信息。 26 | - getsockopt 也可以检测错误。 27 | 28 | ### 2.阻塞IO 和 非阻塞IO 29 | 30 | - 阻塞在网络线程 31 | - 连接的fd阻塞属性决定了io函数是否阻塞 32 | - 具体差异在:io函数在数据未到达时是否立刻返回。 33 | 34 | ``` 35 | //默认情况下,fd时阻塞的,设置非阻塞的方法如下: 36 | int flag=fcntl(fd,F_GETFL,0); 37 | fcntl(fd,F_SETFL,flag | O_NONBLOCK); 38 | ``` 39 | 40 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163418_42728.jpg) 41 | 42 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163419_88279.jpg) 43 | 44 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163420_39848.jpg) 45 | 46 | - timeout == 0 是非阻塞效果,检测一下立即返回。 47 | - timeout == -1 是永久阻塞 48 | - timeout == 1000 49 | 50 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163420_99925.jpg) 51 | 52 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163422_19459.jpg) 53 | 54 | epoll_create 会去创建红黑树和就绪队列。 55 | 56 | epoll_ctl 会去注册事件,会建立回调关系。当事件被触发,epoll_ctl 会将fd从红黑树中放到就绪队列。 57 | 58 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163422_58852.jpg) 59 | 60 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163423_87320.png) 61 | 62 | 问:代码第9行能不能监听写事件? 63 | 64 | 答:不能,因为刚开始的时候,写缓冲区是空的,会被一直触发可写。 65 | 66 | ### 3.编程细节,返回值以及错误码 67 | 68 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163424_77964.jpg) 69 | 70 | 读端关闭了。 71 | 72 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163424_39067.jpg) 73 | 74 | 建议read()函数使用非阻塞io,因为出现错误会立刻返回,不会卡在这里影响别人。 75 | 76 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163425_67893.jpg) 77 | 78 | 将数据写到缓冲区,协议栈会将数据发送到对端。 79 | 80 | ## 4.redis、nginx、memcached reactor具体使用 81 | 82 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163426_74357.jpg) 83 | 84 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163427_45667.jpg) 85 | 86 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163427_92045.jpg) 87 | 88 | redis-6.0支持IO多线程,封装在networking.cz中。 89 | 90 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/13/20221213163428_84292.jpg) 91 | 92 | 原文链接:https://zhuanlan.zhihu.com/p/493857761 93 | 94 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.44】浅谈NIO和Epoll实现原理.md: -------------------------------------------------------------------------------- 1 | # 【NO.44】浅谈NIO和Epoll实现原理 2 | 3 | ## **1 前置知识** 4 | 5 | ### **1.1 socket是什么?** 6 | 7 | 就是传输控制层tcp连接通信。 8 | 9 | ### **1.2 fd是什么?** 10 | 11 | fd既file descriptor-文件描述符。Java中用对象代表输入输出流等...在Linux系统中不是面向对象的,是一切皆文件的,拿文件来代表输入输出流。其实是一个数值,例如1,2,3,4...0是标准输入,1是标准输出,2是错误输出... 12 | 13 | 任何进程在操作系统中都有自己IO对应的文件描述符,参考如下: 14 | 15 | ```text 16 | [root@iZj6c1dib207c054itjn30Z ~]# ps -ef | grep redis 17 | root 20688 1 0 Nov23 ? 00:01:01 /opt/redis/bin/redis-server 127.0.0.1:6379 18 | root 20786 1 0 Nov23 ? 00:01:00 /opt/redis/bin/redis-server 127.0.0.1:6380 19 | root 30741 30719 0 12:26 pts/1 00:00:00 grep --color=auto redis 20 | [root@iZj6c1dib207c054itjn30Z ~]# cd /proc/20688/fd 21 | [root@iZj6c1dib207c054itjn30Z fd]# ll 22 | total 0 23 | lrwx------ 1 root root 64 Nov 23 21:50 0 -> /dev/null 24 | lrwx------ 1 root root 64 Nov 23 21:50 1 -> /dev/null 25 | lrwx------ 1 root root 64 Nov 23 21:50 2 -> /dev/null 26 | lr-x------ 1 root root 64 Nov 23 21:50 3 -> pipe:[27847377] 27 | l-wx------ 1 root root 64 Nov 23 21:50 4 -> pipe:[27847377] 28 | lrwx------ 1 root root 64 Nov 23 21:50 5 -> anon_inode:[eventpoll] 29 | lrwx------ 1 root root 64 Nov 23 21:50 6 -> socket:[27847384] 30 | ``` 31 | 32 | ## **2 从BIO到epoll** 33 | 34 | ### **2.1 BIO** 35 | 36 | 计算机有内核,客户端和内核连接产生fd,计算机的进程或线程会读取相应的fd。 37 | 因为socket在这个时期是blocking的,线程读取socket产生的文件描述符时,如果数据包没到,读取命令不能返回,会被阻塞。导致会有更多的线程被抛出,单位CPU在某一时间片只能处理一个线程,出现数据包到了的线程等着数据包没到的线程的情况,造成CPU资源浪费,CPU切换线程成本巨大。 38 | 例如早期Tomcat7.0版默认就是BIO。 39 | 40 | ![img](https://pic1.zhimg.com/80/v2-dbb0aa89b63d7608ed3e6ce233b2dd60_720w.webp) 41 | 42 | ### **2.2 早期NIO** 43 | 44 | socket对应的fd是非阻塞的 45 | 单位CPU只用一个线程,就一颗CPU只跑一个线程,没有CPU切换损耗,在用户空间轮询遍历所有的文件描述符。从遍历到取数据都是自己完成,所以是同步非阻塞IO。 46 | 问题是如果有1000个fd,代表用户进程轮询调用1000次kernel, 47 | 用户空间查询一次文件描述符就得调用一次系统调用,让后内核态用户态来回切换成本很大。 48 | 49 | ![img](https://pic1.zhimg.com/80/v2-71a08825a2f4b3b9056b8e77d0389efc_720w.webp) 50 | 51 | ### **2.3 多路复用NIO** 52 | 53 | 内核向前发展,轮询放到内核里,内核增加一个系统调用select函数,统一把一千个fd传给select函数,内核操作select函数,fd准备好后返回给线程,拿着返回的文件描述符找出ready的再去调read。如果1000个fd只有1个有数据,以前要调1000次,现在只调1次,相对前面在系统调用上更加精准。还是同步非阻塞的,只是减少了用户态和内核态的切换。 54 | 55 | 56 | 注意Linux只能实现NIO,不能实现AIO。 57 | 问题:用户态和内核态沟通的fd相关数据要来回拷贝。 58 | 59 | ![img](https://pic3.zhimg.com/80/v2-75a2e0d3d06804b35b3bc49b1cd73ea6_720w.webp) 60 | 61 | ### **2.4 epoll** 62 | 63 | 内核通过mmap实现共享空间,用户态和内核态有一个空间是共享的,文件描述符fd存在共享空间实现用户态和内核态共享。epoll里面有三个调用,用户空间先epoll_create准备一个共享空间mmap,里面维护一个红黑树,内核态将连接注册进红黑树,epoll_ctl写入。当有数据准备好了,调用epoll_wait中断阻塞,取链表fd,再单独调用read。 64 | mmap应用:kafka实现数据通过socket存到服务器文件上的过程也是mmap。 65 | 66 | 67 | epoll应用:redis和nginx的worker进程等等。 68 | 69 | ![img](https://pic1.zhimg.com/80/v2-4939e2fc0d43fbdf3a3df57e7b36f068_720w.webp) 70 | 71 | 原文链接:https://zhuanlan.zhihu.com/p/482549633 72 | 73 | 原文作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.450】手写实现分布式锁.md: -------------------------------------------------------------------------------- 1 | # 【NO.450】手写实现分布式锁 2 | 3 | 前言 4 | 5 | 分布式锁需要考虑很多事情,第一网络是否正常,第二个提供分布式锁这台机器的高可用性。 6 | 7 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215165657_60281.jpg) 8 | 9 | ## 1.网络模块封装 10 | 11 | ### 1.1.明线 12 | 13 | #### 1.1.1io检测部分的接口: 14 | 15 | - 添加读事件是因为当有客户端连接事件来临时,我们好去处理这个事件。 16 | - 写事件这是因为当我们服务端作为客户端去连接第三方服务时候,需要注册写事件。 17 | - 删除事件就比如关闭连接时,我们需要将事件从检测模块中删除。 18 | - 修改事件就比如当客户端发来事件我们需要检测读事件,但是接收失败了我们要把读事件修改为注册写事件进行反馈。 19 | - 检测事件肯定是必须的啦!io操作: 20 | 绑定监听,接收连接,建立连接,关闭fd,读,写,fd属性。 21 | 22 | ### 1.2.暗线 23 | 24 | 需要注意的是:fd类型中clientfd写事件触发的情况,服务器收到客户端的数据可以直接调用write()函数,**只有当写入失败时才要注册写事件,检测何时可写。**应该将发送失败的数据缓存起来,等事件可写了,再将缓冲区的数据进行发送。 25 | 断开连接或者错误,都是交给读写操作。 26 | 27 | ## 2.协程调度 28 | 29 | 没有协程之前,当成功接收到一个数据后,要调用多个回调函数,(解析数据、查库拿数据、返回给客户端)等等。现在我们考虑的是,将三个序列利用协程的方式进行粘合,三个回调变成一个协程回调执行序列。 30 | 为每一个fd执行一个序列,每个协程是一个执行序列。 31 | lua虚拟机不支持协程,并且没有类似于pthread_create()函数,也就是说主协程被自动创建。**lua虚拟机同时只能有一个协程在运行。**主协程负责调度其他协程。 32 | 主协程会进行事件检测,不断地从epoll中去取事件,根据事件去唤醒其他协程。 33 | 34 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215165658_75077.jpg) 35 | 36 | - lua和go语言中的协程方案是最完善的。 37 | - epoll注册写事件触发,说明三次握手后同步包可以发送了,表示连接已经建立成功了。 38 | - 异步的执行逻辑,同步的写法。 39 | - 当连接建立成功后,connectfd和clientfd的流程变成一样。 40 | 41 | ## 3.异步连接池 42 | 43 | ### 3.1.为什么需要异步连接池? 44 | 45 | 现在所有的连接已经变成一个执行序列,连接由异步变为同步。此时,一个连接同时只能在一个协程中运行,是并发不是并行,也就是说只有一个执行序多个对列,依次执行。 46 | pool_size是尺子最大连接数,backlog堆积的操作数。 47 | 48 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215165658_16897.jpg) 49 | 50 | ## 4.缓冲池设计 用户读写缓冲区 51 | 52 | Mark老师举例,4个协程在使用,4个协程在等待,超出的协程会报错。cache记录的是随时可用连接,free记录的是正在使用的连接。connect>8是要报错,小于pool_size要创建或是从cache里去取,cache没有说明都在free里。 53 | 给连接池起名字,默认是ip:端口格式,比如127.0.0.1:8888。 54 | 55 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215165659_27152.jpg) 56 | 57 | ## 5.定时器设计 58 | 59 | **定时器是在lua层实现了一个最小堆,每一个任务生成一个协程,但是要考虑回收协程,尤其是在删除的时候。**这块做的还不完善,Mark老师也希望大家一起帮忙搞一搞,先记下这件事吧! 60 | 61 | ## 6.总结 62 | 63 | Mrk老师说道:做任何事情,拆分的思想很重要,联想起以前一个数学老师的话,“老太太吃柿子,要捡软的捏。”以后遇到问题和困难,也应该先按照这个思路去处理问题。作为开发还有一点是非常重的,就是**测试**的能力,这方面得想办法学习提升一下。通过本节课,我初步了解了分布式锁,感觉对于skynet也有了更好的认识。在成长的路上,有Mark老师陪伴,好幸福啊~ 64 | 65 | 原文链接:https://zhuanlan.zhihu.com/p/495651179 66 | 67 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.457】用WinDbg断点调试FFmpeg.md: -------------------------------------------------------------------------------- 1 | # 【NO.457】用WinDbg断点调试FFmpeg 2 | 3 | 本文主要讲解 WinDbg 调试器的使用。WinDbg 在 Windows 里面的地位,就跟 GDB 在 Linux 的地位一样。可以通过 微软的官方网站 下载 安装 WinDbg。 4 | 5 | WinDbg 是比较轻量级的调试工具,在一些场景下比较实用,例如不方便安装 vs2019。 6 | 7 | 只要有 符号信息表(symbols) 跟 调试信息表(debug info),一样能用 WinDbg 进行 源码级调试 跟 各种断点调试。这些信息,windows 是放在 pdb 文件里面的,在源码目录,可以看到 ffmpeg_g.pdb 这个文件。 8 | 9 | FFmpeg 的编译过程跟 前面文章 《用msys2与msvc编译FFmpeg 》一样的,都是使用 msys2 + msvc。请按照之前的教程编译出来 ffmpeg.exe 文件,如果已经编译出来就可以直接用之前的 ffmpeg.exe。 10 | 11 | ![img](https://pic4.zhimg.com/80/v2-b05e74c959ab3fef0175cf1bb5cfedab_720w.webp) 12 | 13 | WinDbg 有 32 位跟64位,我们的 ffmpeg.exe 是 64 位的,所以选用 WinDbg 64位来调试。如下: 14 | 15 | ![img](https://pic4.zhimg.com/80/v2-9f76a503925633095ed906d9057bbd03_720w.webp) 16 | 17 | 打开 windbg.exe ,界面如下: 18 | 19 | ![img](https://pic2.zhimg.com/80/v2-9c42a0f6de1b828180ea6fec7b8dd93d_720w.webp) 20 | 21 | 然后点击 菜单栏的 File → Open Executable,会弹出窗口,如下: 22 | 23 | ![img](https://pic3.zhimg.com/80/v2-a38e871146e272a6b1b9ad45da9e585e_720w.webp) 24 | 25 | 上图中 设置了 Arguments 参数 跟工作目录,参数如下: 26 | 27 | Arguments :-i walking-dead.mp4 -c copy walking-dead.flv -y 28 | 29 | Start directory:C:\msys64\home\loken\ffmpeg\build64\ffmepg-4.4-msvc 30 | 31 | walking-dead.mp4 是本书经常用到的视频素材,请下载保存好。 32 | 33 | 打开之后,界面如下: 34 | 35 | ![img](https://pic3.zhimg.com/80/v2-eccb1d758c5297d383251388c03b9962_720w.webp) 36 | 37 | 这里简单讲解一些 WinDbg 的界面,底部是命令输入框,上图中,我输入了一个 k, 查看当前断点的调用栈。 38 | 39 | 可以看到,WinDbg 会默认停在 ntdll 模块 的 LdrpDoDebuggerBreak 函数,这是 WinDbg 的默认断点,现在还没有跑进去 ffmpeg.exe 的main函数,所以我们需要加一个断点,如下: 40 | 41 | ```text 42 | # 设置断点 43 | bu ffmpeg_g!main 44 | # 继续执行 45 | g 46 | ``` 47 | 48 | 注意,是 ffmpeg_g ,后面有个 _g 。 设置完断点之后,再敲入一个命令 g,g 代表 go。代码就会执行到 main 那里停下来。如下: 49 | 50 | ![img](https://pic1.zhimg.com/80/v2-2985ec40e7acdf455683d470eaa67bfc_720w.webp) 51 | 52 | 现在讲一下 WinDbg 常用的一些命令。 53 | 54 | 1,k :查看函数调用栈。 55 | 56 | 2,bu :根据符号进行断点,例如 bu ffmpeg_g.exe!main ,前面要有模块名,跟gdb 有点不一样。 57 | 58 | 3,bl:查看所有断点。 59 | 60 | 4,p:单步步进。 61 | 62 | 5,g:代码继续执行,go 的意思,快捷键 F5 63 | 64 | WinDbg 的更多命令,请看 《微软WinDbg 文档》,《WinDbg 调试器文档》 。 65 | 66 | ------ 67 | 68 | WinDbg 还有更多的调试窗口可以调出来,这些窗口都在菜单栏的 View 里面,这里简单介绍一下这些窗口。 69 | 70 | 1,Watch,观察窗口。点击可以添加自己想观察的全局变量或者局部变量。 71 | 72 | 2,Locals,局部变量窗口,运行到某个函数,这个窗口就是显示这个函数的局部变量信息。 73 | 74 | 3,Registers,寄存器窗口。 75 | 76 | 4,Memory,内存窗口。 77 | 78 | 5,Call Stack ,函数调用栈。 79 | 80 | 6,Disassembly,汇编代码窗口。 81 | 82 | 下面我把一些窗口调出来看看效果,如下: 83 | 84 | ![img](https://pic4.zhimg.com/80/v2-7c84aa515b3d4ebde2690de196a2daef_720w.webp) 85 | 86 | 调试器的功能都是类似的,常用的功能无非就是 数据断点,函数断点,然后可以观察变量之类的。 87 | 88 | WinDbg 这个调试器的具体架构实现跟用法,墙裂推荐 《软件调试》卷二 windows 下册第30章 ,通过这本书,你可以了解到如何实现一个调试器。 89 | 90 | 参考资料: 91 | 92 | 1,《软件调试》卷二 windows 下册第30章 - 张银奎 93 | 94 | 原文作者:零声音视频开发 95 | 96 | 原文链接:https://zhuanlan.zhihu.com/p/529621196 -------------------------------------------------------------------------------- /【NO.458】FFplay源码分析-nobuffer.md: -------------------------------------------------------------------------------- 1 | # 【NO.458】FFplay源码分析-nobuffer 2 | 3 | 在使用 FFplay 播放 RTMP 流的时候,如果 不开启 nobuffer 选项,画面延迟会高达 7 秒左右,开启了,局域网延迟可降低到100毫秒左右。 4 | 5 | 因此本文主要研究 nobuffer 的具体实现,以及播放端 缓存 7 秒的数据有何作用。 6 | 7 | fflags 的定义在 libavformat/options_table.h,如下图代码,这是一个通用选项,所有的 解复用器都有这个选项。 8 | 9 | ![img](https://pic1.zhimg.com/80/v2-d0ecf694be9557a0f4f2f50a7a6d57fc_720w.webp) 10 | 11 | 也就是说,这个命令行参数,是在调 avformat_open_input() 函数的时候丢进去的,我为什么知道是在这个地方丢进去的?请看之前的专栏《FFplay源码分析》,所有的解复用参数,也叫格式参数,都是在 avformat_open_input() 丢进去的。可以看下图证明一下: 12 | 13 | ![img](https://pic4.zhimg.com/80/v2-6b4c1e0d67bb35945a555c72eea3b99b_720w.webp) 14 | 15 | 记得改 Clion 的调试参数,把 `-fflags nobuffer` 加上去。 16 | 17 | ------ 18 | 19 | 在 `avformat_open_input()` 函数内部,会把 `fflags` 这个 AVOption 丢给 AVClass,如下图所示,AVClass 里面存储了好几个 AVOption ,fflags 这个 AVOption 的下标是 5 ,前面的是 默认的选项,自动加进去的。 20 | 21 | ![img](https://pic1.zhimg.com/80/v2-235344190b78c850b0d5fbdef4b38750_720w.webp) 22 | 23 | 注意 ,`av_opt_set_dict()` 这个函数会改变 `tmp` 的值,把能用 选项应用之后剔除。 24 | 25 | 26 | 27 | https://link.zhihu.com/?target=https%3A//docs.qq.com/doc/DYXlQZVZoRkRBRlFk) 28 | 29 | 30 | 31 | ------ 32 | 33 | `nobuffer` 这个参数 传递过程中的函数调用有点长,推荐看之前的《[FFmpeg源码分析-参数解析篇](https://link.zhihu.com/?target=https%3A//www.xianwaizhiyin.net/%3Fcat%3D14)》,原理类似,我知道这个 参数最后会赋值到 `struct AVFormatContext` 的 `flags` 字段里面,如下图: 34 | 35 | ![img](https://pic2.zhimg.com/80/v2-fc9816793f1a3a3d0c8443347118cc99_720w.webp) 36 | 37 | 如上图所示,所以 `avformat_open_input()` 函数执行完之后 `AVFormatContext::flags` 的第7位应该会被置为1,因为 0x40 的二进制是 1000000。请看下图: 38 | 39 | ![img](https://pic1.zhimg.com/80/v2-5ea510ed6cca9e7c93220dc1b653988c_720w.webp) 40 | 41 | 从上图可以看出, ic->flags 直接就是 64 ,也就是 16 进制的 0x40。所以上面的分析没错。 avformat_open_input() 函数只是把 命令行参数解析 到这个 flags 字段,但是真正使用这个字段 是在 avformat_find_stream_info() 里面。直接搜 AVFMT_FLAG_NOBUFFER 就能找到使用的位置。 42 | 43 | avformat_find_stream_info() 函数的内部逻辑实际上非常复杂,我直接讲重点代码,如下: 44 | 45 | ```text 46 | if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) { 47 | ret = avpriv_packet_list_put(&ic->internal->packet_buffer, 48 | &ic->internal->packet_buffer_end, 49 | pkt1, NULL, 0); 50 | if (ret < 0) 51 | goto unref_then_goto_end; 52 | 53 | pkt = &ic->internal->packet_buffer_end->pkt; 54 | } else { 55 | pkt = pkt1; 56 | } 57 | ``` 58 | 59 | AVFMT_FLAG_NOBUFFER 标记 如果没设置,就会导致 探测的数据包丢进去队列,我们知道 avformat_find_stream_info() 会先读一段数据包分析出流是什么编码器之类的,为了重用这个 探测的数据包,这里就会丢进去队列,播放的时候,就从这些数据包开始,但是整个探测过程,长达 5秒,也就是 ffplay 大概会读 5秒的数据,来分析输入流的情况。如果开启 nobuffer,就不会重用这些探测数据,ffpaly 探测完输入流之后,就会重新读取新的数据包来播放。不用缓存的,所以延迟就低了。 60 | 61 | 如下图,我在 ffpaly.c 的 avformat_find_stream_info() 前后输出了个时间,正好相差5秒。 62 | 63 | ```text 64 | double start_time3 = av_gettime_relative() / 1000000.0; 65 | if (find_stream_info) { 66 | ...省略代码... 67 | err = avformat_find_stream_info(ic, opts); 68 | ...省略代码... 69 | } 70 | double end_time3 = av_gettime_relative() / 1000000.0; 71 | printf("start is %f , end is %f \r\n",start_time3,end_time3); 72 | ``` 73 | 74 | ![img](https://pic4.zhimg.com/80/v2-aa55afd51e3011dd6d7fa7e947bd7703_720w.webp) 75 | 76 | 所以实际上 ffplay 在 实时的场景下,缓存是个鸡肋,本来这个 buffer 功能是为了分析本地文件,避免重复读取,但是影响到了实时的场景。实时场景必须把 buffer 关掉。 77 | 78 | 补充,因为我是虚拟机做服务器,所以都是本机通信,不启用 buffer 也能很流畅,但是如果我把 SRS 部署在局域网另一台机器,不开启 buffer ,视频有卡顿,估计是还没来得及解码丢进去队列,所以 ffplay 不断丢弃视频帧。因为视频比音频慢了,得丢弃。 79 | 80 | 原文作者:零声音视频开发 81 | 82 | 原文链接:https://zhuanlan.zhihu.com/p/492176676 -------------------------------------------------------------------------------- /【NO.464】FFMPEG 之 AVDevice.md: -------------------------------------------------------------------------------- 1 | # 【NO.464】FFMPEG 之 AVDevice 2 | 3 | ## 1.综述 4 | 5 | 在使用FFMPEG 作为编码时,可以采用FFMPEG 采集本地的音视频设备的数据,然后进行编码,封装,传输等操作。 在不同的平台上,音视频数据采集驱动并不一致。 在linux 平台上有fbdex,v4l2,x11grab ,在ios 上有avfoundation. 在window 平台上有dshow,vfwcap.gdigrab。 6 | 7 | ![img](https://pic4.zhimg.com/80/v2-3880ea1dc2ebf76fe33b0f144d4ccf83_720w.webp) 8 | 9 | ## 2.使用流程 10 | 11 | 对于FFMPEG 来说,device 也是一个文件, 在使用device 进行录屏,摄像时, 与普通播放一个片源差不多。 就初始化处稍有点区别。 12 | 13 | 播放普通片源,初始化方式: 14 | 15 | ```text 16 | AVFormatContext *pFormatCtx = avformat_alloc_context(); 17 | avformat_open_input(&pFormatCtx, "test.h265",NULL,NULL); 18 | ``` 19 | 20 | 而使用device 去录屏时: 21 | 22 | ```text 23 | AVFormatContext *pFormatCtx = avformat_alloc_context(); 24 | AVInputFormat *ifmt=av_find_input_format("vfwcap"); 25 | avformat_open_input(&pFormatCtx, 0, ifmt,NULL); 26 | ``` 27 | 28 | 可以看出录屏与播放普通视频,就多一个av_find_input_forma() 的调用。 29 | 30 | 此处仅讲关于FFMPEG 有哪些API , 关于如何使用FFMPEG 操作device 录像,录屏请参考雷神csdn ,写的非常详细。 31 | 32 | ## 3.AVDevice 相关API 33 | 34 | 如下是AVDevice 库开出的API , 很清晰, 比如查当前平台有哪些可用的device, 可用avdevice_list_devices() 35 | 36 | ![img](https://pic2.zhimg.com/80/v2-162eeda0c10a15856115719e8f0e5371_720w.webp) 37 | 38 | 原文作者:零声音视频开发 39 | 40 | 原文链接:https://zhuanlan.zhihu.com/p/451806536 -------------------------------------------------------------------------------- /【NO.466】【网络通信 -- WebRTC】WebRTC 基础知识 -- 基础知识总结【1】WebRTC 简介.md: -------------------------------------------------------------------------------- 1 | # 【NO.466】【网络通信 -- WebRTC】WebRTC 基础知识 -- 基础知识总结【1】WebRTC 简介 2 | 3 | ## 1.WebRTC 简介 4 | 5 | WebRTC (Web Real­Time Communication,网页实时通信) 是 Google 于 2010 以 6829 万美元从 Global IP Solutions 公司购买,并于 2011 年将其开源,旨在建立一个互联网浏览器间的实时通信的平台,让 WebRTC 技术成为 H5 标准之一; 6 | 7 | WebRTC 是一个基于浏览器的实时多媒体通信技术,该项技术旨在使 Web 浏览器具备实时通信能力;同时,通过将这些能力封装并以 JavaScript API 的方式开放给 Web 应用开发人员,使得 Web 应用开发人员能够通过 HTML 标签和 JavaScript API 快速地开发出基于 Web 浏览器的实时音视频应用,而无需依赖任何第三方插件; 8 | 9 | WebRTC 由 IETF (Internet Engineering Task Force,互联网工程任务组) 和 W3C (World Wide Web Consortium,万维网联盟) 联合负责其标准化工作; 10 | 11 | - IETF 定制 WebRTC 的互联网基础协议标准,该标准也被称为 RTC Web (Real-Time Communication in Web-browsers) 12 | - W3C 则负责定制 WebRTC 的客户端 JavaScript API 接口的标准 13 | 14 | 文章福利:[分享一个每晚8-10点都有的免费技术直播的链接,订阅即可学习~宝藏地址!!](https://link.zhihu.com/?target=https%3A//ke.qq.com/course/3202131%3FflowToken%3D1040743) 15 | 16 | 下面收藏的一些视频可以加我群领取 17 | 18 | ![img](https://pic4.zhimg.com/80/v2-f1fddd71350ab3261823c3defb1d3263_720w.webp) 19 | 20 | ## 2.WebRTC 框架简介 21 | 22 | **WebRTC 整体框架图示** 23 | 24 | ![img](https://pic4.zhimg.com/80/v2-2a02ae4a6a0df5193bcde0f64f3b723b_720w.webp) 25 | 26 | ### 2.1 Your Web App 27 | 28 | Web 开发者开发的程序,Web 开发者可以基于集成 WebRTC 的浏览器提供的 Web API 开发基于视频、音频的实时通信应用 29 | 30 | ### 2.2 Web API 31 | 32 | 面向第三方开发者的 WebRTC 标准 API(Javascript),使开发者能够容易地开发出类似于网络视频聊天的 Web 应用,这些 API 提供三个功能接口,分别是 MediaStream、RTCPeerConnection 和 RTCDataChannel; 33 | 34 | MediaStream 接口用于捕获和存储客户端的实时音视频流,便于客户端进行音视频采集和渲染 35 | 36 | RTCPeerConnection 接口是 WebRTC 的核心接口,封装了 WebRTC 连接的管理,负责 WebRTC 连接机制的接口 37 | 38 | RTCDataChannel 接口是进行 WebRTC 连接数据传输的数据通道接口 39 | 40 | ### 2.3 WebRTC Native C++ API 41 | 42 | 本地 C++ API 提供给浏览器厂商、平台 SDK 开发者使用的 C++ API,不同的平台可以通过各自的 C++ 接口调用能力,对其进行上层封装以满足跨平台的需求 43 | 44 | Session management/Abstract signaling 45 | 46 | 会话管理 / 抽象信令,WebRTC 的会话层,主要用于进行信令交互和管理 RTCPeerConnection 的连接状态 47 | 48 | ### 2.4 VoiceEngine 49 | 50 | 音频处理引擎,包含一系列音频多媒体处理的框架,包括 Audio Codecs、NetEQ for voice、Acoustic Echo Canceller (AEC) 和 Noise Reduction (NR) 51 | 52 | Audio Codecs 是音频编解码器,当前 WebRTC 支持 ilbc、isac、G711、G722 和 opus 等 53 | 54 | NetEQ for voice 是自适应抖动控制算法以及语音包丢失隐藏算法,用于适应不断变化的网络环境,从而保持尽可能低的延迟,同时保持最高的语音质量 55 | 56 | AEC 是回声消除器,用于实时消除麦克风采集到的回声 57 | 58 | NR 是噪声抑制器,用于消除与相关 VoIP 的某些类型的背景噪音 59 | 60 | ### 2.5 VideoEngine 61 | 62 | 视频处理引擎,包含一系列视频处理的整体框架,从摄像头采集视频到视频信息网络传输再到视频显示整个完整过程的解决方案,包括 Video Codec、Video Jitter Buffer 和 Image Enhancement 63 | 64 | Video Codec 是视频编解码器,当前 WebRTC 支持 VP8、VP9 和 H.264 编解码; 65 | 66 | Video Jitter Buffer 是视频抖动缓冲器,用于降低由于视频抖动和视频信息包丢失带来的不良影响; 67 | 68 | Image Enhancement 是图像质量增强模块,用于对摄像头采集回来的图像进行处理,包括明暗度检测、颜色增强、降噪处理等; 69 | 70 | ### 2.6 Transport 71 | 72 | 数据传输模块,WebRTC 对音视频进行 P2P 传输的核心模块,包括 SRTP、Multiplexing 和 P2P; 73 | 74 | SRTP 是基于 UDP 的安全实时传输协议,为 WebRTC 中音视频数据提供安全单播和多播功能 75 | 76 | Multiplexing 是多路复用技术,采用多路复用技术能把多个信号组合在一条物理信道上进行传输,减少对传输线路的数量消耗 77 | 78 | P2P 是端对端传输技术,WebRTC 的 P2P 技术集成了 STUN、TURN 和 ICE,这些都是针对 UDP 的 NAT 的防火墙穿越方法,是连接有效性的保障 79 | 80 | ## 3.WebRTC 一对一通话 81 | 82 | **一对一通话结构图示** 83 | 84 | ![img](https://pic1.zhimg.com/80/v2-42f00c6d92e3de214b7ceb310bc08610_720w.webp) 85 | 86 | - 两个 WebRTC 终端,负责音视频采集、编解码、NAT 穿越、音视频数据传输; 87 | - 一个 Signal(信令)服务器,负责信令处理,如加入房间、离开房间、媒体协商消息的传递等; 88 | - 一个 STUN/TURN 服务器,负责获取 WebRTC 终端在公网的 IP 地址,以及 NAT 穿越失败后的数据中转; 89 | 90 | ### 3.1 **一对一通话流程图示** 91 | 92 | ![img](https://pic2.zhimg.com/80/v2-bc16270cf4fe167a950e3efa15bf8311_720w.webp) 93 | 94 | 原文作者:零声音视频开发 95 | 96 | 原文链接:https://zhuanlan.zhihu.com/p/445233381 -------------------------------------------------------------------------------- /【NO.467】神器 ffmpeg——操作视频,极度舒适.md: -------------------------------------------------------------------------------- 1 | # 【NO.467】神器 ffmpeg——操作视频,极度舒适 2 | 3 | 现在短视频很流行,有很多视频编辑软件,功能丰富,而我们需要的只是裁剪功能,而且需要用编程的方式调用,那么最合适的莫过于 **ffmpeg[1]** 了。 4 | 5 | ffmpeg 是一个命令行工具,功能强大,可以编程调用。 6 | 7 | 从 ffmpeg 官网上下载对应操作系统的版本,我下的是 **Windows 版[2]**。 8 | 9 | 下载后解压到一个目录,然后将目录下的 bin,配置到环境变量里。然后打开一个命令行,输入: 10 | 11 | ```text 12 | > ffmpeg -version 13 | ffmpeg version 2021-10-07-git-b6aeee2d8b-full_build- ... 14 | ``` 15 | 16 | 测试一下,能显示出版本信息,说明配置好了。 17 | 18 | 现在读一下文档,发现拆分视频文件的命令是: 19 | 20 | ```text 21 | ffmpeg -i [filename] -ss [starttime] -t [length] -c copy [newfilename] 22 | ``` 23 | 24 | - i 为需要裁剪的文件 25 | - ss 为裁剪开始时间 26 | - t 为裁剪结束时间或者长度 27 | - c 为裁剪好的文件存放 28 | 29 | 好了,用 Python 写一个调用: 30 | 31 | ```text 32 | import subprocess as sp 33 | 34 | def cut_video(filename, outfile, start, length=90): 35 | cmd = "ffmpeg -i %s -ss %d -t %d -c copy %s" % (filename, start, length, outfile) 36 | p = sp.Popen(cmd, shell=True) 37 | p.wait() 38 | return 39 | ``` 40 | 41 | - 定义了一个函数,通过参数传入 ffmpeg 需要的信息 42 | - 将裁剪命令写成一个字符串模板,将参数替换到其中 43 | - 用 subprocess 的 Popen 执行命令,其中参数 shell=True 表示将命令作为一个整体执行 44 | - p.wait() 很重要,因为裁剪需要一会儿,而且是另起进程执行的,所以需要等执行完成再做后续工作,否则可能找不到裁剪好的文件 45 | 46 | 这样视频裁剪工作就完成了,然后再看看什么是最重要的。 47 | 48 | ## 1.计算分段 49 | 50 | 视频裁剪时,需要一些参数,特别是开始时间,如何确定呢?如果这件事做不好,裁剪工作就很麻烦。 51 | 52 | 所以看看如何计算裁剪分段。 53 | 54 | 我需要将视频裁剪成一分半的小段,那么将需要知道目标视频文件的时间长度。 55 | 56 | ## 2.获取视频长度 57 | 58 | 如何获得长度呢?ffmpeg 提供了另一个命令 —— ffprobe。 59 | 60 | 找了一下,可以合成一个命令来获取: 61 | 62 | ```text 63 | > ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 -i a.flv 64 | 65 | 920.667 66 | ``` 67 | 68 | 命令比较复杂哈,可以先不用管其他参数,只要将要分析的视频文件传入就好了。命令的结果是显示一行视频文件的长度。 69 | 70 | 于是可以编写一个函数: 71 | 72 | ```text 73 | import subprocess as sp 74 | 75 | def get_video_duration(filename): 76 | cmd = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 -i %s" % filename 77 | p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE) 78 | p.wait() 79 | strout, strerr = p.communicate() # 去掉最后的回车 80 | ret = strout.decode("utf-8").split("\n")[0] 81 | return ret 82 | ``` 83 | 84 | - 函数只有一个参数,就是视频文件路径 85 | - 合成命令语句,将视频文件路径替换进去 86 | - 用 subprocess 来执行,注意这里需要设置一下命令执行后的输出 87 | - 用 wait 等待命令执行完成 88 | - 通过 communicate 提取输出结果 89 | - 从结果中提取视频文件的长度,返回 90 | 91 | ## 3.分段 92 | 93 | 得到了视频长度,确定好每个分段的长度,就可以计算出需要多少分段了。 94 | 95 | 代码很简单: 96 | 97 | ```text 98 | import math 99 | duration = math.floor(float(get_video_duration(filename))) 100 | part = math.ceil(duration / length) 101 | ``` 102 | 103 | 注意,计算分段时,需要进行向上取整,即用 ceil,以包含最后的一点尾巴。 104 | 105 | 得到了需要的分段数,用一个循环就可以计算出每一段的起始时间了。 106 | 107 | ## 4.获取文件 108 | 109 | 因为处理的文件很多,所以需要自动获取需要处理的文件。 110 | 111 | 方法很简单,也很常用,一般可以用 os.walk 递归获取文件,还可以自己写,具体根据实际情况。 112 | 113 | ```text 114 | for fname in os.listdir(dir): 115 | fname = os.path.join(dir, os.path.join(dir, fname)) 116 | basenames = os.path.basename(fname).split('.') 117 | mainname = basenames[0].split("_")[0] 118 | ... 119 | ``` 120 | 121 | 提供视频文件所在的目录,通过 os.listdir 获取目录中的文件,然后,合成文件的绝对路径,因为调用裁剪命令时需要绝对路径比较方便。 122 | 123 | 获取文件名,是为了在后续对裁剪好的文件进行命名。 124 | 125 | ## 5.代码集成 126 | 127 | 现在每个部分都写好了,可以将代码集成起来了: 128 | 129 | ```text 130 | def main(dir): 131 | outdir = os.path.join(dir, "output") 132 | if not os.path.exists(outdir): 133 | os.mkdir(outdir) 134 | 135 | for fname in os.listdir(dir): 136 | fname = os.path.join(dir, os.path.join(dir, fname)) 137 | if os.path.isfile(fname): 138 | split_video(fname, outdir) 139 | ``` 140 | 141 | - main 方法是集成后的方法 142 | - 先创建一个裁剪好的存储目录,放在视频文件目录中的 output 目录里 143 | - 通过 listdir 获取到文件后,对每个文件进行处理,其中判断了一下是否为文件 144 | - 调用 split_video 方法开始对一个视频文件进行裁剪 145 | 146 | ## 6.总结 147 | 148 | 总体而言,这是个很简单的应用,核心功能就是调用了一个 ffmpeg 命令。 149 | 150 | 相对于技术,更重要的是如何对一个项目进行分析和分解,以及从什么地方开始。 151 | 152 | 这里的方式起始时,不断地找最重要地事情,以最重要的事情为线索不断地推进,最终以自下而上地方式解决整个问题。 153 | 154 | 期望这篇文章对你有所启发,比心。 155 | 156 | 原文作者:零声音视频开发 157 | 158 | 原文链接:https://zhuanlan.zhihu.com/p/429558315 -------------------------------------------------------------------------------- /【NO.468】音视频面试问题面试技巧.md: -------------------------------------------------------------------------------- 1 | # 【NO.468】音视频面试问题|面试技巧 2 | 3 | 从事音视频四五年,面试官都会问的面试题都有哪些? 4 | 在这里要告诉大家的是对于自己特别熟悉的,拿手的,一定要重点多说一些,其他的,不熟悉的,一带而过。而且自己做的项目,一定要特别熟悉,内部啥原理,甚至代码细节,都会被问到。 还要养成一个好习惯:多做笔记!!!俗话说,好记性不如烂笔头!所以做笔记的习惯一定要有!下面给一些示例给大家讲解一下!! 5 | 6 | ## **1.常见的音视频格式有哪些?** 7 | 8 | **参考答案** 9 | 10 | 1. MPEG(运动图像专家组)是Motion Picture Experts Group 的缩写。这类格式包括了MPEG-1,MPEG-2和MPEG-4在内的多种视频格式。 11 | 2. AVI,音频视频交错(Audio Video Interleaved)的英文缩写。AVI这个由微软公司发布的视频格式,在视频领域可以说是最悠久的格式之一。 12 | 3. MOV,使用过Mac机的朋友应该多少接触过QuickTime。QuickTime原本是Apple公司用于Mac计算机上的一种图像视频处理软件。 13 | 4. ASF(Advanced Streaming format高级流格式)。ASF 是MICROSOFT 为了和的Real player 竞争而发展出来的一种可以直接在网上观看视频节目的文件压缩格式。 14 | 5. WMV,一种独立于编码方式的在Internet上实时传播多媒体的技术标准,Microsoft公司希望用其取代QuickTime之类的技术标准以及WAV、AVI之类的文件扩展名。 15 | 6. NAVI,如果发现原来的播放软件突然打不开此类格式的AVI文件,那你就要考虑是不是碰到了n AVI。n AVI是New AVI 的缩写,是一个名为Shadow Realm 的地下组织发展起来的一种新视频格式。 16 | 7. 3GP是一种3G流媒体的视频编码格式,主要是为了配合3G网络的高传输速度而开发的,也是目前手机中最为常见的一种视频格式。 17 | 8. REAL VIDEO(RA、RAM)格式由一开始就是定位在视频流应用方面的,也可以说是视频流技术的始创者。 18 | 9. MKV,一种后缀为MKV的视频文件频频出现在网络上,它可在一个文件中集成多条不同类型的音轨和字幕轨,而且其视频编码的自由度也非常大,可以是常见的DivX、XviD、3IVX,甚至可以是RealVideo、QuickTime、WMV 这类流式视频。 19 | 10. FLV是FLASH VIDEO的简称,FLV流媒体格式是一种新的视频格式。由于它形成的文件极小、加载速度极快,使得网络观看视频文件成为可能,它的出现有效地解决了视频文件导入Flash后,使导出的SWF文件体积庞大,不能在网络上很好的使用等缺点。 20 | 11. F4V,作为一种更小更清晰,更利于在网络传播的格式,F4V已经逐渐取代了传统FLV,也已经被大多数主流播放器兼容播放,而不需要通过转换等复杂的方式。 21 | 22 | ## **2.列举一些音频编解码常用的实现方案?** 23 | 24 | **参考答案** 25 | 26 | - 第一种就是采用专用的音频芯片对 语音信号进行采集和处理,音频编解码算法集成在硬件内部,如 MP3 编解码芯片、语音合成 分析芯片等。使用这种方案的优点就是处理速度块,设计周期短;缺点是局限性比较大,不灵活,难以进行系统升级。 27 | - 第二种方案就是利用 A/D 采集卡加上计算机组成硬件平台,音频编解码算法由计算机上的软件来实现。使用这种方案的优点是价格便 宜,开发灵活并且利于系统的升级;缺点是处理速度较慢,开发难度较大。 28 | - 第三种方案是使用高精度、高速度 的 A/D 采集芯片来完成语音信号的采集,使用可编程的数据处理能力强的芯片来实现语音信号处理的算法,然后 用 ARM 进行控制。采用这种方案的优点是系统升级能力强,可以兼容多种音频压缩格式甚至未来的音频压缩格 式,系统成本较低;缺点是开发难度较大,设计者需要移植音频的解码算法到相应的 ARM 芯片中去。 29 | 30 | ## **3.请叙述AMR基本码流结构?** 31 | 32 | **参考答案** 33 | 34 | AMR文件由文件头和数据帧组成,文件头标识占6个字节,后面紧跟着就是音频帧; 35 | 36 | 格式如下所示: 37 | 38 | 文件头(占 6 字节)| :--- | 语音帧1 | 语音帧2 | … | 39 | 40 | - 文件头: 单声道和多声道情况下文件的头部是不一致的,单声道情况下的文件头只包括一个Magic number,而多声道情况下文件头既包含Magic number,在其之后还包含一个32位的Chanel description field。多声道情况下的32位通道描述字符,前28位都是保留字符,必须设置成0,最后4位说明使用的声道个数。 41 | 42 | 语音数据: 文件头之后就是时间上连续的语音帧块了,每个帧块包含若干个8位组对齐的语音帧,相对于若干个声道,从第一个声道开始依次排列。每一个语音帧都是从一个8位的帧头开始:其中P为填充位必须设为0,每个帧都是8位组对齐的。 43 | 44 | 除此之外还很多**面试题**需要去掌握的,这里我就不一一列举啦! 45 | 46 | ![img](https://pic2.zhimg.com/80/v2-91f19bb5dca86b219cb1117c868c8541_720w.webp) 47 | 48 | ![img](https://pic3.zhimg.com/80/v2-57c8d3973ded64074499f3d5799445ce_720w.webp) 49 | 50 | ![img](https://pic2.zhimg.com/80/v2-223d78688da065a9c060746c48651a59_720w.webp) 51 | 52 | ![img](https://pic3.zhimg.com/80/v2-2777a6e0e7104730035c6124e0ba4f66_720w.webp) 53 | 54 | ![img](https://pic1.zhimg.com/80/v2-bd4c021d09a6712c067ed9ebb1ee3904_720w.webp) 55 | 56 | 原文作者:零声音视频开发 57 | 58 | 原文链接:https://zhuanlan.zhihu.com/p/428517343 -------------------------------------------------------------------------------- /【NO.469】什么是码率控制 在视频编码中,码率控制的概念是什么,它是通过什么实现的.md: -------------------------------------------------------------------------------- 1 | # 【NO.469】什么是码率控制? 在视频编码中,码率控制的概念是什么,它是通过什么实现的 2 | 3 | 码率控制是指视频编码中决定输出码率的过程。首先介绍一下 X264 中使用到的与码率控制相关的几个概念: 4 | 5 | CQP(Constant QP) 恒 定QP(Quantization Parameter),追求量化失真的恒定,瞬时码率会随场景 复杂度而波动,该模式基本被淘汰(被 CRF 取代),只有用”-pq 0”来进行无损编码还有价值。 6 | 7 | CRF(Constant Rate Factor),恒定质量因子,与恒定 QP 类似,但追求主观感知到的质量恒定,瞬时码率也 会随场景复杂度波动。对于快速运动或细节丰富的场景会适当增大量化失真(因为人眼不易注意到),反之 对于静止或平坦区域则减少量化失真。 8 | 9 | ABR(Average Bitrate),平均码率,追求整个文件的码率平均达到指定值(对于流媒体有何特殊之处?)。瞬时码率也会随着场景复杂度波动,但最终要受平均值的约束。 10 | 11 | CBR(Constant Bitrate),恒定码率。前面几个模式都属于可变码率(瞬时码率在波动),即VBR(Variable Bitrate);恒定码率与之相对,即码率保持不变。 12 | 13 | x264 并没有直接提供 CBR 这种模式,但可以通过在 VBR 模式的基础上做进一步限制来达到恒定码率的目标。CRF 和 ABR 模式都能通过--vbv-maxrate --vbv-bufsize来限制码率波动。 14 | 15 | > 关于这几个概念的参考如下: 16 | 17 | - 1.Waht are CBR,VBV and CPB? 18 | - 2.FFmpeg and H.264 Encoding Guide 19 | - 3.CRF Guide(Constant Rate Factor in X264 and X265) 20 | - 4.MeGUI/x264 setting 21 | 22 | ## 1.X264 中码率控制 23 | 24 | X264 中对于码率控制方法有三种:X264_RC_CQP、X264_RC_CRF、X264_RC_ABR。默认情况是选择 CRF 方法,设置是在 x264_param_default函数里设置的 25 | 26 | ```text 27 | param->rc.i_rc_method = X264_RC_CRF; 28 | param->rc.f_rf_constant = 23; 29 | ``` 30 | 31 | 关于这三种方法,网上有提到优先级是ABR>CQP>CRF的,但分析 X264 的源码,并没有看出有优先级顺序,关于码率控制方法的设置代码如下: 32 | 33 | ```text 34 | OPT("bitrate") 35 | { 36 | p->rc.i_bitrate = atoi(value); 37 | p->rc.i_rc_method = X264_RC_ABR; 38 | } 39 | OPT2("qp", "qp_constant") 40 | { 41 | p->rc.i_qp_constant = atoi(value); 42 | p->rc.i_rc_method = X264_RC_CQP; 43 | } 44 | OPT("crf") 45 | { 46 | p->rc.f_rf_constant = atof(value); 47 | p->rc.i_rc_method = X264_RC_CRF; 48 | } 49 | ``` 50 | 51 | ## 2.X264 中关于 QP设置 52 | 53 | 首先看一段 X264 中关于 QP 值的代码,该段代码在x264_ratecontrol_new: 54 | 55 | ```text 56 | rc->ip_offset = 6.0 * log2f(h->param.rc.f_ip_factor); 57 | rc->pb_offset = 6.0 * log2f(h->param.rc.f_pb_factor); 58 | rc->qp_constant[SLICE_TYPE_P] = h->param.rc.i_qp_constant; 59 | rc->qp_constant[SLICE_TYPE_I] = x264_clip3(h->param.rc.i_qp_constant - rc->ip_offset + 0.5, 0, QP_MAX); 60 | rc->qp_constant[SLICE_TYPE_B] = x264_clip3(h->param.rc.i_qp_constant + rc->pb_offset + 0.5, 0, QP_MAX); 61 | ``` 62 | 63 | 从上面的代码可以看出,默认的i_qp_constant或者通过命令行传入的qp qp_constant实际设置的是 P 帧的 QP。I 帧和 B 帧的 QP 设置是根据f_ip_factor f_pb_factor计算得到。 64 | 65 | 在研究编码算法的时候,一般会选用 CQP 方法,设定 QP 为 24、28、32、36、40等(一般选 4 个 QP 值),然后比较算法优劣。在 X264 中,关于QPmin、QPmax、QPstep的默认设置如下: 66 | 67 | ```text 68 | param->rc.i_qp_min = 0; 69 | param->rc.i_qp_max = QP_MAX; 70 | param->rc.i_qp_step = 4; 71 | ``` 72 | 73 | ### 2.1 QPmin,默认值 74 | 75 | \0. 定义 X264 可以使用的最小量化值,量化值越小,输出视频质量越好。当 QP 小于某一个值后, 编码输出的宏块质量与原始块极为相近,此时没必要继续降低 QP。如果开启了自适应量化器(默认开启),不建议 提高 QPmin 的值,因为这会降低平滑背景区域的视觉质量。 76 | 77 | ### 2.2 QPmax,默认值 78 | 79 | \51. 定义 X264 可以使用的最大量化值。默认值 51 是 H.264 规格中可供使用的最大量化值。如果 想要控制 X264 输出的最低品质,可以将此值设置的小一些。QPmin 和 QPmax 在CRF,ABR方法下是有效的,过低的设置 QPmax,可能造成 ABR 码率控制失败。不建议调整该参数。 80 | 81 | ### 2.3 QPstep,默认值 82 | 83 | 4.设置两帧间量化值的最大变化幅度。 84 | 85 | . 比较三种码率控制方式如下: 86 | 87 | | 码率控制方法 | 视觉质量稳定性 | 即时输出码率 | 输出文件大小 | 88 | | ------------ | -------------- | ------------ | ------------ | 89 | | | | | | 90 | 91 | 帧间 QP 变化,帧内宏块 QP 不变,输出码率未知,各帧输出视觉质量有变化(高 QP 低码率的情况下会更明显)。 92 | 93 | 原文作者:零声音视频开发 94 | 95 | 原文链接:https://zhuanlan.zhihu.com/p/428259095 -------------------------------------------------------------------------------- /【NO.470】FFmpeg命令行格式和转码过程.md: -------------------------------------------------------------------------------- 1 | # 【NO.470】FFmpeg命令行格式和转码过程 2 | 3 | ## 1.命令行基本格式为: 4 | 5 | ```text 6 | ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ... 7 | ``` 8 | 9 | 格式分解如下: 10 | 11 | ```text 12 | ffmpeg 13 | global_options 14 | input1_options -i input1 15 | input2_options -i input2 16 | ... 17 | output1_options output1 18 | output2_options output2 19 | ... 20 | ``` 21 | 22 | “ffmpeg” 读取任意数量的输入 “文件”(可以是常规文件、管道、网络流、录制设备等,由 “-i” 选项指定),写入任意数量的输出 “文件”。命令行中无法被解释为选项(option)的任何元素都会被当作输出文件。 23 | 24 | 每个输入或输出文件,原则上都可以包含任意数量的流。FFmpeg 中流的类型有五种:视频(video)、音频(audio)、字幕(subtitle)、附加数据(attachment)、普通数据(data)。文件中流的数量和(或)流类型种数的极限值由文件封装格式决定。选择哪一路输入文件的哪一路流输出到哪一路输出,这个选择过程既可以由 FFmpeg 自动完成,也可以通过 “-map” 选项手动指定(后面第 6 节 “流选择” 章节会深入描述)。 25 | 26 | 注:关于附加数据(attachment)和普通数据(data)的说明如下: 27 | 28 | > Attachments could be liner notes, related images, metadata files, fonts, etc. Data tracks would be for things like timecode, navigation items, cmml, streaming tracks. 参考资料[3] “What are the the data and attachment stream type?” 29 | 30 | 命令行中的输入文件及输入文件中的流都可以通过对应的索引引用,文件、流的索引都是从 0 开始。例如,2:4 表示第 3 个输入文件中的第 5 个流。(后面 6.3 节 “stream specifier” 章节会详细介绍)。 31 | 32 | 一个通用规则是:**输入/输出选项(options)作用于跟随此选项后的第一个文件。因此,顺序很重要,并且可以在命令行中多次指定同一选项。每个选项仅作用于离此选项最近的下一输入或输出文件。全局选项不受此规则限制。** 33 | 34 | 不要把输入文件和输出文件混在一起———应该先将输入文件写完,再写输出文件。也不要把不同文件的选项混在一起,各选项仅对其下一输入或输出文件有效,一个选项不能跨越一个文件传递到后续文件。 35 | 36 | 举几个命令行例子: 37 | 38 | ■ 设置输出文件码率为 64 kbit/s: 39 | 40 | ```text 41 | ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi 42 | ``` 43 | 44 | 其中 “-b:v 64k” 和 “-bufsize 64k” 是输出选项。 45 | 46 | ■ 强制输入文件帧率(仅对 raw 格式有效)是 1 fps,输出文件帧率为 24 fps: 47 | 48 | ```text 49 | ffmpeg -r 1 -i input.m2v -r 24 output.avi 50 | ``` 51 | 52 | 其中 “-r 1” 是输入选项,“-r 24” 是输出选项。 53 | 54 | ■ 转封装:将 avi 格式转为 mp4 格式,并将视频缩放为 vga 分辨率: 55 | 56 | ```text 57 | ffmpeg -y -i video.avi -s vga video.mp4 58 | ``` 59 | 60 | 其中 “-y” 是全局选项,“-s vga” 是输出选项。 61 | 62 | ## 2. 转码过程 63 | 64 | ```text 65 | _______ ______________ 66 | | | | | 67 | | input | demuxer | encoded data | decoder 68 | | file | ---------> | packets | -----+ 69 | |_______| |______________| | 70 | v 71 | _________ 72 | | | 73 | | decoded | 74 | | frames | 75 | |_________| 76 | ________ ______________ | 77 | | | | | | 78 | | output | <-------- | encoded data | <----+ 79 | | file | muxer | packets | encoder 80 | |________| |______________| 81 | ``` 82 | 83 | “ffmpeg” 调用 libavformat 库(包含解复用器 demuxer),从输入文件中读取到包含编码数据的包(packet)。如果有多个输入文件,“ffmpeg” 尝试追踪多个有效输入流的最小时间戳(timestamp),用这种方式实现多个输入文件的同步。 84 | 85 | 然后编码包(packet)被传递到解码器(decoder),解码器解码后生成原始帧(frame),原始帧可以被滤镜(filter)处理(图中未画滤镜),经滤镜处理后的帧送给编码器,编码器将之编码后输出编码包。最终,由复用器(muxex)将编码包写入特定封装格式的输出文件。 86 | 87 | 原文作者:零声音视频开发 88 | 89 | 原文链接:https://zhuanlan.zhihu.com/p/427312103 -------------------------------------------------------------------------------- /【NO.481】Linux服务器开发,libeventlibev框架实战那些坑.md: -------------------------------------------------------------------------------- 1 | # 【NO.481】Linux服务器开发,libevent/libev框架实战那些坑 2 | 3 | ## 1.前言 4 | 5 | libevent、libev和libuv都是c语言实现的异步事件库。注册异步事件,检测异步事件,根据事件的触发先后顺序调用相对应的函数处理事件。 6 | 处理的时间包括:**网络IO事件,定时事件以及信号事件。**这三个是驱动服务器逻辑的三个重要事件。 7 | libevent和libev解决了跨平台的问题,封装了异步事件库与操作系统的交互。 8 | libevent使用了大量的全局变量,很难安全得在多线程环境中运行;event的数据结构太大,包含了io、时间以及信号 处理全封装在一个结构体中,额外的组件如http、dns、openssl等实现质量差,计时器采用最小二叉堆(libuv改为最小四叉堆)不能很好的处理时间事件。 9 | 10 | 11 | 12 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/c4fa6a6ac20b4fcbb337ac0cf4e35a7f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center) 13 | 14 | 15 | 16 | ## 2.如何阅读网络库 17 | 18 | 记住两个线索: 19 | 20 | ### 2.1 网络封装 21 | 22 | - IO检测 23 | - IO操作 24 | 25 | ### 2.2 事件操作 26 | 27 | - 连接建立的问题 (限制最大连接数,设置黑白名单,创建用户的对象) 28 | - 连接断开 29 | - 数据到达 (具体消息的分发,解密) 30 | - 数据发送 31 | 32 | 不注重效率的都会使用getdateoftime(),会对我们的系统有较大的考验,因为它会读一个文件,把时间取出来。如果我们要经常获取时间,我可以运用缓存时间。 33 | systime_mono() 机器启动到现在的时间。 34 | systime_wall() 现在的时间。 35 | wall+mono2-mono1为什么要这样计算时间?担心有的人会更好系统时间,导致我们服务器出现错乱。 36 | 更新时间缓存的目的是避免过多的调用系统调用,影响性能。 37 | 最小堆的顶端是最近要触发的任务。 38 | 收集网络事件,收集定时事件,将事件进行包装,根据事件优先级不同放入不同队列中,并没有急着处理。 39 | event_asign()相当于epoll_mod,是个增强版,没有还会增加。 40 | 41 | ### 2.3 巧妙设计 42 | 43 | linux协议栈中每一个fd在内核态中都有一个读写缓冲区,而在内核态也同样有一个读写缓冲区,为什么用户态需要实现一个对写缓冲区? 44 | 能不能确保一次int n=read(fd,buf,size)读出内核态所有数据?答案肯定是不能,因为数据包的界定是有固定长度和特殊字符两种,size参数的填写是盲猜的。有了用户态缓冲区就更容易读出完整的数据。 45 | 而写的时候int n=write(fd,buf,size),size是打算写的值,而n表示实际写入的值,如果n8是要报错,小于pool_size要创建或是从cache里去取,cache没有说明都在free里。 66 | 给连接池起名字,默认是ip:端口格式,比如127.0.0.1:8888。 67 | 68 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/d72c6b45546748718c40a650321c00e7.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center) 69 | 70 | 71 | 72 | ## 5.定时器设计 73 | 74 | **定时器是在lua层实现了一个最小堆,每一个任务生成一个协程,但是要考虑回收协程,尤其是在删除的时候。**这块做的还不完善,Mark老师也希望大家一起帮忙搞一搞,先记下这件事吧! 75 | 76 | ## 6.总结 77 | 78 | Mrk老师说道:做任何事情,拆分的思想很重要,联想起以前一个数学老师的话,“老太太吃柿子,要捡软的捏。”以后遇到问题和困难,也应该先按照这个思路去处理问题。作为开发还有一点是非常重的,就是**测试**的能力,这方面得想办法学习提升一下。通过本节课,我初步了解了分布式锁,感觉对于skynet也有了更好的认识。在成长的路上,有Mark老师陪伴,好幸福啊~ 79 | 80 | 原文作者:[屯门山鸡叫我小鸡](https://blog.csdn.net/sinat_28294665) 81 | 82 | 原文链接:https://bbs.csdn.net/topics/604975373 -------------------------------------------------------------------------------- /【NO.484】Linux服务器开发,手写内存检测组件.md: -------------------------------------------------------------------------------- 1 | # 【NO.484】Linux服务器开发,手写内存检测组件 2 | 3 | ## 1.前言 4 | 5 | 导致内存泄漏的根本原因是因为C++没有内存回收机制gc,内存分配和内存释放次数不对等,是一个很常见的问题,常用的工具有valgrind、mtrace等等,不过这些工具的问题点是我们已经发现了内存泄漏。危害是堆上内存被耗尽,当我们想使用的时候就分配不出来,代码运行不下去被系统强行回收。 6 | 7 | - 如何预防内存泄漏 8 | - 如何解决内存泄漏 9 | - 如何发现内存泄漏 10 | 11 | 分配时候计数器加1,释放时候计数器减1。当我们看到计数器不为0,正常退出的话计数器不等于0,可以大胆的推测出可能存在内存泄漏。 12 | 如何定位哪一行内存泄漏 13 | 14 | - _FILE,*FUNCTION*,_LINE宏定位代码执行情况 15 | - built_return_address() 返回函数被哪里调用的_libc_malloc()。 16 | 破除递归的方法的代码需要注意。 17 | 18 | 19 | 20 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/97bee5b5ada6427cae59f313420b606d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center) 21 | 22 | **通过addr2line贡酒对指定地址进行代码行数定位,这个做法也是我第一次见,感觉开启了新世界的大门。** 23 | 24 | 25 | 26 | ## 2.第一种方法 文件做法 27 | 28 | malloc时创建文件,free时删除文件,看程序最后剩下什么文件就可以直观的看到内存泄漏情况。 29 | 30 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/23065953cd9641d29359f3277257635a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center) 31 | 32 | 33 | 可以通过cat命令,将内存泄漏的地址进行查看,再通过addr2line工具对泄漏位置进行定位。 34 | 35 | 36 | 37 | ## 3.第二种方法 宏定义 38 | 39 | **别人的库有内存泄漏怎么办?** 40 | 41 | 只要它底层调用的malloc也是可行的。 42 | 43 | ## 4.第三种方法 指针方案(好用,不推荐) 44 | 45 | 在/usr/local/malloc.h 46 | __malloc_hook是一个函数指针,当调用malloc时会调用这个函数指针。相对应的也有__free_hook这个函数指针。我们重新将指针赋一个新的地址,这样就有点类似于钩子的感觉,调用malloc/free时将被我们截获。 47 | 48 | ## 5.第四种方法mtrace工具 版本 纯操作 49 | 50 | 导入库 export MALLOC_TRACE=./test.log 否则日志文件无法生成。需要把分配释放的地址进行对应分析,然后找出落单的,再用add2line工具计算出具体的泄漏行数,和上面三种方法有异曲同工之妙。 51 | 52 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/01b642f5577c45dc94f92714bf44b9ae.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center) 53 | 54 | 55 | 56 | ## 6.总结 57 | 58 | 首先,不要认为内存泄漏这个问题复杂,核心主要两个问题。如果发现内存泄漏,发现泄漏找到具体出错位置,对错误进行排查。虽然认真学了一上午,感觉还是有些收获,但是在代码层面上还需要进一步加强。从另一个角度来说,尽然C++11封装了智能指针,还是应该充分的使用智能指针,要不然内存泄漏真的是让人头痛的一个事啊! 59 | 60 | ## 7.补充 61 | 62 | **共享内存mmap shmget** 63 | 64 | 用户空间内专门有一个地方所谓的共享内存,在task_struct结构体中的mm_struct结构体中,堆栈,代码段数据段,参数环境变量等等都整洁的排布着。进程初始化时将各个数值进行赋,mmap_base是共享内存的初始值。值得注意的是,共享内存不是在堆栈上,是有一块虚拟的空间,不同的进程映射在相同的内存空间。有点匿名管道的意思。 65 | 66 | 原文作者:[屯门山鸡叫我小鸡](https://blog.csdn.net/sinat_28294665) 67 | 68 | 原文链接:https://bbs.csdn.net/topics/604975478 -------------------------------------------------------------------------------- /【NO.485】Linux服务器开发,mysql连接池的实现.md: -------------------------------------------------------------------------------- 1 | # 【NO.485】Linux服务器开发,mysql连接池的实现 2 | 3 | ## 0.前言 4 | 5 | 连接池和线程池的的关系,当线程数量小于连接数量时就需要等到连接释放再去争夺连接资源。线程池是主动连接执行任务,连接池和内存池相似都是被动获取,执行任务后归还。 6 | 7 | ## 1.池化技术 8 | 9 | 池化技术的作用是减少资源创建次数,提高程序的响应性能。 10 | 11 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/379c7c1fce8b48fdb4a221929b248d43.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center) 12 | 13 | 14 | 可以看到,在sql执行部分的执行效率是非常低的,只有执行sql语句才是真正干活的时间。 15 | 16 | 17 | 18 | ## 2.什么是数据库连接池 19 | 20 | 用一个连接容器进行存储,7个线程请求到7个连接,握手挥手的时间都可以节省。 21 | 22 | 23 | 24 | ![img](https://img-community.csdnimg.cn/images/32b5323c679d41808ed8e632a49689d9.png) 25 | 26 | 27 | 28 | ## 3.为什么使用数据库连接池 29 | 30 | 优点: 31 | 32 | - 降低网络开销 33 | - 连接复用,有效减少连接数 34 | - 提升性能,避免频繁的新建连接,新建连接开销比较大 35 | - 没有TIME_WAIT状态问题 36 | 缺点: 37 | - 设计优点复杂 38 | 假如每个线程绑定一个连接,这会导致代码的耦合性比较高。有些任务不需要连接数据库,代码冗余。 39 | 测试发现,数据库在同一台电脑上使用连接池性要比不使用连接池快一倍,如果在不同电脑上可能会差更多。 40 | 41 | ## 4.数据库连接池运行机制 42 | 43 | 当需要访问数据库时去连接池取,使用完毕后进行归还。系统关闭之前,要断开所有连接并释放连接占用的系统资源,个人感觉这个挺好理解,符合人类的常规逻辑。上图: 44 | 45 | 46 | 47 | ![img](https://img-community.csdnimg.cn/images/51e449a371ac4ec1b3d1e6da0e09ea7d.png) 48 | 49 | 50 | 51 | ## 5.连接池和线程池的关系 52 | 53 | 首先,连接池的连接对象和线程池的线程数量是相对应的。其次线程执行完任务时要关闭连接对象。 54 | 最后,线程池是主动调用任务,而线程池是被动的接受,感觉更像是一个小受。 55 | 56 | ## 6.线程池设计要点 57 | 58 | 1、连接数据库:涉及数据库ip、端口、用户名、密码、数据库名字 59 | 60 | - 独立的连接通道 61 | - 配置最小连接数和最大的连接数。 62 | 2、需要一个管理连接的队列:管理连接,获取连接。list、queue 63 | 3、获取连接对象 64 | 4、归还连接对象 65 | 5、连接池的名字 66 | 67 | ## 7.连接池的具体实现 68 | 69 | ### 7.1 编译步骤 70 | 71 | ```bash 72 | rm -rf build 73 | 74 | 75 | 76 | mkdir build 77 | 78 | 79 | 80 | cd build 81 | 82 | 83 | 84 | cmake .. 85 | 86 | 87 | 88 | make 89 | 90 | 91 | 92 | ./test_dbpool 4 4 1 2000 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | ![img](https://img-community.csdnimg.cn/images/97d1f1f8540948508a944aa2cdb1a14f.png "#left") 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | ## (4是线程数量 4 是连接数量 1是开启连接池 2000线程数量) 113 | ``` 114 | 115 | 构造函数中初始化必要参数,先创建最小连接数。 116 | 117 | ```c 118 | GetDBConn(int timeout_ms);//线程数量多而连接数量不够时,进行超时判断防止等待。 119 | ``` 120 | 121 | - 当当前连接数小于最大连接数时,说明可以新建连接,将连接插入空闲连接,然后再去给予连接句柄。 122 | - 当当前连接数大于最大连接数时,就不能再去创建连接,等待超时时间,可以设置的比较大。过了超时时间仍然拿不到,那很遗憾就返回空啦!有空闲连接就能给予句柄了。 123 | - 决不允许两个任务共用一个连接。 124 | 125 | 如果真的要销毁连接池: 126 | 127 | - 1、先销毁线程池,确保所有任务退出; 128 | - 2、再去销毁连接池。 129 | 130 | 连接需要归还了我们才去销毁: 131 | 132 | - 1、资源申请释放的顺序非常重要 133 | - 2、异步编程是比如容易崩,资源释放异步函数还在使用 134 | 135 | 数据库重连机制: 136 | 137 | - 每次操作之前先去测试链路是否通。 138 | - 先去执行任务,再去处理连接不同的问题。 139 | 140 | ## 8.连接池连接设置数量 141 | 142 | 根据测试结果,线程数连接数开到32的时候性能不再提高、 143 | 开启多少线程是最快的:N为核心数 144 | 145 | - 计算密集型 N+1 146 | 147 | - IO密集型 根据实际阻塞的时间去测试,2N+2 这一块感觉接下来要去思考。 148 | 149 | 150 | 151 | ## 9.总结 152 | 153 | 通过今天听了零声学院Darren老师的课程,真的是如醍醐灌顶般,瞬间明白了mysql异步连接池的运行原理和实际开发中遇到性能问题该如何解决。自己觉得收获颇丰,对异步连接池有了新的认识,自己有信心有能力对老师的连接池进行改造。从最初的懵懂少年,通过不断地学习和努力,我已经逐渐成为了翩翩少年。是零声学院的老师助我成长,伴我入眠。 154 | 155 | 156 | 157 | 原文作者:[屯门山鸡叫我小鸡](https://blog.csdn.net/sinat_28294665) 158 | 159 | 原文链接:https://bbs.csdn.net/topics/605005724 -------------------------------------------------------------------------------- /【NO.487】TCP三次握手、四次挥手以及TIME_WAIT详解.md: -------------------------------------------------------------------------------- 1 | # 【NO.487】TCP三次握手、四次挥手以及TIME_WAIT详解 2 | 3 | ### 1.前提概述 4 | 5 | TCP网络编程中常用的api函数有: 6 | 7 | socket、bind、listen、accept、recv、send、close、connect 8 | 9 | 其中socket函数返回一个文件描述符fd,这个fd并不单纯,而是对应着内核创建的TCB(transport control block),可以理解为一个下标索引,而不同TCB则是根据不同的五元组(源ip、源port、目的ip、目的port、协议类型)来进行区分。 10 | 11 | bind函数则是将socket函数创建的fd和本机ip联系起来。 12 | 13 | listen的功能是通知协议进程准备接收socketfd 上的连接请求,套接字也将从CLOSED转换到LISTEN状态,它同时也指定socket上可以排队等待的连接数的门限值。超过门限值时,socket将拒绝进入的连接请求排队等待。当这种情况出现时,TCP将忽略进入的连接请求,进程可以通过调用accept来得到队列中的连接。 14 | 15 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215173232_27144.jpg) 16 | 17 | ### 2.三次握手过程 18 | 19 | 在服务端调用listen函数进行监听时,客户端就可以准备通信了,而在通信之前自然离不开一些准备工作了,也就是常说的三次握手。 20 | 21 | 当客户端调用connect函数时,三次握手也随之发生,如图: 22 | 23 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215173232_91736.jpg) 24 | 25 | 1、客户端首先会发送一个SYN分节(SYN位置位),它将告诉服务器客户端将在连接中发送的数据初始序列号,通常该数据包只有包头。 26 | 27 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215173233_68098.jpg) 28 | 29 | 当服务端收到客户端的第一个SYN分节数据包时,服务端将为本次连接建立对应的TCB并存入一个所谓的半连接队列当中,此时半连接队列中的套接字都将处于SYN_RCVD状态。 30 | 31 | 2、服务器必须确认客户端发送的SYN,置位SYN和ACK位。如图: 32 | 33 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215173234_43101.jpg) 34 | 35 | 客户端在收到服务器的确认之后,connect将返回0,即建立连接成功。 36 | 37 | 3、客户端最后再对服务器发送的SYN进行确认,如图: 38 | 39 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215173234_77736.jpg) 40 | 41 | 当服务器收到来自客户端的确认时,处于半连接队列中的套接字将被移到已完成连接队列的队尾,当进程调用accept时,将从已完成连接队列中的队头项返回给进程。此外,listen函数中的第二个参数backlog在一些系统中规定为两个队列之和的最大值,而在有些系统中则是已完成连接队列和的最大值。当队列已满时,服务端将会忽略客户端发送的SYN分节。 42 | 43 | 此时已连接队列中的套接字都将处于ESTABLISHED状态,即双方连接建立。 44 | 45 | ### 3.三次握手过程中的问题 46 | 47 | **为什么需要三次握手?** 48 | 49 | 三次握手其实是为了建立双方之间的通信,即双方都要确认通信状态,而不是单向的通信。如果变成两次握手,那么服务器不能确认客户端的接收状态,此外,服务器发送的ACK数据包若丢失,客户端将拒绝后续的数据,则服务器一直在发送,而客户端一直再拒绝。 50 | 51 | ### 4.四次挥手 52 | 53 | 挥手过程可以理解为一对情侣的分手场景,如图: 54 | 55 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215173235_76269.jpg) 56 | 57 | 结合三次握手过程,我们能很快清楚四次挥手的过程,继续情侣分手的场景,当一方提出分手时,另一方会做出回应,当然后续会有一些争执或者收拾彼此的东西,然后另一方觉得确实没办法继续在一起了,于是也说出了分手,此时当初提出分手的一方会很干脆的进行确认,此后两方再无瓜葛。 58 | 59 | ### 5.四次挥手过程中的问题 60 | 61 | **1、两端可能同时close吗,此时什么场景?** 62 | 63 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/15/20221215173235_99373.jpg) 64 | 65 | **2、主动方出现大量的TIME_WAIT?TIME_WAIT作用?** 66 | 67 | 这种情况一般发生在高并发短连接的场景,一般可以通过设置reuseaddr的方法来解决。 68 | 69 | TIME_WAIT状态作用: 70 | 71 | 主动关闭放发送最后一个ack包后,可能出现丢失(这时对方还会重发FIN,收到两个FIN的时间间隔一定小于2MSL),使对方没有收到最后一个ACK包时有时间可以重发ACK包; 72 | 73 | 防止前一个连接中老的分组在新连接中再现,TIME_WAIT存在时间为2MSL,而某个连接上的分组最多存活1MSL就会被丢弃。 74 | 75 | **3、出现大量的CLOSE_WAIT状态?** 76 | 77 | 被动方处于在收到FIN分节时,处于CLOSE_WAIT,接着就需要进行程序收尾工作,一旦耗时操作比较多,对应的CLOSE_WAIT时间就越长。此时需要调整代码逻辑,即时的调用close函数。 78 | 79 | **4、出现大量的FIN_WAIT1/2状态?** 80 | 81 | 此时对端没有即时发送ACK,而主动方也没有其他办法过渡到TIME_WAIT状态,只能选择kill掉进程。 82 | 83 | **5、socket描述符和对应的TCB控制块回收的时间点?** 84 | 85 | 调用close函数后fd被立即回收; 86 | 87 | 而TCB的话,被动方在接收到ack后被回收,主动方则在TIME_WAIT时间到之后再进行回收。 88 | 89 | 原文链接:https://zhuanlan.zhihu.com/p/499127830 90 | 91 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.496】Linux服务器开发【干货知识】—MySQL事务原理分析.md: -------------------------------------------------------------------------------- 1 | # 【NO.496】Linux服务器开发【干货知识】—MySQL事务原理分析 2 | 3 | 前言 4 | 5 | 今天的目标是学习MySQL事务原理分析,但是却似乎总是非常不顺利,概念和实操实在多到令人发指,故干脆轻松学完一节课,等到时机到了再重新刷一遍吧! 6 | 7 | ### 1.事务是什么? 8 | 9 | 将数据库从一致性状态转化成另一种一致性状态。 10 | 11 | 单条语句是隐含得事务,多条语句需要手动开启事务。 12 | 13 | ### 2.ACID特性是什么? 14 | 15 | 原子性依靠undolog(共享表空间)实现,记录进行操作,然后进行反向操作。 16 | 17 | 一致性最难理解,换句话说在事务执行前后,数据库完整性约束不能被破坏。 18 | 19 | ### 3.隔离级别 20 | 21 | mvcc提供了一种快照读的方式提升读的并发性能,通过读历史版本的方式。 22 | 23 | 个人理解:各个级别其实就像是一种胆大策略,没有对错之分。只要不担心错读数据,就可以选用读不加锁而写加排他锁,依次类推。串行化读取虽然保险,但是效率也是最低,也许是我天生性格的原因,似乎对这种方式情有独钟。人生路慢慢,欲速则不达也。 24 | 25 | - 快照读就是不加任何参数。 26 | - 如果是当前读,需要加上lock in share mode 或 for update 加读写锁。 27 | - 查看锁信息select * from information_schema.innodb_locks; 28 | 29 | myisam不会发生并发读异常、并发死锁,因为是表锁。所以说当出现问题,试着换一下引擎也许是一个很好的选择。 30 | 31 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/16/20221216165913_23211.jpg) 32 | 33 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/16/20221216165914_56724.jpg) 34 | 35 | 删除、更改需要增加排他锁,而增加插入需要将插入位置加入位置意向锁,然后间接的加入增加排他锁。三个事项加在一起是完整的写操作。 36 | 37 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/16/20221216165914_88571.jpg) 38 | 39 | 表级别的对写锁才会和意向锁冲突,行级别咱就都不考虑啦。 40 | 41 | ### 4.Record Lock 42 | 43 | 记录单个行上的锁。 44 | 45 | ### 5.Gap Lock 46 | 47 | 间隙锁,锁定一个范围。只有在可重复读上面才会有间隙锁。 48 | 49 | 插入意向锁容易造成死锁,如果没有这个锁要一个一个的插入,有了它是为了提升效率。 50 | 51 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/16/20221216165915_27939.jpg) 52 | 53 | 死锁80%都是上图第二行造成的! 54 | 55 | ### 6.小计 56 | 57 | 更改mysql5.7的字符集 58 | 59 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/16/20221216165916_17067.jpg) 60 | 61 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/16/20221216165917_86738.jpg) 62 | 63 | ![img](https://linuxcpp.0voice.com/zb_users/upload/2022/12/16/20221216165917_95247.jpg) 64 | 65 | 原文链接:https://zhuanlan.zhihu.com/p/507239547 66 | 67 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.513】深入 malloc 函数,带你真正理解内存分配!.md: -------------------------------------------------------------------------------- 1 | # 【NO.513】深入 malloc 函数,带你真正理解内存分配! 2 | 3 | 内存是计算机中必不可少的资源,因为 CPU 只能直接读取内存中的数据,所以当 CPU 需要读取外部设备(如硬盘)的数据时,必须先把数据加载到内存中。 4 | 5 | 我们来看看可爱的内存长什么样子的吧,如图所示: 6 | 7 | ![img](https://pic4.zhimg.com/80/v2-80b864b5445fae9e7ea50b33e32df383_720w.webp) 8 | 9 | ## **1.内存申请** 10 | 11 | 通常使用高级语言(如Go、Java 或 Python 等)都不需要自己管理内存(因为有垃圾回收机制),但 C/C++ 程序员就经常要与内存打交道。 12 | 13 | 当我们使用 C/C++ 编写程序时,如果需要使用内存,就必须先调用 `malloc` 函数来申请一块内存。但是,`malloc` 真的是申请了内存吗? 14 | 15 | 我们通过下面例子来观察 `malloc` 到底是不是真的申请了内存: 16 | 17 | ```text 18 | 1#include 19 | 2 20 | 3int main(int argc, char const *argv[]) 21 | 4{ 22 | 5 void *ptr; 23 | 6 24 | 7 ptr = malloc(1024 * 1024 * 1024); // 申请 1GB 内存 25 | 8 26 | 9 sleep(3600); // 睡眠3600秒, 方便调试 27 | 10 28 | 11 return 0; 29 | 12} 30 | ``` 31 | 32 | 上面的程序主要通过调用 `malloc` 函数来申请了 1GB 的内存,然后睡眠 3600 秒,方便我们查看其内存使用情况。 33 | 34 | 现在,我们编译上面的程序并且运行,如下: 35 | 36 | ```text 37 | 1$ gcc malloc.c -o malloc 38 | 2$ ./malloc 39 | ``` 40 | 41 | 并且我们打开一个新的终端,然后查看其内存使用情况,如图所示: 42 | 43 | ![img](https://pic1.zhimg.com/80/v2-306ed2fabc3c52ca8b99903b2fa40134_720w.webp) 44 | 45 | 图中的 `VmRSS` 表示进程使用的物理内存大小,但我们明明申请了 1GB 的内存,为什么只显示使用 404KB 的内存呢?这里就涉及到 `虚拟内存` 和 `物理内存` 的概念了。 46 | 47 | ## **2.物理内存与虚拟内存** 48 | 49 | 下面先来介绍一下 `物理内存` 与 `虚拟内存` 的概念: 50 | 51 | - `物理内存`:也就是安装在计算机中的内存条,比如安装了 2GB 大小的内存条,那么物理内存地址的范围就是 0 ~ 2GB。 52 | - `虚拟内存`:虚拟的内存地址。由于 CPU 只能使用物理内存地址,所以需要将虚拟内存地址转换为物理内存地址才能被 CPU 使用,这个转换过程由 `MMU(Memory Management Unit,内存管理单元)` 来完成。`虚拟内存` 大小不受 `物理内存` 大小的限制,在 32 位的操作系统中,每个进程的虚拟内存空间大小为 0 ~ 4GB。 53 | 54 | 程序中使用的内存地址都是虚拟内存地址,也就是说,我们通过 `malloc` 函数申请的内存都是虚拟内存。实际上,内核会为每个进程管理其虚拟内存空间,并且会把虚拟内存空间划分为多个区域,如图所示: 55 | 56 | ![img](https://pic4.zhimg.com/80/v2-4710dcd77e35523345965f9d3aba0dab_720w.webp) 57 | 58 | 我们来分析一下这些区域的作用: 59 | 60 | - `代码段`:用于存放程序的可执行代码。 61 | - `数据段`:用于存放程序的全局变量和静态变量。 62 | - `堆空间`:用于存放由 `malloc` 申请的内存。 63 | - `栈空间`:用于存放函数的参数和局部变量。 64 | - `内核空间`:存放 Linux 内核代码和数据。 65 | 66 | ## **3.brk指针** 67 | 68 | 由此可知,通过 `malloc` 函数申请的内存地址是由 `堆空间` 分配的(其实还有可能从 `mmap` 区分配,这种情况暂时忽略)。在内核中,使用一个名为 `brk` 的指针来表示进程的 `堆空间` 的顶部,如图所示: 69 | 70 | ![img](https://pic3.zhimg.com/80/v2-f84c20dd688accf3608532a5904dbf76_720w.webp) 71 | 72 | 所以,通过移动 `brk` 指针就可以达到申请(向上移动)和释放(向下移动)堆空间的内存。例如申请 1024 字节时,只需要把 `brk` 向上移动 1024 字节即可,如图所示: 73 | 74 | ![img](https://pic4.zhimg.com/80/v2-9e3484ac80904eafbf0cfb4758767a4f_720w.webp) 75 | 76 | 事实上,`malloc` 函数就是通过移动 `brk` 指针来实现申请和释放内存的,Linux 提供了一个名为 `brk()` 的系统调用来移动 `brk` 指针。 77 | 78 | ## **4.内存映射** 79 | 80 | 现在我们知道,`malloc` 函数只是移动 `brk` 指针,但并没有申请物理内存。前面我们介绍虚拟内存和物理内存的时候介绍过,虚拟内存地址必须映射到物理内存地址才能被使用。如 图所示: 81 | 82 | ![img](https://pic2.zhimg.com/80/v2-51615028ceb171463172ff50c8ec0d0d_720w.webp) 83 | 84 | 如果对没有进行映射的虚拟内存地址进行读写操作,那么将会发生 `缺页异常`。Linux 内核会对 `缺页异常` 进行修复,修复过程如下: 85 | 86 | - 获取触发 `缺页异常` 的虚拟内存地址(读写哪个虚拟内存地址导致的)。 87 | - 查看此虚拟内存地址是否被申请(是否在 `brk` 指针内),如果不在 `brk` 指针内,将会导致 Segmention Fault 错误(也就是常见的coredump),进程将会异常退出。 88 | - 如果虚拟内存地址在 `brk` 指针内,那么将此虚拟内存地址映射到物理内存地址上,完成 `缺页异常` 修复过程,并且返回到触发异常的地方进行运行。 89 | 90 | 从上面的过程可以看出,不对申请的虚拟内存地址进行读写操作是不会触发申请新的物理内存。所以,这就解释了为什么申请 1GB 的内存,但实际上只使用了 404 KB 的物理内存。 91 | 92 | ## **5.总结** 93 | 94 | 本文主要解释了内存申请的原理,并且了解到 `malloc` 申请的只是虚拟内存,而且物理内存的申请延迟到对虚拟内存进行读写的时候,这样做可以减轻进程对物理内存使用的压力 95 | 96 | 原文地址:https://zhuanlan.zhihu.com/p/423692007 97 | 98 | 作者:linux -------------------------------------------------------------------------------- /【NO.526】Linux服务器开发,fastdfs架构分析和配置.md: -------------------------------------------------------------------------------- 1 | # 【NO.526】Linux服务器开发,fastdfs架构分析和配置 2 | 3 | ## 1.前言 4 | 5 | 了解fastdfs存储,对了解ceph、hdfs都有很大的帮助。对于数据存储,我们最关心的问题是: 6 | 7 | - 文件丢失,比如某一个盘崩了会不会导致我们所有的盘丢失。 8 | - 上传速度 9 | - 下载速度 10 | - 水平扩展-group多个分组 11 | 12 | fastdfs可以应对单点故障,是弱一致性的存储方案,一个storage是一个服务器,3个storage就够了否则会影响同步的效率。 13 | 14 | ## 2.框架 15 | 16 | 17 | 18 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/49c0a312577f4e71941d438646f08276.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 19 | 20 | 21 | 22 | Tracker Server:只是作为代理,并非实际存储文件的。 23 | Storage Server:是文件存储服务,同一个group可以有多喝storage,每个同group的storage文件是一样的。一个storage可以挂载多个磁盘。 24 | 25 | - /组名/磁盘/目录/文件名 26 | 27 | 比如当我们上传一个文件到group1时,存储到了storage1,那就要询问storage2有没有被同步,否则不能到storage2中去下载,后面会继续深入探讨。 28 | 29 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/997e685c93ab4c91a7c3407083800bfd.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 30 | 31 | 32 | 33 | 在少写多读的场景,可以一个group多个storage。小视频播放,1000人观看,3个和10个storage对比,肯定10个storage更加滋润。 34 | 35 | 一般的,Tracker Server和Storage Server不会部署在同一台服务器上,要分开部署。 36 | 37 | ```bash 38 | lsof -i:23200 39 | ``` 40 | 41 | ## 3.总结 42 | 43 | Darren老师建议备份一下nginx服务,因为担心不熟悉。我觉得从这件事可以明白一个道理,就是对于不熟悉的东西,应该留雨余地谨慎操作。通过今天老师的讲解,对fastfds项目有了一个大致的了解,对于一个新手来说,配置文件和通讯流程还是比较复杂的,相信下一节课我会涨更多的见识。 44 | 45 | 原文作者:[屯门山鸡叫我小鸡](https://blog.csdn.net/sinat_28294665) 46 | 47 | 原文链接:https://bbs.csdn.net/topics/605063191 -------------------------------------------------------------------------------- /【NO.535】服务器开发,无锁消息队列实现(初步认识).md: -------------------------------------------------------------------------------- 1 | # 【NO.535】服务器开发,无锁消息队列实现(初步认识) 2 | 3 | ## 1.多种锁效率对比 4 | 5 | 6 | 7 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/76463554390143c4958019abe5b00fb1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 8 | 9 | 10 | mutex 11 | blockqueue:condition+mutex 12 | lockfreequeue:指针方式的无锁队列 13 | arraylockfreeque:数组方式实现无锁队列 14 | ypipe:是接下来的额重点 15 | 无锁队列应用在元素非常多,每秒处理几十万的数据,几百个数据的话就是杀鸡用牛刀,没有任何帮助。为什么几百个数据不合适?因为几百个数据经常会造成condition_wait处于阻塞的状态,所以建议数据量要足够的大。 16 | 17 | 18 | 19 | ## 2.ypipe的特性: 20 | 21 | - 一写一读,不支持多读多写,多读多写会消耗一定的内容。 22 | 23 | - 链表方式去分配节点,采用chunk机制,减少节点的分配时间。chunk就是一次分配多个节点。 24 | 25 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/f44336eb2e294b9883d671723e48c671.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 26 | 27 | 个人理解Darren老师讲解的意思是使用chunk一次分配多个节点,可以节省分配资源的耗时。从20-30-20数据量有这个变动的过程中,chunk元素也会有所波动。为了提上性能,中间30会回到20个元素的状态,不过多出来的10给元素位置并不会立刻释放,而是保留最新的数据,等到时机成熟便可以轻松使用。 28 | 29 | ## 批量写入 30 | 31 | 批量写入在kafka,tcp都有所应用,通过多次写入统一调用flush函数读端才能显示,目的是提高吞吐量。 32 | 33 | ## 3.无锁队列原理 34 | 35 | 如果使用condition+mutex这种方式,如何知道读端是在休眠我正好去唤醒?以前也从来没有考虑过这个问题,不可能发一个消息就去notify,效率会大打折扣。notify这个东西导致用户态和内核态相互切换,肯定是会影响效率的。 36 | 37 | ### 3.1 当读端没有数据,这时候应该怎么办? 38 | 39 | - sleep睡一两毫秒算是一个办法,不过这样会导致吞吐量上不去。只能用condition_wait+mutx 这钟方式。 40 | 41 | #### 写端如何去唤醒数据呢? 42 | 43 | condition_wait+mutex这种方式了。 44 | 45 | 46 | 47 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/5b16795d2e104438a0fd214c6bf5c71f.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 48 | 49 | 50 | 51 | - 大部分情况下,end_chunk和back_chunk指向的都是相同的一个chunk。 52 | 53 | - back_pos表示当前chunk要插入的位置,而end_pos表示结束的位置。 54 | 55 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/2fcdf8ce0cf54fdc9662763b8414005c.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 56 | 57 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/bfbef2f5b3164deb994b1438475c20a1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 58 | 59 | 需要注意的是,使用无锁队列也要解决阻塞和唤醒的问题,如果性能差别不大那使用无锁队列就值得商榷了,性能提示10%都没必要考虑使用无锁队列。 60 | 61 | ## 4.总结 62 | 63 | 源码剖析这部分笔者的理解确实还不到位,所以不想妄加评论,留在下一期继续更新。通过Darren老师本节课程的讲解,我只是初步认识到了无锁队列的概念,大概明白了原理。更重要的是,认识到了无锁队列的重要性,如果性能提升不大就可以不优先考虑无锁队列。如果项目上有需要,最优的策略也是先完成工作,应付好任务,到项目性能需要提升时,再仔细的研究无锁队列,毕竟无锁队列想要商业上应用,还是一件比较困难的事。确实是有必要学习上一期的无锁队列,一天能把无锁队列研究清楚就已经是一件令人开心的事了! 64 | 65 | 原文作者:[屯门山鸡叫我小鸡](https://blog.csdn.net/sinat_28294665) 66 | 67 | 原文链接:https://bbs.csdn.net/topics/604954011 -------------------------------------------------------------------------------- /【NO.537】Linux服务器开发,异步请求池框架实现,协程前传.md: -------------------------------------------------------------------------------- 1 | # 【NO.537】Linux服务器开发,异步请求池框架实现,协程前传 2 | 3 | ## 1.前言 4 | 5 | 服务器发送完请求不用等待对端回数据,而是用另一个线程进行等待接收收据。 6 | \# 7 | 1、发送请教,1个io还是多个io 是多个io 8 | 2、接收结果的线程,如何拿到fd 9 | 解决方法是每次发送出的fd,放到epoll中,接收线程有io可读就读。 10 | 11 | ## 2.King式四元组 12 | 13 | 1、init 14 | 15 | - epoll_create 16 | - pthread_create 17 | 2、commit 18 | - 创建socket 19 | - 连接server 20 | - 准备协议encode 21 | - send 22 | - 加入epoll 23 | 3、thread_callback 24 | - while(){ 25 | recv(); 26 | parser(); 27 | fd->epoll_delete 28 | } 29 | 4、destory 30 | - close(fd) 31 | - phthred_cancel 32 | 33 | 服务器向第三方服务提供请求,拿redis举例:set,get等命令,每一个命令都有一个回调函数处理请教,通过epoll_ctl()增加节点,epoll_wait()函数去等待检测的数据。只是传入一个指针,不会影响红黑树的大小。 34 | 35 | c和c++最大的特点就是内存泄漏。 36 | 发送信号提交commit后,让出协程call_back,检测是否有数据到达。 37 | 38 | 线程创建确实会失败,程序运行时间较短确实不容易捕捉这一现象,但是长时间就不一定了,所以要判断返回值。 39 | 40 | ## 3.DNS 工作原理举例 41 | 42 | 43 | 44 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/6c28b303ff584d9aa5dadd8758cadc91.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 45 | 46 | 47 | 48 | ## 4.补充 49 | 50 | 内存池最不好理解,需要重点去关注的方面: 51 | 52 | - 内存块的组织 53 | - 内存分配 54 | - 内存回收 55 | 56 | ## 5.总结 57 | 58 | 通过今天的异步请求池框架的实现学习,我对异步请求池的实现思路已经是非常清晰了。异步请求池被King老师成为协程的前传,和后面要学的skynet以及协程的调度有着紧密的关系,后续的课程始终在一种懵懂的状态,看完这节课有一种如梦方醒的感觉,这个感觉美妙极了。 59 | 最后,感谢一路上帮助小生的朋友们,感谢零声学院的King老师~ 60 | 61 | 原文作者:[屯门山鸡叫我小鸡](https://blog.csdn.net/sinat_28294665) 62 | 63 | 原文链接:https://bbs.csdn.net/topics/604990131 -------------------------------------------------------------------------------- /【NO.539】Linux服务器开发,线程池原理与实现.md: -------------------------------------------------------------------------------- 1 | # 【NO.539】Linux服务器开发,线程池原理与实现 2 | 3 | ## 0.前言 4 | 5 | ## 1.线程池究竟解决了什么问题? 6 | 7 | - 可能第一感觉是线程池减少线程创建销毁,但是这是站在线程的角度去思考的。 8 | - 异步解耦的作用 主循环只做一个抛任务,写日志交给线程池,提高了程序运行效率。 9 | 10 | **注重cpu的处理能力的时候才会去粘合,注重的任务的话还是不要粘合为好。** 11 | 12 | ## 2.API 13 | 14 | - create、init 15 | - push_task 返回的结果对于业务来说作用不大 16 | - destroy、deinit 17 | 锦上添花的api:task_count,free_thread。 18 | 19 | ## 3.线程池的作用 20 | 21 | - 线程池是管理任务队列和工作队列的管理组件。 22 | 23 | ## 4.工作原理 24 | 25 | - 线程池并非内存池那般,从池子里面取用完归还,而是线程之间争夺资源。 26 | 27 | - 通过线程池的入口函数去获取任务,循环往复,再取任务再循环。 28 | 29 | - 生产者消费者模型:任务队列是生产者,工作队列是消费者,加锁获取资源。 30 | 31 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/720ae11ba22c4e8182e488396a6d1f32.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center) 32 | 33 | ## 5.手写线程池 34 | 35 | - 可以对任务进行区分,加优先级,更优秀的做法可以创建多个线程池对象。 36 | - 条件等待,进来时候解锁,出来加锁。 37 | - 当往任务队列中添加任务,应该先加锁锁住资源,中间发送条件变量的信号。 38 | 39 | ```c 40 | #define LL_ADD(item, list) do { \ 41 | 42 | 43 | 44 | item->prev = NULL; \ 45 | 46 | 47 | 48 | item->next = list; \ 49 | 50 | 51 | 52 | if(list !< NULL) list *prev =item \ 53 | 54 | 55 | 56 | list = item; \ 57 | 58 | 59 | 60 | } while(0) 61 | ``` 62 | 63 | - 利用宏向链表中添加数据,这个感觉奇妙啊~ 64 | 65 | ```c 66 | while (1) { 67 | 68 | 69 | 70 | pthread_mutex_lock(&worker->workqueue->jobs_mtx); 71 | 72 | 73 | 74 | while (worker->workqueue->waiting_jobs == NULL) { 75 | 76 | 77 | 78 | if (worker->terminate) break; 79 | 80 | 81 | 82 | pthread_cond_wait(&worker->workqueue->jobs_cond, &worker->workqueue->jobs_mtx); 83 | 84 | 85 | 86 | } 87 | 88 | 89 | 90 | if (worker->terminate) { 91 | 92 | 93 | 94 | pthread_mutex_unlock(&worker->workqueue->jobs_mtx); 95 | 96 | 97 | 98 | break;} 99 | 100 | 101 | 102 | nJob *job = worker->workqueue->waiting_jobs; 103 | 104 | 105 | 106 | if (job != NULL) { 107 | 108 | 109 | 110 | LL_REMOVE(job, worker->workqueue->waiting_jobs);} 111 | 112 | 113 | 114 | pthread_mutex_unlock(&worker->workqueue->jobs_mtx); 115 | 116 | 117 | 118 | if (job == NULL) continue; 119 | 120 | 121 | 122 | job->job_function(job); 123 | 124 | 125 | 126 | } 127 | ``` 128 | 129 | - 条件变量这块,是先解锁再加锁,King老师反复强调。不停的判断job为空,需要注意一下,担心万一被别的线程占用了怎么办。 130 | pthread_mutex_lock(&workqueue->jobs_mtx); 131 | 132 | ```c 133 | workqueue->workers = NULL; 134 | 135 | 136 | 137 | workqueue->waiting_jobs = NULL; 138 | 139 | 140 | 141 | pthread_cond_broadcast(&workqueue->jobs_cond); 142 | 143 | 144 | 145 | pthread_mutex_unlock(&workqueue->jobs_mtx); 146 | ``` 147 | 148 | - 通知等待着的线程,不折不扣的生产者,不过发送信号也需要加锁。防止竞争。 149 | 150 | ## 6.对比nginx线程池 151 | 152 | 我们的线程池为双链表,nginx的队列为什么使用二级指针。因为一级指针能指向struct ngx_thread_task_s的第一项ngx_thread_task_t *next的位置,这也正是ngx的巧妙之处。 153 | 154 | ## 7.总结 155 | 156 | 通过今天的学习,不仅学习到了有关线程池的知识,更重要的King老师讲述了学习的三个过程:1、逻辑想通;2、代码调通;3、对照学习。目测小生只是刚刚能做好第一步,接下来还是要付出更多的努力才能将这些知识驾驭娴熟。 157 | 这节课确实是比较轻松的,不过需要注意的是King老师在添加删除链表时使用的宏方法,这个也不是第一次见到了,希望能掌握好成为自己的东西。线程之间的条件等待也绝不是第一次听说,wait使得所有线程都爬在此处,等待任务队列中有数据,随时触发线程之前抢占资源。最后就是不停的判断job是否为空,因为担心被其他线程抢走。 158 | 在未来的时间里,我会利用C++11的std::thread函数从新改造这个线程池,希望达到异曲同工之妙。 159 | 160 | 原文作者:[屯门山鸡叫我小鸡](https://blog.csdn.net/sinat_28294665) 161 | 162 | 原文链接:hhttps://bbs.csdn.net/topics/604976777 -------------------------------------------------------------------------------- /【NO.540】Linux服务器开发,应用层协议设计ProtoBufThrift.md: -------------------------------------------------------------------------------- 1 | # 【NO.540】Linux服务器开发,应用层协议设计ProtoBuf/Thrift 2 | 3 | ## 0.前言 4 | 5 | 协议对于前后端通讯是非常重要的,作为开发新手往往不能理解和掌握协议的设计,导致增加了很多不必要的工作。 6 | 7 | ## 1.为什么需要协议设计 8 | 9 | 让发送端和接收端的通道能理解之间发送的数据。 10 | 11 | ## 2.消息帧的完整性判断 12 | 13 | 四种判断消息的完整性 14 | 15 | - 固定长度 16 | - 特别标记/r/n 17 | - 固定消息头+消息体结构 18 | - 序列化后的buffer前面增加一个字符流的头部,其中有一个字段村村消息总长度。 19 | 20 | ## 3.协议设计范例 21 | 22 | 23 | 24 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/f85bc2f79e8a4c59b1ec54b2e33bcc15.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 25 | 26 | 27 | 28 | ## 4.jason/xml/protobuf不同序列化对比 29 | 30 | - xm本地配置,ui配置qt,Androidl 31 | - json,websocket http协议,注册账号,web里面登录 32 | - protobuf,业务内部使用,各个服务之间rpc调用,即时通讯项目,游戏项目,带宽占用少的。 33 | 34 | IDL(Interface description language)接口描述语言:将文件通过工具生成.c文件。 35 | 完整的protobuf库支持C++反射。 36 | 37 | 项目.模块.proto 38 | 39 | 40 | 41 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/08a22ca87cdf492082acf5c88a78f4c7.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bGv6Zeo5bGx6bih5Y-r5oiR5bCP6bih,size_20,color_FFFFFF,t_70,g_se,x_16) 42 | 43 | 44 | 45 | ## 5.protobuf工程实践和原理 46 | 47 | base128 Bariants 表示值 : 每个字节的最高位不能直接用来表示我们的value,它是用来判断自己是否结束。0代表解释,1表示没有结束。小端形式base128。 48 | Zigzag:针对负数进行优化,内部将int32类型负数转化为uint64来处理。都是整数用int32,有负数用sint32。 49 | 50 | ## 6.总结 51 | 52 | Darren老师建议把person对象进行手写一下,最起码要把protobuf文件进行修改,我觉得也是非常有必要的。从目前自己对知识掌握的程度上来看,二次学习是必须的,既然已经花了大力气就应该把知识完全掌握,那就既来之则安之吧! 53 | 54 | 原文作者:[屯门山鸡叫我小鸡](https://blog.csdn.net/sinat_28294665) 55 | 56 | 原文链接:https://bbs.csdn.net/topics/605060882 -------------------------------------------------------------------------------- /【NO.543】redis计数,布隆过滤器,hyperloglog.md: -------------------------------------------------------------------------------- 1 | # 【NO.543】redis计数,布隆过滤器,hyperloglog 2 | 3 | # 1.redis计数 4 | 5 | 6 | 7 | ![img](https://img-community.csdnimg.cn/images/92ed170fa1b045df9424d0f3b041be73.png) 8 | 9 | 10 | 11 | # 2.布隆过滤器 12 | 13 | ## 2.1 redis扩展 14 | 15 | redis通过对外提供一套API和一些数据结构,可以供开发者开发自己的模块并加载到redis中。 16 | 17 | ### 2.1.1 本质 18 | 19 | 在不侵入redis源码的基础上,提供一种高效的扩展数据结构的方式。 20 | 21 | ### 2.1.2 API及数据结构 22 | 23 | 参考redismodule.h 24 | 25 | ## 2.2 RedisBloom 26 | 27 | RedisBloom是redis的一个扩展,我们主要使用了它的布隆过滤器。 28 | 29 | 关于布隆过滤器的原理,参考[《hash,bloomfilter,分布式一致性hash》](https://blog.csdn.net/congchp/article/details/122882760) 30 | 31 | ### 2.2.1 加载到redis中的方法 32 | 33 | ```shell 34 | git clone https://github.com/RedisBloom/RedisBloom.git 35 | 36 | 37 | 38 | cd RedisBloom 39 | 40 | 41 | 42 | git submodule update --init --recursive 43 | 44 | 45 | 46 | make 47 | 48 | 49 | 50 | cp redisbloom.so /path/to 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | # 命令方式加载 59 | 60 | 61 | 62 | redis-server --loadmodule /path/to/redisbloom.so 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | #配置文件加载 71 | 72 | 73 | 74 | vi redis.conf 75 | 76 | 77 | 78 | # loadmodules /path/to/redisbloom.so 79 | ``` 80 | 81 | ### 2.2.2 命令 82 | 83 | redis计数中的使用方法如下: 84 | 85 | ```shell 86 | # 为 bfkey 所对应的布隆过滤器分配内存,参数是误差率以及预期元素数量,根据运算得出需要多少hash函数 87 | 88 | 89 | 90 | 以及所需位图大小 91 | 92 | 93 | 94 | bf.reserve bfkey 0.0000001 10000 95 | 96 | 97 | 98 | # 检测 bfkey 所对应的布隆过滤器是否包含 userid 99 | 100 | 101 | 102 | bf.exists bfkey userid 103 | 104 | 105 | 106 | # 往 key 所对应的布隆过滤器中添加 userid 字段 107 | 108 | 109 | 110 | bf.add bfkey userid 111 | ``` 112 | 113 | # 3.hyperloglog 114 | 115 | 布隆过滤器提供了一种节省内存的概率型数据结构,它不保存具体数据,存在误差。 116 | 117 | hyperloglog也是一种概率型数据结构,相对于布隆过滤器,使用的内存更少。 118 | 119 | 为什么叫hyperloglog? 120 | 121 | 是因为空间复杂度非常低,是$O(log_2log_2n)$。 122 | 123 | redis提供了hyperloglog。在redis实现中,每个hyperloglog只是用**固定的12kb**进行计数,使用16384($2^{14}$)个桶子,标准误差为`0.8125%`,可以统计$2^{64}$个元素。 124 | 125 | 126 | 127 | ![img](https://img-community.csdnimg.cn/images/013f90d6c04f46058abd4b1e29cce768.png) 128 | 129 | 130 | 131 | ## 3.1 本质 132 | 133 | 使用少量内存统计集合的基数的近似值。存在一定的误差。 134 | 135 | HLL的误差率:$\frac{1.04}{\sqrt{m}}$, m是桶子的个数;对于redis,误差率就是`0.8125%`。 136 | 137 | ## 3.2 原理 138 | 139 | HyperLogLog 原理是通过给定 n 个的元素集合,记录集合中数字的比特串第一个1出现位置的最大值k,也可以理解为统计二进制低位连续为零(前导零)的最大个数。通过k值可以估算集合中不重复元素的数量m,m近似等于$2^k$。 140 | 141 | 但是这种预估方法存在较大误差,为了改善误差情况,HyperLogLog中引入分桶平均的概念,计算 m 个桶的调和平均值。下面公式中的const是一个修正常量。 142 | 143 | $DV_HLL = const*m*\frac{m}{\sum_{j=1}^m\frac{1}{2^{R_j}}}$ 144 | 145 | $\frac{1}{2^{R_j}}$: 每个桶的估算值 146 | 147 | $\frac{m}{\sum_{j=1}^m\frac{1}{2^{R_j}}}$:所有桶估值计值的调和平均值。 148 | 149 | redis的hyperloglog中有16384($2^{14}$)个桶,每一个桶占6bit; 150 | 151 | 对于一个要放入集合中的字符串,hash生成64位整数,其中后14位用来索引桶子;前面50位用来统计低位连续为0的最大个数, 最大个数49。6bit对应的是$2^6$对应的整数值64可以存储49。在设置前,要设置进桶的值是否大于桶中的旧值,如果大于才进行设置,否则不进行设置。 152 | 153 | 154 | 155 | ![img](https://img-community.csdnimg.cn/images/a3ffcbeccef64e329af797c9b68bb51b.png) 156 | 157 | 158 | 159 | ## 3.3 去重 160 | 161 | 相同元素通过hash函数会生成相同的 64 位整数,它会索引到相同的桶子中,累计0的个数也会相同,按照上面计算最长累计0的规则,它不会改变该桶子的最长累计0; 162 | 163 | ## 3.4 存储 164 | 165 | redis 中的 hyperloglog 存储分为稀疏存储和紧凑存储; 166 | 167 | 当元素很少的时候,redis采用节约内存的策略,hyperloglog采用稀疏存储方案;当存储大小超过3000 的时候,hyperloglog 会从稀疏存储方案转换为紧凑存储方案;紧凑存储不会转换为稀疏存储,因为hyperloglog数据只会增加不会减少(不存储具体元素,所以没法删除); 168 | 169 | ## 3.5 命令 170 | 171 | ```shell 172 | # 往key对应的hyperloglog对象添加元素 173 | 174 | 175 | 176 | pfadd key userid_1 userid_2 userid_3 177 | 178 | 179 | 180 | # 获取key对应hyperloglog中集合的基数 181 | 182 | 183 | 184 | pfcount key 185 | 186 | 187 | 188 | # 往key对应过的hyperloglog中添加重复元素。已存在,并不会添加 189 | 190 | 191 | 192 | pfadd key userid_1 193 | 194 | 195 | 196 | pfadd key1 userid_2 userid_3 userid_4 197 | 198 | 199 | 200 | # 合并key,key1到key2,会去重 201 | 202 | 203 | 204 | pfmerge key2 key key1 205 | ``` 206 | 207 | 原文作者:[congchp](https://blog.csdn.net/congchp) 208 | 209 | 原文链接:https://bbs.csdn.net/topics/604985776 -------------------------------------------------------------------------------- /【NO.548】MYSQL---服务器配置相关问题.md: -------------------------------------------------------------------------------- 1 | # 【NO.548】MYSQL---服务器配置相关问题 2 | 3 | ## 1.相关面试问题 4 | 5 | 1. 请分析一个Group By 语句的异常原因 6 | 2. 如何比较系统运行配置和配置文件中配置是否一致? 7 | 3. 举几个mysql 中的关键性能参数 8 | 9 | ## 2. 请分析一个Group By 语句的异常原因 10 | 11 | 12 | 可能这个问题会被认为是sql语句的原因, 刚开始我也是这么认为的。不过不急,可以思考思考在看看下面解释 13 | 14 | 假设有一个这样的表 15 | 16 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/9acbac0309bc4cce8c4710cd7290165e.png) 17 | 18 | sql 语句 : 19 | select prodcut_id, warehouse_id, sum(count) as cnt from stock group by product_id; 20 | 21 | 结果: 22 | 23 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/f558e972ef4145b48f8a3aeb56287ce1.png) 24 | 25 | 在mysql 中的能执行成功,在其他的数据库可能出现语法错误 26 | 27 | 为什么能在mysql 中执行成功,是因为在mysql中的一个配置起了作用, 它就是 SQL MODE。 28 | 29 | ### 2.1 SQL MODE 的作用及设置 30 | 31 | SQL_MODE值 : 会影响mysql 执行sql语句的结果。 32 | 33 | 配置mysql 处理SQL 的方式 34 | set [session/global/persist] sql_mode = ‘xxxxxx’ (persist是在mysql 8.0 中的) 35 | [mysqld] sql_mode = xxxxxxx 36 | 37 | ### 2.2 常用的SQL Mode 38 | 39 | SQL_MODE= ‘ANSI’ 40 | 41 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/8c609b6b2345484e9219ca9465ea7f16.png)SQL_MODE = ‘TRADITIONAL’ 42 | 43 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/05d87f00bb0d48fdbabf3f605283acee.png) 44 | 45 | ### 2.3 演示 only_full_group_by 46 | 47 | 1.刚开始查看sql_mode 48 | 49 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/d4f1db7f25f941a5b2afa145c810561d.png) 50 | 51 | 2.执行sql语句, 可以的看到是执行成功的 52 | 53 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/3c777b3e4ff24d09a887cbb3144d8951.png) 54 | 55 | 3.修改sql_mode 56 | 57 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/0136ee82d3b24bccac1728aa8d4211b0.png) 58 | 59 | 4.再次执行上面的sql语句,会报错 60 | 61 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/2c7abcb14df54fa5a8146616945c822d.png) 62 | 63 | 5.此时必须在group by 后面写完整 64 | 65 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/95675139bc554415871e59d68a44ac33.png) 66 | 67 | ### 2.4 演示 ansi_quotes 68 | 69 | 使用之后只能用单引号引字符串 70 | 71 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/6ed2b6d18446476081b8010f2199399a.png) 72 | 73 | ### 2.5 演示 strict_trans_table 74 | 75 | 用普通模式,字符串插入int 类型 会成功 76 | 用严格模式,则会进行检查,字符串不能插入int 类型成功 77 | 78 | 79 | 80 | ## 3.比较系统运行配置和配置文件中配置 81 | 82 | ### 3.1 知识点 83 | 84 | ### 3.2 使用set 命令 配置动态参数 85 | 86 | 使用pt-config-diff 工具比较配置文件 (检查在运行中的配置和系统配置) 87 | 使用set 命令 配置动态参数 88 | set [session | @@session.] system_var_name = expr 89 | set [global | @@global .] system_var_name = expr 90 | set [persist | @@persist .] system_var_name = expr (mysql 8.0 中增加) 91 | 92 | ### 3.3 检查在运行中的配置和系统配置(mysql 5.x) 93 | 94 | pt-config-diff u=root, p=, h = localhost /etc/my.cnf 95 | 96 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/24e7155427734dc5a0cc8d1636b0d341.png) 97 | 98 | ## 4.举几个mysql 中的关键性能参数 99 | 100 | ### 4.1 常用的性能参数 101 | 102 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/6b2bb2968aea45ce8edb6a9638db3a89.png) 103 | 104 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/0178572f3afb49ce8fdd1ab55b51f1f2.png) 105 | 106 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/214d16326f104c66b000a92c69a25ab9.png)版权声明:本文为CSDN博主「_刘小雨」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 107 | 原文链接:https://blog.csdn.net/qq_39486027/article/details/125210884 -------------------------------------------------------------------------------- /【NO.555】数据库设计的三范式和反范式.md: -------------------------------------------------------------------------------- 1 | # 【NO.555】数据库设计的三范式和反范式 2 | 3 | ## 1.范式的概念 4 | 5 | 为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。 6 | 7 | 三范式和反范式是空间和时间的关系。三范式是为了降低空间;反范式是通过增加空间来提升运行效率。 8 | 9 | ## 2.三范式 10 | 11 | (1)目的:减少空间占用。 12 | (2)内容:列不可分、依赖主键(联合索引)、在依赖主键(联合索引)的基础上直接依赖。 13 | 14 | ### 2.1 范式一 15 | 16 | 确保每列保持原子性;数据库表中的所有字段都是不可分解的原子值。 17 | 例如:某表中有一个地址字段,如果经常需要访问地址字段中的城市属性,则需要将该字段拆分为多个字段,省份、城市、详细地址等。 18 | 19 | ![image-20230225155314101](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230225155314101.png) 20 | 可以才分为: 21 | 22 | ![image-20230225155327216](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230225155327216.png) 23 | 24 | ### 2.2 范式二 25 | 26 | 确保表中的每列都和主键相关,而不能只与主键的某一部分相关(组合索引)。 27 | 28 | ![image-20230225155346455](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230225155346455.png) 29 | 因为订单编号与客户的信息相关,订单编号和商品编号一起唯一确定数量,商品编号和商品信息相关;所以可以拆分成三个表: 30 | 31 | ![image-20230225155359192](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230225155359192.png) 32 | 33 | ### 2.3 范式三 34 | 35 | 确保每列都和主键直接相关,而不是间接相关;减少数据冗余。 36 | 37 | 例如 38 | 39 | ![image-20230225155412047](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230225155412047.png) 40 | 可以拆分为: 41 | 42 | ![image-20230225155432650](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230225155432650.png) 43 | 44 | ## 3.反范式 45 | 46 | 反范式是经常使用的设计。比如用户表采用的就是反范式,因为如果用户表不采用反范式设计,将会产生很多的关联关系表,这就会涉及到联表查询,非常影响效率,特别对登录来说,是不可容忍的。 47 | 48 | 因此,反范式允许冗余存储,为了提升查询效率。 49 | 50 | ## 4.总结 51 | 52 | 范式二中,对于联合索引,主键不能依赖一部分,而要依赖整体;出现重复的要拆分。 53 | 反范式是经常使用的设计。三范式可以避免数据冗余,减少数据库的空间,减小维护数据完整性的麻烦。但是采用数据库范式化设计,可能导致数据库业务涉及的表变多,并且造成更多的联表查询,将导致整个系统的性能降低。因此处于性能考虑,可能需要进行反范式设计。 54 | ———————————————— 55 | 版权声明:本文为CSDN博主「Lion Long」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 56 | 原文链接:https://blog.csdn.net/Long_xu/article/details/127532598 -------------------------------------------------------------------------------- /【NO.557】Linux内存管理-详解mmap原理.md: -------------------------------------------------------------------------------- 1 | # 【NO.557】Linux内存管理-详解mmap原理 2 | 3 | ## **1. 一句话概括mmap** 4 | 5 | mmap的作用,在应用这一层,是让你把文件的某一段,当作内存一样来访问。将文件映射到物理内存,将进程虚拟空间映射到那块内存。 6 | 7 | 这样,进程不仅能像访问内存一样读写文件,多个进程映射同一文件,还能保证虚拟空间映射到同一块物理内存,达到内存共享的作用。 8 | 9 | ## **2.** 虚拟内存**?虚拟空间?** 10 | 11 | 其实是一个概念,前一篇对于这个词没有确切的定义,现在定义一下: 12 | 13 | 虚拟空间就是进程看到的所有地址组成的空间,虚拟空间是某个进程对分配给它的所有物理地址(已经分配的和将会分配的)的重新映射。 14 | 15 | 而虚拟内存,为啥叫虚拟内存,是因为它就不是真正的内存,是假的,因为它是由地址组成的空间,所以在这里,使用虚拟空间这个词更加确切和易懂。(不过虚拟内存这个词也不算错) 16 | 17 | ### **2.1 虚拟空间原理** 18 | 19 | #### **2.1.1物理内存** 20 | 21 | 首先,物理地址实际上也不是连续的,通常是包含作为主存的DRAM和IO寄存器 22 | 23 | ![img](https://pic4.zhimg.com/80/v2-acf9edf723cc9fec950f48a4bf72158f_720w.webp) 24 | 25 | 以前的CPU(如X86)是为IO划分单独的地址空间,所以不能用直接访问内存的方式(如指针)IO,只能用专门的方法(in/read/out/write)诸如此类。 26 | 27 | 现在的CPU利用PCI总线将IO寄存器映射到物理内存,所以出现了基于内存访问的IO。 28 | 29 | 还有一点补充的,就如同进程空间有一块内核空间一样,物理内存也会有极小一部分是不能访问的,为内核所用。 30 | 31 | #### **2.1.2三个总线** 32 | 33 | 这里再补充下三个总线的知识,即:地址总线、数据总线、控制总线 34 | 35 | - 地址总线,用来传输地址 36 | - 数据总线,用来传输数据 37 | - 控制总线,用来传输命令 38 | 39 | 比如CPU通过控制总线发送读取命令,同时用地址总线发送要读取的数据虚地址,经过MMU后到内存 40 | 41 | 内存通过数据总线将数据传输给CPU。 42 | 43 | 虚拟地址的空间和指令集的地址长度有关,不一定和物理地址长度一致,比如现在的64位处理器,从VA角度看来,可以访问64位的地址,但地址总线长度只有48位,所以你可以访问一个位于2^52这个位置的地址。 44 | 45 | #### **2.1.3虚拟内存地址转换(虚地址转实地址)** 46 | 47 | 上面已经明确了虚拟内存是虚拟空间,即地址的集合这一概念。基于此,来说说原理。 48 | 49 | 如果还记得操作系统课程里面提到的虚地址,那么这个虚地址就是虚拟空间的地址了,虚地址通过转换得到实地址,转换方式课程内也讲得很清楚,虚地址头部包含了页号(段地址和段大小,看存储模式: 页存储、段存储,段页式),剩下部分是偏移量,经过MMU转换成实地址。 50 | 51 | ![img](https://pic3.zhimg.com/80/v2-77aad2f066c524ae9e3e12123f54ea42_720w.webp) 52 | 53 | 存储方式 54 | 55 | ![img](https://pic3.zhimg.com/80/v2-210e67b7c2b59e0c583e3c9f0c3221de_720w.webp) 56 | 57 | 如图则是页式存储动态地址变换的方式 58 | 59 | 虚拟地址头部为页号通过查询页表得到物理页号,假设一页时1K,那么页号*偏移量就得到物理地址 60 | 61 | ![img](https://pic4.zhimg.com/80/v2-135c6be0231c3323b0a3aa5a315d4447_720w.webp) 62 | 63 | 如图所示,段式存储 64 | 65 | 虚拟地址头部为段号,段表中找到段基地址加上偏移量得到实地址 66 | 67 | ![img](https://pic2.zhimg.com/80/v2-940e5377430e3cb2d64f3afea2a20489_720w.webp) 68 | 69 | 段页式结合两者,如图所示。 70 | 71 | ## **3. mmap映射** 72 | 73 | 至此,如果对虚拟空间已经了解了,那么接下来,作为coder,应该自动把虚拟空间无视掉,因为Linux的目的也是要让更多额进程能享用内存,又不让进程做麻烦的事情,是将虚拟空间和MMU都透明化,让进程(和coder)只需要管对内存怎样使用。 74 | 75 | 所以现在开始不再强调虚拟空间了。 76 | 77 | mmap就是将文件映射到内存上,进程直接对内存进行读写,然后就会反映到磁盘上。 78 | 79 | ![img](https://pic2.zhimg.com/80/v2-888968f6d713ccdcb5837e2ec85d5565_720w.webp) 80 | 81 | - 虚拟空间获取到一段连续的地址 82 | - 在没有读写的时候,这个地址指向不存在的地方(所以,上图中起始地址和终止地址是还没分配给 进程的) 83 | - 好了,根据偏移量,进程要读文件数据了,数据占在两个页当中(物理内存着色部分) 84 | - 这时,进程开始使用内存了,所以OS给这两个页分配了内存(即缺页异常)(其余部分还是没有分配) 85 | - 然后刚分配的页内是空的,所以再将相同偏移量的文件数据拷贝到物理内存对应页上。 86 | 87 | 原文地址:https://zhuanlan.zhihu.com/p/465336136 88 | 89 | 作者:linux -------------------------------------------------------------------------------- /【NO.618】浅谈linux定时器时间轮算法.md: -------------------------------------------------------------------------------- 1 | # 【NO.618】浅谈linux定时器时间轮算法 2 | 3 | ## 1.时间轮实现 4 | 5 | Linux定时器分为低精度定时器和高精度定时器两种类型,内核对其均有实现。本文讨论的是我们在应用程序开发中比较常见的低精度定时器。作为常用的基础组件,定时器常用的几种实现方法包括:基于排序链表实现、基于小根堆实现、基于红黑树实现、基于时间轮实现。本文讲解的是时间复杂度最优,也是linux内核采用的基于时间轮的实现方式。 6 | 7 | 采用有序链表实现的定时器,添加定时器的时间复杂度为O(n);采用小根堆或红黑树实现的定时器,添加定时器的时间复杂度为O(lgn)。之所以没法做到O(1)的复杂度,究其原因是所有定时器节点挂在一条链表(或一棵树)上。时间轮算法的核心思路是将定时器散列到多条链上,是典型的空间换时间的策略。下文从单个时间轮出发讲解,逐步扩展至linux实现定时器所采用的多级时间轮算法。 8 | 9 | ## 2.简单的单时间轮 10 | 11 | 单时间轮只有一个由bucket串起来的轮子,下图所示的时间轮有8个bucket,每个bucket下链接着未来对应时刻到期的节点。假设图中相邻bucket到期时间的间隔为slot=1s,从当前时刻0s开始计时,1s时到期的定时器节点挂在bucket[1]下,2s时到期的定时器节点挂在bucket[2]下……当tick检查到时间过去了1s时,bucket[1]下所有节点执行超时动作,当时间到了2s时,bucket[2]下所有节点执行超时动作……. 12 | 13 | ![img](https://pic3.zhimg.com/80/v2-cdd812bd00ff94c2375cacdec8303c42_720w.webp) 14 | 15 | 由于bucket是一个数组,能直接根据下标定位到具体定时器节点链,因此添加删除节点、定时器到期执行的时间复杂度均为O(1)。 16 | 17 | 但使用这个定时器所受的限制也显而易见:待添加的timer到期时间必须在8s以内。这显然不能满足实际需求。当然要扩展也很容易,直接增加bucket的个数就可以了。在 Linux 系统中,我们可以设置slot为1个jiffy(1/HZ)的定时器,假设最大的到期时间范围要达到 2^32个 jiffies,如果采用上面这样的单时间轮,我们就需要2^32个 bucket,这会带来巨大的内存消耗,显然是需要优化改进的。 18 | 19 | ## 3.改进的单时间轮 20 | 21 | 改进的单时间轮其实是一个对时间和空间折中的思路,即不会像单时间轮那样有O(1)的时间复杂度,但也不会像单时间轮那样对bucket个数有巨大的需求。其原理也很简单,就是每个bucket不单可以挂接到期时间expire=slot的定时器,还可挂接expire%N=slot的定时器(N为bucket个数)。这也正好顺应时间轮的轮回作用。如图2所示,定时器中expire表示到期时间,rotation表示节点在时间轮转了几圈后才到期。当当前时间指针指向某个bucket时,不能像简单时间轮那样直接对bucket下的所有节点执行超时动作,而是需要对链表中节点遍历一遍,判断轮子转动的次数是否等于节点中的rotation值,当两者相等时,方可执行超时操作。 22 | 23 | ![img](https://pic3.zhimg.com/80/v2-660ad476cd1aae5ac39ef5a59e391d46_720w.webp) 24 | 25 | ## 4.多时间轮 26 | 27 | 上面所述的时间轮都是单枪匹马战斗的,因此很难在时间和空间上都达到理想效果。Linux所实现的多时间轮算法,借鉴了日常生活中水表的度量方法,通过低刻度走得快的轮子带动高一级刻度轮子走动的方法,达到了仅使用较少刻度即可表示很大范围度量值的效果。 28 | 29 | ![img](https://pic1.zhimg.com/80/v2-cce7c9b5e10b03519576045d7dae3938_720w.webp) 30 | 31 | Linux定时器时间轮分为5个级别的轮子(tv1 ~ tv5),如图3所示。每个级别的轮子的刻度值(slot)不同,规律是次级轮子的slot等于上级轮子的slot之和。Linux定时器slot单位为1jiffy,tv1轮子分256个刻度,每个刻度大小为1jiffy。tv2轮子分64个刻度,每个刻度大小为256个jiffy,即tv1整个轮子所能表达的范围。相邻轮子也只有满足这个规律,才能达到“低刻度轮子转一圈,高刻度轮子走一格”的效果。tv3,tv4,tv5也都是分为64个刻度,因此容易算出,最高一级轮子tv5所能表达的slot范围达到了256*64*64*64*64 = 2^32 jiffies。 32 | 33 | Linux时间轮定时器算法的关键在于添加定时器操作和时间轮进位迁移链表操作。先来说添加定时器。添加定时器的关键又在于知道每个时间轮每一个刻度所能表示的到期时间的范围。图4列出了每一级时间轮能度量的jiffies的大小。假设有一个定时器在1000个jiffies后到期,根据图4容易看出其应该挂在tv2轮上。tv2轮每个刻度表示的大小为256个jiffies,则其应该挂在(1000/256)=3即第三个bucket上。 34 | 35 | Linux在定时器到期检查上的操作也实现得很巧妙。假设curr_time=0x12345678,那么下一个检查的时刻为0x12345679。如果tv1.bucket[0x79]上链表非空,则下一个检查时刻tv1.bucket[0x79]上的定时器节点超时。如果curr_time到了0x12345700,低8位为空,说明有进位产生,这时移出8~13位对应的定时器链表(即正好对应着tv2轮),重新加入定时器系统,这就完成了一次进位迁移操作。同样地,当curr_time的第8-13位为0时,这表明tv2轮对tv3轮有进位发生,将curr_time第14-19位的值作为下标,移出tv3中对应的定时器链表,然后将它们重新加入到定时器系统中来。tv4,tv5依次类推。之所以能够根据curr_time来检查超时链,是因为tv1~tv5轮的度量范围正好依次覆盖了整型的32位:tv1(1-8位),tv2(9-14位),tv3(15-20位),tv4(21-26位),tv5(27-32位);而curr_time计数的递增中,低位向高位的进位正是低级时间轮转圈带动高级时间轮走动的过程。 36 | 37 | ## 5.对比 38 | 39 | 最后比较一下多级时间轮和单个简单时间轮的时间复杂度及空间复杂度:linux使用了总计256+64+64+64+64=512个bucket,即可实现[0,2^32) jiffies的超时范围。相比简单的单时间轮,时间上仅仅多了1/256次(为约等于值,忽略了tv2以上产生的进位操作)的链表迁移操作耗时。可以认为其添加、删除定时器节点及到期check的操作时间复杂度均为O(1)。 40 | 41 | 原文地址:https://zhuanlan.zhihu.com/p/342285267 42 | 43 | 作者:linux -------------------------------------------------------------------------------- /【NO.7】什么是B+树?(详细图解).md: -------------------------------------------------------------------------------- 1 | # 【NO.7】什么是B+树?(详细图解) 2 | 3 | ## 1.简介 4 | 5 | ### 1.1一个m阶的B+树具有如下几个特征: 6 | 7 | 1. 有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。 8 | 2. 所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。 9 | 3. 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。 10 | 11 | **首先来看看B+树的结构:** 12 | 13 | ![image-20221206223121478](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206223121478.png?lastModify=1670851010) 14 | 15 | 上面的这颗树中,得出结论: 16 | 17 | - 根节点元素8是子节点2,5,8 的最大元素,也是叶子节点6,8 的最大元素; 18 | - 根节点元素15是子节点11,15 的最大元素,也是叶子节点13,15 的最大元素; 19 | - 根节点的最大元素也就是整个B+树的最大元素,以后无论插入删除多少元素,始终要保持最大的元素在根节点当中。 20 | - 由于父节点的元素都出现在子节点中,因此所有的叶子节点包含了全部元素信息,并且每一个叶子节点都带有指向下一个节点的指针,形成了一个有序链表。 21 | 22 | 23 | 24 | ## 2.卫星数据 25 | 26 | 指的的是索引元素所指向的数据记录,比如数据库中的某一行,在B-树中,无论中间节点还是叶子节点都带有卫星数据。 27 | 28 | **B-树中的卫星数据(Satellite Information)**: 29 | 30 | ![image-20221206223220726](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206223220726.png?lastModify=1670851010) 31 | 32 | **B+树中的卫星数据(Satellite Information):** 33 | 34 | 只有叶子节点带有卫星数据,其余中间节点仅仅是索引,没有任何的数据关联。 35 | 36 | ![image-20221206223307007](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206223307007.png?lastModify=1670851010) 37 | 38 | 需要补充的是,在数据库的聚集索引(Clustered Index)中,叶子节点直接包含卫星数据。在非聚集索引(NonClustered Index)中,叶子节点带有指向卫星数据的指针。 39 | 40 | ## 3.B+ 树的查询 41 | 42 | 下面分别通过单行查询和范围查询来做分析: 43 | 44 | ### 3.1 单行查询: 45 | 46 | 比如我们要查找的元素是3: 47 | 48 | B+树会自顶向下逐层查找节点,最终找到匹配的叶子节点。 49 | 50 | 第一次磁盘IO: 51 | 52 | ![image-20221206223403066](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206223403066.png?lastModify=1670851010) 53 | 54 | 第二次磁盘IO: 55 | 56 | ![image-20221206223410321](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206223410321.png?lastModify=1670851010) 57 | 58 | 第三次磁盘IO: 59 | 60 | ![image-20221206223418343](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206223418343.png?lastModify=1670851010) 61 | 62 | ### 3.2 B+树查询与B-数据对比: 63 | 64 | - B+树的中间节点没有卫星数据,所以同样大小的磁盘可以容纳更多的节点元素; 65 | - 在数据量相同的情况下,B+树的结构比B-树更加“矮胖”,因此查询的IO次数也更少; 66 | - B+树的查询必须最终查找到叶子节点,而B-树只要查找匹配的元素即可,无论匹配的元素处于中间节点还是叶子节点。 67 | - B-树的查找性能并不稳定(最好情况只查询根节点,最坏情况是查找叶子节点)。而B+树的每一次查找都是稳定的。 68 | 69 | **范围查询:** 比如我们要查询3到11的范围数据: 70 | 71 | **B-树的范围查询过程:** 自顶向下,查找到范围的下限(3): 72 | 73 | ![image-20221206224008082](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206224008082.png?lastModify=1670851010) 74 | 75 | 中序遍历到元素6: 76 | 77 | ![image-20221206224018546](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206224018546.png?lastModify=1670851010) 78 | 79 | 中序遍历到元素8: 80 | 81 | ![image-20221206224028847](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206224028847.png?lastModify=1670851010) 82 | 83 | 中序遍历到元素9: 84 | 85 | ![image-20221206224037348](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206224037348.png?lastModify=1670851010) 86 | 87 | 中序遍历到元素11,遍历结束: 88 | 89 | ![image-20221206224045156](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206224045156.png?lastModify=1670851010) 90 | 91 | **B+树的范围查找过程:** 自顶向下,查找到范围的下限(3): 92 | 93 | ![image-20221206224057364](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206224057364.png?lastModify=1670851010) 94 | 95 | 通过链表指针,遍历到元素6, 8: 96 | 97 | ![image-20221206224111959](file://C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20221206224111959.png?lastModify=1670851010) 98 | 99 | 100 | 101 | ## 4.总结 102 | 103 | B+树比B-树的优势三个: 104 | 105 | 单一节点存储更多的元素,使得查询的IO次数更少。 所有查询都要查找到叶子节点,查询性能稳定。 所有叶子节点形成有序链表,便于范围查询。 ———————————————— 版权声明:本文为CSDN博主「初念初恋」的原创文章,遵循CC 4.0 BY-SA版权协议, -------------------------------------------------------------------------------- /【NO.83】关于【零声教育】2022年12代CC++Linux服务器开发高级架构师课程学习心得,资源分享.md: -------------------------------------------------------------------------------- 1 | # 【NO.83】关于【零声教育】2022年12代C/C++Linux服务器开发高级架构师课程学习心得,资源分享 2 | 3 | ## 1.前言 4 | 5 | 对于零声教育的C/C++Linux服务器高级架构师的课程到2022目前已经迭代到12代了,像之前小编也总结过,但是课程每期都有做一定的更新,也是为了更好的完善课程跟上目前互联网大厂的岗位技术需求,之前课程里面也包含了一些小的分支,其中就有音视频开发、Linux内核开发、DPDK、golang等等一些程序员所需要的硬核技术。今天总结分析是2022年最新的课程体系。 6 | 7 | ## 2.课程定位为中高级课程,学习这个课程也有一定的要求,以下是适应学习的一些人群: 8 | 9 | 1.从事业务开发多年,对底层原理理解不够深入的在职工程师 10 | 11 | 2.从事嵌入式方向开发,想转入互联网开发的在职工程师 12 | 13 | 3.从事Qt/MFC等桌面开发的,薪资多年涨幅不大的在职工程师 14 | 15 | 4.从事非开发岗位(算法岗,运维岗,测试岗),想转后台开发岗位的在职工程师 16 | 17 | 5.工作中技术没有挑战,工作中接触不到新技术的在职工程师 18 | 19 | 6.自己研究学习速度较慢,不能系统构建知识体系的开发人员 20 | 21 | 7.了解很多技米名词,但是深入细问又不理解的工程师 22 | 23 | 8.自己研究学习速度较慢,不能系统构建知识体系的开发人员了解很多技米名词,但是深入细问又不理解的工程师 24 | 25 | ## 3.课程方式介绍:课程是在腾讯课堂以直播的形式教学 26 | 27 | 1.98次直播课,持续8个半月,直播每周二四六晚8点到10点 28 | 29 | 2.课前预习资料课后思考实践作业 30 | 31 | 3.班主任督学作业统计博客统计 32 | 33 | 4.老师答疑工作问题课程问题 34 | 35 | 5.课程涉及编程语言45%的c,25%的c++,20%的go,5%的lua,5%的其他语言(Rust, shell, java, awk, python) 36 | 37 | 6.奖学金机制最高1000元,公开透明(一-期评选一-次) 38 | 39 | ## 4.课程优势: 40 | 41 | 1.简历梳理技术点凸显项目技术梳理 42 | 43 | 2.模拟面试技术表述 44 | 45 | 3.薪资谈判福利争取 46 | 47 | 4.offer选择职业规划技术前景 48 | 49 | ## **5.往期学员学习过程的心得总结:** 50 | 51 | **1.学习要有主动性**。无论是开始的自学,还是后面的培训学习,学习的主观能动性一定要有,特别是报班学习之后,不要觉得万事有老师,外部的辅导条件能够让你有更好的学习效率和氛围,但是最终需要掌握技能的还是你自己的,所以学习的过程不要懈怠。 52 | 53 | **2.学完技术内容之后,要形成自己的技术栈体系。**我在学完之后,就根据我自己的技术内容花了三天时间整理一份c/c++后端开发需要掌握的技术体系路线图,来帮助自己梳理自己所学的技术点。 54 | 55 | **3.善于总结自己的学习过程。**每当自己学完一个小块的知识点之后,最好是将自己对它的理解整理成博客文章,这样既能自我梳理自己的学习成果,又能作为自己在面试工作时向面试官展现的一个亮点。 56 | 57 | **4.一定要复盘自己的面试过程。**在我学习之后的面试过程,并不是一帆风顺。但是我在老师的建议下,不管成功的还是失败的面试过程,场场复盘!找出自己回答的不好的地方做备注修改,这样一次次下来,对于面试,我也是越来越胸有成竹。 58 | 59 | **5.学习方式,不管黑猫白猫,抓住老鼠的就是好猫。**对于也想从事或是转行到c/c++后端开发岗的兄弟,如果考虑报班培训的话,可以推荐大家了解一下我之前学习过的课程,整个课程体系对标的是腾讯的T9级别。 60 | 61 | ## 6.课程内容总结: 62 | 63 | ### **6.1.精进基是专栏** 64 | 65 | ![img](https://pic3.zhimg.com/80/v2-b13527702b26f0a455e8e7cbb8db5852_720w.webp) 66 | 67 | ### 6.2.高性能网络专栏 68 | 69 | ![img](https://pic4.zhimg.com/80/v2-343424c17b3551c1e8e2f77e91789433_720w.webp) 70 | 71 | ### 6.3.基础组件设计专栏 72 | 73 | ![img](https://pic2.zhimg.com/80/v2-3b3396eae08403a2c21827ca57d57b5d_720w.webp) 74 | 75 | ### 6.4.中间件开发专栏 76 | 77 | ![img](https://pic2.zhimg.com/80/v2-9e9d4876f12cab678596bd4c9406bf35_720w.webp) 78 | 79 | ### 6.5.开源框架专栏 80 | 81 | ![img](https://pic3.zhimg.com/80/v2-0618943057dfe8eac2cc924c94222246_720w.webp) 82 | 83 | ### 6.6.云原生专栏 84 | 85 | ![img](https://pic4.zhimg.com/80/v2-3d26e2d0d8e6ed7003b0dc579e7c8c7f_720w.webp) 86 | 87 | ### 6.7.性能分析专栏 88 | 89 | ![img](https://pic2.zhimg.com/80/v2-ca3fbd03b4e1b68350ac071b5905e465_720w.webp) 90 | 91 | ### 6.8.分布式架构专栏 92 | 93 | ![img](https://pic4.zhimg.com/80/v2-4c995b9acf2deddfeda5f6f3ecf1b9bb_720w.webp) 94 | 95 | ### 6.9.上线项目实战 96 | 97 | ![img](https://pic3.zhimg.com/80/v2-d3d788b71c3ce34f58fb27a331249a8a_720w.webp) 98 | 99 | ### 7.**课程总结:** 100 | 101 | 现在的技术的学习曲线的增加,让我的忍耐性越来越低。各种新技术,因为新奇让人兴奋,但最终变成一场场争论。我越来越无法忍受这些充满市场宣传气息的喧嚣。我对技术看重的是稳定,清晰。 102 | 103 | 据不完全统计,截至目前(2018.07)为止,中国C++程序员的数量已经超过了100万。而且,随着IT培训业的持续发展和大量的应届毕业生进入社会,C++程序员面临的竞争压力越来越大。那么,作为一名C++程序员,怎样努力才能快速成长为一名高级的程序员或者架构师,或者说一名优秀的高级工程师或架构师应该有怎样的技术知识体系,这不仅是一个刚刚踏入职场的初级程序员,也是工作三五年之后开始迷茫的老程序员,都必须要面对和想明白的问题。 104 | 105 | 技术的瓶颈是认知的问题, 认知不是知其名,还需要知其因,更需要知其原。 106 | 107 | 最后祝大家都学有所成。 108 | 109 | 原文链接:https://zhuanlan.zhihu.com/p/519780422 110 | 111 | 作者:[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10) -------------------------------------------------------------------------------- /【NO.9】TCPIP 介绍.md: -------------------------------------------------------------------------------- 1 | # 【NO.9】TCP/IP 介绍 2 | 3 | TCP/IP 是用于因特网 (Internet) 的通信协议。 4 | 5 | ## 1.计算机通信协议(Computer Communication Protocol) 6 | 7 | 计算机通信协议是对那些计算机必须遵守以便彼此通信的的规则的描述。 8 | 9 | ## 2.什么是 TCP/IP? 10 | 11 | TCP/IP 是供已连接因特网的计算机进行通信的通信协议。 12 | 13 | TCP/IP 指传输控制协议/网际协议(***T\*ransmission \*C\*ontrol \*P\*rotocol** / ***I\*nternet \*P\*rotocol**)。 14 | 15 | TCP/IP 定义了电子设备(比如计算机)如何连入因特网,以及数据如何在它们之间传输的标准。 16 | 17 | ## 3.在 TCP/IP 内部 18 | 19 | 在 TCP/IP 中包含一系列用于处理数据通信的协议: 20 | 21 | - TCP (传输控制协议) - 应用程序之间通信 22 | - UDP (用户数据报协议) - 应用程序之间的简单通信 23 | - IP (网际协议) - 计算机之间的通信 24 | - ICMP (因特网消息控制协议) - 针对错误和状态 25 | - DHCP (动态主机配置协议) - 针对动态寻址 26 | 27 | ## 4.TCP 使用固定的连接 28 | 29 | TCP 用于应用程序之间的通信。 30 | 31 | 当应用程序希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方"握手"之后,TCP 将在两个应用程序之间建立一个全双工 (full-duplex) 的通信。 32 | 33 | 这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。 34 | 35 | UDP 和 TCP 很相似,但是更简单,同时可靠性低于 TCP。 36 | 37 | ## 5.IP 是无连接的 38 | 39 | IP 用于计算机之间的通信。 40 | 41 | IP 是无连接的通信协议。它不会占用两个正在通信的计算机之间的通信线路。这样,IP 就降低了对网络线路的需求。每条线可以同时满足许多不同的计算机之间的通信需要。 42 | 43 | 通过 IP,消息(或者其他数据)被分割为小的独立的包,并通过因特网在计算机之间传送。 44 | 45 | IP 负责将每个包路由至它的目的地。 46 | 47 | ## 6.IP 路由器 48 | 49 | 当一个 IP 包从一台计算机被发送,它会到达一个 IP 路由器。 50 | 51 | IP 路由器负责将这个包路由至它的目的地,直接地或者通过其他的路由器。 52 | 53 | 在一个相同的通信中,一个包所经由的路径可能会和其他的包不同。而路由器负责根据通信量、网络中的错误或者其他参数来进行正确地寻址。 54 | 55 | ## 7.TCP/IP 56 | 57 | TCP/IP 意味着 TCP 和 IP 在一起协同工作。 58 | 59 | TCP 负责应用软件(比如您的浏览器)和网络软件之间的通信。 60 | 61 | IP 负责计算机之间的通信。 62 | 63 | TCP 负责将数据分割并装入 IP 包,然后在它们到达的时候重新组合它们。 64 | 65 | IP 负责将包发送至接受者。 66 | 67 | 原文链接:https://www.runoob.com/tcpip/tcpip-intro.html --------------------------------------------------------------------------------