关于内核:Cloud-Kernel-SIG-月度动态ANCK支持国产指令集架构和处理器使能kABI机制

Cloud Kernel SIG(Special Interest Group):撑持龙蜥内核版本的研发、公布和服务,提供生产可用的高性价比内核产品。 01 SIG 整体停顿ANCK 5.10 实现国产申威架构的反对。ANCK 5.10 和 ANCK 4.19 反对海光四号 CPU,ANCK 5.10 进一步反对 CSV2 新个性。实现 ANCK 5.10 和 ANCK 4.19 驱动版本基线。ANCK 5.10 开发分支使能 kABI 机制 。公布 ANCK 5.10-015.1 小版本。02 具体停顿龙蜥社区反对国产申威架构建设龙蜥社区内核侧已实现适配国产申威 (sw_64) 架构,相干代码已合入 ANCK 5.10 内核。后续将推出残缺 OS 镜像反对 sw_64 架构。龙蜥社区反对海光新平台新个性建设ANCK 5.10 和 4.19 均已实现海光四号 CPU 的根本反对,并打算在下个版本进行公布。ANCK 5.10 实现 CSV2 新个性的反对,将在下个版本进行公布。软件方面打算继续推动 CSV3 和国密新个性的反对,硬件方面打算推动 CPP 的反对。龙蜥社区上游驱动基线和 kABI 建设驱动版本基线曾经实现,并打算逐渐将基线的驱动提交至龙蜥社区。使能 ANCK 5.10 kABI 机制,并且将来将确保 kABI 白名单上的接口的二进制兼容性。在硬件驱动方面,引入了深圳云芯智联的自研国产 sssraid 驱动程序。Cloud Kernel 新增 sssraid 驱动 maintainer 宋延陵,将负责 sssraid 的适配和保护。公布 ANCK 5.10-015.1 小版本版本更新至 5.10.134-015.1公布工夫: 2023-08-16公布链接:anolis8https://anas.openanolis.cn/errata/detail/ANBA-2023:0472anolis23https://anas.openanolis.cn/errata/detail/ANBA-2023:0471重要修复关上 CONFIG_CRYPTO_OFB,以反对 crypto policy 可被设置为 FIPS 模式。(PR#2045:https://gitee.com/anolis/cloud-kernel/pulls/2045)修复因为 erofs 的 rafs v6 模式的合入导致 erofs 块设施模式异样的问题。(PR#1988:https://gitee.com/anolis/cloud-kernel/pulls/1988)在 arm64 上默认启用 eRDMA 模块(PR#1764:https://gitee.com/anolis/cloud-kernel/pulls/1764)相干链接: ...

September 12, 2023 · 1 min · jiezi

关于内核:Cloud-Kernel-SIG月度动态ANCK-510016将落地kABI机制510015版本规划发布

Cloud Kernel SIG(Special Interest Group):撑持龙蜥内核版本的研发、公布和服务,提供生产可用的高性价比内核产品。 01 SIG 整体停顿1.1、ANCK 公布 5.10-014.1 小版本。 1.2、ANCK 公布 4.19-027.3 小版本。 1.3、ANCK 公布 4.19-027.4 小版本。 02 我的项目具体停顿2.1 ANCK 公布 5.10-014.1 小版本 版本更新至5.10.134-14.1。重要 CVE 修复。重要 CVE 列表 CVE-2023-1380 (PR#1636)CVE-2023-28466 (PR#1631)CVE-2023-26545 (PR#1632)CVE-2023-1075 (PR#1630)CVE-2022-47929 (PR#1101)CVE-2023-30456 (PR#1573)CVE-2023-32233 (PR#1629)CVE-2023-0386 (PR#1608)注:每个 CVE 都带有修复补丁的超链接,点击 SIG 主页-月报或浏览原文查看。 2.2 ANCK 公布 4.19-027.3 小版本 版本更新至4.19.91-27.3。修复 nvme 盘在创立 IO 队列失败时,内核应用的 IO 队列数与理论创立的 IO 队列数不匹配,引发内核拜访空指针宕机的问题 (PR#1621)。2.3 ANCK 公布 4.19-027.4 小版本 版本更新至 4.19.91-27.4。修复 CVE-2023-32233 (PR#1637)。修复在仅反对 48-bit guest address width 的 IOMMU 硬件上,当开启 IOMMU 时, IOMMU 驱动 bug 导致系统 panic 的问题。问题起因在于驱动在解决这类 IOMMU 硬件时, IOMMU driver 混同了 agaw 的概念。依据 spec 形容,agaw 应该是 mgaw 和 sagaw 的最小值,并不间接等于 magaw (PR#1647)。03 重要议题ANCK 5.10-015 版本布局2023 年 5 月 31 日 需要解冻。2023 年 6 月 15 日 代码解冻。2023 年 6 月 30 日 RC1 完结。2023 年 7 月 14 日 RC2 完结。2023 年 7 月 17 日 版本 release。决策 kABI 机制的最终落地版本为 5.10-016,在此之前会收集各合作伙伴的 kABI 需要。发展「龙蜥社区驱动基线和驱动品质建设」的探讨,决策驱动基线列表与投入打算。相干链接: ...

July 11, 2023 · 1 min · jiezi

关于内核:Cloud-Kernel-SIG月度动态发布-ANCK-新版本及-Plugsched-v120

Cloud Kernel SIG(Special Interest Group):撑持龙蜥内核版本的研发、公布和服务,提供生产可用的高性价比内核产品。 01 2 月 SIG 整体停顿公布 ANCK 4.19.91-27.1 版本。公布 ANCK 5.10.134-13.1 版本。调度器热降级相干事项更新。02 ANCK 4.19.91-27.1 版本内核更新版本更新至 4.19.91-27.1重要内核缺点及安全漏洞(CVE)修复CVE 修复列表详情请参考: Anolis OS 8: https://anas.openanolis.cn/errata/detail/ANSA-2023:0050 Anolis OS 7: https://anas.openanolis.cn/errata/detail/ANSA-2023:0051 重要 CVE 列表: CVE-2022-0812,CVE-2022-1516,CVE-2022-1836,CVE-2022-20141,CVE-2022-20369,CVE-2022-20422,CVE-2022-23960,CVE-2022-26373,CVE-2022-2663,CVE-2022-26966,CVE-2022-3028,CVE-2022-3169,CVE-2022-32296,CVE-2022-32981,CVE-2022-33740,CVE-2022-33744,CVE-2022-33981,CVE-2022-3521,CVE-2022-3545,CVE-2022-3565,CVE-2022-3586,CVE-2022-3594,CVE-2022-3628,CVE-2022-3629,CVE-2022-3635,CVE-2022-39189,CVE-2022-39842,CVE-2022-40307,CVE-2022-42895,CVE-2022-43750,CVE-2022-4378。 03 ANCK 5.10.134-13.1 版本内核更新版本更新至 5.10.134-13.1重要内核缺点及安全漏洞(CVE)修复CVE修复列表重要 CVE 列表:CVE-2022-4696 调度器热降级Plugsched v1.2.0 公布,晋升调度器模块稳定性,加强多平台多内核反对。 在 5.10、4.19、3.10 三个内核及 x86_64、AArch64 两个平台上测试通过。修复若干稳定性问题。具体参见 Release Note。 Plugsched Release Note v1.2.0: https://github.com/aliyun/plugsched/releases/tag/v1.2.0 04 经营流动助力 Koordinator 云原生单机混部,龙蜥混部技术晋升 CPU 利用率达 60%,龙蜥社区的三大原生技术为 Koordinator 社区提供了弱小的 CPU 混部底层技术支持,包含Group Identity 混部技术、Plugsched 调度器热降级技术、CPU 混部插件产品。 ...

March 9, 2023 · 1 min · jiezi

关于内核:龙蜥白皮书精选面向-DPU-场景的软硬协同协议栈

文/陆扬、施威 01 背景概述随着数据中心网络带宽的大幅晋升和时延的一直降落,传统基于以太网的 TCP 协定栈面临着新的挑战。此时传统的以太网卡和 TCP 协定栈已不能满足其对于网络吞吐、传输时延和增效降本的要求。与此同时云、硬件厂商提供了高性能 DPU 解决方案,因而须要一个高性能的软硬协同网络协议栈,对下适配 DPU 并充分发挥硬件性能,对上撑持大规模云上利用场景,开发部署和运维敌对,兼容支流的云原生等业务架构。 02 技术计划共享内存通信 SMC 是由 IBM 首次奉献至 Linux 社区,并由龙蜥社区加强和保护的软硬协同的高性能协定栈。针对不同的规模场景、硬件和利用模型,SMC 提供多位一体的计划以解决以后传统协定栈的问题: 借助云厂商 VPC 或者数据中心 RDMA,实现不同规模和场景下的高性能通信,撑持不同的业务规模和场景。兼容 RDMA verbs 生态,实现协定栈卸载至硬件,晋升网络性能,升高 CPU 资源应用,反对多种硬件。通明替换网络应用,SMC 齐全兼容 TCP socket 接口,并可疾速回退 TCP。应用对立高效的共享内存模型,借助硬件卸载实现高性能的共享内存通信。 03 技术劣势1、通明减速传统 TCP 利用,对于应用程序、运行环境镜像、部署形式无侵入,对 DevOps 和云原生敌对。 2、DPU 软硬协同的网络协议栈,更高的网络性能和更低的资源应用。 3、Linux 原生反对的标准化、开源的网络协议栈,SMC-R 实现自 IETF RFC7609,由社区独特保护。 04 利用场景SMC 是一个 Linux 内核原生反对的通用高性能网络协议栈,反对 socket 接口和疾速回退 TCP 的能力,任何 TCP 利用均可实现通明替换 SMC 协定栈。因为业务逻辑与网络开销占比的差别,不同利用的减速收益存在差别。上面是几个典型的利用场景和业务最佳实际: 内存数据库,Redis 和局部 OLAP 数据库,Redis QPS 最高晋升 50%,时延降落 55%。分布式存储系统,云原生分布式存储 Curve 在 3 volume 256 depth randwrite 场景下性能晋升18.5%。Web service,NGINX 长链接下 QPS 最高晋升 49.6%,时延降落 55.48%。总的来说,应用 SMC 协定栈能够进步 TCP 利用的性能,缩小时延,进步 QPS,并且不须要批改利用程序代码。然而,减速成果受到业务逻辑和网络开销占比的影响,不同利用的减速成果存在差别。在一些特定的利用场景下,如高性能计算,大数据等,应用 SMC 协定栈可能带来显著的性能晋升。 ...

March 9, 2023 · 1 min · jiezi

关于内核:Cloud-Kernel-SIG月度动态建立社区第三方驱动研发流程发布ANCK-419027版本-龙蜥-SIG

云内核 (Cloud Kernel) 是一款定制优化版的内核产品,在 Cloud Kernel 中实现了若干针对云基础设施和产品而优化的个性和改良性能,旨在进步云端和云下客户的应用体验。 01 12月 SIG 整体停顿公布 ANCK 4.19-027 版本。初步建设了龙蜥社区第三方驱动的研发流程,并试用该流程集成了国产化网迅网卡驱动。ANCK-5.10 内核产品化。成立"浪潮信息龙蜥联结实验室"。02 ANCK 4.19-027 版本内核更新版本更新至 4.19.91-27。重要内核缺点及安全漏洞(CVE)修复。在 namespace_unlock 中应用 synchronize_rcu_expedited 减速 rcu 宽限期,使并发启动 100个 busybox 容器的速度晋升 19%。调整 Trusted Platform Module 驱动的缓冲区大小,防止上下文切换时因内存不足报错。默认使能 mq-deadline IO 调度器。晋升 NVMe、megaraid_sas 和 mpt3sas 三个驱动的稳定性。全面反对 Aero 系列 raid 卡。修复了飞腾处理器 SMMU 的硬件缺点导致的问题。自研性能反对动静开启 Group Identity 个性。反对稠密文件映射应用零碎零页,缩小启动虚拟机时的内存耗费。CVE 修复列表详情请参考: Anolis OS 8: https://anas.openanolis.cn/er... Anolis OS 7: https://anas.openanolis.cn/er... 重要 CVE 列表: CVE-2021-33656,CVE-2021-4037,CVE-2021-4159,CVE-2022-0001,CVE-2022-0002,CVE-2022-0494,CVE-2022-1012,CVE-2022-1048,CVE-2022-1048,CVE-2022-1184,CVE-2022-1198,CVE-2022-1462,CVE-2022-1679,CVE-2022-1729,CVE-2022-1734,CVE-2022-21125,CVE-2022-21166,CVE-2022-2153,CVE-2022-2318,CVE-2022-24958,CVE-2022-2503,CVE-2022-25258,CVE-2022-2586,CVE-2022-2588,CVE-2022-2602,CVE-2022-26365,CVE-2022-2639,CVE-2022-26490,CVE-2022-27223,CVE-2022-28388,CVE-2022-28389,CVE-2022-28390,CVE-2022-2978,CVE-2022-30594,CVE-2022-3176,CVE-2022-3202,CVE-2022-32250,CVE-2022-3542,CVE-2022-36879,CVE-2022-36946,CVE-2022-39188。 03 龙蜥社区第三方驱动建设第三方驱动的社区研发流程:https://openanolis.cn/sig/Clo... 网迅网卡曾经按此流程合入 ANCK : 反对ANCK-5.10 10GB 网迅网卡驱动::https://gitee.com/anolis/clou...反对ANCK-4.19 10GB 网迅网卡驱动: https://gitee.com/anolis/clou...反对ANCK-4.19 1GB 网迅网卡驱动: https://gitee.com/anolis/clou...反对ANCK-5.10 1GB 网迅网卡驱动:https://gitee.com/anolis/clou...04 重要议题探讨并决策了将 ANCK-5.10 作为 Anolis 8 的默认产品化内核,详情请点击:http://suo.nz/2klFG8 查看。探讨并决策了将 ANCK-5.10 作为 Anolis 23 的以后默认内核,详情请点击:http://suo.nz/2VS42f 查看。05 经营流动1. 浪潮信息正式公布基于龙蜥 Anolis OS 的服务器操作系统 Inspur KOS。 ...

January 18, 2023 · 1 min · jiezi

关于内核:当-WASM-遇见-eBPF使用-WebAssembly-编写分发加载运行-eBPF-程序-龙蜥技术

文/郑昱笙 eBPF 技术摸索 SIG Contributor、浙江大学学生 当今云原生世界中两个最热门的轻量级代码执行沙箱/虚拟机是 eBPF 和 WebAssembly。它们都运行从 C、C++ 和 Rust 等语言编译的高性能字节码程序,并且都是跨平台、可移植的。二者最大的区别在于:eBPF 在 Linux 内核中运行,而 WebAssembly 在用户空间中运行。咱们心愿能做一些将二者互相交融的尝试:应用 WASM 来编写通用的 eBPF 程序,而后能够将其散发到任意不同版本、不同架构的 Linux 内核中,无需从新编译即可运行。 WebAssembly vs eBPFWebAssembly(缩写 Wasm)是基于堆栈虚拟机的二进制指令格局。Wasm 是为了一个可移植的指标而设计的,可作为 C/C+/RUST 等高级语言的编译指标,使客户端和服务器应用程序可能在 Web 上部署。WASM 的运行时有多种实现,包含浏览器和独立的零碎,它能够用于视频和音频编解码器、图形和 3D、多媒体和游戏、明码计算或便携式语言实现等利用。 只管 WASM 是为了进步网页中性能敏感模块体现而提出的字节码规范, 然而 WASM 却不仅能用在浏览器(broswer)中, 也能够用在其余环境中。WASM 曾经倒退成为一个轻量级、高性能、跨平台和多语种的软件沙盒环境,被使用于云原生软件组件。与 Linux 容器相比,WebAssembly 的启动速度能够进步 100 倍,内存和磁盘占用空间要小得多,并且具备更好定义的平安沙箱。然而,衡量是 WebAssembly 须要本人的语言 SDK 和编译器工具链,使其成为比 Linux 容器更受限制的开发环境。WebAssembly 越来越多地用于难以部署 Linux 容器或应用程序性能至关重要的边缘计算场景。 WASM 的编译和部署流程如下: wasm-compile-deploy 通常能够将 C/C+/RUST 等高级语言编译为 WASM 字节码,在 WASM 虚拟机中进行加载运行。WASM 虚构机会通过解释执行或 JIT 的形式,将 WASM 字节码翻译为对应平台( x86/Arm 等)的机器码运行。 ...

October 14, 2022 · 4 min · jiezi

关于内核:西安邮电陈莉君教授领衔业界首个产学研-eBPF技术探索SIG成立

“一场军备竞赛曾经打响:越来越多的商业产品利用 eBPF 技术进步其可观测性。” 性能优化大神 Gregg 如是说。 eBPF 作为 Linux 内核一项革命性的技术,起源于 Linux 内核,该技术能够平安而高效地拓展内核的能力。目前,国内外基于 eBPF 的开源我的项目如雨后春笋般呈现,如高效内核追踪的工具集和库函数 BCC;高级追踪语言 Bpftrace;云原生环境下优化网络传输门路、进步微服务网络性能的 Cilium;确保内核运行时平安的KRSI;实现4层网络负载均衡器的Katran;DDOS 攻打的流量优化我的项目 CloudFlare;轻量化容器治理引擎 bedshim ;一次编译、到处运行的 coolbpf;Linux 显微镜 LMP 等等,这些我的项目在产学研畛域正施展越来越重要的作用。 成立初衷现在,云网端边等场景下,eBPF 技术倒退热火朝天、方兴未艾,在 Linux 内核专家陈莉君传授的引领下,各路沉闷的 eBPF 领军者和实践者相遇了,这样的相遇促成了一个新 GROUP 的诞生。因而龙蜥社区正式成立了eBPF 技术摸索 SIG(Special Interest Group)。此次成立 SIG 组,将突破枷锁,创立一个适宜业界和学术界深入研究 eBPF 前沿技术、理论落地演进成果的良好生态,同时也带来了业界和学界两大开源我的项目 Coolbpf 和 LMP,Coolbpf 着重于晋升开发效率,后续也会从 Security 角度进行深刻摸索;LMP 则是孵化于高校,通过构建机器学习模型等计划,从可视化平台的角度来进一步基于 eBPF 技术深刻调试内核。 eBPF 技术摸索 SIG 地址: https://openanolis.cn/sig/ebp... 邮件列表: ebpfresearch@lists.openanolis.cn SIG 组翻新点1、业界首个 eBPF 产学研社区 SIG 组突破以往 eBPF 钻研学习在业界和学术界的隔离,汇合高校传授(学术界)、一线大厂开发专家(业界)、企业研究院的钻研人员及应用 eBPF 技术的创业者。深度摸索 eBPF 在可观测性、性能、平安上的前沿技术,并疏导落地优化,造福宽广开发者和厂商。优化 eBPF 内核适配性,向下适配低版本内核,更加实用于理论生产环境。2、Coolbpf:六大性能,助力开发效率急速晋升本地编译服务,根底库封装。近程编译服务。高版本个性通过 kernel module 形式补齐到低版本。BTF 的主动生成和全网最新内核版本爬虫。各内核版本功能测试自动化。Python、Rust、Go、C 等高级语言反对。3、LMP:孵化于高校,打造基于BPF技术深刻调试内核的可视化平台可视化 Web 页面。性能数据实时化展现。机器学习模型。SIG 指标代码我的项目: 我的项目在 Networking、Tracing、Observability、Security 四大畛域有突破性或前沿技术。国内上对标出名 eBPF 社区。国内做领先者社区。引领 BPF 技术浪潮。SIG 路线图及工作打算eBPF 技术摸索 SIG 成立后,基于 SIG 指标,接下来工作打算将有以下布局: ...

August 18, 2022 · 1 min · jiezi

关于内核:龙蜥开源内核追踪利器-Surftrace协议包解析效率提升-10-倍-龙蜥技术

文/零碎运维 SIG Surftrace 是由零碎运维 SIG 推出的一个 ftrace 封装器和开发编译平台,让用户既能基于 libbpf 疾速构建工程进行开发,也能作为 ftrace 的封装器进行 trace 命令编写。我的项目蕴含 Surftrace 工具集和 pylcc、glcc(python or generic C language for libbpf Compiler Collection),提供近程和本地 eBPF 的编译能力。 通过对 krobe 和 ftrace 相干性能最大化形象,同时对各种场景下的追踪能力加强(比方网络协议抓包),使得用户十分疾速的上手,对定位问题效率晋升 10 倍以上。另外,现如今火到天际的技术——eBPF,Surftrace 反对通过 libbpf 及 CO-RE 能力,对 bpf 的 map 和 prog 等罕用函数进行了封装和形象,基于此平台开发的 libbpf 程序能够无差别运行在各个支流内核版本上,开发、部署和运行效率晋升了一个数量级。 Surftrace 最大的劣势在于将以后支流的 trace 技术一并提供给宽广开发者,能够通过 ftrace 也能够应用 eBPF,利用场景笼罩内存、IO 等 Linux 各个子系统,特地是在网络协议栈跟踪下面,对 skb 外部数据结构,网络字节序解决做到行云流水,把简单留给本人,简略留给你。明天就让咱们来见识一下 Surftrace 在网络畛域的强劲体现吧。 一、了解 Linux 内核协定栈定位网络问题是一个软件开发者必备一项根底技能,诸如 ping 连通性、tcpdump 抓包剖析等伎俩,能够对网络问题进行初步定界。然而,当问题深刻内核协定栈外部,如何将网络报文与内核协定栈清晰关联起来,精准追踪到关注的报文前进门路呢? 1.1 网络报文分层构造援用自《TCP/IP 详解》卷一。如上图所示,网络报文对数据报文数据在不同层进行封装。不同 OS 均采纳统一的报文封装形式,达到跨软件平台通信的目标。 ...

May 12, 2022 · 2 min · jiezi

关于内核:读书笔记Linux内核设计与实现3

过程治理过程是处于执行期的程序,和相干资源的总称包含代码段,关上的文件,挂起的信号,内核外部数据,内存地址空间,多个执行线程的资源,数据段等。虚构处理器和虚拟内存的机制让过程感觉本人独占处理器和该零碎的所有内存资源。同一个过程里的线程共享内存地址空间,是内核调度的最小单位。每个线程领有本人的程序计数器,过程栈和一组过程寄存器。Linux对过程(task)和线程不做辨别,把线程作为非凡的过程进行解决。 通常用fork()创立新的过程(父子过程),调用完结后,在返回点这个雷同的地位,父过程复原执行,子过程开始执行。从内核中返回两次,一次回到父过程,一次回到子过程。接着调用exec()函数创立新的地址空间,将程序载入其中。最终应用exit()推出执行,开释所有资源父过程能够通过wait4()查问子过程是否终结,领有期待特定过程执行结束的能力。过程推出后被设置为僵死状态,直到他的父过程调用wait()或者waitpid()为止。过程描述符和工作构造task list寄存内存队列,双向链表,每个节点类型为task_struct(process descriptor),定义在<linux/sched.h>。task_struct在32位机器上1.7KB,蕴含关上的文件,过程的地址空间,挂起的状态和其余信息。Linux通过slab分配器调配task_struct构造,地位在过程内核栈尾端,起因1是让寄存器比拟弱的硬件能够通过栈指针计算出他的地位,不必额定的寄存器记录(有的硬件就能够间接记录PowerPC);起因2是汇编代码中计算偏移量容易。+thread_info构造x86,两头蕴含task_struct指针。PID用来标识每个过程,pid_t类型(int实际上),默认大小32768,2^15(short int),最大值在<linux/threads.h>中配置,即容许最大的过程数量,能够通过/proc/sys/kernel/pid_max来进步。current宏能够找到以后过程task_struct,但实现形式决定于硬件体系x86须要借助thread_info构造,current_thread_info()->task;过程状态包含: TASK_RUNNING运行时 可执行的正在执行在运行队列中期待执行的过程在用户空间中执行的惟一可能状态TASK_INTERRUPTIBLE 可中断, 过程正在睡眠(阻塞),待条件达成或者收到信号进入运行TASK_UNINTERRUPTIBLE 不可中断 过程在期待时不受烦扰或者期待事件很快会产生时呈现ps 标记为D而不能被杀死的过程的起因,kill信号不承受_TASK_TRACED 被其余过程追踪,ptrace_TASK_STOPPED 没有投入运行也不能运行,产生在SIGINT,SIGTSTP,SIGTOU等信号呈现能够通过set_task_state(task, state);设置过程状态;set_current_state(state)和set_task_state(current, state)雷同<linux/sched.h>对于内核中的过程上下文: 如果用户过程是通过零碎调用或者触发异样陷入的内核空间,内核就代表过程执行并处于过程的上下文,在此上下文上,current宏定义是无效的,除非在此间隙有更高优先级的过程须要执行并由调度器作出了相应的调整,否则在内核退出的时候,程序复原在用户空间中执行。过程间的关系。Linux所有过程都是init过程的子过程。该过程读取零碎的初始化脚本,并执行其余的相干程序,并最终实现系统启动的整个过程。过程必有一个父过程,能够有一个或者多个子过程,同一个父过程的子过程被称为兄弟过程。这些关系被放在过程描述符中:current->parent, current->children子过程链表;next_task(task), prev_task(task)来别离获区前一个过程和后一个过程。for_each_process(task)获取每一个过程。 过程的创立fork() + exec() fork()通过拷贝在新的地址空间内创立过程,子过程和父过程区别在于PID和某些资源及统计量上;挂起的信号没有必要被继承。写时拷贝(COW),推延或者罢黜拷贝数据的技术,内核并不复制整个过程地址空间,而让父过程和子过程共享同一个拷贝。只有在须要写入的时候,数据才会被复制,从而使各个过程领有各自的拷贝。地址空间上页的拷贝被推延到理论产生写入的时候才进行。在页基本不会被写入的状况下(fork后间接调用exec),他们无需复制开销在于复制父过程的页表以及给子过程创立惟一的过程描述符底层实现是clone(),参数表明父子须要共享的资源->do_fork() (kernel/fork.c) -> copy_process() 调用dup_task_struct(),为新过程创立一个内核栈、thread_info构造和task_struct,这些值父子过程临时雷同子过程对描述符内局部成员清0或者设为初始化值(次要是统计信息)子过程为Task_UNINTERRUPTIBLE,以保障他不会被投入云南行调用copy_flags()更新flags成员: PF_SUPERPRIV(超级用户权限)=0;PF_FORKNOEXE(表明未被exec)=1调用alloc_pid()生成新的PIDcopy_process()拷贝或共享关上的文件、文件系统信息、信号处理函数、过程地址空间和命名空间等。开头并返回子过程指针。do_fork()取得子过程指针后,子过程呗唤醒并投入运行,内核选子过程先执行,个别子过程都会马上调用exec()函数,防止写时拷贝的开销exec()读入可执行文件,并执行线程的实现Linux自身在内核中不对线程和过程进行划分,线程只是非凡的过程,与其余过程共享某些资源。每个线程都有本人的task_struct. 创立线程clone()的时候传递的一些参数标记来指明须要共享的资源:clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);通过调用fork()差不多,只不过共享了内存空间,文件系统,文件描述符,信号处理程序,两者成为线程关系。 一般的fork()调用是: clone(SIGCHLD, 0)<linux/sched.h> 参数标记含意CLONE_FILES父子过程共享关上的文件CLONE_FS父子过程共享文件系统信息CLONE_IDLETASK将PID设置为0只给idle过程应用CLONE_NEWNS为子过程创立新的命名空间CLONE_PARENT指定子过程与父过程领有同一个父过程CLONE_PTRACE持续调试子程序CLONE_SETTID将TID回写到用户空间CLONE_SETTLS为子过程创立新的TLS(thread-local storage)CLONE_SIGHAND父过程共享信号处理函数及被阻断的信号CLONE_SYSVSEM父子过程共享System V SEM_UNDO含意CLONE_THREAD父子过程放入雷同的线程组CLONE_VFORK调用vfork()CLONE_UNTRACED避免跟踪过程在子过程强行执行CLONE_PTRACECLONE_STOP以TASK_STOPPED状态开始过程CLONE_CHILD_CLEARTID革除子过程的TIDCLONE_CHILD_SETTID设置子过程的TIDCLONE_PARENT_SETTID设置父过程的TIDCLONE_VM父子过程共享内存空间内核线程内核在后盾操作应用的线程并没有独立的地址空间只在内核空间运行,只能由其余内核线程创立(kthreadd初始内核线程)能够被抢占,调度相似于flush和ksofirqd创立:<linux/kthread.h>struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...)新的kthread会执行threadfn函数,参数为data,名字被命名为namefmt,新创建的过程处于不可执行状态,须要wake_up_process()来明确唤醒他struct task_struct *kthread_run(int (*threadfn), void *data, const char namefmt[],...)创立并让他运行,宏函数,调用create+wake_up_process创立后始终运行直到do_exit()退出,或者其余调用kthread_stop()退出int kthread_stop(struct task_struct *k)过程终结当过程终结时,内核必须开释他所占有的资源并告知其父过程。过程的析构是本身引起的,产生在过程被动调用exit()零碎调用(显式或隐式)时;或者被动收到它不能疏忽的信号或者异样。do_exit() (kernel/exit.c) tast_struct标记成员变量设置为PF_EXITING调用del_timer_sync()删除任意一个内核定时器,依据返回后果,确保没有定时器在排队或者没有定时处理程序在运行如果BSD的过程记账性能是开启的话,do_exit()调用acct_update_integrals()来输入记账信息exit_mm()开释过程占用的mm_struct,该地址空间没有被共享的话就开释。sem_exit(),如果过程排队等待IPC信号,他来到队列exit_files()和exit_fs(),别离递加文件描述符、文件系统数据的援用计数。如果降为0,开释把寄存在task_struct的exit_code成员中的工作推出代码置为由exit()提供的推出代码,或者去实现任何其余由内核机制规定的退出动作。退出代码寄存在这里供父过程随时检索。调用exit_notify()告诉父过程,给子过程找养父,为线程组中其余线程或者为init过程,把过程状态task_struct->exit_state中设为EXIT_ZOMBIE.do_exit()调用schedule()切换到新的过程,永不返回。他处于不可运行状态,没有内存空间,只保留内核栈,thread_info构造和task struct构造,给父过程提供信息,父过程收到后,告诉内核他不须要该信息,开释所有资源(task struct)。 release_task()调用: ...

May 2, 2022 · 2 min · jiezi

关于内核:读书笔记Linux内核设计与实现2

从内核登程原以为这章能够飞快跳过,但还是发现很多新的有意思的点。 获取源码https://www.kernel.org 压缩源码或者增量补丁 bz2 tar xvjf linux-x-y-z.tar.bz2gz tar xvzf linux-x-y-x.tar.gzhttps://github.com/torvalds/l...装置源码/usr/src/linux不要把这个源码树用于开发,因为编译用的C库所用的内核版本就链接到这棵树。正确:建设本人的主目录,而后仅用root进行装置。对于补丁:patch -p1 < ../patch-x.y.z 内核源码树目录形容arch特定体系结构的源码block块设施io层crypto秘密APIDocuments内核源码文档drivers设施驱动程序firmware应用某些驱动程序而须要的设施固件fsVFS和各种文件系统include内核同文件init内核疏导和初始化ipc过程间通信kernel外围子系统lib通用内核函数mm内存管理子系统和VMnet网络子系统samples实例scripts编译内核所用的脚本securityLinux平安模块sound语音子系统usr晚期用户空间代码toolsLinux开发工具virt虚拟化体系编译编译Linux之前须要进行配置:CONFIG_XXXX: yes|no|module; 决定哪些文件能够被编译进内核;module意味着该配置选项被选定,但编译的时候这部分性能的实现代码是以模块的模式生成的(动静装置的独立代码块)。通过预处理的命令解决代码;配置选项能够为字符串和证书,指定内核源码能够拜访的值,以预处理宏的模式;如制订动态调配数组的大小。配置命令:逐个遍历make config图形界面工具make menuconfiggtk+图形工具make gconfig默认配置make defconfig配置选项会被放在根目录下.config文件中;每次编译前,更新配置make oldconfig能够配置CONFIG_IKCONFIG_PROC,把配置压缩放入/proc/config.gz下,能够从/proc下复制出配置文件并且应用它来编译一个新内核:zcat /proc/config.gz > .configmake oldconfig配置实现后: make > /dev/null进步make效率多核,如16核:make -j32 > /dev/null;装置查阅启动疏导工具的阐明,将内核映像拷贝到/boot目录下,按启动要求装置。内核模块的装置是主动的,make modules_install,装到/lib/modules下编译时也会在内核代码树的根目录下创立一个System.map文件,符号对照表,将内核符号和他们的起始地址对应,须要把内存地址翻译成容易了解的函数名以及变量名。内核开发内核编译时不能拜访c库文件(libc)和规范c头文件lib/string.c => <linux/string.h> 内核开发头文件根本都在源码include目录下,头文件<linux/inotify.h>体系相干的头文件arch/x86/include/asm上面<asm/ioctl.h>没有实现的printf() -> prink 将格式化的字符串拷贝到日志缓冲区中,syslog程序就能够通过读取该缓冲区来获取内核信息。 能够指定一个优先级标志符,相似于宏定义prink(KERN_ERR "this is an error.\n")应用GNU Cgcc工具蕴含了多种GNU编译器,能够编译内核。内核C语言蕴含了ISO C99规范和GNU C扩大个性。 与规范c语言不同的大多是GNU C的扩大上。 内联函数inline: 函数在调用地位扩大,打消函数调用和返回的开销,代码变长,占用内存空间或者指令缓存更多;函数较大,被重复调用,没有工夫要求不举荐应用与static合用 static inline void wolf(unsigned long tail_size)应用前定义,举荐在头文件中定义,优先应用inline函数而不是宏内联汇编 只有晓得体系结构才能够应用,偏近底层或对执行工夫要求严格asm() unsigned int low, hight asm volatile("rdtsc" : "=a" (low), "=d" (high));分支申明 gcc优化分支,经常出现likely, unlikely if (unlikely (error))肯定要弄清是否真的有偏差,对性能影响微小没有内存保护机制用户程序非法内存拜访,内核会发现,发送SIGSEGV信号,完结过程内核中产生内存拜访谬误导致oops ...

May 1, 2022 · 1 min · jiezi

关于内核:系列解读SMCR透明无感提升云上-TCP-应用网络性能一-龙蜥技术

文/龙蜥社区高性能网络SIG 引言Shared Memory Communication over RDMA (SMC-R) 是一种基于 RDMA 技术、兼容 socket 接口的内核网络协议,由 IBM 提出并在 2017 年奉献至 Linux 内核。SMC-R 可能帮忙 TCP 网络应用程序通明应用 RDMA,取得高带宽、低时延的网络通信服务。阿里云云上操作系统 Alibaba Cloud Linux 3 以及龙蜥社区开源操作系统 Anolis 8 配合神龙弹性 RDMA (eRDMA) 首次将 SMC-R 带上云上场景,助力云上利用取得更好的网络性能:《技术揭秘:阿里云公布第四代神龙 ,SMC-R 让网络性能晋升 20%》。 因为 RDMA 技术在数据中心畛域的宽泛应用,龙蜥社区高性能网络 SIG 认为 SMC-R 将成为下一代数据中心内核协定栈的重要方向之一。为此,咱们对其进行了大量的优化,并踊跃将这些优化回馈到上游 Linux 社区。目前,龙蜥社区高性能网络 SIG 是除 IBM 以外最大的 SMC-R 代码奉献个人。因为 SMC-R 相干中文材料极少,咱们心愿通过一系列文章,让更多的国内读者理解并接触 SMC-R,也欢送有趣味的读者退出龙蜥社区高性能网络 SIG 一起沟通交流(二维码见文末)。 本篇作为系列文章的第一篇,将从宏观的角度率领读者初探 SMC-R。 一、从 RDMA 谈起Shared Memory Communication over RDMA 的名称蕴含了 SMC-R 网络协议的一大特点——基于 RDMA。因而,在介绍 SMC-R 前咱们先来看看这个高性能网络畛域中的相对主力:Remote Direct Memory Access (RDMA) 技术。 ...

March 31, 2022 · 5 min · jiezi

关于内核:SELinux入门学习总结

前言 平安增强型 Linux(Security-Enhanced Linux)简称 SELinux,它是一个 Linux 内核模块,也是 Linux 的一个平安子系统。 SELinux 次要由美国国家安全局开发。2.6 及以上版本的 Linux 内核都曾经集成了 SELinux 模块。 SELinux 的构造及配置非常复杂,而且有大量概念性的货色,要学精难度较大。很多 Linux 系统管理员嫌麻烦都把 SELinux 敞开了。 如果能够熟练掌握 SELinux 并正确使用,我感觉整个零碎基本上能够达到“坚不可摧”的境地了(请永远记住没有相对的平安)。 把握 SELinux 的基本概念以及简略的配置办法是每个 Linux 系统管理员的必修课。 一、基本概念 1、TE模型的平安上下文 所有的操作系统访问控制都基于主体、客体,以及与他们相干的访问控制属性。 在selinux中,访问控制属性叫做平安上下文。所有对象(文件、过程间通信通道、套接字、网络主机等)和主体(过程)都有一个与之关联的平安上下文。 一个平安上下文蕴含三个元素:用户(user)、角色(role)和类型标识符(type identifiers) 平安上下文的模式如下:user:role:type 对过程来说:别离示意用户、角色、类型标识符也被称为域 对客体来说:前两项根本没有理论用处,role通常为object\_r,user通常位创立这个对象的过程的user,对访问控制没有影响 总结: SELinux是通过MAC(Mandatory Access Control)形式来控管过程,它管制的主体是过程,而指标则是该过程是否读取的”文件资源”。 主体 SELinux 次要是想管理控制过程。 注*:为了不便了解,如无特地阐明,以下均把过程视为主体。 指标 主体过程是否拜访的”指标资源”个别是文件系统。 对象 被主体拜访的资源。能够是文件、目录、端口、设施等。 注*:为了不便了解,如无特地阐明,以下均把文件或者目录视为对象。 策略 因为过程和文件的数量宏大,因而 SELiunx会依据某些服务来制订根本的拜访安全性策略。 这些策略外部还有具体的规定来指定不同的服务凋谢某些资源的拜访与否。 零碎中通常有大量的文件和过程,为了节省时间和开销,通常咱们只是选择性地对某些过程进行管制。 而哪些过程须要管制、要怎么管制是由政策决定的。 一套政策外面有多个规定。局部规定能够依照需要启用或禁用(以下把该类型的规定称为布尔型规定)。 规定是模块化、可扩大的。在装置新的应用程序时,应用程序可通过增加新的模块来增加规定。用户也能够手动地增减规定。 SELINUX参数值: enforcing:强制执行SELinux性能; permissive:只显示正告信息; disabled:停用SELinux性能。 SELINUXTYPE参数值: targeted:针对网络服务限度较多,针对本机限度较少,是默认的策略; strict:残缺的爱护性能,包含网络服务、个别指令、应用程序,限度方面较为严格。 平安上下文 ...

September 6, 2021 · 5 min · jiezi

关于内核:不服跑个分-是噱头还是实力-龙蜥技术

背景:性能之战“不服跑个分”曾经沦为手机行业的调侃用语,然而实话实说,在操作系统畛域“跑分”的确是最重要的评估形式之一。比方 Linux 内核社区经常以跑分软件得分,来评估一个优化补丁的价值。甚至还有 phoronix 这样专一于 Linux 跑分的媒体。而且明天我还想说一点,让软件跑分高,这是实力的体现,是建设在对内核的深刻理解根底上的。本文的故事就源于一次日常的性能优化剖析。咱们在评估自动化性能调优软件 tuned 的时候,发现它在服务器场景,对 Linux 内核调度器相干的参数做了一些渺小的批改,然而这些批改却很大水平改善了 hackbench 这款跑分软件的性能。是不是很有意思?让咱们一起来一探到底。本文将从几个方面开展,并重点介绍加粗标出局部: 相干常识简介hackbench 工作模式简介hackbench 性能受损之源双参数优化思考与拓展相干常识简介CFS调度器Linux 中大部分(能够粗略认为是实时工作之外的所有)线程/过程,都由一个叫 CFS(齐全偏心调度器)的调度器进行调度,它是 Linux 最外围的组件之一。(在Linux中,线程和过程只有细微差别,下文对立用过程表述) CFS 的外围是红黑树,用于管理系统中过程的运行工夫,作为抉择下一个将要运行的过程的根据。此外,它还反对优先级、组调度(基于咱们熟知的 cgroup 实现)、限流等性能,满足各种高级需要。CFS 的具体介绍。 hackbenchhackbench 是一个针对 Linux 内核调度器的压力测试工具,它的次要工作是创立指定数量的调度实体对(线程/过程),并让它们通过 sockets/pipe 进行数据传输,最初统计整个运行过程的工夫开销。 CFS 调度器参数本文重点关注以下两个参数,这两个参数也是影响 hackbench 跑分性能的重要因素。系统管理员能够应用 sysctl 命令来进行设置。 最小粒度工夫:kernel.sched_min_granularity_ns通过批改 kernel.sched_min_granularity_ns,能够影响 CFS 调度周期(sched period)的工夫长短。例如:设置kernel.sched_min_granularity_ns = m,当零碎中存在大量可运行过程时,m 越大,CFS 调度周期就越长。 如图 1 所示,每个过程都可能在 CPU 上运行且工夫各有长短,sched_min_granularity_ns 保障了每个过程的最小运行工夫(优先级雷同的状况下),sched_min_granularity_ns 越大每个过程单次可运行的工夫就越长。 图 1:sched_min_granularity_ns 示意图 唤醒抢占粒度:kernel.sched_wakeup_granularity_nskernel.sched_wakeup_granularity_ns 保障了从新唤醒的过程不会频繁抢占正在运行的过程,kernel.sched_wakeup_granularity_ns 越大,唤醒过程进行抢占的频率就越小。如图 2 所示,有 process-{1,2,3} 三个过程被唤醒,因为 process-3 的运行工夫大于 curr(正在 CPU 上运行的过程)无奈抢占运行,而 process-2 运行工夫小于 curr 但其差值小于 sched_wakeup_granularity_ns 也无奈抢占运行,只有 process-1 可能抢占 curr 运行,因而 sched_wakeup_granularity_ns 越小,过程被唤醒后的响应工夫就越快(期待运行工夫越短)。 ...

August 30, 2021 · 2 min · jiezi

关于内核:谁来拯救存量SGX1平台又一个内核特性合并的血泪史

简介: 明天的故事配角,是一个被称为Flexible Launch Control的SGX平台个性。 前言自从Intel内核开发人员Jarkko Sakkinen于2017年9月2日在intel-sgx-kernel-dev@lists.01.org邮件列表上收回v1版的SGX in-tree驱动以来,工夫曾经过来了3年多了。这期间这个驱动前前后后共批改了41个版本,终于在2020年11月13日,v41版本的补丁合入了5.11-rc1内核。Jarkko松了一口气,工作实现啦!不过,为什么合并一个一般的驱动模块会这么难? 事实上,因为SGX所代表的新的秘密计算畛域的特殊性,围绕着它的争议和探讨就从未进行过。甚至在最终的v41补丁中,也没能看到大佬们整齐划一的LGTM(社区黑话,Looks Good To Me的缩写,示意本人认可这个补丁),它仍旧存在一些问题,同时还有人在一直提出批改倡议。这不禁让人联想到另一个x86处理器个性FSGSBASE合入upstream时的命运多舛:后者的合入前后花了5年工夫,甚至最初都不是Intel的人合入的(当然也不是AMD的人合入的,大家能够猜猜是谁)。 明天的故事配角,是一个被称为Flexible Launch Control(简称FLC)的SGX平台个性。对这个个性的争议,最终导致SGX in-tree驱动无奈反对不具备FLC个性或敞开了FLC个性的所有SGX零碎。大白话是啥意思呢?意思就是一批较早的反对Intel SGX的机器用Linux内核自带的官网驱动是无奈加载和应用SGX的......纳尼!这个驱动居然不向下兼容!哎,当初让咱们先对FLC技术背景进行一个简略的介绍,而后再对其被社区摈弃的故事简略做个复盘,最初再给出咱们的思考,以及咱们的解决方案。 FLC的技术背景情谊揭示:想听故事不想理解技术细节的同学请间接跳到下一节。 在执行SGX EINIT指令初始化enclave的时候,被初始化的enclave必须通过Launch Control机制的查看,因而Launch Control是一种用于管制enclave是否启动的安全检查机制。 被初始化的enclave分为两类:Launch Enclave和一般enclave。Launch Enclave与一般的enclave的不同之处在于其SECS.ATTRIBUTES.EINITTOKEN_KEY == 1,即具备通过执行SGX EGETKEY指令派生出einit token key的权限。Einit token实质上是一个带有CMAC签名的token,其中的CMAC是Launch Enclave用einit token key生成的,意在对einit token自身提供完整性爱护;在执行EINIT初始化一般enclave的时候,从Launch Enclave处返回的einit token须要作为输出参数的一部分,而后EINIT指令的微码会将einit token作为派生出einit token key的输出因子,最初用派生出的einit token key来验证输出的einit token中的CMAC,以确保einit token从生成到传给EINIT的过程中没有被篡改。 下面的流程中提到的einit token完整性检查实用于所有的一般enclave,而对于Launch Enclave,再执行EINIT初始化时有非凡解决。上面是在执行EINIT时残缺的Launch Control流程: image.gifC05A1907-7AF2-47e3-B8C3-5F743C36AE8F.png (下面的流程图是对Intel SDM手册中对于EINIT指令在微架构层的执行流程进行的总结;其中Launch Enclave验证两次IA32_SGXLEPUBKEYHASH的逻辑应该是冗余的,但咱们还是依照原始流程的逻辑把它给残缺地画进去了) 能够简略地将上述流程总结为以下两点规定: 1.只有在初始化enclave时能提供非法的einit token, 就能通过Launch Control的查看。 如果在初始化enclave时没有提供非法的einit token, 则enclave的MRSIGNER必须与IA32_SGXLEPUBKEYHASH的值统一。此外,还能够从第二点规定推导出实用于Launch Enclave的非凡规定: Launch Enclave必须可能通过IA32_SGXLEPUBKEYHASH的校验能力被初始化。通过EINIT初始化Launch Enclave的时候,能够不提供einit token。在处理器复位时,IA32_SGXLEPUBKEYHASH的默认值是Intel的MRSIGNER。如果处理器不反对FLC,任何一般的enclave在初始化时,必须持有非法的einit token,而负责颁发einit token的Launch Enclave则能够制订本人的策略来决定是否给一个一般enclave颁发einit token。 具体到Intel在不反对FLC的SGX1上施行的策略则是:用户运行的enclave必须是由Intel受权的密钥签过的,这样能力失去Intel开发的Launch Enclave的受权,并取得einit token,否则用户无奈运行本人开发的一般enclave(严格讲是无奈运行产品级的一般enclave,debug级的一般enclave还是能够运行的)。 ...

August 18, 2021 · 2 min · jiezi

关于内核:内核热补丁真的安全么-龙蜥技术

文/扶风、丁缓、 雏雁 Linux 内核热补丁能够修复正在运行的 linux 内核,是一种维持线上稳定性不可短少的措施,当初比拟常见的比方 kpatch 和 livepatch。内核热补丁能够修复内核中正在运行的函数,用已修复的函数替换掉内核中存在问题的函数从而达到修复目标。 函数替换的思维比较简单,就是在执行旧函数时绕开它的执行逻辑而跳转到新的函数中,有一种比较简单粗犷的形式,就是将原函数的第一条指令批改为“jump 指标函数”指令,即间接跳转到新的函数以达到替换目标。 那么,问题来了,这么做靠谱吗?间接将原函数的第一条指令批改为 jump 指令,会毁坏掉原函数和它的调用者之间的寄存器上下文关系,存在安全隐患!本文会针对该问题进行摸索和验证。 安全性冲击:问题出现对于函数调用,假如存在这样两个函数 funA 和 funB,其中 funA 调用 funB 函数,这里称 funA 为 caller(调用者),funB 为 callee(被调用者),funA 和 funB 都应用了雷同的寄存器 R,如下所示: 图1 funA 和 funB 都应用了寄存器 R,funA 再次应用 R 时曾经被 funB 批改 因而,当 funA 再次应用到 R 的数据曾经是谬误的数据了。如果 funA 在调用 funB 前保留寄存器 R 中的数据,funB 返回后再将数据恢复到 R 中,或者 funB 先保留 R 中原有的数据,而后在返回前复原,就能够解决这类问题。 惟一的调用约定那寄存器该由 caller 还是 callee 来保留?这就须要遵循函数的调用约定(call convention),不同的 ABI 和不同的平台,函数的调用约定是不一样的,对于 Linux 来说,它遵循的是 System V ABI 的 call convention,x86_64 平台下函数调用约定有且只有一种,调用者 caller 和被调用者 callee 须要对相应的寄存器进行保留和复原操作: ...

July 29, 2021 · 7 min · jiezi

关于内核:性能工具之内核调试工具

最近给本人定了些工作,把PPT从新编写一下,所有性能相干的话题都在打算的范畴里。 最近这几天在整顿调试工具的培训PPT,原本是在7D Group的云服务器上做实例的。后果发现有些数据显示不进去。看来当初的调试工具也是须要更新了,还要再出新版反对当初的云主机了。 今天下午顺便找了个物理机做了下示例。反对和不反对的示例如下: 上图的上半局部是在云服务器上执行的,显示不进去cycles/instructions/branches了。上图的下半局部是在物理主机上执行的,能够显示进去cycles/instructions/branches了。这是一个区别,揭示大家在工作中留神一下。 这里也把工具的应用稍提一下。这里以perf为例,其余工具如果有感兴趣的,也能够来探讨,像systemstap之类的。GDB最近就不打算整了,毕竟有点老,并且应用上感觉不是顺藤摸瓜型。 perf是大部分linux上都带了的工具。一些前提条件就不提了,就是在编译内核的时候要各个抉择什么的,琐碎得很。遇到这样的问题,我个别都扔给另一个兄弟去解决了。哈哈。拿CPU耗费为例。这里最好带上-g的参数,这样能够看到相似上面这样的调用关系。这里能够看到符号表那一列有[k]或者[.],这里[k]的意思是内核态的;[.]的意思是用户态的。看你想看什么内容。 如果这里跟踪本人的应用程序,就能够间接去依据函数名找到了。 并且能够生成火焰图,如下所示,三步就能够生成。perf script -i perf.data &> perf.unfoldperl stackcollapse-perf.pl perf.unfold &> perf.foldedperl flamegraph.pl perf.folded >perf.svg 通过Brendan Gregg的写好的工具(stackcollapse-perf.pl ,flamegraph.pl),基本上能够满足大部分的要求。有趣味和有能力的也能够本人写一下。Cor-Paul Bezemer也写过一个差分火焰图的工具flamegraphdiff,一个页面显示三个屏,点函数名时,其余图上也会高亮显示。如下所示: 最近调试工具的整顿告一段落,上面该重写其余内容了。 之前有人探讨说,高级的性能工程师是玩性能测试工具的。中级的性能工程师是玩性能监控工具的。高级的性能工程师是玩性能调试工具的。 我想说,这样划分是有问题的,应该这么说:高级的性能工程师是玩工具的。(包含:压力工具、监控工具、调试工具)中级的性能工程师是玩原理的。高级的性能工程师是玩布局的。 我想说,工具其实都是浮云,了解原理之后,工具的应用只在想和不想之间。而布局取决于是不是拍脑袋,拍脑袋的人贻害四方,要揍死为止。

June 4, 2021 · 1 min · jiezi

关于内核:STM32标准库开发实战指南

摘要:从STM32新建工程、编译下载程序登程,让老手由浅入深,尽享STM32规范库开发的乐趣。自从CubeMX等图像配置软件的呈现,同学们往往点几下鼠标就解决了单片机的配置问题。对于谋求开发速度的业务场景下,应用疾速配置软件是正当的,高效的,但对于学生的学习场景下,更为重要的是知其然并知其所以然。 以下是学习(包含但不限于)嵌入式的三个重要内容, 1、学会如何参考官网的手册和官网的代码来独立写本人的程序。 2、积攒罕用代码段,晓得哪里的问题须要哪些代码解决。 3、追随大佬步调,一步一个脚印。 首先:咱们都晓得编程时个别查的是《参考手册》,而进行芯片选型或须要芯片数据时,查阅的是《数据手册》。此外市面上所有对于STM32的书籍都是立足于前二者(+Cortex内核手册)进行编著。 其次:要分清什么是内核外设与内核之外的外设,为了便于辨别,依照网上的一种说法,将“内核之外的外设”以“处理器外设”代替。 再者:现在很少应用规范库了,都是HAL库,但作为高校目前教学方式, 咱们将以STM32f10xxx为例对规范库开发进行概览。 一、STM32 系统结构 STM32f10xxx 系统结构 内核 IP 从构造框图上看,Cortex-M3 外部有若 干个总线接口,以使 CM3 能同时取址和访内(拜访内存),它们是: 指令存储区总线(两条)、系统总线、公有外设总线。有两条代码存储区总线负责对代 码存储区(即 FLASH 外设)的拜访,别离是 I-Code 总线和 D-Code 总线。 I-Code 用于取指,D-Code 用于查表等操作,它们按最佳执行速度进行优化。 系统总线(System)用于拜访内存和外设,笼罩的区域包含 SRAM,片上外设,片外 RAM,片外扩大设施,以及零碎级存储区的局部空间。 公有外设总线负责一部分公有外设的拜访,次要就是拜访调试组件。它们也在零碎级 存储区。 还有一个 MDA 总线,从字面上看,DMA 是 data memory access 的意思,是一种连贯内核和外设的桥梁,它能够拜访外设、内存,传输不受 CPU 的管制,并且是双向通信。简而言之,这个家伙就是一个速度很快的且不受老大管制的数据搬运工。 处理器外设(内核之外的外设) 从构造框图上看,STM32 的外设有 串口、定时器、IO 口、FSMC、SDIO、SPI、I2C 等,这些外设按 照速度的不同,别离挂载到 AHB、APB2、APB1 这三条总线上。 二、寄存器什么是寄存器?寄存器是内置于各个 IP 外设中,是一种用于配置外设性能的存储器,并且有想对应的地址。所有库的封装始于映射。 是不是“又臭又长”,如果进行寄存器开发,就须要怼地址以及对寄存器进行字节赋值,不仅效率低而且容易出错。 来,开个玩笑。 你兴许据说过“国内 C 语言乱码大赛(IOCCC)”上面这个例子就是网上广为流传的 一个经典作品: #include <stdio.h>main(t,_,a)char *a;{return!0<t?t<3?main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13?main(2,_+1,"%s %d %dn"):9:16:t<0?t<-72?main(_,t,"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##(!!/"):t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1):0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a,"!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:nuwloca-O;m.vpbks,fxntdCeghiry"),a+1);}复制代码库的存在就是为了解决这类问题,将代码语义化。语义化思维不仅仅是嵌入式有的,前端代码也在谋求语义个性。 ...

January 11, 2021 · 8 min · jiezi

五分钟了解操作系统内核

操作系统和内核关于操作系统是这样定义的:操作系统(英语:Operating System,缩写:OS)是管理计算机硬件与软件资源的系统软件,同时也是计算机系统的内核与基石。操作系统需要处理如管理与配置内存、决定系统资源供需的优先次序、控制输入与输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互的操作界面。 对我们来说,操作系统最直观的感受就是桌面系统,以及上层的应用程序,而后面的资源处理等等就是操作系统背后的黑盒。 读者朋友们现在阅读我的这篇文章,是在浏览器,或者某个应用程序上进行的。而一个应用程序是要运行在特定的操作系统上的。操作系统,则是要运行在硬件上的。所以这三者关系如图。 再来看看内核的定义:“内核”指的是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。内核是操作系统最基本的部分。它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间。直接对硬件操作是非常复杂的,所以内核通常提供一种硬件抽象的方法来完成这些操作。硬件抽象隐藏了复杂性,为应用软件和硬件提供了一套简洁,统一的接口,使程序设计更为简单。 简单来说,内核是一个操作系统的核心。它负责管理系统的进程、内存、设备驱动程序、文件和网络系统等等,决定着系统的性能和稳定性。是连接应用程序和硬件的桥梁。 内核就是操作系统背后黑盒的核心。 这时候我们回过头来看看操作系统和内核的定义,是不是有些明白了呢。 内核的分类既然我们知道了内核是什么,接着来看看内核的分类,现在内核的主要分类有四类: 宏内核(单内核),微内核,混合内核,外内核。 什么是宏内核我们上面说到,内核管理着操作系统的内存,文件,IO,网络等等,每个功能可以看做一个模块,在宏内核中,这些模块都是集成在一起的,运行在内核进程中,模块之间的交互直接通过方法调用。 什么是微内核而在微内核中,内核只提供最核心的功能,比如任务调度,内存管理等等,其他模块被移出内核,运行在不同的进程中,这样即使某一个模块出现问题,只要重启这个模块的进程即可,不会影响到其他模块,稳定性大大增加。甚至可以在系统运行过程中替换现有模块的实现。而且由于模块独立的性质,可以做到模块的按需加载。但是模块间的相互调用需要通过进程间通信,通信效率相对较低。 什么是混合内核我们上面看了宏内核和微内核的实现,就会发现,两者各有千秋,也各有缺点,所以混合内核就是集中了两者的特点,让微内核中的一些核心模块运行在内核中,从而使内核效率更高一些。 什么是外内核外内核是把硬件暴露给应用程序,应用程序可以直接访问硬件,外内核对系统提供保护。目前还在研究阶段。 宏内核 微内核对比看了上述宏内核和微内核的实现,我们可以总结一下:宏内核最大的特点就是模块集成在一起,而微内核是模块间分离。基于此点出发,对比如下: 宏内核微内核通信效率高(函数调用)低(进程间通信)稳定性低(模块集成在一起)高(模块间互不影响)扩展性低(模块集成在一起)高(模块间互不影响)代码量多(需要实现所有模块)少(只需要实现核心功能)当前主流操作系统内核宏内核 Linux Windows 9X 系列 MacOS 8.6 版本之前 微内核 Fuchsia 鸿蒙 Minix 混合内核 Windows XP Windows 7 Mac OS X XNU 外内核Nemesis 参考资料https://zh.wikipedia.org/wiki... https://www.cnblogs.com/smwik... https://zh.wikipedia.org/wiki... https://www.oschina.net/news/...

September 10, 2019 · 1 min · jiezi

这个情人节,工程师用阿里云来试着表达不一样的爱意

年轻的时候谈的恋爱就像TCP链接,恋爱时三次握手即可,可分手时却分了四次。而常常久久的爱情,更像是icmp协议,无论对方身在何处,无论是否是可靠连接,无论你何时去ping她/他,她/他都默默地响应你。这篇文章就是说说,如何在内核中增加几行代码,让你的女神/男神当ping你(的服务器)的时候,来传达表达你的爱。效果如下(左边为ping的结果,需要破解ascii码转换为对应字符,右边为使用tcpdump抓包直接读取的信息):对于UNIX_LIKE系统来说,如果ping的发送内容与接收内容不同,会显示不同的部分,那么就让你的女神或者男神,慢慢将ASCII码解析成你想告诉她/他的话吧。或者告诉她/他,使用tcpdump来直接抓包隐藏在ping中的悄悄话。(对于windows来说本人没有充分测试,只是知道不会像unix_like系统一样直接显示出请求消息和回显消息的不同,所以需要大家抓包认真提取信息)一、ICMP协议这些你需要了解:学过计算机网络的一定知道,一个网络包的封装主要由多个属于不同网络协议层的报文头和用户数据共同组成:链路层报文头+网络层IP报文头+传输层报文头+携带的内容+帧尾。而ICMP报文在整个以太帧位于如下位置: 上图显示的是一个未分片ICMP报文或者是一个较长ICMP报文的第一个IP分片的报文(被分片的报文中不会带有ICMP报头)。RFC792(https://tools.ietf.org/html/r…)中定义了11种ICMP报文类型,通过ICMP报头8bit"类型"字段进行区分。并且每种"类型“会和其”代码"字段以及报文头的最后4字节,共同表达每种报文类型所表示的信息。这些ICMP报文类型被主要分为差错报文和查询报文:查询报文主要包括:回送请求(TYPE8),回送应答(TYPE0),地址掩码或时间戳的请求/应答等差错报文主要包括:目标主机不可达(TYPE3),超时,源抑制,路由重定向等 ping作为ICMP协议最为典型的运用,主要和回送请求,和回送应答这两个类型相关,这也是本文主要关心的两个类型。当然,当主机不可达或者网络路由不可达出现的时候,ping会收到路由器传来的TYPE为3的目标主机不可达的报文(我们可以通过tcpdump抓包获取)。对于其他的类型,有兴趣的同学可以自行学习,如icmp重定向攻击,洪水攻击都是利用了ICMP协议进行的网络攻击。二、动手写一个简单的ping,了解Linux ping作为本文的主角之一ping,有必要动手写一个简单的ping,帮助我们更好的理解整个请求应答的过程。我本人的测试机器centos 7中使用的是iputils这个工具进行ping操作,所以我们可以从iputils源码入手学习如何写一个简单的ping。学习过c网络编程的一定都了解socket套接字这个概念。对于ping来说发送请求和接受应答也同样是通过套接字来完成。只不过,ICMP协议虽然在内核中和TCP、UDP相似属于L4层协议,但是本质是附属于IP协议的网络层协议,所以需要使用原始套接字(SOCK_RAW)构建套接字,而非TCP或UDP使用的流式套接字(SOCK_STREAM)和数据包式套接字(SOCK_DGRAM)。SOCK_RAW的用途在于用户可以自定义填充IP报文头,并且对于ICMP报文自定义填充ICMP报文头。下面一张图,展示了代码中整个ping的逻辑发送以及处理应答的逻辑。具体代码可以参考这个:https://github.com/xiaobaidemu/myping/blob/master/ping.c 整个流程非常简单,需要说明的是,对于ping 127.0.0.1来说,程序极有可能先收到type为0的回显请求报文,再收到type为8的回显应答报文。这是因为icmp报文可以同时被内核接收处理,也会被原始套接字接收处理,如下为Understanding Linux Network Internals书中所述。三、添加内核代码前,你只需要知道一个结构体和icmp.c理解了ping的整个过程,接下来就是需要修改内核来传达你想说的话。但是最重要的是,需要分析出修改的位置,即回显应答可能发送的字节在内核代码中的位置。这里有一个非常重要的结构体——struct sk_buff,其定义位于<include/linux/skbuff.h>。内核中sk_buff结构体做到了可以不使用拷贝或删除的方式,使得数据在各层协议之间传输——即移动指针头的方式,具体为在处理不同的协议头时,代表协议头的指针,指向的是不同数据区域(如从L2到L4层协议,分别指向二层mac头,三层IP头,四层传输头)。以下是几个比较重要和混淆的字段说明,结合示意图说明: 上图简单说明了四个指针和指向区域之间的关系。另外对于data_len和len的关系,如果假设icmp报文比较小,ip层不会对其分片,那么data_len即为0,而len即为当前协议头长度+数据报文长度。关于data_len和len之间的关系涉及到skb_shared_info这个结构体的相关内容,因为和文章中心关系不大,有兴趣的同学可以自行查阅一下文章来学习http://blog.51cto.com/weiguozhihui/1586777https://0x657573.wordpress.com/2010/11/22/the-relation-between-skb-len-and-skb-data_len-and-what-they-represent/https://blog.csdn.net/farmwang/article/details/54233975 上述内容中data指针和表征协议层数据长度的len,和后文中修改的sk_buff指向的数据直接相关。另外sk_buff关联了众多其他结构体,这里只简要的讲解部分重要的字段含义,更为具体详细的说明可以参考Understanding Linux Network Internal第二章或者https://blog.csdn.net/YuZhiHui_No1/article/details/38666589系列文章进行更深入学习。 了解了sk_buff结构体,之后需要定位处理icmp协议的文件在哪里。icmp.c位于内核目录中net/ipv4/icmp.c中,且ICMP协议通常是静态编译至内核中,而非通过模块配置的。这里我从Understanding Linux Network Internal这本书中抠出来一张Big Picture,来简要说明一下对于ping发出的回显请求,sk_buff结构体对象是如何在icmp中众多函数中传递。首先ip_local_deliver_finish会传递ICMP消息到icmp_rcv, icmp_rcv会解析icmp报头中类型字段,对于属于查询报文的类型(如type8)会传递给icmp_reply, 而对于差错报文会传递给icmp_send处理,并且ICMP协议也会和其他诸如TCP/UDP协议进行交互传递信息。对于ping进程发出的请求,会先传递给icmp_echo函数进行处理。而icmp_echo正是处理ping请求很重要的一步,内核会把请求中附带的数据报文部分原封不动的拷贝并发送回源主机。因此我们可以在icmp_echo函数中,添加进我们"爱的语句"。static bool icmp_echo(struct sk_buff skb){ struct net net; net = dev_net(skb_dst(skb)->dev); if (!net->ipv4.sysctl_icmp_echo_ignore_all) { struct icmp_bxm icmp_param; icmp_param.data.icmph = icmp_hdr(skb); icmp_param.data.icmph.type = ICMP_ECHOREPLY; icmp_param.skb = skb; //———–添加开始———– char sentence1[] = “I LOVE U, xxxx.”; char sentence2[] = “I MISS U, xxxx.”; char sentence3[] = “Happy Valentine’s Day!”; int sentence_len_list[] = {sizeof(sentence1), sizeof(sentence2), sizeof(sentence3)}; char sentence_list[] = {sentence1, sentence2, sentence3}; int sentence_index = icmp_param.data.icmph.un.echo.sequence % 3; if(skb->len >= 16 + sentence_len_list[sentence_index]) { char tmp = (char)(skb->data+16); char* target_sentence = sentence_list[sentence_index]; int i=0; for(;i<sentence_len_list[sentence_index];++i) { tmp[i] = target_sentence[i]; } for(;i < skb->len-16;++i) { tmp[i] = 0; } } //———–添加结束———— icmp_param.offset = 0; icmp_param.data_len = skb->len; icmp_param.head_len = sizeof(struct icmphdr); icmp_reply(&icmp_param, skb); } /* should there be an ICMP stat for ignored echos? */ return true;}上述代码中icmp_bxm结构体包含了在后续icmp消息传递过程中的所有需要的信息,包括icmp报文头,sk_buff对象,icmp 报文payload大小等。需要注意的是,由于icmp_rcv已经解析过sk_buff中属于icmp协议的报文头部分,所以参数中skb->data指向的是icmp数据部分,即不包含报文头,而skb->len也只有icmp数据部分的长度。假设ping请求中所带的数据部分为56字节,则此时skb->len大小为56。由于ping数据部分的前16字节为携带的是发送是struct timeval对象——发送时的时间,所以在真实替换时,从data指向的数据部分的第16个字节开始,用memcpy复制到对应区域,或者如上例子傻傻的循环赋值即可。上面代码所表示的就是根据echo请求中seq_id循环回复上述三句话。当然有创意的小伙伴可以增加更多表达难度。四、创建一个阿里云ECS服务器,十分钟完成所有修改分析完了整个icmp处理流程,和修改方法,我们只需要创建一个阿里云ECS,并简单编译修改后的内核即可。具体流程如下:阿里云创建任意规格服务器(大规格可以加快内核编译速度,此处创建一个4vcpu服务器),使用centos作为os下载linux内核代码,并解压放置到/usr/src/kernels目录下,本文使用的是4.20.6内核版本。编译前基于原centos系统中/boot目录下的config文件,生成编译配置项,根据此编译项来定制内核。拷贝原配置文件至内核文件目录 sudo cp /boot/config-3.10.0-693.el7.x86_64 ./.config;执行make oldconfig,生成新的.config文件编译源码:make -j 4 ,可能编译过程中缺少某些库,此时yum安装缺少的库,如openssl-devel, elfutils-libelf-devel安装内核模块:make modules_install -j 4拷贝内核和配置文件至/boot目录,并生成System.map文件:make install -j 4更新引导:grub2-mkconfig -o /boot/grub2/grub.cfg修改默认默认启动引导内核:修改/etc/default/grub文件,将GRUB_DEFAULT设为0,0表示第一个启动项,即为最新编译的内核。重启服务器:reboot 至此告诉你的女神/男神,你想说的话都在ping中。部分参考文章:Understanding Linux Network Internal 第2章&第25章https://www.geeksforgeeks.org/ping-in-c/https://medium.freecodecamp.org/building-and-installing-the-latest-linux-kernel-from-source-6d8df5345980https://github.com/iputils/ip…本文作者:贺小白同学阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 18, 2019 · 1 min · jiezi

新书推荐|Windows黑客编程技术详解

《Windows黑客编程技术详解》面向对计算机系统安全开发感兴趣,或者希望提升安全开发水平的读者,以及从事恶意代码分析研究的安全人员。理论技术与实战操作相辅相成,凸显“道与术”庖丁解牛式剖析Windows用户层和内核层黑客技术原理代码兼容性高,支持Windows 7到Windows 10全平台系统近年来,全球大规模爆发勒索病毒和挖矿病毒,让沉寂许久的黑客技术,又重新回到了人们的视野中。Windows操作系统市场占有率高达90%以上,所以面对勒索病毒、挖矿病毒,Windows用户首当其冲。 为了揭开病毒木马的神秘面纱,更好地服务于信息安全,本书总结并剖析了常见的Windows黑客编程技术,用通俗易懂的语言介绍了用户层下的Windows编程和内核层下的Rootkit编程。内容简介《Windows黑客编程技术详解》介绍的是黑客编程的基础技术,涉及用户层下的Windows编程和内核层下的Rootkit编程。本书分为用户篇和内核篇两部分,用户篇包括11章,配套49个示例程序源码;内核篇包括7章,配套28个示例程序源码。本书介绍的每个技术都有详细的实现原理,以及对应的示例代码(配套代码均支持32位和64位Windows 7、Windows 8.1及Windows 10系统),旨在帮助初学者建立起黑客编程技术的基础。目录第1篇 用户篇第1章 开发环境 1.1 环境安装 1.2 工程项目设置 1.3 关于Debug模式和Release模式的小提示第2章 基础技术 2.1 运行单一实例 2.2 DLL延时加载 2.3 资源释放第3章 注入技术 3.1 全局钩子注入 3.2 远线程注入 3.3 突破SESSION 0隔离的远线程注入 3.4 APC注入第4章 启动技术 4.1 创建进程API 4.2 突破SESSION 0隔离创建用户进程 4.3内存直接加载运行第5章 自启动技术 5.1 注册表 5.2 快速启动目录 5.3 计划任务 5.4 系统服务第6章 提权技术 6.1 进程访问令牌权限提升 6.2 Bypass UAC第7章 隐藏技术 7.1 进程伪装 7.2傀儡进程 7.3 进程隐藏 7.4 DLL劫持第8章 压缩技术 8.1 数据压缩API 8.2 ZLIB压缩库第9章 加密技术 9.1 Windows自带的加密库 9.2 Crypto++密码库第10章 传输技术 10.1 Socket通信 10.2 FTP通信 10.3 HTTP通信 10.4 HTTPS通信第11章 功能技术 11.1 进程遍历 11.2 文件遍历 11.3 桌面截屏 11.4 按键记录 11.5 远程CMD 11.6 U盘监控 11.7 文件监控 11.8 自删除第2篇 内核篇第12章 开发环境 12.1 环境安装 12.2 驱动程序开发与调试 12.3 驱动无源码调试 12.4 32位和64位驱动开发第13章 文件管理技术 13.1 文件管理之内核API 13.2 文件管理之IRP 13.3 文件管理之NTFS解析第14章 注册表管理技术 14.1 注册表管理之内核API 14.2 注册表管理之HIVE文件解析第15章 HOOK技术 15.1 SSDT Hook 15.2过滤驱动第16章 监控技术 16.1 进程创建监控 16.2 模块加载监控 16.3 注册表监控 16.4 对象监控 16.5 Minifilter文件监控 16.6 WFP网络监控第17章 反监控技术 17.1 反进程创建监控 17.2 反线程创建监控 17.3 反模块加载监控 17.4 反注册表监控 17.5 反对象监控 17.6 反Minifilter文件监控第18章 功能技术 18.1 过PatchGuard的驱动隐藏 18.2 过PatchGuard的进程隐藏 18.3 TDI网络通信 18.4 强制结束进程 18.5 文件保护 18.6 文件强删附录 函数一览表书籍配套源码下载地址https://github.com/DemonGan/W…书籍购买链接天猫:https://detail.tmall.com/item…京东:https://item.jd.com/12464379….当当:http://product.dangdang.com/2… ...

February 16, 2019 · 1 min · jiezi