├── LICENSE ├── README.md ├── changelogs └── index.md ├── chapter-02 ├── test-01 │ ├── Makefile │ ├── clock.c │ ├── clock.h │ └── main.c └── test-02 │ ├── Makefile │ ├── clock.c │ ├── clock.h │ └── main.c ├── chapter-04 └── test-01 │ └── main.c ├── chapter-06 ├── test-01 │ └── main.c ├── test-02 │ └── main.c ├── test-03 │ └── main.c └── test-04 │ └── main.c ├── chapter-07 ├── test-01 │ └── main.c └── test-02 │ └── main.c ├── chapter-08 └── test-01 │ └── cpu_stat.sh ├── chapter-09 └── test-01 │ └── main.go ├── chapter-12 └── test-01 │ └── main.c ├── chapter-13 ├── test-01 │ └── main.c ├── test-02 │ └── main.go └── test-03 │ └── main.c ├── chapter-14 ├── test-01 │ └── main.c └── test-02 │ └── main.c ├── chapter-15 ├── test-01 │ └── cpu_stat.sh └── test-02 │ └── cpu_stat.sh ├── chapter-16 └── test-01 │ ├── Makefile │ ├── likely │ ├── likely.c │ ├── likely.txt │ ├── unlikely │ ├── unlikely.c │ └── unlikely.txt └── imgs ├── author.png ├── double.png └── official_accounts.jpg /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 本工程是《深入理解Linux进程与内存 - 修炼底层内功、掌握高性能原理》一书的配套代码。 3 | 4 | 飞哥目前已经出版了两本书,名字分别叫做 **《深入理解Linux进程与内存》** 和 **《深入理解Linux网络》** 。这两本书的市场接受度非常的高,都是在首发当天就斩获了京东科技类图书的第一名。希望这两本书能帮大家驾驭技术,掌舵人生! 5 | 6 | ![avatar](imgs/double.png) 7 | 8 | 另外大家也可以加飞哥微信(zhangyanfei748528)进读者交流群,或者互相围观朋友圈。 9 | 10 | **敬请扫码关注微信公众号「开发内功修炼」,及时获得最新硬核文章!** 11 | ![avatar](imgs/official_accounts.jpg) 12 | 13 | 14 | ## 书中相关开源代码 15 | - glibc源码下载地址: 16 | - 官网:[http://ftp.gnu.org/gnu/glibc/](http://ftp.gnu.org/gnu/glibc/) 17 | - 清华大学开源软件镜像: [https://mirrors.tuna.tsinghua.edu.cn/gnu/libc/](https://mirrors.tuna.tsinghua.edu.cn/gnu/libc/) 18 | - Golang源码下载地址:[https://github.com/golang/go](https://github.com/golang/go) 19 | - libcontainer源码地址:[https://github.com/opencontainers/runc/tree/main/libcontainer](https://github.com/opencontainers/runc/tree/main/libcontainer) 20 | - CAdvisor源码地址:[https://github.com/google/cadvisor](https://github.com/google/cadvisor) 21 | - Linux源码下载地址:[https://mirrors.edge.kernel.org/pub/linux/kernel/](https://mirrors.edge.kernel.org/pub/linux/kernel/) 22 | - lxcfs源码下载地址:[https://github.com/lxc/lxcfs](https://github.com/lxc/lxcfs) 23 | - Nginx源码下载地址:[https://github.com/nginx/nginx](https://github.com/nginx/nginx) 24 | - redis 源码下载地址:[https://github.com/redis/redis](https://github.com/redis/redis) 25 | - 内核在线源码阅读网站:[http://elixir.bootlin.com/](http://elixir.bootlin.com/) 26 | - 书的配套实验:[https://github.com/yanfeizhang/deep_linux_process_tests](https://github.com/yanfeizhang/deep_linux_process_tests) 27 | 28 | ## 书中实验源码 29 | - 第二章 30 | - [内存延时性能测试](chapter-02/test-01) 31 | - [内存带宽性能测试](chapter-02/test-01) 32 | - 第四章 33 | - [查看ELF文件格式用的demo程序](chapter-04/test-01) 34 | - 第六章 35 | - [堆栈溢出程序](chapter-06/test-01) 36 | - [查看进程虚拟地址空间状态](chapter-06/test-02) 37 | - [使用mmap匿名映射后的进程虚拟地址空间状态](chapter-06/test-03) 38 | - [使用brk/sbrk后的进程虚拟地址空间状态](chapter-06/test-04) 39 | - 第七章 40 | - [进程上下文切换开销测试](chapter-07/test-01) 41 | - [线程上下文切换开销测试](chapter-07/test-02) 42 | - 第八章 43 | - [直接使用内核伪文件来计算CPU利用率](chapter-08/test-01) 44 | - 第九章 45 | - [协程上下文切换测试](chapter-09/test-01) 46 | - 第十二章 47 | - [容器内存限制实验](chapter-12/test-01) 48 | - 第十三章 49 | - [C语言函数工作原理](chapter-13/test-01) 50 | - [Golang函数工作原理](chapter-13/test-02) 51 | - [read系统调用开销测试](chapter-13/test-03) 52 | - [](chapter-13/test-06) 多出来的 53 | - 第十四章 54 | - [火焰图工作原理查看代码](chapter-14/test-01) 55 | - [使用内核系统调用查看硬件指标](chapter-14/test-02) 56 | - 第十五章 57 | - [根据内核伪文件计算进程的CPU利用率](chapter-15/test-01) 58 | - [根据内核伪文件计算容器的CPU利用率](chapter-15/test-02) 59 | - 第十六章 60 | - [内核中的likely与unlikely](chapter-16/test-01) 61 | 62 | 63 | ## 公众号 64 | 扫一扫关注公众号「开发内功修炼」,获取最新硬核技术文章! 65 | ![](imgs/official_accounts.jpg) 66 | 67 | ## 技术交流 && 意见反馈 68 | 添加作者微信加技术交流群,或者反馈您宝贵的改进建议! 69 | ![](imgs/author.png) 70 | 71 | ## 书籍勘误列表 72 | - [书籍勘误列表](changelogs/index.md) 73 | 74 | -------------------------------------------------------------------------------- /changelogs/index.md: -------------------------------------------------------------------------------- 1 | ## 第一轮 2 | - P3: 图1.1 中的 "沟通" ==> "沟道" 3 | - P39/P40 行分组列分组描述有误 4 | - P39 "第二个列分组方式是将 2、3、7、8 列看做一个分组" => "第二个列分组方式是将 3、4、7、8 列看做一个分组" 5 | - P40 图2.11参考图2.16参考 6 | - P40 "第二个行分组方式是将2、3、7、8行看做一个分组" => "第二个行分组方式是将3、4、7、8行看做一个分组" 7 | - P46: 页面中间, "对于 1R X 6的内存条" => "对于 1R X 6的内存条" 这里应该是 16 8 | - P55: 页面下方 "再等待tRPC个时钟周期" => "再等待tRCD个时钟周期" 9 | - P61: 第二段中"任务获得 CPU 后进入 TASK_INTERRUPTIBLE 执行状态进行运行" => "任务获得 CPU 还是 TASK_RUNNING 状态但开始运行" 10 | - P62: 倒数第二段中最后一句 "轻量级线程" => "轻量级进程" 11 | - P71/P86: CLONE_VM等标记描述有误 12 | - P71: 页面中间对CLONE_VM、CLONE_FS、CLONE_FILES的描述中,"如果用了" 都改成 "如果没用" 13 | - P86: 页面下方对CLONE_VM、CLONE_FS、CLONE_FILES的描述中,"如果用了" 都改成 "如果没用" 14 | - P94: 3.5本章总结中由同一个内核参数kernel_clone来实现的这一句汇总 "内核参数" => "内核函数" 15 | - P111: 这个函数中申请并初始化brm对象中,"brm"改成"bprm"对象。还有上方注释中、图4.6图名中也是。 16 | - P151: 页面最后一句,“该分配器只用于内存” => “该分配器只用于内核” 17 | - P175: 页尾 "chapter04/test01" => "chapter-06/test01" 18 | - P216/P242: 使用内核版本描述不准确 19 | - P216: 第一段中, "6.2" => "6.1" 20 | - P242: 最后一段中,"5.4" => "6.1" 21 | - P259: 第一段,采样是1毫米一次,"毫米"=>"毫秒" 22 | - P343: 11.1.1.2小节中 "首先找到 cpu,cpuacct 这个group" => "首先找到/sys/fs/cgroup/这个根group" 23 | 24 | 25 | ## 第二轮 26 | - P50:图2.28上面的一句话,“64字节” => “64比特” 27 | - P52:第三段中,“后面的7字节” => “后面的7次8字节” 28 | - P57:页面最后顺序IO描述中的“tRP” => "CL" 29 | - P88: 3.3.2标题中,"fork创建线程" 中把 "fork"删掉,原因是创建线程时没有过fork函数。 30 | - P111: 图中的文字 31 | - 图4.6中 mm_struct 中的 "*mmap" 改成 “mm_rb”,和图6.3保持一致。 32 | - P112 图4.7中修改方式同上 33 | - P122: 4.5.5 上面的一句话中。"最后设置虚拟地址空间中的代码段。" 这句话后面应该是顿号,而不是句号。 34 | - P244: 倒数第二段“其vruntime下降的越快” => “其vruntime增加的越慢”。原因是vruntime是单调递增的,不会出现下降的情况。 35 | - P254: "但是本书使用的3.10版本" => "但是现在内核版本" 36 | - P506: 16.1.6第一段中,"8个Bank" => "8个Chip" 37 | - P530: 第一段最后一句话,“72字节相对56字节” => “56字节相对72字节” 38 | 39 | ## 第二轮 40 | - P48:页面倒数第三段。"Speed/2就可以得出时钟周期",这里应该是"时钟周期数"。本行后面的"则时钟周期为533MHz"里也应该修改为"时钟周期数"。 41 | - P111:第一行错了,“path->name” 应该改为“filename”。这里错误的原因是最早写文章用的是5.4版本,后来写书升级到6.1之后,这里描述没有对应做修改。 42 | - P210:图中的 “struct rb_root” 应该改为 “struct rb_root_cached”。原因是 6.1 中这个名字变了。 43 | - P242:调度器发展简史中的“显示” => “先是” 44 | - P430:页面倒数第11行中 "/proc/[pid]/pwd" -> "/proc/[pid]/cwd" 45 | 46 | ## 致谢 47 | 感谢以下同学(排名不分先后,仅按拼音首字母排序): 48 | - @3Xpl0ui3r 49 | - @4ever 50 | - @andylee 51 | - @Chaos John 52 | - @Chunel 53 | - @Climer 54 | - @陈昆吾 55 | - @蝶影残殇 56 | - @equals 57 | - @Ever 58 | - @飞扬 59 | - @Forrest 60 | - @反方向的钟 61 | - @GI 62 | - @含英咀华 63 | - @昊 64 | - @Hubo 65 | - @Homer 66 | - @j 67 | - @唧唧复唧唧 68 | - @今天 69 | - @康康 70 | - @看南山 71 | - @Lea 72 | - @Lina 73 | - @Liu Ye 74 | - @梁士兴 75 | - @刘家禄 76 | - @linkerrors 77 | - @彭东林 78 | - @请输入昵称 79 | - @全然大丈夫 80 | - @肖益龙 81 | - @瘦瘦 82 | - @Trust_yourself 83 | - @TZ 84 | - @徐衍振 85 | - @杨振雷 86 | - @远知不知 87 | - @袁世超 88 | - @张华华 89 | - @张华华 90 | - @微软大法好 91 | - @ZXC 92 | - @大河 93 | - @贝加尔湖畔的微风 94 | - @onceday 95 | - @j 96 | -------------------------------------------------------------------------------- /chapter-02/test-01/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -O2 -lrt -D__i386__ 3 | 4 | main: main.c clock.c 5 | $(CC) $(CFLAGS) -o main main.c clock.c 6 | 7 | clean: 8 | rm -f main *.o *~ 9 | 10 | 11 | -------------------------------------------------------------------------------- /chapter-02/test-01/clock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "clock.h" 6 | #include 7 | 8 | 9 | /* Routines for using cycle counter */ 10 | 11 | /* Detect whether running on Alpha */ 12 | #ifdef __alpha 13 | #define IS_ALPHA 1 14 | #else 15 | #define IS_ALPHA 0 16 | #endif 17 | 18 | /* Detect whether running on x86 */ 19 | #ifdef __i386__ 20 | #define IS_x86 1 21 | #else 22 | #define IS_x86 0 23 | #endif 24 | 25 | 26 | 27 | 28 | /* Keep track of most recent reading of cycle counter */ 29 | static unsigned cyc_hi = 0; 30 | static unsigned cyc_lo = 0; 31 | 32 | #if IS_ALPHA 33 | /* Use Alpha cycle timer to compute cycles. Then use 34 | measured clock speed to compute seconds 35 | */ 36 | 37 | /* 38 | * counterRoutine is an array of Alpha instructions to access 39 | * the Alpha's processor cycle counter. It uses the rpcc 40 | * instruction to access the counter. This 64 bit register is 41 | * divided into two parts. The lower 32 bits are the cycles 42 | * used by the current process. The upper 32 bits are wall 43 | * clock cycles. These instructions read the counter, and 44 | * convert the lower 32 bits into an unsigned int - this is the 45 | * user space counter value. 46 | * NOTE: The counter has a very limited time span. With a 47 | * 450MhZ clock the counter can time things for about 9 48 | * seconds. */ 49 | static unsigned int counterRoutine[] = 50 | { 51 | 0x601fc000u, 52 | 0x401f0000u, 53 | 0x6bfa8001u 54 | }; 55 | 56 | /* Cast the above instructions into a function. */ 57 | static unsigned int (*counter)(void)= (void *)counterRoutine; 58 | 59 | 60 | void start_counter() 61 | { 62 | /* Get cycle counter */ 63 | cyc_hi = 0; 64 | cyc_lo = counter(); 65 | } 66 | 67 | double get_counter() 68 | { 69 | unsigned ncyc_hi, ncyc_lo; 70 | unsigned hi, lo, borrow; 71 | double result; 72 | ncyc_lo = counter(); 73 | ncyc_hi = 0; 74 | lo = ncyc_lo - cyc_lo; 75 | borrow = lo > ncyc_lo; 76 | hi = ncyc_hi - cyc_hi - borrow; 77 | result = (double) hi * (1 << 30) * 4 + lo; 78 | if (result < 0) { 79 | fprintf(stderr, "Error: Cycle counter returning negative value: %.0f\n", result); 80 | } 81 | return result; 82 | } 83 | #endif /* Alpha */ 84 | 85 | #if IS_x86 86 | void access_counter(unsigned *hi, unsigned *lo) 87 | { 88 | /* Get cycle counter */ 89 | asm("rdtsc; movl %%edx,%0; movl %%eax,%1" 90 | : "=r" (*hi), "=r" (*lo) 91 | : /* No input */ 92 | : "%edx", "%eax"); 93 | } 94 | 95 | void start_counter() 96 | { 97 | access_counter(&cyc_hi, &cyc_lo); 98 | } 99 | 100 | double get_counter() 101 | { 102 | unsigned ncyc_hi, ncyc_lo; 103 | unsigned hi, lo, borrow; 104 | double result; 105 | /* Get cycle counter */ 106 | access_counter(&ncyc_hi, &ncyc_lo); 107 | /* Do double precision subtraction */ 108 | lo = ncyc_lo - cyc_lo; 109 | borrow = lo > ncyc_lo; 110 | hi = ncyc_hi - cyc_hi - borrow; 111 | result = (double) hi * (1 << 30) * 4 + lo; 112 | if (result < 0) { 113 | fprintf(stderr, "Error: Cycle counter returning negative value: %.0f\n", result); 114 | } 115 | return result; 116 | } 117 | #endif /* x86 */ 118 | struct timespec time1 = {0, 0}; 119 | void start_timer() 120 | { 121 | clock_gettime(CLOCK_REALTIME, &time1); 122 | } 123 | 124 | long int get_timer() 125 | { 126 | struct timespec time2 = {0, 0}; 127 | clock_gettime(CLOCK_REALTIME, &time2); 128 | 129 | long int usedNanoSecond = (time2.tv_sec-time1.tv_sec)*1000000000 + (time2.tv_nsec-time1.tv_nsec); 130 | return usedNanoSecond; 131 | } 132 | 133 | double ovhd() 134 | { 135 | /* Do it twice to eliminate cache effects */ 136 | int i; 137 | double result; 138 | for (i = 0; i < 2; i++) { 139 | start_counter(); 140 | result = get_counter(); 141 | } 142 | return result; 143 | } 144 | 145 | /* Determine clock rate by measuring cycles 146 | elapsed while sleeping for sleeptime seconds */ 147 | double mhz_full(int verbose, int sleeptime) 148 | { 149 | double rate; 150 | start_counter(); 151 | sleep(sleeptime); 152 | rate = get_counter()/(1e6*sleeptime); 153 | if (verbose) 154 | printf("Processor Clock Rate ~= %.1f MHz\n", rate); 155 | return rate; 156 | } 157 | 158 | /* Version using a default sleeptime */ 159 | double mhz(int verbose) 160 | { 161 | return mhz_full(verbose, 2); 162 | } 163 | 164 | /** Special counters that compensate for timer interrupt overhead */ 165 | 166 | static double cyc_per_tick = 0.0; 167 | 168 | #define NEVENT 100 169 | #define THRESHOLD 1000 170 | #define RECORDTHRESH 3000 171 | 172 | /* Attempt to see how much time is used by timer interrupt */ 173 | static void callibrate(int verbose) 174 | { 175 | double oldt; 176 | struct tms t; 177 | clock_t oldc; 178 | int e = 0; 179 | times(&t); 180 | oldc = t.tms_utime; 181 | start_counter(); 182 | oldt = get_counter(); 183 | while (e = THRESHOLD) { 186 | clock_t newc; 187 | times(&t); 188 | newc = t.tms_utime; 189 | if (newc > oldc) { 190 | double cpt = (newt-oldt)/(newc-oldc); 191 | if ((cyc_per_tick == 0.0 || cyc_per_tick > cpt) && cpt > RECORDTHRESH) 192 | cyc_per_tick = cpt; 193 | /* 194 | if (verbose) 195 | printf("Saw event lasting %.0f cycles and %d ticks. Ratio = %f\n", 196 | newt-oldt, (int) (newc-oldc), cpt); 197 | */ 198 | e++; 199 | oldc = newc; 200 | } 201 | oldt = newt; 202 | } 203 | } 204 | if (verbose) 205 | printf("Setting cyc_per_tick to %f\n", cyc_per_tick); 206 | } 207 | 208 | static clock_t start_tick = 0; 209 | 210 | void start_comp_counter() { 211 | struct tms t; 212 | if (cyc_per_tick == 0.0) 213 | callibrate(0); 214 | times(&t); 215 | start_tick = t.tms_utime; 216 | start_counter(); 217 | } 218 | 219 | double get_comp_counter() { 220 | double time = get_counter(); 221 | double ctime; 222 | struct tms t; 223 | clock_t ticks; 224 | times(&t); 225 | ticks = t.tms_utime - start_tick; 226 | ctime = time - ticks*cyc_per_tick; 227 | /* 228 | printf("Measured %.0f cycles. Ticks = %d. Corrected %.0f cycles\n", 229 | time, (int) ticks, ctime); 230 | */ 231 | return ctime; 232 | } 233 | -------------------------------------------------------------------------------- /chapter-02/test-01/clock.h: -------------------------------------------------------------------------------- 1 | /* Routines for using cycle counter */ 2 | 3 | /* Start the counter */ 4 | void start_counter(); 5 | 6 | /* Get # cycles since counter started */ 7 | double get_counter(); 8 | 9 | 10 | void start_timer(); 11 | long int get_timer(); 12 | 13 | 14 | /* Measure overhead for counter */ 15 | double ovhd(); 16 | 17 | /* Determine clock rate of processor */ 18 | double mhz(int verbose); 19 | 20 | /* Determine clock rate of processor, having more control over accuracy */ 21 | double mhz_full(int verbose, int sleeptime); 22 | 23 | /** Special counters that compensate for timer interrupt overhead */ 24 | 25 | void start_comp_counter(); 26 | 27 | double get_comp_counter(); 28 | -------------------------------------------------------------------------------- /chapter-02/test-01/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "clock.h" 4 | 5 | // 测试用的全局内存区域 6 | double data[MAXELEMS]; 7 | 8 | // 内存测试区域从 2 KB 开始,最大到 64 MB 9 | #define MINBYTES (1 << 11) // 2 KB 10 | #define MAXBYTES (1 << 26) // 64 MB 11 | 12 | // 循环步长从1 到 64 字节 13 | #define MAXSTRIDE 64 14 | #define MAXELEMS MAXBYTES/sizeof(double) 15 | 16 | void init_data(double *data, int n); 17 | void run_delay_testing(); 18 | double get_seque_access_result(int size, int stride, int type); 19 | double get_random_access_result(int size, int type); 20 | void seque_access(int elems, int stride); 21 | void random_access(int* random_index_arr, int count); 22 | void create_rand_array(int max, int count, int* pArr); 23 | 24 | int main() 25 | { 26 | init_data(data, MAXELEMS); 27 | 28 | printf("Delay (ns)\n"); 29 | run_delay_testing(); 30 | printf("\n\n"); 31 | 32 | exit(0); 33 | } 34 | 35 | // init_data 初始化要访问的内存数据 36 | void init_data(double *data, int n) 37 | { 38 | int i; 39 | for (i = 0; i < n; i++) 40 | { 41 | data[i] = i; 42 | } 43 | } 44 | 45 | // 运行内存访问延时测试 46 | void run_delay_testing(){ 47 | int size; // 测试内存区域大小 48 | int stride; // 内存区域访问循环步长 49 | 50 | // 打印内存区域大小头信息 51 | printf("\t"); 52 | for (size = MINBYTES; size <= MAXBYTES; size <<= 1) { 53 | if (size > (1 << 20)){ 54 | printf("%dm\t", size / (1 << 20)); 55 | }else{ 56 | printf("%dk\t", size / 1024); 57 | } 58 | } 59 | printf("\n"); 60 | 61 | // 多次实验,进行内存顺序访问延时评估 62 | // 外层循环控制步长依次从 1 到 64,目的是不同的顺序步长的访问效果差异 63 | // 内存循环控制数据大小依次从 2KB 开始到 64MB,目的是要保证数据大小依次超过 L1、L2、L3 64 | for (stride = 1; stride <= MAXSTRIDE; stride=stride*2) { 65 | printf("s%d\t", stride); 66 | for (size = MINBYTES; size <= MAXBYTES; size <<= 1) { 67 | printf("%.2f\t", get_seque_access_result(size, stride, 1)); 68 | } 69 | printf("\n"); 70 | } 71 | 72 | // 多次实验,进行内存随机访问延时评估 73 | printf("random\t"); 74 | for (size = MINBYTES; size <= MAXBYTES; size <<= 1) { 75 | printf("%.2f\t", get_random_access_result(size,1)); 76 | } 77 | printf("\n"); 78 | } 79 | 80 | // get_seque_access_result 测试存储访问延迟(L1/L2/L3,内存) 81 | // 参数说明 82 | // - size: 要测试的数据大小 83 | // - stride: 步长 84 | // - type: 0 获取带宽测试结果 85 | // - 1 获取延时测试结果,单位是 CPU 周期数 86 | double get_seque_access_result(int size, int stride, int type) 87 | { 88 | int i; 89 | long int operations; 90 | long int total_accessed_bytes; 91 | long int used_nanoseconds; 92 | 93 | int samples = 1000; 94 | int elems = size / sizeof(double); 95 | 96 | //循环测试 1000 次,以最大程度减少实验计算结果误差 97 | start_timer(); 98 | for(i=0; i 2 | #include 3 | #include 4 | #include 5 | #include "clock.h" 6 | #include 7 | 8 | 9 | /* Routines for using cycle counter */ 10 | 11 | /* Detect whether running on Alpha */ 12 | #ifdef __alpha 13 | #define IS_ALPHA 1 14 | #else 15 | #define IS_ALPHA 0 16 | #endif 17 | 18 | /* Detect whether running on x86 */ 19 | #ifdef __i386__ 20 | #define IS_x86 1 21 | #else 22 | #define IS_x86 0 23 | #endif 24 | 25 | 26 | 27 | 28 | /* Keep track of most recent reading of cycle counter */ 29 | static unsigned cyc_hi = 0; 30 | static unsigned cyc_lo = 0; 31 | 32 | #if IS_ALPHA 33 | /* Use Alpha cycle timer to compute cycles. Then use 34 | measured clock speed to compute seconds 35 | */ 36 | 37 | /* 38 | * counterRoutine is an array of Alpha instructions to access 39 | * the Alpha's processor cycle counter. It uses the rpcc 40 | * instruction to access the counter. This 64 bit register is 41 | * divided into two parts. The lower 32 bits are the cycles 42 | * used by the current process. The upper 32 bits are wall 43 | * clock cycles. These instructions read the counter, and 44 | * convert the lower 32 bits into an unsigned int - this is the 45 | * user space counter value. 46 | * NOTE: The counter has a very limited time span. With a 47 | * 450MhZ clock the counter can time things for about 9 48 | * seconds. */ 49 | static unsigned int counterRoutine[] = 50 | { 51 | 0x601fc000u, 52 | 0x401f0000u, 53 | 0x6bfa8001u 54 | }; 55 | 56 | /* Cast the above instructions into a function. */ 57 | static unsigned int (*counter)(void)= (void *)counterRoutine; 58 | 59 | 60 | void start_counter() 61 | { 62 | /* Get cycle counter */ 63 | cyc_hi = 0; 64 | cyc_lo = counter(); 65 | } 66 | 67 | double get_counter() 68 | { 69 | unsigned ncyc_hi, ncyc_lo; 70 | unsigned hi, lo, borrow; 71 | double result; 72 | ncyc_lo = counter(); 73 | ncyc_hi = 0; 74 | lo = ncyc_lo - cyc_lo; 75 | borrow = lo > ncyc_lo; 76 | hi = ncyc_hi - cyc_hi - borrow; 77 | result = (double) hi * (1 << 30) * 4 + lo; 78 | if (result < 0) { 79 | fprintf(stderr, "Error: Cycle counter returning negative value: %.0f\n", result); 80 | } 81 | return result; 82 | } 83 | #endif /* Alpha */ 84 | 85 | #if IS_x86 86 | void access_counter(unsigned *hi, unsigned *lo) 87 | { 88 | /* Get cycle counter */ 89 | asm("rdtsc; movl %%edx,%0; movl %%eax,%1" 90 | : "=r" (*hi), "=r" (*lo) 91 | : /* No input */ 92 | : "%edx", "%eax"); 93 | } 94 | 95 | void start_counter() 96 | { 97 | access_counter(&cyc_hi, &cyc_lo); 98 | } 99 | 100 | double get_counter() 101 | { 102 | unsigned ncyc_hi, ncyc_lo; 103 | unsigned hi, lo, borrow; 104 | double result; 105 | /* Get cycle counter */ 106 | access_counter(&ncyc_hi, &ncyc_lo); 107 | /* Do double precision subtraction */ 108 | lo = ncyc_lo - cyc_lo; 109 | borrow = lo > ncyc_lo; 110 | hi = ncyc_hi - cyc_hi - borrow; 111 | result = (double) hi * (1 << 30) * 4 + lo; 112 | if (result < 0) { 113 | fprintf(stderr, "Error: Cycle counter returning negative value: %.0f\n", result); 114 | } 115 | return result; 116 | } 117 | #endif /* x86 */ 118 | struct timespec time1 = {0, 0}; 119 | void start_timer() 120 | { 121 | clock_gettime(CLOCK_REALTIME, &time1); 122 | } 123 | 124 | long int get_timer() 125 | { 126 | struct timespec time2 = {0, 0}; 127 | clock_gettime(CLOCK_REALTIME, &time2); 128 | 129 | long int usedMircoSecond = (time2.tv_sec-time1.tv_sec)*1000000000 + (time2.tv_nsec-time1.tv_nsec); 130 | return usedMircoSecond; 131 | } 132 | 133 | double ovhd() 134 | { 135 | /* Do it twice to eliminate cache effects */ 136 | int i; 137 | double result; 138 | for (i = 0; i < 2; i++) { 139 | start_counter(); 140 | result = get_counter(); 141 | } 142 | return result; 143 | } 144 | 145 | /* Determine clock rate by measuring cycles 146 | elapsed while sleeping for sleeptime seconds */ 147 | double mhz_full(int verbose, int sleeptime) 148 | { 149 | double rate; 150 | start_counter(); 151 | sleep(sleeptime); 152 | rate = get_counter()/(1e6*sleeptime); 153 | if (verbose) 154 | printf("Processor Clock Rate ~= %.1f MHz\n", rate); 155 | return rate; 156 | } 157 | 158 | /* Version using a default sleeptime */ 159 | double mhz(int verbose) 160 | { 161 | return mhz_full(verbose, 2); 162 | } 163 | 164 | /** Special counters that compensate for timer interrupt overhead */ 165 | 166 | static double cyc_per_tick = 0.0; 167 | 168 | #define NEVENT 100 169 | #define THRESHOLD 1000 170 | #define RECORDTHRESH 3000 171 | 172 | /* Attempt to see how much time is used by timer interrupt */ 173 | static void callibrate(int verbose) 174 | { 175 | double oldt; 176 | struct tms t; 177 | clock_t oldc; 178 | int e = 0; 179 | times(&t); 180 | oldc = t.tms_utime; 181 | start_counter(); 182 | oldt = get_counter(); 183 | while (e = THRESHOLD) { 186 | clock_t newc; 187 | times(&t); 188 | newc = t.tms_utime; 189 | if (newc > oldc) { 190 | double cpt = (newt-oldt)/(newc-oldc); 191 | if ((cyc_per_tick == 0.0 || cyc_per_tick > cpt) && cpt > RECORDTHRESH) 192 | cyc_per_tick = cpt; 193 | /* 194 | if (verbose) 195 | printf("Saw event lasting %.0f cycles and %d ticks. Ratio = %f\n", 196 | newt-oldt, (int) (newc-oldc), cpt); 197 | */ 198 | e++; 199 | oldc = newc; 200 | } 201 | oldt = newt; 202 | } 203 | } 204 | if (verbose) 205 | printf("Setting cyc_per_tick to %f\n", cyc_per_tick); 206 | } 207 | 208 | static clock_t start_tick = 0; 209 | 210 | void start_comp_counter() { 211 | struct tms t; 212 | if (cyc_per_tick == 0.0) 213 | callibrate(0); 214 | times(&t); 215 | start_tick = t.tms_utime; 216 | start_counter(); 217 | } 218 | 219 | double get_comp_counter() { 220 | double time = get_counter(); 221 | double ctime; 222 | struct tms t; 223 | clock_t ticks; 224 | times(&t); 225 | ticks = t.tms_utime - start_tick; 226 | ctime = time - ticks*cyc_per_tick; 227 | /* 228 | printf("Measured %.0f cycles. Ticks = %d. Corrected %.0f cycles\n", 229 | time, (int) ticks, ctime); 230 | */ 231 | return ctime; 232 | } 233 | -------------------------------------------------------------------------------- /chapter-02/test-02/clock.h: -------------------------------------------------------------------------------- 1 | /* Routines for using cycle counter */ 2 | 3 | /* Start the counter */ 4 | void start_counter(); 5 | 6 | /* Get # cycles since counter started */ 7 | double get_counter(); 8 | 9 | 10 | void start_timer(); 11 | long int get_timer(); 12 | 13 | 14 | /* Measure overhead for counter */ 15 | double ovhd(); 16 | 17 | /* Determine clock rate of processor */ 18 | double mhz(int verbose); 19 | 20 | /* Determine clock rate of processor, having more control over accuracy */ 21 | double mhz_full(int verbose, int sleeptime); 22 | 23 | /** Special counters that compensate for timer interrupt overhead */ 24 | 25 | void start_comp_counter(); 26 | 27 | double get_comp_counter(); 28 | -------------------------------------------------------------------------------- /chapter-02/test-02/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "clock.h" 4 | 5 | // 内存测试区域从 2 KB 开始,最大到 64 MB 6 | #define MINBYTES (1 << 11) // 2 KB 7 | #define MAXBYTES (1 << 26) // 64 MB 8 | 9 | // 循环步长从1 到 64 字节 10 | #define MAXSTRIDE 64 11 | #define MAXELEMS MAXBYTES/sizeof(double) 12 | 13 | // 测试用的全局内存区域 14 | double data[MAXELEMS]; 15 | 16 | void init_data(double *data, int n); 17 | void run_width_testing(); 18 | double get_seque_access_result(int size, int stride, int type); 19 | double get_random_access_result(int size, int type); 20 | void seque_access(int elems, int stride); 21 | void random_access(int* random_index_arr, int count); 22 | void create_rand_array(int max, int count, int* pArr); 23 | 24 | int main() 25 | { 26 | init_data(data, MAXELEMS); 27 | 28 | printf("Band Width (MB/sec)\n"); 29 | run_width_testing(); 30 | printf("\n\n"); 31 | 32 | exit(0); 33 | } 34 | 35 | // init_data 初始化要访问的内存数据 36 | void init_data(double *data, int n) 37 | { 38 | int i; 39 | for (i = 0; i < n; i++) 40 | { 41 | data[i] = i; 42 | } 43 | } 44 | 45 | // 运行内存访问带宽测试 46 | void run_width_testing(){ 47 | int size; // 测试内存区域大小 48 | int stride; // 内存区域访问循环步长 49 | 50 | // 打印内存区域大小头信息 51 | printf("\t"); 52 | for (size = MINBYTES; size <= MAXBYTES; size <<= 1) { 53 | if (size > (1 << 20)){ 54 | printf("%dm\t", size / (1 << 20)); 55 | }else{ 56 | printf("%dk\t", size / 1024); 57 | } 58 | } 59 | printf("\n"); 60 | 61 | // 多次实验,进行内存顺序访问带宽评估 62 | // 外层循环控制步长依次从 1 到 64,目的是不同的顺序步长的访问效果差异 63 | // 内存循环控制数据大小依次从 2KB 开始到 64MB,目的是要保证数据大小依次超过 L1、L2、L3 64 | for (stride = 1; stride <= MAXSTRIDE; stride=stride*2) { 65 | printf("s%d\t", stride); 66 | for (size = MINBYTES; size <= MAXBYTES; size <<= 1) { 67 | printf("%.2f\t", get_seque_access_result(size, stride, 0)); 68 | } 69 | printf("\n"); 70 | } 71 | 72 | // 多次实验,进行内存随机访问带宽评估 73 | printf("random\t"); 74 | for (size = MINBYTES; size <= MAXBYTES; size <<= 1) { 75 | printf("%.2f\t", get_random_access_result(size, 0)); 76 | } 77 | printf("\n"); 78 | } 79 | 80 | // get_seque_access_result 测试存储访问延迟(L1/L2/L3,内存) 81 | // 参数说明 82 | // - size: 要测试的数据大小 83 | // - stride: 步长 84 | // - type: 0 获取带宽测试结果 85 | // - 1 获取延时测试结果,单位是 CPU 周期数 86 | double get_seque_access_result(int size, int stride, int type) 87 | { 88 | int i; 89 | long int operations; 90 | long int total_accessed_bytes; 91 | long int used_nanoseconds; 92 | 93 | int samples = 1000; 94 | int elems = size / sizeof(double); 95 | 96 | //循环测试 1000 次,以最大程度减少实验计算结果误差 97 | start_timer(); 98 | for(i=0; i 2 | int data1; 3 | int data2 = 100 ; 4 | int somefunc() 5 | { 6 | return 0; 7 | } 8 | int main() 9 | { 10 | printf("Hello, World!\n"); 11 | return 0; 12 | } -------------------------------------------------------------------------------- /chapter-06/test-01/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | int somefunc() 3 | { 4 | somefunc(); 5 | } 6 | int main() 7 | { 8 | somefunc(); 9 | return 0; 10 | } -------------------------------------------------------------------------------- /chapter-06/test-02/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | printf("请另起一个命令行查看虚拟地址空间状态,命令是 cat /proc/%d/maps\n", getpid()); 8 | printf("然后按任意键退出程序...\n"); 9 | getchar(); 10 | return 0; 11 | } -------------------------------------------------------------------------------- /chapter-06/test-03/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void static inline errExit(const char* msg) 10 | { 11 | printf("%s failed. Exiting the process\n", msg); 12 | exit(-1); 13 | } 14 | 15 | int main() 16 | { 17 | int ret = -1; 18 | printf("这是一个mmap匿名映射的例子!\n"); 19 | printf("请另起一个命令行查看虚拟地址空间状态,命令是 cat /proc/%d/maps\n", getpid()); 20 | printf("然后按任意键继续...\n"); 21 | 22 | getchar(); 23 | char* addr = NULL; 24 | addr = mmap(NULL, (size_t)132*1024, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 25 | if (addr == MAP_FAILED) 26 | errExit("mmap错误"); 27 | printf("mmap私有映射成功,再次查看虚拟地址空间状态,观察有什么变化\n"); 28 | printf("然后按任意键继续...\n"); 29 | 30 | getchar(); 31 | ret = munmap(addr, (size_t)132*1024); 32 | if(ret == -1) 33 | errExit("munmap错误"); 34 | printf("接触mmap私有映射成功,再次查看虚拟地址空间状态,观察有什么变化\n"); 35 | printf("然后按任意键退出程序...\n"); 36 | 37 | getchar(); 38 | return 0; 39 | } -------------------------------------------------------------------------------- /chapter-06/test-04/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void static inline errExit(const char* msg) 7 | { 8 | printf("%s failed. Exiting the process\n", msg); 9 | exit(-1); 10 | } 11 | 12 | int main() 13 | { 14 | void *curr_brk, *tmp_brk = NULL; 15 | 16 | printf("这是一个brk/sbrk使用的例子!\n"); 17 | // sbrk(0) 获取当前 program break 位置 18 | tmp_brk = curr_brk = sbrk(0); 19 | printf("当前Program Break位置是:%p\n", curr_brk); 20 | printf("也可以另起一个命令行查看虚拟地址空间中heap状态,命令是 cat /proc/%d/maps\n", getpid()); 21 | printf("然后按任意键继续...\n"); 22 | getchar(); 23 | 24 | // 使用 brk 增加 program break 位置 25 | brk(curr_brk+4096); 26 | curr_brk = sbrk(0); 27 | printf("brk增加4096字节后,当前Program Break位置变成了:%p\n", curr_brk); 28 | printf("然后按任意键继续...\n"); 29 | 30 | getchar(); 31 | 32 | // 使用 brk 减小 program break 位置 33 | brk(tmp_brk); 34 | curr_brk = sbrk(0); 35 | printf("brk缩小4096字节后,当前Program Break位置回到了:%p\n", curr_brk); 36 | printf("然后按任意键退出程序...\n"); 37 | getchar(); 38 | 39 | return 0; 40 | } -------------------------------------------------------------------------------- /chapter-07/test-01/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define EXEC_COUNT 10000 10 | 11 | int main() 12 | { 13 | int x, i, fd[2], p[2]; 14 | char send = 'hello'; 15 | char receive; 16 | pipe(fd); 17 | pipe(p); 18 | struct timeval tv; 19 | struct sched_param param; 20 | param.sched_priority = 0; 21 | 22 | while ((x = fork()) == -1); 23 | if (x==0) { 24 | sched_setscheduler(getpid(), SCHED_FIFO, ¶m); 25 | gettimeofday(&tv, NULL); 26 | printf("开始测试时间:%u s, %u us\n", tv.tv_sec, tv.tv_usec); 27 | for (i = 0; i < EXEC_COUNT; i++) { 28 | read(fd[0], &receive, 1); 29 | write(p[1], &send, 1); 30 | } 31 | exit(0); 32 | } 33 | else { 34 | sched_setscheduler(getpid(), SCHED_FIFO, ¶m); 35 | for (i = 0; i < EXEC_COUNT; i++) { 36 | write(fd[1], &send, 1); 37 | read(p[0], &receive, 1); 38 | } 39 | gettimeofday(&tv, NULL); 40 | printf("结束测试时间:%u s, %u us\n", tv.tv_sec, tv.tv_usec); 41 | } 42 | return 0; 43 | } -------------------------------------------------------------------------------- /chapter-07/test-02/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int pipes[20][3]; 8 | char buffer[10]; 9 | int running = 1; 10 | 11 | // 为实验建立管道 12 | void init() 13 | { 14 | int i =20; 15 | while(i--) 16 | { 17 | if(pipe(pipes[i])<0) 18 | exit(1); 19 | pipes[i][2] = i; 20 | } 21 | } 22 | 23 | // 关闭所有管道 24 | void distroy() 25 | { 26 | int i =20; 27 | while(i--) 28 | { 29 | close(pipes[i][0]); 30 | close(pipes[i][1]); 31 | } 32 | } 33 | 34 | 35 | // 测试管道开销 36 | double pipe_test() 37 | { 38 | int i =20000; 39 | struct timeval start, end; 40 | gettimeofday(&start, NULL); 41 | while(i--) 42 | { 43 | if(write(pipes[0][1],buffer,10)==-1) 44 | exit(1); 45 | read(pipes[0][0],buffer,10); 46 | } 47 | gettimeofday(&end, NULL); 48 | return (double)(1000000*(end.tv_sec-start.tv_sec)+ end.tv_usec-start.tv_usec)/20000; 49 | } 50 | 51 | void *thread_func(void *arg) 52 | { 53 | int pos = ((int *)arg)[2]; 54 | int in = pipes[pos][0]; 55 | int to = pipes[(pos + 1)%20][1]; 56 | while(running) 57 | { 58 | read(in,buffer,10); 59 | if(write(to,buffer,10)==-1) 60 | exit(1); 61 | } 62 | } 63 | 64 | double thread_switch_test() 65 | { 66 | int i = 20; 67 | struct timeval start, end; 68 | pthread_t tid; 69 | while(--i) 70 | { 71 | pthread_create(&tid,NULL,thread_func,(void *)pipes[i]); 72 | } 73 | i = 10000; 74 | gettimeofday(&start, NULL); 75 | while(i--) 76 | { 77 | if(write(pipes[1][1],buffer,10)==-1) 78 | exit(1); 79 | read(pipes[0][0],buffer,10); 80 | } 81 | gettimeofday(&end, NULL); 82 | running = 0; 83 | if(write(pipes[1][1],buffer,10)==-1) 84 | exit(1); 85 | return (double)(1000000*(end.tv_sec-start.tv_sec)+ end.tv_usec-start.tv_usec)/10000/20; 86 | } 87 | 88 | 89 | int main() 90 | { 91 | init(); 92 | 93 | // 测试管道开销 94 | printf("%6.6f\n", pipe_test()); 95 | 96 | // 测试线程上下文切换开销 97 | printf("%6.6f\n", thread_switch_test()); 98 | 99 | distroy(); 100 | exit(0); 101 | } 102 | -------------------------------------------------------------------------------- /chapter-08/test-01/cpu_stat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #获取宿主机的 CPU 使用情况 4 | function get_host_cpu_usage(){ 5 | #内核会在/proc/stat中输出整机CPU的使用情况, 例如:cat /proc/stat 输出如下 6 | #cpu 52635657 657000 57094567 7675992570 422057 0 545206 0 0 0 7 | #其中各列中的数值都是从启动到现在的累计和,单位是jiffies 8 | #除了第一列外,其余每列的含义分别是: 9 | # 1.user:用户态花费的cpu时间 10 | # 2.nice:用户态在低优先级花费的cpu时间 11 | # 3.system:系统态花费的cpu时间 12 | # 4.idel:在空闲任务上花费的cpu时间 13 | # 5.iowait:等待I/O花费的cpu时间 14 | # 6.irq:硬中断花费的cpu时间 15 | # 7.softirq:软中断花费的cpu时间 16 | # 8.steal:系统处在虚拟化环境中,你的虚拟机被其他虚拟机占用的 CPU 时间 17 | # 9.guest:运行虚拟机花费的cpu时间 18 | # 10.guest_nice:运行低优先级虚拟机花费的cpu时间 19 | 20 | #获取宿主机的 CPU 用量的原理,是选择两个时间点, 21 | #cpu总时间=user+system+nice+idle+iowait+irq+softirq 22 | #cpu_usage=100-(idle2-idle1)/(cpu总时间2-cpu总时1)*100 23 | 24 | T1_CPU_INFO=$(cat /proc/stat | grep -w cpu | awk '{print $2,$3,$4,$5,$6,$7,$8}') 25 | T1_IDLE=$(echo $T1_CPU_INFO | awk '{print $4}') 26 | T1_TOTAL=$(echo $T1_CPU_INFO | awk '{print $1+$2+$3+$4+$5+$6+$7}') 27 | 28 | sleep 10 29 | 30 | T2_CPU_INFO=$(cat /proc/stat | grep -w cpu | awk '{print $2,$3,$4,$5,$6,$7,$8}') 31 | T2_IDLE=$(echo $T2_CPU_INFO | awk '{print $4}') 32 | T2_TOTAL=$(echo $T2_CPU_INFO | awk '{print $1+$2+$3+$4+$5+$6+$7}') 33 | 34 | CPU_UTILIZATION=`echo ${T1_IDLE} ${T1_TOTAL} ${T2_IDLE} ${T2_TOTAL}| awk '{printf "%.2f", (1-($3-$1)/($4-$2))*100}'` 35 | echo "Host CPU Utiliztion:${CPU_UTILIZATION}%" 36 | } 37 | 38 | get_host_cpu_usage -------------------------------------------------------------------------------- /chapter-09/test-01/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | "runtime" 7 | ) 8 | 9 | func cal() { 10 | for i :=0 ; i<1000000 ;i++{ 11 | //fmt.Printf("call:%d\n",i) 12 | runtime.Gosched() 13 | } 14 | } 15 | 16 | func main() { 17 | runtime.GOMAXPROCS(1) 18 | 19 | currentTime:=time.Now() 20 | fmt.Println(currentTime) 21 | 22 | go cal() 23 | for i :=0 ; i<1000000 ;i++{ 24 | //fmt.Printf("main:%d\n",i) 25 | runtime.Gosched() 26 | } 27 | 28 | currentTime=time.Now() 29 | fmt.Println(currentTime) 30 | } 31 | -------------------------------------------------------------------------------- /chapter-12/test-01/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | int n; 7 | printf("请输入要申请的内存大小(单位:字节):"); 8 | scanf("%d", &n); 9 | 10 | // 申请指定大小的内存 11 | int* arr = (int*)malloc(n * sizeof(int)); 12 | 13 | if (arr == NULL) { 14 | printf("内存申请失败!\n"); 15 | return 1; // 如果内存申请失败则退出程序 16 | } 17 | 18 | // 对内存进行访问,以触发缺页中断真正分配物理内存 19 | for (int i = 0; i < n; i++) { 20 | arr[i] = i + 1; 21 | } 22 | 23 | // 让程序休眠600秒方便观察 24 | sleep(600); 25 | 26 | // 释放内存 27 | free(arr); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /chapter-13/test-01/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | int func(int p){ 3 | return 1; 4 | } 5 | int main() 6 | { 7 | int i; 8 | for(i=0; i<100000000; i++){ 9 | func(2); 10 | } 11 | return 0; 12 | } -------------------------------------------------------------------------------- /chapter-13/test-02/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func myFunction(p1, p2, p3,p4, p5 int) (int,int) { 4 | var a int = p1+p2+p3+p4+p5 5 | var b int = 3 6 | return a,b 7 | } 8 | 9 | func main() { 10 | myFunction(1, 2, 3, 4, 5) 11 | } -------------------------------------------------------------------------------- /chapter-13/test-03/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | char c; 8 | int in; 9 | int i; 10 | 11 | in = open("in.txt", O_RDONLY); 12 | for(i=0; i<1000000; i++){ 13 | read(in,&c,1); 14 | } 15 | return 0; 16 | } -------------------------------------------------------------------------------- /chapter-14/test-01/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void caculate(){ 6 | for (i = 0; i < 10000000; i++) { 7 | } 8 | } 9 | 10 | void funcE(){ 11 | caculate(); 12 | } 13 | 14 | void funcD(){ 15 | funcE(); 16 | } 17 | 18 | void funcA(){ 19 | funcD(); 20 | } 21 | 22 | void funcB(){ 23 | caculate(); 24 | } 25 | 26 | void funcC(){ 27 | caculate(); 28 | } 29 | 30 | int main() { 31 | int i; 32 | for (i = 0; i < 100; i++) { 33 | if (i < 10) { 34 | funcA(); 35 | } else if (i < 16) { 36 | funcB(); 37 | } else { 38 | funcC(); 39 | } 40 | } 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /chapter-14/test-02/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // 封装perf_event_open系统调用 9 | int perf_event_open(struct perf_event_attr *attr,pid_t pid,int ,int group_fd,unsigned long flags) 10 | { 11 | return syscall(__NR_perf_event_open,attr,pid,cpu,group_fd,flags); 12 | } 13 | 14 | int main() 15 | { 16 | // 第一步:创建perf文件描述符 17 | // 准备参数 18 | struct perf_event_attr attr; 19 | memset(&attr,0,sizeof(struct perf_event_attr)); 20 | attr.size=sizeof(struct perf_event_attr); 21 | attr.type=PERF_TYPE_HARDWARE; // 监测硬件 22 | attr.config=PERF_COUNT_HW_INSTRUCTIONS; // 监测指令数 23 | 24 | // pid=0表示只检测当前进程 25 | // cpu=-1表示检测所有cpu核 26 | int fd=perf_event_open(&attr,0,-1,-1,0); 27 | if(fd<0) 28 | { 29 | perror("Cannot open perf fd!"); 30 | return 1; 31 | } 32 | 33 | // 第二步:定时获取指标计数 34 | // 每隔1秒打印一次当前计数 35 | while(1) 36 | { 37 | uint64_t instructions; 38 | read(fd,&instructions,sizeof(instructions)); 39 | printf("instructions=%ld\n",instructions); 40 | sleep(1); 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /chapter-15/test-01/cpu_stat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 获取进程 ID 4 | pid=$1 5 | 6 | # 读取进程的 CPU 时间数据 7 | utime=$(awk '{print $14}' /proc/"$pid"/stat) 8 | stime=$(awk '{print $15}' /proc/"$pid"/stat) 9 | cutime=$(awk '{print $16}' /proc/"$pid"/stat) 10 | cstime=$(awk '{print $17}' /proc/"$pid"/stat) 11 | 12 | # 获取的 CPU 时间总数 13 | cpu_total_time=$(grep '^cpu ' /proc/stat | awk '{print $2+$3+$4+$5+$6+$7+$8}') 14 | 15 | # 计算进程的 CPU 时间总数 16 | process_total_time=$((utime+stime+cutime+cstime)) 17 | 18 | # 计算进程的 CPU 利用率 19 | cpu_usage=$(echo "scale=2;100*$process_total_time/$cpu_total_time" | bc) 20 | 21 | echo "PID: $pid, CPU Usage: $cpu_usage%" -------------------------------------------------------------------------------- /chapter-15/test-02/cpu_stat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TIME_INTERVAL=10 # 获取10秒的平均CPU利用率 4 | 5 | function is_cgroup_v2(){ 6 | hierarchy=`cat /proc/cgroups | grep cpuacct | awk '{print $2}'` 7 | if [ $hierarchy -eq 0 ]; then 8 | IS_CGROUP_V2=true 9 | else 10 | IS_CGROUP_V2=false 11 | fi 12 | echo "IS_CGROUP_V2:${IS_CGROUP_V2}" 13 | } 14 | 15 | # 获取容器的 CPU 用量 16 | # 注意:用量返回的是实际用了几个核的时间,比如 0.5 代表的是使用了 0.5 个核,而不是 50% 的核。 17 | function get_cgroup_v1_pod_cpu_usage(){ 18 | # 获取当前容器的 cgroup 路径 & usage 文件 19 | fs_usage="/sys/fs/cgroup/cpu,cpuacct"$(cat /proc/1/cgroup | grep "cpu,cpuacct" | awk -F ':' '{print $3}')"/cpuacct.usage" 20 | 21 | # 获取 t1 的当前时间(纳秒)和 cpu usage(纳秒) 22 | T1_USAGE=`cat ${fs_usage}` 23 | T1=`date +%s%N` 24 | sleep ${TIME_INTERVAL} 25 | 26 | # 获取 t2 的当前时间(纳秒)和 cpu usage(纳秒) 27 | T2_USAGE=`cat ${fs_usage}` 28 | T2=`date +%s%N` 29 | CPU_USAGE_POD=`echo ${T1_USAGE} ${T1} ${T2_USAGE} ${T2}| awk '{printf "%.5f", ($3-$1)/($4-$2)}'` 30 | echo "Pod CPU Usage:${CPU_USAGE_POD}" 31 | } 32 | 33 | # 获取容器的 CPU 用量 34 | function get_cgroup_v2_pod_cpu_usage(){ 35 | # 获取当前容器的 cgroup 路径 36 | cgroup=$(cat /proc/1/cgroup | awk -F ':' '{print $3}') 37 | cgroup=`echo ${cgroup%init.scope}` 38 | 39 | # 获取当前容器的 cpu.stat 路径 40 | fs_usage="/sys/fs/cgroup"$cgroup"cpu.stat" 41 | 42 | # 获取 t1 的当前时间(微秒)和 cpu usage(微秒) 43 | T1_USAGE=`cat ${fs_usage} | grep usage_usec | awk '{print $2}'` 44 | T1=`echo $(date +%s%N) 1000 | awk '{printf "%.2f", ($1/$2)}'` 45 | sleep ${TIME_INTERVAL} 46 | 47 | # 获取 t2 的当前时间(微秒)和 cpu usage(微秒) 48 | T2_USAGE=`cat ${fs_usage} | grep usage_usec | awk '{print $2}'` 49 | T2=`echo $(date +%s%N) 1000 | awk '{printf "%.2f", ($1/$2)}'` 50 | CPU_USAGE_POD=`echo ${T1_USAGE} ${T1} ${T2_USAGE} ${T2}| awk '{printf "%.5f", ($3-$1)/($4-$2)}'` 51 | echo "Pod CPU Usage:${CPU_USAGE_POD}" 52 | } 53 | 54 | # 获取当前POD cgroup版本 && 获取pod的CPU利用率 55 | is_cgroup_v2 56 | if [[ "$IS_CGROUP_V2" = true ]] ; then 57 | get_cgroup_v2_pod_cpu_usage 58 | else 59 | get_cgroup_v1_pod_cpu_usage 60 | fi -------------------------------------------------------------------------------- /chapter-16/test-01/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: likely 2 | likely: 3 | gcc -O2 likely.c -o likely 4 | objdump -d -S likely > likely.txt 5 | 6 | .PHONY: unlikely 7 | unlikely: 8 | gcc -O2 unlikely.c -o unlikely 9 | objdump -d -S unlikely > unlikely.txt 10 | 11 | -------------------------------------------------------------------------------- /chapter-16/test-01/likely: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanfeizhang/deep_linux_process_memory_tests/d8f826a42a76cf2ba142266e7ce77ee4e96b6d3c/chapter-16/test-01/likely -------------------------------------------------------------------------------- /chapter-16/test-01/likely.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define likely(x) __builtin_expect(!!(x), 1) 5 | #define unlikely(x) __builtin_expect(!!(x), 0) 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | int n; 10 | n = atoi (argv[1]); 11 | 12 | if (likely(n == 10)){ 13 | n = n + 2; 14 | } else { 15 | n = n - 2; 16 | } 17 | printf("%d\n", n); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /chapter-16/test-01/likely.txt: -------------------------------------------------------------------------------- 1 | 2 | likely: file format mach-o-arm64 3 | 4 | 5 | Disassembly of section .text: 6 | 7 | 0000000100003f58 <_main>: 8 | 100003f58: d10083ff sub sp, sp, #0x20 9 | 100003f5c: a9017bfd stp x29, x30, [sp, #16] 10 | 100003f60: 910043fd add x29, sp, #0x10 11 | 100003f64: f9400420 ldr x0, [x1, #8] 12 | 100003f68: 9400000d bl 100003f9c <_main+0x44> 13 | 100003f6c: 52800188 mov w8, #0xc // #12 14 | 100003f70: 51000809 sub w9, w0, #0x2 15 | 100003f74: 7100281f cmp w0, #0xa 16 | 100003f78: 1a890108 csel w8, w8, w9, eq // eq = none 17 | 100003f7c: f90003e8 str x8, [sp] 18 | 100003f80: 100001a0 adr x0, 100003fb4 <_main+0x5c> 19 | 100003f84: d503201f nop 20 | 100003f88: 94000008 bl 100003fa8 <_main+0x50> 21 | 100003f8c: 52800000 mov w0, #0x0 // #0 22 | 100003f90: a9417bfd ldp x29, x30, [sp, #16] 23 | 100003f94: 910083ff add sp, sp, #0x20 24 | 100003f98: d65f03c0 ret 25 | 26 | Disassembly of section __TEXT.__stubs: 27 | 28 | 0000000100003f9c <__TEXT.__stubs>: 29 | 100003f9c: b0000010 adrp x16, 100004000 <_main+0xa8> 30 | 100003fa0: f9400210 ldr x16, [x16] 31 | 100003fa4: d61f0200 br x16 32 | 100003fa8: b0000010 adrp x16, 100004000 <_main+0xa8> 33 | 100003fac: f9400610 ldr x16, [x16, #8] 34 | 100003fb0: d61f0200 br x16 35 | 36 | Disassembly of section __TEXT.__unwind_info: 37 | 38 | 0000000100003fb8 <__TEXT.__unwind_info>: 39 | 100003fb8: 00000001 udf #1 40 | 100003fbc: 0000001c udf #28 41 | 100003fc0: 00000000 udf #0 42 | 100003fc4: 0000001c udf #28 43 | 100003fc8: 00000000 udf #0 44 | 100003fcc: 0000001c udf #28 45 | 100003fd0: 00000002 udf #2 46 | 100003fd4: 00003f58 udf #16216 47 | 100003fd8: 00000034 udf #52 48 | 100003fdc: 00000034 udf #52 49 | 100003fe0: 00003f9d udf #16285 50 | 100003fe4: 00000000 udf #0 51 | 100003fe8: 00000034 udf #52 52 | 100003fec: 00000003 udf #3 53 | 100003ff0: 0001000c .inst 0x0001000c ; undefined 54 | 100003ff4: 00010010 .inst 0x00010010 ; undefined 55 | 100003ff8: 00000000 udf #0 56 | 100003ffc: 04000000 add z0.b, p0/m, z0.b, z0.b 57 | -------------------------------------------------------------------------------- /chapter-16/test-01/unlikely: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanfeizhang/deep_linux_process_memory_tests/d8f826a42a76cf2ba142266e7ce77ee4e96b6d3c/chapter-16/test-01/unlikely -------------------------------------------------------------------------------- /chapter-16/test-01/unlikely.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define likely(x) __builtin_expect(!!(x), 1) 5 | #define unlikely(x) __builtin_expect(!!(x), 0) 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | int n; 10 | n = atoi (argv[1]); 11 | 12 | if (unlikely(n == 10)){ 13 | n = n + 2; 14 | } else { 15 | n = n - 2; 16 | } 17 | printf("%d\n", n); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /chapter-16/test-01/unlikely.txt: -------------------------------------------------------------------------------- 1 | 2 | unlikely: file format mach-o-arm64 3 | 4 | 5 | Disassembly of section .text: 6 | 7 | 0000000100003f58 <_main>: 8 | 100003f58: d10083ff sub sp, sp, #0x20 9 | 100003f5c: a9017bfd stp x29, x30, [sp, #16] 10 | 100003f60: 910043fd add x29, sp, #0x10 11 | 100003f64: f9400420 ldr x0, [x1, #8] 12 | 100003f68: 9400000d bl 100003f9c <_main+0x44> 13 | 100003f6c: 51000808 sub w8, w0, #0x2 14 | 100003f70: 52800189 mov w9, #0xc // #12 15 | 100003f74: 7100281f cmp w0, #0xa 16 | 100003f78: 1a880128 csel w8, w9, w8, eq // eq = none 17 | 100003f7c: f90003e8 str x8, [sp] 18 | 100003f80: 100001a0 adr x0, 100003fb4 <_main+0x5c> 19 | 100003f84: d503201f nop 20 | 100003f88: 94000008 bl 100003fa8 <_main+0x50> 21 | 100003f8c: 52800000 mov w0, #0x0 // #0 22 | 100003f90: a9417bfd ldp x29, x30, [sp, #16] 23 | 100003f94: 910083ff add sp, sp, #0x20 24 | 100003f98: d65f03c0 ret 25 | 26 | Disassembly of section __TEXT.__stubs: 27 | 28 | 0000000100003f9c <__TEXT.__stubs>: 29 | 100003f9c: b0000010 adrp x16, 100004000 <_main+0xa8> 30 | 100003fa0: f9400210 ldr x16, [x16] 31 | 100003fa4: d61f0200 br x16 32 | 100003fa8: b0000010 adrp x16, 100004000 <_main+0xa8> 33 | 100003fac: f9400610 ldr x16, [x16, #8] 34 | 100003fb0: d61f0200 br x16 35 | 36 | Disassembly of section __TEXT.__unwind_info: 37 | 38 | 0000000100003fb8 <__TEXT.__unwind_info>: 39 | 100003fb8: 00000001 udf #1 40 | 100003fbc: 0000001c udf #28 41 | 100003fc0: 00000000 udf #0 42 | 100003fc4: 0000001c udf #28 43 | 100003fc8: 00000000 udf #0 44 | 100003fcc: 0000001c udf #28 45 | 100003fd0: 00000002 udf #2 46 | 100003fd4: 00003f58 udf #16216 47 | 100003fd8: 00000034 udf #52 48 | 100003fdc: 00000034 udf #52 49 | 100003fe0: 00003f9d udf #16285 50 | 100003fe4: 00000000 udf #0 51 | 100003fe8: 00000034 udf #52 52 | 100003fec: 00000003 udf #3 53 | 100003ff0: 0001000c .inst 0x0001000c ; undefined 54 | 100003ff4: 00010010 .inst 0x00010010 ; undefined 55 | 100003ff8: 00000000 udf #0 56 | 100003ffc: 04000000 add z0.b, p0/m, z0.b, z0.b 57 | -------------------------------------------------------------------------------- /imgs/author.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanfeizhang/deep_linux_process_memory_tests/d8f826a42a76cf2ba142266e7ce77ee4e96b6d3c/imgs/author.png -------------------------------------------------------------------------------- /imgs/double.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanfeizhang/deep_linux_process_memory_tests/d8f826a42a76cf2ba142266e7ce77ee4e96b6d3c/imgs/double.png -------------------------------------------------------------------------------- /imgs/official_accounts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanfeizhang/deep_linux_process_memory_tests/d8f826a42a76cf2ba142266e7ce77ee4e96b6d3c/imgs/official_accounts.jpg --------------------------------------------------------------------------------