Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(routes/oschina/user): fix wrong link for user #17998

Merged
merged 1 commit into from
Dec 28, 2024
Merged

Conversation

cscnk52
Copy link
Contributor

@cscnk52 cscnk52 commented Dec 28, 2024

Involved Issue / 该 PR 相关 Issue

None

Example for the Proposed Route(s) / 路由地址示例

oschina/u/6150560

New RSS Route Checklist / 新 RSS 路由检查表

  • New Route / 新的路由
  • Anti-bot or rate limit / 反爬/频率限制
    • If yes, do your code reflect this sign? / 如果有, 是否有对应的措施?
  • Date and time / 日期和时间
    • Parsed / 可以解析
    • Correct time zone / 时区正确
  • New package added / 添加了新的包
  • Puppeteer

Note / 说明

fix wrong link in previous route, https://my.oschina.net/6150560 should be https://my.oschina.net/u/6150560
image

@github-actions github-actions bot added the Route label Dec 28, 2024
Copy link
Contributor

Successfully generated as following:

http://localhost:1200/oschina/u/6150560 - Success ✔️
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>字节跳动SYS Tech的博客</title>
    <link>https://my.oschina.net/u/6150560</link>
    <atom:link href="http://localhost:1200/oschina/u/6150560" rel="self" type="application/rss+xml"></atom:link>
    <description>聚焦系统技术领域,分享前沿技术动态、技术创新与实践、行业技术热点分析。 - Powered by RSSHub</description>
    <generator>RSSHub</generator>
    <webMaster>[email protected] (RSSHub)</webMaster>
    <language>en</language>
    <lastBuildDate>Sat, 28 Dec 2024 03:40:53 GMT</lastBuildDate>
    <ttl>5</ttl>
    <item>
      <title>字节跳动开源Linux内核网络抓包工具netcap</title>
      <description>&lt;div class=&quot;content&quot;&gt;
        &lt;div class=&quot;ad-wrap&quot; style=&quot;margin-bottom: 8px;&quot;&gt;
        &lt;div data-traceid=&quot;news_comment_top_ad&quot; data-tracepid=&quot;news_comment_top&quot; style=&quot;text-align: center;&quot;&gt;
        &lt;a style=&quot;color:#A00;font-weight:bold;&quot; href=&quot;https://www.oschina.net/live/&quot; target=&quot;_blank&quot;&gt;
        本周六,「源创会年终盛典」珠海站再次回归!错过等一年&lt;img src=&quot;https://www.oschina.net/img/hot3.png&quot; align=&quot;absmiddle&quot; style=&quot;max-height: 32px;max-width: 32px;margin-top: -4px;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;
        &lt;/div&gt;
        &lt;/div&gt;
        &lt;blockquote&gt;
        &lt;p&gt;文章转载至【字节跳动SYS Tech】公众号。原文链接点击:&lt;a href=&quot;https://my.oschina.net/u/6150560/blog/%22https://mp.weixin.qq.com/s?__biz=Mzg3Mjg2NjU4NA==&amp;amp;mid=2247485050&amp;amp;idx=1&amp;amp;sn=6b18af60f6dd6c890da8721368cb6525edd2a75050%22&quot;&gt;字节跳动开源Linux内核网络抓包工具netcap&lt;/a&gt;&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;h1&gt;背景介绍&lt;/h1&gt;
        &lt;p&gt;在 Linux 内核网络开发过程中,网络丢包问题是一个常见的挑战。传统的网络抓包工具(如 tcpdump)虽然能够帮助开发者定位问题,但其效率较低,且在深度网络问题定位方面能力有限。随着 eBPF 技术的快速发展,出现了更高级的问题跟踪能力。&lt;strong&gt;字节跳动 STE 团队&lt;/strong&gt;基于此技术开发了&lt;strong&gt;下一代内核网络抓包工具&lt;/strong&gt;&lt;strong&gt;netcap&lt;/strong&gt; (net capture,内部原名:xcap),并正式对外开源:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fbytedance%2Fnetcap&quot; target=&quot;_blank&quot;&gt;GitHub - bytedance/netcap&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;与 tcpdump 工具只能作用于内核网络协议栈准备发包和收包的固定点相比,netcap 可以几乎跟踪整个内核网络协议栈(有skb作为参数的函数)。字节跳动 STE 团队使用 tcpdump 语法作为过滤条件,以 skb(socket buffer)为上下文,可以轻松掌握整个报文在内核网络协议栈的完整踪迹,从而帮助开发者大大提高内核网络丢包问题的定位效率。&lt;/p&gt;
        &lt;h1&gt;使用举例&lt;/h1&gt;
        &lt;p&gt;使用方式举例:&lt;/p&gt;
        &lt;p&gt;例1:查看 ip 10.227.0.45 的 icmp 包是否到达内核预期的函数调用点, 这样做的好处是:在定位排查网络问题的时候,可以方便的缩小怀疑范围,提高效率。&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;netcap skb -f icmp_rcv@1 -i eth0 -e &quot;host 10.227.0.45&quot; -t &quot;-nnv&quot;
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;其中 -f 后面的参数是 kprobe 或者 tracepoint 的具体函数(默认是kprobe),并且需要告诉 netcap,skb 在这个函数(本例是 icmp_rcv )的第几个参数(从1开始),本例是第1个。&lt;/p&gt;
        &lt;p&gt;-i 后面是指skb的dev参数对应的网卡,这里要谨慎使用,因为有些函数的 skb 是没有设置 dev 的。&lt;/p&gt;
        &lt;p&gt;-e 的参数是 tcpdump 的过滤语法。&lt;/p&gt;
        &lt;p&gt;-t 的参数是 tcpdump 的显示方式,netcap 并没有自己显示数据包内容,而是借用了 tcpdump 的显示方式。&lt;/p&gt;
        &lt;p&gt;例2:查看内核对于 tcp 端口 9000 的报文的丢包位置:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;netcap skb -f tracepoint:skb:kfree_skb -e &quot;tcp port 9000&quot; -S
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;其中 -f 后面的参数是 kprobe 或者 tracepoint 的具体函数,tracepoint 不需要传递 skb 是第几个参数。&lt;/p&gt;
        &lt;p&gt;-S 表示连带着打印出此调用的 stack,本例中通过 stack 可以看到是哪里丢包的。&lt;/p&gt;
        &lt;p&gt;举个例子,在机器上配置一个丢包的 iptables 规则把来访的 tcp 9000 的包丢掉,如下图所示:&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;iptables -A INPUT -p tcp --dport 9000 -j DROP
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;然后使用 netcap上面的命令观察丢包情况: &lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a77077a6f9b2cc00ea7c5307fc4b02e2fc7.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;其它命令行参数可以通过阅读开源代码的 &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fbytedance%2Fnetcap&quot; target=&quot;_blank&quot;&gt;README&lt;/a&gt; 或命令 &lt;code&gt;netcap help skb&lt;/code&gt; 来详细了解。&lt;/p&gt;
        &lt;ol start=&quot;3&quot;&gt;
        &lt;li&gt; &lt;h1&gt;设计与实现&lt;/h1&gt; &lt;/li&gt;
        &lt;/ol&gt;
        &lt;h2&gt;3.1 主体框架&lt;/h2&gt;
        &lt;p&gt;netcap 通过 kprobe / tracepoint 方式实现函数的 hook,通过函数参数获取 skb 和 sock 关键结构体,拿到网络包的数据,通过 bpf map 和用户态进行数据传递。 &lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-4844dc0042ab0494bf826805977fb7bbee5.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h2&gt;3.2 实现原理&lt;/h2&gt;
        &lt;p&gt;netcap 的 工作原理大体如下:在 eBPF 程序中完成数据包的过滤,找出 tcpdump 语法过滤的包,然后把这个包给到 netcap 应用程序,netcap 应用程序再把这个包发去给 tcpdump 显示,或者直接输出 pcap 文件。如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-4c19c2e4e3a9f3b6b9356db687173d92d73.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h3&gt;3.2.1 如何按 tcpdump 语法过滤&lt;/h3&gt;
        &lt;p&gt;tcpdump 的过滤语法是基于 cBPF的,使用开源库:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fcloudflare%2Fcbpfc&quot; target=&quot;_blank&quot;&gt;https://github.com/cloudflare/cbpfc&lt;/a&gt; 这里可以把 tcpdump 的过滤语法转化成一个 C 函数,这个 C 函数可以嵌入到 netcap 的 eBPF 的程序中。转成 C 函数的基本原理如下:先利用 libpcap 库把 tcpdump 过滤语法转成 cBPF 指令码,然后基于此指令码转化成 C 语言的函数。如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-4bd1fd4c8478e2ebcbbd57c5bb72e5b96a8.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h3&gt;3.2.2 如何把数据包内容用 tcpdump 显示出来&lt;/h3&gt;
        &lt;p&gt;netcap 程序启动后,也会启动一个 tcpdump 的程序,tcpdump 的标准输入接收 pcap 格式的输入流,然后以不同的参数(例如 -e 是显示 mac 地址)从其标准输出打印出解析后的格式。如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-42ea60c0b8a18bed59d923d13f95db480c4.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h3&gt;3.2.3 如何找到数据包的内容&lt;/h3&gt;
        &lt;p&gt;在内核中,是用 skb 来描述数据包的,找到 skb 中所指定的不同 header 的位置,就可以找到整个数据包,skb 的结构大体如下所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a26e8c31e9a13cff85f6a9cbce913157710.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h3&gt;3.2.4 发送方向数据包不完整,如何过滤数据包&lt;/h3&gt;
        &lt;p&gt;在发送数据包的时候,例如 __ip_finish_output 函数,有时未填充完整的 eth头、ip 头、tcp 头,那么是怎么得到完整的包呢?&lt;/p&gt;
        &lt;p&gt;netcap 会尽力根据 skb 的 sock 结构来推导,还原数据包,此时抓出来的包有些非关键信息会与实际情况不一致(比如 ip 头的 id 字段)。skb 通过sock来推导数据包内容的逻辑大体如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-1437be41caa807a9c1bf0531365bedd6b72.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h1&gt;4.其他用法及扩展&lt;/h1&gt;
        &lt;h2&gt;4.1 多Trace点汇总分析&lt;/h2&gt;
        &lt;p&gt;netcap 可以统计数据包经过多个点的时间,然后汇总输出,从而分析性能,举个例子,使用下面的命令:&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;netcap skb -f tracepoint:net:netif_receive_skb,ip_local_deliver@1,ip_local_deliver_finish@3,icmp_rcv@1 -e &quot;host 10.227.0.72 and icmp&quot; -i eth0 --gather --gather-output-color cyan
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;可以观察到输出如下,根据到达 trace 点的时间,就能够分析出数据包性能损耗在哪里,或者在哪里可能引入了延迟。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-54fbaba29a3f94b7324e4819fb6c5de87ae.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h2&gt;4.2 扩展功能&lt;/h2&gt;
        &lt;p&gt;用户可以自定义自己的过滤函数和输出函数,这里举例如下,&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;netcap skb -f icmp_rcv@1 -e &quot;host 10.227.0.72&quot; -i eth0 --user-filter skb_user_filter.c --user-action skb_user_action.c --user-output-color green
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;其中扩展过滤文件 skb_user_filter.c 如下:&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;// Return 0 means it&#39;s not need, pls filter out it.
        static inline int xcap_user_filter(void *ctx, void *pkt, u16 trace_index)
        {
        return 1;
        }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;这个扩展函数的返回值如果是 0,表示在 tcpdump 语法的过滤后,再进行一次用户自定义过滤,比如可以方便的写几行脚本,然后按照 skb-&amp;gt;mark 来过滤。&lt;/p&gt;
        &lt;p&gt;其中扩展输出文件 skb_user_action.c 如下:&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;struct xcap_user_extend {
        int a; // format: 0x%x
        uint32_t b; //
        int64_t c;
        uint8_t x1; // format: %c
        uint8_t x2; // format: 0x%x
        uint16_t x3; // format: 0x%x
        };
        // Return 0 means not need to ouput
        static inline int xcap_user_action(void *ctx, void *pkt, u32 pkt_len, struct xcap_user_extend *user, u16 trace_index)
        {
        user-&amp;gt;a = 0x12345678;
        user-&amp;gt;b = 1000;
        user-&amp;gt;c = 2002;
        user-&amp;gt;x1 = &#39;M&#39;;
        user-&amp;gt;x2 = 0x11;
        user-&amp;gt;x3 = 0xabcd;
        return 1;
        }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;其中 struct xcap_user_extend 是用户自定义的结构体,想输出什么信息,就在这个结构体定义并赋值即可。结构体可支持的类型如下 (注:不支持指针,也不支持数组):int8_t, uint8_t, char, int16_t, uint16_t, int, uint32_t,int64_t, uint64_t 。&lt;/p&gt;
        &lt;p&gt;这样就可以附带一些信息输出了,如下图:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a3521e3550363e78ebd2e64a3618df6b622.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h1&gt;未来展望&lt;/h1&gt;
        &lt;p&gt;在开发者的日常工作中,网络抓包工具成为了网络工程师、测试工程师等必备的技能之一,字节跳动 STE 团队开源的 netcap 网络抓包工具,期望能够帮助大家提高定位内核网络丢包问题的效率,非常欢迎开发者们一起加入并贡献 PR,共同推进开源项目发展。未来我们也将在以下几个方向进行优化,敬请关注。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;对 DPDK 的进一步支持,由于 usdt 的上游库存在问题,故无法支持应用程序的 usdt,有兴趣的读者可以修改支持。&lt;/li&gt;
        &lt;li&gt;对多内核版本的统一支持。&lt;/li&gt;
        &lt;li&gt;在自定义输出的时候,数据包较多的情况下,会出现打印错乱,原因是 tcpdump 的输出信息和用户自定义的输出信息共同使用了标准输出,未来也将针对该问题做后续优化。&lt;/li&gt;
        &lt;/ul&gt;
        &lt;/div&gt;
      </description>
      <link>https://my.oschina.net/u/6150560/blog/15154212</link>
      <guid isPermaLink="false">https://my.oschina.net/u/6150560/blog/15154212</guid>
      <pubDate>Mon, 05 Aug 2024 06:23:00 GMT</pubDate>
      <author>字节跳动SYS Tech</author>
    </item>
    <item>
      <title>字节跳动新型网络装机方案HTTPBOOT(Linux),轻松解决运维困扰</title>
      <description>&lt;div class=&quot;content&quot;&gt;
        &lt;div class=&quot;ad-wrap&quot; style=&quot;margin-bottom: 8px;&quot;&gt;
        &lt;div data-traceid=&quot;news_comment_top_ad&quot; data-tracepid=&quot;news_comment_top&quot; style=&quot;text-align: center;&quot;&gt;
        &lt;a style=&quot;color:#A00;font-weight:bold;&quot; href=&quot;https://www.oschina.net/live/&quot; target=&quot;_blank&quot;&gt;
        本周六,「源创会年终盛典」珠海站再次回归!错过等一年&lt;img src=&quot;https://www.oschina.net/img/hot3.png&quot; align=&quot;absmiddle&quot; style=&quot;max-height: 32px;max-width: 32px;margin-top: -4px;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;
        &lt;/div&gt;
        &lt;/div&gt;
        &lt;blockquote&gt;
        &lt;p&gt;文章转载至【字节跳动SYS Tech】公众号。原文链接点击:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg3Mjg2NjU4NA%3D%3D%26mid%3D2247484773%26idx%3D1%26sn%3D173de6fddd2216fb7376a6edd2a75050&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;字节跳动新型网络装机方案HTTPBOOT(Linux),轻松解决运维困扰&lt;/a&gt;&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;p&gt;HTTPBOOT 是由字节跳动 STE 团队和 ICS 团队合作开发的新型网络装机方案,用于解决日常字节服务器装机运维中出现的一系列问题,目前已在字节机房大批量上线使用,帮助节省了大量的运维人力。&lt;/p&gt;
        &lt;p&gt;本文主要分为三个部分:HTTPBOOT Overview,HTTPBOOT 实践和 HTTPBOOT 成果与规划。通过阅读此文,能够对 HTTPBOOT 的需求背景,原理优势,发展现状以及未来方向有更清晰的了解。&lt;/p&gt;
        &lt;span id=&quot;OSC_h1_1&quot;&gt;&lt;/span&gt;
        &lt;h1&gt;HTTPBOOT Overview&lt;/h1&gt;
        &lt;p&gt;在本节中,将介绍目前装机上存在的问题与需求,为什么选择 HTTPBOOT,以及 HTTPBOOT 和其他装机方案相比有哪些优势。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_2&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;一、目前存在的装机问题和需求&lt;/h2&gt;
        &lt;p&gt;在使用 HTTPBOOT 装机之前,装机大多采用 UEFI+PXE 方式,其 IPV4 和 IPV6 装机方式流程图如下:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-edb3e2091a78c696da0d537ac301ff0b728.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;该方式存在两个不足:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt;&lt;strong&gt;对 MAC 地址存在强依赖:&lt;/strong&gt; 在 PXE 方式下,如果运维团队想要识别一台服务器的身份,只能通过 MAC 地址和 DUID 实现,但是使用 MAC 地址作为身份识别 ID 来对服务器装机进行管控不仅不可靠,还会带来一些问题。例如:如果手动更改 MAC 地址,或者更换网卡后,会使机器无法被正确的识别,导致装机失败。运维一般会有专门的人力去维护 MAC 地址和机器的对应关系,但人工有时也会出现错漏,还会占用更多人力去 处理问题,这是一个痛点。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;基于 TFTP / UDP 的传输不稳定:&lt;/strong&gt; PXE 使用基于 UDP 的 TFTP 协议进行装机程序传输,由于装机网络的环境复杂,网络波动较大。在程序下载的过程中经常由于网络质量差导致传输失败,十分影响整体的机器交付时效。&lt;/li&gt;
        &lt;/ol&gt;
        &lt;p&gt;同时,UEFI 的生态模式还带来了另一个问题:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt;&lt;strong&gt;部分装机问题依赖厂商发版解决:&lt;/strong&gt; 如果发现问题出现在 UEFI 固件上,比如网卡驱动有问题,或者需要做驱动适配,往往需要等厂商去解决,但是受厂商解决问题发布版本的时效影响,这些问题往往不能很快得到响应和及时解决。&lt;/li&gt;
        &lt;/ol&gt;
        &lt;p&gt;除此之外,ICS 团队也在积极改善和优化服务器装机交付流程,以提高效率,这些改造也带来了新的需求:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt;&lt;strong&gt;支持服务器自由上架:&lt;/strong&gt; 以往我们在采购服务器时,会提前为每台机器在机房预留固定的机位和IP,并在服务器到货后需要把机器安装到固定的位置上。不仅流程繁琐且需要人力去维护与核对机位状态。于是我们设计了自由上架规则,无需预先对机位进行预留,机器在上架后线上进行动态绑定,启用该规则需要固件侧进行适配。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;定制化引导 API 接口兼容:&lt;/strong&gt; 为了加强对服务器装机行为的管控,以及灵活适配不同的机型,支持多种装机方案,我们开发了一个引导 API,支持根据机型和状态下发不同的指令或装机配置,但该API 需要固件支持。&lt;/li&gt;
        &lt;/ol&gt;
        &lt;p&gt;而现有的 UEFI+PXE 装机方案不能解决上述问题和需求,我们迫切需要一款&lt;strong&gt;高稳定性、高兼容度、高可靠性、高灵活度&lt;/strong&gt;的装机引导程序。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_3&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;二、为什么选择 HTTPBOOT&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_4&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;1. HTTPBOOT 合作背景&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_5&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;(1)HTTPBOOT 由来&lt;/h4&gt;
        &lt;p&gt;HTTPBOOT 最初为 STE 自研固件 CloudFirmware 中的一个规划功能。因为 PXE 装机问题一直存在。在自研服务器交付或者重装到时候,常因为装机问题影响交付,所以在开发自研固件时,将装机问题作为一个重点关注的问题去解决,进行了一些先验合作。后来 CloudFirmware 上线后,装机问题得到了有效解决;ICS 团队同学也在寻找更好的装机解决方案。于是在这些基础上开展了后续的合作。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-9ca0086e31e34831023429adff519329086.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_6&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;(2)HTTPBOOT 是什么?&lt;/h4&gt;
        &lt;p&gt;上文提到 HTTPBOOT 来源于 CloudFirmware,那么 CloudFirmware 又是什么?HTTPBOOT 和它有哪些联系?这里简单介绍一下。&lt;/p&gt;
        &lt;p&gt;CloudFirmware 是字节跳动STE团队自定义的适用于云厂商的固件解决方案,目前已经被 OCP,OSFF 等社区接受并推广。它将整个固件分为三层:Silicon 代码,coreboot 和 LinuxBoot。其中 Silicon 代码是 Vendor 提供的,用于 Silicon 初始化的代码。coreboot 则是平台相关的,它会调用 Silicon 代码做初始化工作,同时也对外提供一些抽象硬件接口如 ACPI,SMBIOS。coreboot 追求轻量化,希望程序越小越好,有些类似嵌入式使用的 Uboot,这点和 UEFI 是不一样的。LinuxBoot 则是一个小型的 Linux 系统,由 Kernel 和 U-ROOT 文件系统组成。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-2a9f9991bfb4e6cdf4386d990345d8b90f4.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;对于互联网云厂商而言,整个解决方案中,主要的交互方集中在 LinuxBoot 这一层,这样架构也具有更好的灵活性、定制性、可控性,也更适合。为什么这么说?Silicon 代码层由供应商提供,无需操心,coreboot 结构简单,大多是一些流程和框架,操作空间有限。如果需要解决固件问题或者对自研机器提供更好的底层支持,大多在 LinuxBoot 中进行开发工作。而这也是我们的优势所在,因为我们的 Linux 工程师数量远远多于 UEFI 工程师,可以又快又好的在 LinuxBoot 上解决固件问题。&lt;/p&gt;
        &lt;p&gt;而 HTTPBOOT,其实就是 CloudFirmware 将 LinuxBoot 抽离出来,只保留装机相关功能形成的工具。它不但继承了 CloudFirmware 的优点,还是一个跨平台的工具,得益于 Linux 的开源生态和 GO 语言的 U-ROOT,这使得 HTTPBOOT 在移植到不同架构和型号的机器时十分容易。&lt;/p&gt;
        &lt;p&gt;回归到装机本身来说,HTTPBOOT 的这种做法和其他装机方案以及开源装机工具相比,有哪些优势?&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_7&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;2.HTTPBOOT 优势&lt;/h3&gt;
        &lt;p&gt;这里我们从技术方案和开源产品两个层面来进行对比,首先目前主流的几种装机技术方案如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-ccace028aad81bda6b264517b6f1a8044f7.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;该图上下部分,分别是选择 UEFI 和 Linux 作为载体的方案。两者相比,从产品角度来看,使用 Linux 可以直接基于开源技术产品化,而采用 UEFI 则需要通过 IBV,很难进行产品化。从技术角度来看,Linux 拥有更强大的网络驱动和更健壮的代码,更重要的是 Linux 的开源生态可以让我们不必依赖其他厂商,直接入手解决问题。从左右对比,分别是使用 PXE 和使用 HTTP 的方法,这个更不必说,PXE 从诞生至今近20年了,它使用的 TFTP 协议相比当下主流的HTTP协议,并不能提供类似断点续传,安全加密,灵活拓展等特性。&lt;/p&gt;
        &lt;p&gt;所以综上所述,HTTPBOOT 采用的基于 Linux 平台的 HTTP 启动方案是当下最好的技术方案。同时我们也调研了其他的开源装机产品,和其他开源装机工具对比:&lt;/p&gt;
        &lt;table&gt;
        &lt;tbody&gt;
        &lt;tr&gt;
        &lt;th&gt;装机工具&lt;/th&gt;
        &lt;th&gt;支持HTTP&lt;/th&gt;
        &lt;th&gt;代码维护&lt;/th&gt;
        &lt;th&gt;健壮度&lt;/th&gt;
        &lt;/tr&gt;
        &lt;/tbody&gt;
        &lt;tbody&gt;
        &lt;tr&gt;
        &lt;td&gt;syslinux&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;依赖厂商&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
        &lt;td&gt;bootnet64&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;依赖厂商&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
        &lt;td&gt;ipxe&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;依赖厂商&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
        &lt;td&gt;HTTPBOOT&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;开源社区,无依赖&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;/tbody&gt;
        &lt;/table&gt;
        &lt;p&gt;其中 ICS 同学对 ipxe 工具做过灰度测试,发现存在问题: 1. 开源版本对 IPV6 网络的支持不够好 2. 部分机型上存在适配问题。而和原有 PXE 方案对比,HTTPBOOT 在稳定性,可靠性,灵活性和兼容性上都更加出色。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-618c21abf90373e6e5fb9fcbf1ce68c7788.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;毫无疑问的,HTTPBOOT 成为了我们选择的最终装机方案。&lt;/p&gt;
        &lt;span id=&quot;OSC_h1_8&quot;&gt;&lt;/span&gt;
        &lt;h1&gt;HTTPBOOT 实践&lt;/h1&gt;
        &lt;p&gt;在本小节中将介绍 HTTPBOOT 的发展历程及工作原理,并结合较为实际的例子说明 HTTPBOOT 是如何解决第一节提出的装机问题和需求。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_9&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;一、HTTPBOOT Journey in Bytedance&lt;/h2&gt;
        &lt;p&gt;HTTPBOOT的实践发展历程分为四个阶段。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;2021年:HTTPBOOT 作为 CloudFirmware 的 Feature,随 CloudFirmware 一同上线。在装机方面取得突出表现,和ICS进行了一些合作测试。&lt;/li&gt;
        &lt;li&gt;2022年:HTTPBOOT 除了在 CloudFirmware 机器上使用之外,在小部分机房进行使用。多数情况下作为UEFI 装机失败后的修复工具。&lt;/li&gt;
        &lt;li&gt;2023年:ICS 对装机网络进行改造,HTTPBOOT 作为主推装机方案在内场机房大批量上线使用,给运维节省了大量的人力。&lt;/li&gt;
        &lt;li&gt;2024年:一些非服务器形态的板卡项目开始使用 HTTPBOOT 作为他们的装机解决方案。&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;经过四年的实践运行,HTTPBOOT 积累了大量的经验和好评,成为字节内场主推的装机解决方案。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_10&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;二、HTTPBOOT 工作原理&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_11&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;1. HTTPBOOT 装机流程&lt;/h3&gt;
        &lt;p&gt;引入 HTTPBOOT 后,新装机方案流程如下:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-c8010723a22c2040965f2f9a4c7ce2698d4.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;其中,对于UDP装机不稳定的问题,在搭载 CloudFirmware 的机器上得到了完全解决。对于搭载 UEFI 的机器,仍然需要通过 TFTP/UDP 下载一个 HTTPBOOT 工具到本地,问题依旧存在,但是相比于之前同 PXE 下载一个接近40M的最小镜像,下载一个5.8M的 HTTPBOOT 大大减少了下载失败的概率。&lt;/p&gt;
        &lt;p&gt;HTTPBOOT在本地运行流程如下:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-3ebc45c4aff05b9fd3b7e7eb79e3d3863fe.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_12&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;2. HTTPBOOT 关键步骤&lt;/h3&gt;
        &lt;p&gt;下面将通过一些关键步骤展示 HTTPBOOT 是如何适配和解决问题的:&lt;/p&gt;
        &lt;p&gt;对自定义 API 适配分为两部分:接入和执行。接入部分的适配如下:DHCP 服务端通过识别 DHCP 请求中的Option 61 判断客户端是否为HTTPBOOT,如果是,则将 API 的 URL 回发给客户端。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-c85525110a60f78e98075848cc050e2cf26.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;图1 接入自定义API&lt;/p&gt;
        &lt;p&gt;HTTPBOOT 收到 DHCP 返回的 API 后,会附上本机的产品序列号 SN,通过 API 上传到远端服务器,服务器根据 SN 号进行一个 IP 地址的动态的绑定。如此一来,解除了对 MAC 地址强依赖,也无需预先对服务器进行 IP 和机位的分配,实现了服务器的自由上架,节省了大量的人力。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-044c40e753077bc3de928897b0e67f5f568.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;图2 解决对MAC地址依赖,支持自由上架&lt;/p&gt;
        &lt;p&gt;同时后台接收到上传的机器 SN 号信息后,根据后台记录的机器状态下发不同的指令 ,若机器为非装机设备,则会返回如下命令,机器从本地磁盘启动。&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;#!ipxe
        sanboot --no-describe --drive 0x80
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;而在正常状态下,会返回装机配置文件的 URL ,里面包含需要安装的 OS 文件地址,HTTPBOOT 会下载相应的 OS 文件并启动。&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;#!ipxe
        http://[XXXX:XXXX::X:X:X:XX]:88/httpboot.cfg/?IP=fdbddc01002b0f1c0000000000000028
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;HTTPBOOT 可以支持多种指令来对不同的机型和情况进行灵活处理,至此完成对自定义 API 执行部分的适配。&lt;/p&gt;
        &lt;span id=&quot;OSC_h1_13&quot;&gt;&lt;/span&gt;
        &lt;h1&gt;HTTPBOOT成果和规划&lt;/h1&gt;
        &lt;p&gt;上一小节讲述了 HTTPBOOT 的工作原理及如何解决问题,本节讲述 HTTPBOOT 成果规划。&lt;/p&gt;
        &lt;p&gt;目前 HTTPBOOT 已经在系统研发阶段闭环了相关使能和测试流程。并在字节内场被广泛应用,完成了十万次数量级的装机交付,其直观收益体现在以下几个方面:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-2129cf48f16b2e5fe892228a615ca3fb80f.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;在未来 HTTPBOOT 将在字节内部进行进一步的推广和应用,特别感谢 OPC 、OSFF 、LinuxBoot 、U-ROOT 等开源社区及字节跳动相关部门同学在项目过程中提供的帮助及付出的努力,未来将继续和大家并肩携手,共同成长,收获共赢。最后欢迎对 LinuxBoot 开源项目感兴趣的小伙伴一起交流 :&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Flinuxboot%2Flinuxboot&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;https://github.com/linuxboot/linuxboot&lt;/a&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h1_14&quot;&gt;&lt;/span&gt;
        &lt;h1&gt;热门招聘&lt;/h1&gt;
        &lt;p&gt;金三银四的季节,字节跳动STE团队诚邀您的加入!团队长期招聘,&lt;strong&gt;北京、上海、深圳、杭州、US、UK&lt;/strong&gt;&amp;nbsp;均设岗位,点击 &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fjobs.bytedance.com%2Fexperienced%2Fposition%3Fcategory%3D%26current%3D5%26functionCategory%3D%26job_hot_flag%3D%26keywords%3D%25E7%25B3%25BB%25E7%25BB%259F-STE%26limit%3D10%26location%3D%26project%3D%26type%3D&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;招聘详情&lt;/a&gt; 即可查看近期的最新招聘职位信息,有意向者可直接投递简历或咨询小助手微信:sys_tech,期待与你早日相遇,在字节共赴星辰大海!岗位多多,快来砸简历吧!&lt;/p&gt;
        &lt;/div&gt;
      </description>
      <link>https://my.oschina.net/u/6150560/blog/11047367</link>
      <guid isPermaLink="false">https://my.oschina.net/u/6150560/blog/11047367</guid>
      <pubDate>Thu, 14 Mar 2024 07:48:00 GMT</pubDate>
      <author>字节跳动SYS Tech</author>
    </item>
    <item>
      <title>ByteFUSE分布式文件系统的演进与落地</title>
      <description>&lt;div class=&quot;content&quot;&gt;
        &lt;div class=&quot;ad-wrap&quot; style=&quot;margin-bottom: 8px;&quot;&gt;
        &lt;div data-traceid=&quot;news_comment_top_ad&quot; data-tracepid=&quot;news_comment_top&quot; style=&quot;text-align: center;&quot;&gt;
        &lt;a style=&quot;color:#A00;font-weight:bold;&quot; href=&quot;https://www.oschina.net/live/&quot; target=&quot;_blank&quot;&gt;
        本周六,「源创会年终盛典」珠海站再次回归!错过等一年&lt;img src=&quot;https://www.oschina.net/img/hot3.png&quot; align=&quot;absmiddle&quot; style=&quot;max-height: 32px;max-width: 32px;margin-top: -4px;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;
        &lt;/div&gt;
        &lt;/div&gt;
        &lt;blockquote&gt;
        &lt;p&gt;原文链接:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg3Mjg2NjU4NA%3D%3D%26mid%3D2247484577%26idx%3D1%26sn%3Dacf23f871cb1805b8fbdecfd2ddb7ab9&quot; target=&quot;_blank&quot;&gt;ByteFUSE分布式文件系统的演进与落地&lt;/a&gt;&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;p&gt;导语:ByteFUSE是字节ByteNAS团队和STE团队合作研发的一个项目,因其具有高可靠性、极致的性能、兼容Posix语义以及支持丰富的使用场景等优点而被业务广泛使用。目前承接了在线业务ES,AI训练业务,系统盘业务,数据库备份业务,消息队列业务,符号表业务以及编译业务等,字节内部部署机器和日常挂载点均已达到万级规模,总吞吐近百GB/s,容量十几PB,其性能与稳定性能够满足业务需求。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_1&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;背景&lt;/h2&gt;
        &lt;p&gt;ByteNAS是一款全自研、高性能、高扩展,多写多读、低时延并且完全兼容Posix语义的分布式文件系统,目前支撑了字节内部AI训练,数据库备份,在线ES等多个关键业务,也是未来云上NAS主打的产品形态。早期ByteNAS对外提供服务使用的是NFS协议,其依赖TTGW四层负载均衡器将外部流量以TCP连接的粒度均衡到连接的多台Proxy,用户使用TTGW提供的VIP并进行挂载即可与多台Proxy中一台进行通信。如果当前通信的Proxy因为机器宕机等原因挂掉后,TTGW内部探测心跳超时会触发Failover机制,自动将来自该Client的请求Redirect到新的活着的Proxy,该机制对客户端是完全透明的。但是使用TTGW具有以下缺点:&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;无法支持大吞吐场景:用户的吞吐不仅受限于TTGW集群本身吞吐的限制,而且受限于NFS协议单次读写1MB的限制。另外NFS是单TCP连接,同时内核slot并发请求也有限制,这会导致吞吐受限以及元数据和数据相互影响&lt;/li&gt;
        &lt;li&gt;额外的网络延迟:用户访问ByteNAS多两跳网络(用户侧NFS Client -&amp;gt; TTGW -&amp;gt; Proxy -&amp;gt; ByteNAS)&lt;/li&gt;
        &lt;li&gt;额外的机器成本:需要TTGW以及Proxy等机器资源&lt;/li&gt;
        &lt;li&gt;定制化业务需求以及性能优化比较困难:受限于内核NFS Client,NFS协议以及TTGW的影响,其定制化需求以及性能优化比较困难&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;为了解决以上问题,ByteFUSE应运而生。ByteFUSE是一套基于用户态文件系统(FUSE)框架接入ByteNAS的解决方案,通过ByteNAS SDK直连ByteNAS集群,不仅满足了低延迟的目标,同时也解决了协议吞吐受限的问题。除此之外,由于部分文件系统逻辑上移到了用户态,对于问题排查,功能扩展以及性能优化都会变得非常方便。用户使用ByteFUSE和NFS两种协议访问ByteNAS的流程如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-8a4d6ec8bcbbcf24c56d709782620b9599f.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_2&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;目标&lt;/h2&gt;
        &lt;ul&gt;
        &lt;li&gt;高性能、低延迟,对业务友好的架构模型设计&lt;/li&gt;
        &lt;li&gt;完全兼容Posix语义&lt;/li&gt;
        &lt;li&gt;支持一写多读/多写多读&lt;/li&gt;
        &lt;li&gt;自研以及可维护性强,提供定制化特性能力支持&lt;/li&gt;
        &lt;/ul&gt;
        &lt;span id=&quot;OSC_h2_3&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;演进路线&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_4&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;1. ByteFUSE 1.0 — 基础功能完备,云原生化部署支持&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_5&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;通过原生FUSE接入ByteNAS&lt;/h4&gt;
        &lt;p&gt;原生FUSE对接ByteNAS的整体架构图如下所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-e67e94f6abe33f340da622997a5327f6df9.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;ByteFUSE Daemon:&amp;nbsp;集成了ByteNAS SDK的FUSE Daemon,用户的文件系统请求会通过FUSE协议转发给ByteFUSE Daemon,然后,通过ByteNAS SDK被转发到后端存储集群。&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_6&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;云原生化部署支持&lt;/h4&gt;
        &lt;p&gt;ByteFUSE基于K8S CSI接口规范 [1] 开发了CSI插件,以支持在K8S集群中使用ByteFUSE访问ByteNAS集群,其架构如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-27c09c691468983ea14670ff19cff8b126d.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;CSI-Driver:ByteFUSE的云原生架构目前只支持静态卷,Mount/Umount操作会在CSI-Dirver中启动/销毁FUSE Client,CSI-Driver会记录每个挂载点的状态,当CSI-Drvier异常退出重启时会recover所有挂载点来保证高可用性。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;FUSE Client:即上面提到的ByteFUSE Daemon,在1.0架构下,针对每个挂载点,CSI-Driver都会启动一个FUSE Client来提供服务。&lt;/p&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;span id=&quot;OSC_h3_7&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;2. ByteFUSE 2.0 — 云原生架构升级,一致性、可用性和可运维性提升&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_8&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;业务需求和挑战&lt;/h4&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;FUSE Client资源占用不可控以及无法复用&lt;/strong&gt;:多FUSE Client模式下,一个挂载点对应一个FUSE Client进程,FUSE Client的资源占用与挂载点个数强相关,这导致FUSE Client资源占用不可控。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;FUSE Client与CSI-Driver强耦合导致CSI-Driver无法平滑升级&lt;/strong&gt;:FUSE Client进程的生命周期与CSI-Driver关联,当需要升级CSI时,FUSE Client也需要跟随重建,导致业务I/O也会受影响,同时,这个影响时长与CSI-Driver的升级时长(秒级)强相关。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;部分业务希望在Kata容器场景接入ByteFUSE&lt;/strong&gt;:云原生场景下,有部分业务会以Kata容器的方式来运行,为了满足这部分业务接入ByteFUSE的需求,CSI-Driver需要支持kata这种容器运行时,即在kata虚机内能够通过ByteFUSE访问ByteNAS服务。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;原生FUSE一致性模型无法满足某些业务需求&lt;/strong&gt;:某些业务是典型的一写多读场景,对读写吞吐,数据可见性以及尾延迟的要求极高,但原生FUSE在开启内核缓存的情况下,无法提供像CTO (Close-to-Open) 这样的一致性模型。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;原生FUSE可用性/可运维性能力较弱,无法适用于大规模生产环境&lt;/strong&gt;:原生FUSE对高可用、热升级等能力的支持较弱,当出现FUSE进程crash或者内核模块有bug需要升级等情况时,往往需要知会业务重启Pod、甚至重启整个物理节点,这对于大部分业务都是不可接受的。&lt;/li&gt;
        &lt;/ul&gt;
        &lt;span id=&quot;OSC_h4_9&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;云原生架构升级&lt;/h4&gt;
        &lt;p&gt;&lt;strong&gt;FUSE Client架构升级:单Daemon化&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;针对上述业务需求和挑战,我们对架构进行了升级,支持了单FUSE Daemon模式来解决资源不可控以及资源无法复用的问题,采用FUSE Daemon和CSI-Driver的分离解决CSI-Driver无法平滑升级的问题,其架构如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-821c30978fef326de02fb746dc121a4dc2b.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;AdminServer:监控Mountpoint/FUSE Daemon状态,升级FUSE Daemon以及统计集群信息等。&lt;/p&gt;
        &lt;p&gt;FUSE Daemon:管理ByteNAS集群所有的挂载点以及处理读写请求,重启后recover所有的挂载点,恢复时间为ms级别。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Kata Containers 场景支持&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;为了提供Kata场景的支持,同时,解决原生FUSE的高可用和性能可扩展性问题,我们在2.0架构中引入了VDUSE[2]这个字节自主研发的技术框架来实现ByteFUSE Daemon。VDUSE利用了virtio这套成熟的软件框架,使ByteFUSE Daemon能够同时支持从虚机或者宿主机(容器)挂载。同时,相较于传统的FUSE框架,基于VDUSE实现的FUSE Daemon不再依赖/dev/fuse这个字符设备,而是通过共享内存机制来和内核通信,这种方式一方面对后续的性能优化大有裨益,另一方面也很好地解决了Crash Recovery问题。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-927aa354ea97a1899ca26fc9f1dea35fe4c.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_10&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;一致性、可用性和可运维性提升&lt;/h4&gt;
        &lt;p&gt;&lt;strong&gt;一致性模型增强&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;性能和一致性是分布式系统设计中的一对根本性矛盾 —— 保持一致性意味着更多节点的通信,而更多节点的通信意味着性能的下降。为了满足相关业务需求,我们在FUSE原生缓存模式的基础上不断的取舍性能与一致性,实现了 FUSE CTO (Close-to-Open) 一致性模型 [4],并将这些一致性模型根据不同配置抽象成以下五种: &lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a6b41784b8b959f18fea19427941ae91c3e.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Daemon高可用&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;由于ByteFUSE 2.0架构引入了VDUSE [2]这个技术框架,因此支持使用基于共享内存的Virtio协议作为传输层,Virtio协议内置的inflight I/O追踪特性可以将 ByteFUSE 正在处理的请求实时持久化,并在 ByteFUSE 恢复时重新处理未完成请求,这弥补了原生 libfuse 中使用字符设备 /dev/fuse 作为传输层时状态保存功能的缺失。基于该inflight I/O 追踪特性,ByteFUSE 进一步考虑了文件系统状态在恢复前后的一致性和幂等性,实现了用户无感的崩溃恢复 [3],同时基于崩溃恢复实现了Daeamon的热升级。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;内核模块热升级&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;ByteFUSE 在使用定制化内核模块来获得更好的性能、可用性和一致性的同时,也对这些定制化内核模块的升级维护提出挑战。为了解决二进制内核模块无法随内核升级的问题,我们通过 DKMS 来部署定制化内核模块,让内核模块随内核升级而自动重新编译部署。为了解决内核模块自身热升级的问题,我们通过将内核模块所导出的符号名或设备号与版本号绑定的方式实现了同一内核模块的多版本共存。新的 ByteFUSE 挂载将自动使用新的内核模块;旧的 ByteFUSE 挂载延续使用旧内核模块。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-960ff15127049645b8b135632942c7dd265.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;目前,通过上述 DKMS 技术及“多版本共存”技术,我们将ByteFUSE内核模块的升级与内核及ByteFUSE Daemon进行了解耦;未来,我们将进一步实现ByteFUSE内核模块热升级功能,以支持线上运行的存量ByteFUSE卷的热升级功能。&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_11&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;3. ByteFUSE 3.0 — 极致性能优化,打造业界一流的高性能文件存储系统&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_12&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;业务需求和挑战&lt;/h4&gt;
        &lt;p&gt;&lt;strong&gt;大模型训练场景对存储系统的性能需求&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;大模型训练场景下,训练巨量模型需要巨大的算力,但随着数据集和模型规模不断增加,应用程序载入数据所花费的时间变得越长,进而影响了应用程序的性能,缓慢的 I/O 严重拖累GPU 的强大算力。于此同时,模型的评估 &amp;amp; 部署需要并行读取大量模型,要求存储能够提供超高吞吐。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;云原生高密部署的场景,需要进一步降低资源占用开销&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;云原生高密部署场景下,随着ByteFUSE卷的数量级增加,对ByteFUSE单机侧的资源(CPU &amp;amp; Memory)占用及隔离提出了新的要求。&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_13&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;极致性能优化&lt;/h4&gt;
        &lt;p&gt;ByteFUSE 3.0从线程模型,数据拷贝,内核侧以及协议栈进行了全链路的性能优化,性能提高2.5倍,2个core即可打满百Gb网卡。其优化方向如下所示:&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Run-to-Completion线程模型&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;2.0 版本的一次Read/Write请求会有4次线程切换,接入Run-to-Completion(RTC)能够节省这四次线程切换带来的开销。为了做到Run-to-Completion,我们对ByteFUSE和ByteNAS SDK进行了shared-nothing的设计和锁的非阻塞化改造,其目的是保证RTC线程不会被阻塞,避免影响请求的延迟。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-30294a97bd02da03a0a1dd9ee87a635491b.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;RDMA &amp;amp; 用户态协议栈&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;3.0架构相较于2.0,在网络传输这块也做了较大的改进,主要体现在引入了RDMA和用户态协议栈(Tarzan)来替换传统的内核TCP/IP协议栈,相较于内核TCP/IP协议栈,RDMA/Tarzan能够节省用户态与内核态的切换和数据拷贝带来的延迟,并进一步降低CPU占用。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;全链路零拷贝&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;引入RDMA/Tarzan之后,ByteFUSE在网络传输这块的拷贝被成功消除了,但FUSE接入这块,仍然存在Page Cache到Bounce Buffer,Bounce Buffer到RDMA/Tarzan DMA Buffer两次拷贝。为了降低这部分的拷贝开销(经统计1M数据的拷贝消耗100us左右),3.0架构引入了VDUSE umem [5] 特性,通过将RDMA/Tarzan DMA Buffer注册给VDUSE内核模块,减少了其中的一次拷贝。未来,我们还会进一步实现FUSE PageCache Extension特性,来达成全链路零拷贝这个优化目标。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-aab67681860a71918b95eca82fba7620455.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;FUSE内核优化&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;(1)多队列&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;在原生FUSE/viritofs内核模块中,FUSE 请求的处理路径有很多单队列设计:如每个 FUSE 挂载只有一个 IQ (input queue)、一个 BGQ (background queue)、virtiofs 设备使用单队列模型发送 FUSE 请求等。为了减少单队列模型带来的锁竞争、提高可扩展性,我们在 FUSE/virtiofs 的请求路径中支持了 per-cpu 的 FUSE 请求队列和可配置的 virtiofs virtqueue 数量。基于 FUSE 多队列特性的支持,ByteFUSE 可以根据不同部署环境配置不同的 CPU 亲和性策略来减少核间通信或平衡核间负载。ByteFUSE 工作线程也可以打开 FUSE 多队列特性所提供的负载均衡调度来缓解核间请求不均情况下的局部请求排队现象。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-75e690e573177cae56531a666488021e2d0.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;(2)huge块支持&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;为了满足高吞吐场景的性能需求,ByteFUSE 3.0 版本支持定制化的FUSE内核模块参数。Linux 内核原生的FUSE 模块中存在一些对数据传输的硬编码,如单次最大数据传输单元为 1 MB,单次最大目录树读取单元为 4 KB。而在ByteFUSE内核模块中,我们将单次最大数据传输上调为 8 MB, 单次最大目录读取单元上调为 32 KB。在数据库备份场景下,将单次写下刷改成8MB,单机吞吐能提高约20%。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_14&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;演进收益&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_15&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;收益总览&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_16&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;1.0 -&amp;gt; 2.0&lt;/h4&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;降低资源占用,便于资源控制&lt;/strong&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;单FUSE Daemon和多FUSE Client相比,多个挂载点之间的线程、内存、连接等资源可以复用,可以有效降低资源占用。除此之外,将FUSE Daemon单独运行于Pod内能够更好地适应Kubernetes生态,保证其在 Kubernetes 的管控内,用户可以直接在集群中观察到FUSE Daemon的Pod,可观测性强。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;CSI-Driver与FUSE Daemon解耦合&lt;/strong&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;CSI-Driver与FUSE Daemon作为两个独立部署的服务,其部署、升级都可以独立进行而不影响彼此,进一步降低了运维工作对业务的影响。除此之外,我们支持POD内热升级FUSE Daemon,整个升级对业务是无感的。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;支持内核模块热升级&lt;/strong&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;可以在业务无感的情况下,支持对ByteFUSE增量卷进行热升级,修复内核模块的已知bug,降低线上风险。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;支持统一的监控以及管控平台,方便可视化管理&lt;/strong&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;AdminServer监控一个Region内所有FUSE Daemon &amp;amp; 挂载点的状态,支持远程恢复异常挂载点,支持Pod内热升级FUSE Daemon,支持远程挂载点异常探测以及报警等。&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_17&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;2.0 -&amp;gt; 3.0&lt;/h4&gt;
        &lt;p&gt;整个架构实现了Run-to-Complete的线程模型,减少了锁以及上下文切换带来的性能损耗。除此之外,我们将内核态TCP换成用户态TCP,bypass内核以及采用内存注册到内核的方式实现全链路zero-copy进一步的提升性能。对于1MB的写请求,FUSE Daemon侧可以节省百us。&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_18&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;性能对比&lt;/h3&gt;
        &lt;p&gt;FUSE Daemon 机器规格:&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;CPU: 32物理核,64逻辑核&lt;/li&gt;
        &lt;li&gt;内存:251.27GB&lt;/li&gt;
        &lt;li&gt;NIC: 100Gbps&lt;/li&gt;
        &lt;/ul&gt;
        &lt;span id=&quot;OSC_h4_19&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;元数据性能对比&lt;/h4&gt;
        &lt;p&gt;使用mdtest进行性能测试,测试命令&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;mdtest &#39;-d&#39; &#39;/mnt/mdtest/&#39; &#39;-b&#39; &#39;6&#39; &#39;-I&#39; &#39;8&#39; &#39;-z&#39; &#39;4&#39; &#39;-i&#39; &#39;30
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;,性能差异如下所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-5192b79e65e3c29b96c61e598cbcaf686ec.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_20&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;结论&lt;/h3&gt;
        &lt;p&gt;3.0架构相比1.0架构元数据性能大约提高了25%左右。&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_21&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;数据性能对比&lt;/h4&gt;
        &lt;p&gt;FIO采用4线程,其性能如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a6d3f30bde68c9bf55efbbb6c474ed4f83d.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;此外,测试ByteFUSE 3.0 polling线程个数对性能的影响。对于写,2个polling线程基本打满百G网卡,而对于读,则需要4个polling线程(比写操作多一次数据拷贝)。未来,我们将改造用户态协议栈Tarzan,节省读的一次数据拷贝,做到读写zero-copy。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-2a407c914dc1de674301542ff786183d58d.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_22&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;业务落地&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_23&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;ES存算分离架构场景的落地&lt;/h3&gt;
        &lt;p&gt;&lt;strong&gt;场景描述&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;ES的Shared Storage架构,让ES 的多个分片副本使用同一份数据,来解决在 Shared Nothing 架构下的扩容生效缓慢,分片迁移慢,搜索打分振荡以及存储成本高等问题。底层存储使用ByteNAS来共享主副分片的数据以及使用ByteFUSE作为接入协议来满足高性能,高吞吐以及低延迟的要求。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;收益&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;ES存算分离架构的落地,每年节省存储成本近千万&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_24&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;Ai训练场景的落地&lt;/h3&gt;
        &lt;p&gt;&lt;strong&gt;场景描述&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;AI 类 Web IDE场景下使用块存储 + NFS的方式共享根文件系统无法解决由于NFS断联进程进入D状态以及NFS断联触发内核Bug导致mount功能不可用等问题。除此之外,AI训练场景下受限负载均衡的吞吐以及NFS协议性能的影响无法满足训练任务高吞吐 &amp;amp; 低延迟的需求,而ByteNAS提供共享文件系统,超大吞吐以及低延迟满足模型训练。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;收益&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;满足AI训练对高吞吐,低延迟的需求&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_25&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;其他业务场景的落地&lt;/h3&gt;
        &lt;p&gt;受限于TTGW吞吐以及稳定性的原因,数据库备份业务,消息队列业务,符号表业务以及编译业务从NFS切换到ByteFUSE。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_26&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;未来展望&lt;/h2&gt;
        &lt;p&gt;ByteFUSE 3.0架构已经可以满足绝大部分业务的需求,但是,为了追求更极致的性能以及满足更多的业务场景,未来我们还有不少工作有待展开:&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;ByteFUSE推广到ToB场景;满足云上业务超低延迟,超高吞吐的需求&lt;/li&gt;
        &lt;li&gt;支持非Posix语义;定制化接口满足上层应用的需求,譬如 IO fencing语意&lt;/li&gt;
        &lt;li&gt;FUSE PageCache Extension;FUSE支持Page Cache用户态扩展,FUSE Daemon能够直接读写Page Cache&lt;/li&gt;
        &lt;li&gt;支持内核模块热升级;支持用户无感情况下,升级存量及增量ByteFUSE卷的内核模块&lt;/li&gt;
        &lt;li&gt;支持GPU Direct Storage[6];数据直接在RDMA网卡和GPU之间进行传输,bypaas主机内存和CPU&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;[1] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fkubernetes-csi.github.io%2Fdocs%2F&quot; target=&quot;_blank&quot;&gt;https://kubernetes-csi.github.io/docs/&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[2] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.redhat.com%2Fen%2Fblog%2Fintroducing-vduse-software-defined-datapath-virtio&quot; target=&quot;_blank&quot;&gt;https://www.redhat.com/en/blog/introducing-vduse-software-defined-datapath-virtio&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[3] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fjuejin.cn%2Fpost%2F7171280231238467592&quot; target=&quot;_blank&quot;&gt;https://juejin.cn/post/7171280231238467592&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[4] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Flore.kernel.org%2Flkml%2F20220624055825.29183-1-zhangjiachen.jaycee%40bytedance.com%2F&quot; target=&quot;_blank&quot;&gt;https://lore.kernel.org/lkml/[email protected]/&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[5] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Flwn.net%2FArticles%2F900178%2F&quot; target=&quot;_blank&quot;&gt;https://lwn.net/Articles/900178/&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[6] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fdocs.nvidia.com%2Fgpudirect-storage%2Foverview-guide%2Findex.html&quot; target=&quot;_blank&quot;&gt;https://docs.nvidia.com/gpudirect-storage/overview-guide/index.html&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;热门招聘&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;字节跳动STE团队诚邀您的加入!团队长期招聘,&lt;strong&gt;北京、上海、深圳、杭州、US、UK&lt;/strong&gt;&amp;nbsp;均设岗位,以下为近期的招聘职位信息,有意向者可直接扫描海报二维码投递简历,期待与你早日相遇,在字节共赴星辰大海!若有问题可咨询小助手微信:sys_tech,岗位多多,快来砸简历吧!&lt;/p&gt;
        &lt;/div&gt;
      </description>
      <link>https://my.oschina.net/u/6150560/blog/10098437</link>
      <guid isPermaLink="false">https://my.oschina.net/u/6150560/blog/10098437</guid>
      <pubDate>Fri, 18 Aug 2023 03:24:00 GMT</pubDate>
      <author>字节跳动SYS Tech</author>
    </item>
    <item>
      <title>深入分析HWASAN检测内存错误原理</title>
      <description>&lt;div class=&quot;content&quot;&gt;
        &lt;div class=&quot;ad-wrap&quot; style=&quot;margin-bottom: 8px;&quot;&gt;
        &lt;div data-traceid=&quot;news_comment_top_ad&quot; data-tracepid=&quot;news_comment_top&quot; style=&quot;text-align: center;&quot;&gt;
        &lt;a style=&quot;color:#A00;font-weight:bold;&quot; href=&quot;https://www.oschina.net/live/&quot; target=&quot;_blank&quot;&gt;
        本周六,「源创会年终盛典」珠海站再次回归!错过等一年&lt;img src=&quot;https://www.oschina.net/img/hot3.png&quot; align=&quot;absmiddle&quot; style=&quot;max-height: 32px;max-width: 32px;margin-top: -4px;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;
        &lt;/div&gt;
        &lt;/div&gt;
        &lt;blockquote&gt;
        &lt;p&gt;原文链接:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg3Mjg2NjU4NA%3D%3D%26mid%3D2247484531%26idx%3D1%26sn%3D57acfda58d6cc0dffb9fa5e5c6f488bb&quot; title=&quot;深入分析HWASAN检测内存错误原理&quot; target=&quot;_blank&quot;&gt;深入分析HWASAN检测内存错误原理&lt;/a&gt;&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;p&gt;导语:ASAN(AddressSanitizer) 是 C/C++开发者常用的内存错误检测工具,主要用于检测缓冲区溢出、访问已释放的内存等内存错误。 AArch64 上提供了 Top-Byte-Ingore 硬件特性,HWASan(HardWare-assisted AddressSanitizer) 就是利用 Top-Byte-Ignore 特性实现的增强版 ASan,与 ASAN 相比 HWASan 的内存开销更低,检测到的内存错误范围更大。因此在 AArch64 平台,建议使用 HWASAN。本篇文章将深入分析 HWASAN 检测内存错误的原理,帮助大家更好地理解和使用 HWASan 来排查程序中存在的疑难内存错误。&lt;/p&gt;
        &lt;h2&gt;前言&lt;/h2&gt;
        &lt;p&gt;在字节跳动,C++语言被广泛应用在各个业务中,由于C++语言的特性,导致 C++ 程序很容易出现内存问题。ASAN 等内存检测工具在字节跳动内部已经取得了可观的收益和效果(更多内容请查看:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.bilibili.com%2Fvideo%2FBV1YT411Q7BU%2F&quot; target=&quot;_blank&quot;&gt;Sanitizer 在字节跳动 C C++ 业务中的实践&lt;/a&gt;),服务于60个业务线,近一年协助修复上百个内存缺陷。但是仍然有很大的提升空间,特别是在性能开销方面。随着 ARM 进入服务器芯片市场,ARM架构下的一些硬件特性可以用来缓解 ASAN 工具的性能问题,利用这些硬件特性研发的 HWASAN 检测工具在超大型 C++ 服务上的检测能力还有待确认。&lt;/p&gt;
        &lt;p&gt;为此,STE 团队对 HWASAN 进行了深入分析,并在字节跳动 C++ 核心服务上进行了落地验证。在落地 HWASAN 过程中,修复了 HWASAN 实现中的一些关键 bug,并对易用性进行了提升。相关 patch 已经贡献到LLVM开源社区(详情请查看文末链接)。本篇文章将深入分析 HWASAN 检测内存错误的原理,帮助大家更好地理解和使用 HWASan 来排查程序中存在的疑难内存错误。&lt;/p&gt;
        &lt;h2&gt;概述&lt;/h2&gt;
        &lt;p&gt;&lt;strong&gt;HWASAN&lt;/strong&gt;: &lt;strong&gt;H&lt;/strong&gt;ard&lt;strong&gt;W&lt;/strong&gt;are-assisted &lt;strong&gt;A&lt;/strong&gt;ddress&lt;strong&gt;San&lt;/strong&gt;itizer, a tool similar to AddressSanitizer, but based on partial hardware assistance and consumes much less memory.&lt;/p&gt;
        &lt;p&gt;这里所谓的 &quot;partial hardware assistance&quot; 就是指 AArch64 的 &lt;strong&gt;TBI&lt;/strong&gt; (Top Byte Ignore) 特性。&lt;/p&gt;
        &lt;blockquote&gt;
        &lt;p&gt;TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address translation and can be used to store a tag.&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;p&gt;以如下代码举例,Linux/AArch64 下将指针 x 的 top byte 设置为 0xfe,不影响程序执行:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;// $ cat tbi.cpp
        int main(int argc, char **argv) {
        int * volatile x = (int *)malloc(sizeof(int));
        *x = 666;
        printf(&quot;address: %p, value: %d\n&quot;, x, *x);
        x = reinterpret_cast&amp;lt;int*&amp;gt;(reinterpret_cast&amp;lt;uintptr_t&amp;gt;(x) | (0xfeULL &amp;lt;&amp;lt; 56));
        printf(&quot;address: %p, value: %d\n&quot;, x, *x);
        free(x);
        return 0;
        }
        // $ clang++ tbi.cpp &amp;amp;&amp;amp; ./a.out
        address: 0xaaab1845fe70, value: 666
        address: 0xfe00aaab1845fe70, value: 666
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;AArch64 的 TBI 特性使得软件可以在 64-bit 虚拟地址的最高字节中存储任意数据,HWASAN 正是基于 TBI 这一特性设计并实现的内存错误检测工具。&lt;/p&gt;
        &lt;p&gt;举个例子,以下代码中存在 heap-buffer-overflow bug:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;// cat test.c
        #include &amp;lt;stdlib.h&amp;gt;
        int main() {
        int * volatile x = (int *)malloc(sizeof(int)*10);
        x[10] = 0;
        free(x);
        }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;使用 HWASAN 检测上述代码中的 heap-buffer-overflow bug:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;$ clang -fuse-ld=lld -g -fsanitize=hwaddress ./test.c &amp;amp;&amp;amp; ./a.out
        ==3581920==ERROR: HWAddressSanitizer: tag-mismatch on address 0xec2bfffe0028 at pc 0xaaad830db1a4
        WRITE of size 4 at 0xec2bfffe0028 tags: 69/08(69) (ptr/mem) in thread T0
        #0 0xaaad830db1a4 in main ./test.c:4:11
        #1 0xfffd07350da0 in __libc_start_main libc-start.c:308:16
        #2 0xaaad83090820 in _start (./a.out+0x40820)
        [0xec2bfffe0000,0xec2bfffe0030) is a small allocated heap chunk; size: 48 offset: 40
        Cause: heap-buffer-overflow
        0xec2bfffe0028 is located 0 bytes after a 40-byte region [0xec2bfffe0000,0xec2bfffe0028)
        allocated by thread T0 here:
        #0 0xaaad83099248 in __sanitizer_malloc.part.13 llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:151:3
        #1 0xaaad830db17c in main ./test.c:3:31
        #2 0xfffd07350da0 in __libc_start_main libc-start.c:308:16
        #3 0xaaad83090820 in _start (/a.out+0x40820)
        Thread: T0 0xeffc00002000 stack: [0xffffc3a10000,0xffffc4210000) sz: 8388608 tls: [0xfffd076a5030,0xfffd076a5e70)
        Memory tags around the buggy address (one tag corresponds to 16 bytes):
        0xec2bfffdf800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdf900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfa00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfb00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfc00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfd00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        =&amp;gt;0xec2bfffe0000: 69 69 [08] 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
        0xec2bfffdff00: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
        =&amp;gt;0xec2bfffe0000: .. .. [69] .. .. .. .. .. .. .. .. .. .. .. .. ..
        0xec2bfffe0100: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
        See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags
        Registers where the failure occurred (pc 0xaaad830db1a4):
        x0 a100ffffc4201580 x1 6900ec2bfffe0028 x2 0000000000000000 x3 0000000000000000
        x4 0000000000000020 x5 0000000000000000 x6 0000000000100000 x7 fffffffffff00005
        x8 6900ec2bfffe0000 x9 6900ec2bfffe0000 x10 0030f15d14c79f97 x11 00ffffffffffffff
        x12 00001f0d780b69d2 x13 0000000000000001 x14 0000ffffc4200b60 x15 0000000000000696
        x16 0000aaad830a3540 x17 000000000000000b x18 0000000000000100 x19 0000aaad830db600
        x20 0200effd00000000 x21 0000aaad830907f0 x22 0000000000000000 x23 0000000000000000
        x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
        x28 0000000000000000 x29 0000ffffc4201590 x30 0000aaad830db1a8 sp 0000ffffc4201550
        SUMMARY: HWAddressSanitizer: tag-mismatch ./test.c:4:11 in main
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;如上所示,HWASAN 与 ASAN 相比不管是用法 (&lt;code&gt;-fsanitize=hwaddress&lt;/code&gt; v.s. &lt;code&gt;-fsanitize=address&lt;/code&gt;) 还是检测到错误后的报告都很相似。&lt;/p&gt;
        &lt;p&gt;下面对比分析 ASAN 与 HWASAN 检测内存错误的技术原理:&lt;/p&gt;
        &lt;p&gt;ASAN (AddressSanitizer):&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;使用 shadow memory 技术,每 8-bytes 的 application memory 对应 1-byte 的 shadow memory。&lt;/li&gt;
        &lt;li&gt;使用 redzone 来检测 buffer-overflow。不管是栈内存还是堆内存,在申请内存时都会在原本内存的两侧额外申请一定大小的内存作为 redzone,一旦访问到了 redzone 则说明发生了缓冲区溢出。&lt;/li&gt;
        &lt;li&gt;使用 quarantine 检测 use-after-free。应用程序执行 delete 或 free 释放内存时,并不真正释放,而是放在一个暂存区 (quarantine) 中,一旦访问了位于 quarantine 中的内存则说明访问了已释放的内存。&lt;/li&gt;
        &lt;li&gt;每 1-byte 的 shadow memory 编码表示对应的 8-byte application memory 的信息,每次访问 application memory 之前 ASAN 都会检查对应的 shadow memory 判断本次内存访问是否合法。例如:shadow memory 的值为 0xfd 表示对应的 8-bytes application memory 是 freed memory,所以当访问的 application memory 其 shadow memory byte 为 0xfd 就说明此时访问的是已经释放的内存了即 use-after-free;shadow memory 为 0xfa 表示相应的 application memory 是堆内存的 redzone,所以当访问到的 appllcation memory 其 shadow memory 为 0xfa 就说明此时访问的是堆内存附近的 redzone 发生了堆缓冲区溢出错误即 heap-buffer-overflow&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;HWASAN (HardWare-assisted AddressSanitizer)&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;同样使用 shadow memory 技术,不过与 ASAN 不同的是:HWASAN 每 16-bytes 的 application memory 对应 1-byte 的 shadow memory。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;不依赖 redzone 检测 buffer-overflow,不依赖 quarantine 检测 use-after-free,仅基于 TBI 特性就能检测 buffer-overflow 和 use-after-free。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;举例说明 HWASAN 检测内存错误的原理&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;因为 HWASAN 会将每 16-bytes 的 application memory 都对应 1-byte 的 shadow tag,所以 HWASAN 会将申请的内存都对齐到 16-bytes,因此下图中 new char[20] 实际申请的内存是 32-bytes。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;HWASAN 会生成一个随机 tag 保存在 operator new 返回的指针 p 的 top byte 中,同时会将 tag 保存在 p 指向的内存对应 shadow memory 中。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;为了方便说明,下图中用不同的颜色表示不同的 tag,绿色表示 tag 0xa,蓝色表示 tag 0xb,紫色表示 tag 0xc。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;&lt;strong&gt;检测 heap-buffer-overflow&lt;/strong&gt;&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;假设 HWASAN 为 &lt;code&gt;new char[20]&lt;/code&gt; 生成的 tag 为 0xa 即绿色,所以指针 p 的 top byte 为 0xa。在通过 &lt;code&gt;p[32]&lt;/code&gt; 访问内存时,HWASAN 会检查保存在指针 p 的 tag 与 &lt;code&gt;p[32]&lt;/code&gt; 指向的内存所对应的 shadow memory 中保存的 tag 是否一致。显然保存在指针 p 的 tag 是绿色 而&lt;code&gt;p[32]&lt;/code&gt; 指向的内存所对应的 shadow memory 中保存的 tag 是蓝色,即 tag 是不匹配的,这说明访问 &lt;code&gt;p[32]&lt;/code&gt; 时存在内存错误。 &lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-6a016a9327a452214e4a1ef63998c5faab1.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/li&gt;
        &lt;/ul&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;&lt;strong&gt;检测 use-after-free&lt;/strong&gt;&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;假设 HWASAN 为 &lt;code&gt;new char[20]&lt;/code&gt; 生成的 tag 为 0xa 即绿色,所以指针 p 的 top byte 为 0xa。执行 &lt;code&gt;delete[] p&lt;/code&gt; 释放内存时,HWASAN 将这块释放的内存 retag 为紫色,即将这块释放的内存对应的 shadow memory 从绿色修改为紫色。在通过 &lt;code&gt;p[0]&lt;/code&gt; 访问内存时,HWASAN 会检查保存在指针 p 的 tag 与 &lt;code&gt;p[0]&lt;/code&gt; 指向的内存所对应的 shadow memory 中保存的 tag 是否一致。显然保存在指针 p 的 tag 是绿色 而&lt;code&gt;p[0]&lt;/code&gt; 指向的内存所对应的 shadow memory 中保存的 tag 是紫色,即 tag 是不匹配的,这说明访问 &lt;code&gt;p[0]&lt;/code&gt; 时存在内存错误。&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-75a2820861bf87b77c36f17f25b42329c8c.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/li&gt;
        &lt;/ul&gt; &lt;/li&gt;
        &lt;/ul&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;h2&gt;算法&lt;/h2&gt;
        &lt;ul&gt;
        &lt;li&gt;shadow memory:每 16-bytes 的 application memory 对应 1-byte 的 shadow memory。&lt;/li&gt;
        &lt;li&gt;每个 heap/stack/global 内存对象都被对齐到 16-bytes,这样每个 heap/stack/global 内存对象至少对应的 1-byte shadow memory。&lt;/li&gt;
        &lt;li&gt;为每一个 heap/stack/global 内存对象生成一个 1-byte 的随机 tag,将该随机 tag 保存到指向这些内存对象的指针的 top byte 中,同样将该随机 tag 保存到这些内存对象对应的 shadow memory 中。&lt;/li&gt;
        &lt;li&gt;在每一处内存读写之前插桩:比较保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 是否一致,如果不一致则报错。&lt;/li&gt;
        &lt;/ul&gt;
        &lt;h2&gt;实现&lt;/h2&gt;
        &lt;h3&gt;shadow mapping&lt;/h3&gt;
        &lt;p&gt;HWASAN 与 ASAN 一样都使用了 shadow memory 技术。ASAN 默认使用 static shadow mapping,只有对 IOS 和 32-bit Android 平台才使用 dynamic shadow mapping。而 HWASAN 则总是使用 dynamic shadow mapping。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;ASAN: static shadow mapping。在 llvm-project/compiler-rt/lib/asan/asan_mapping.h 中预定义了不同平台下 shadow memory 的布局:HighMem, HighShadow, ShadowGap, LowShadow, LowMem 的地址区间。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;Linux/x86_64 下 ASAN 的 shadow mapping 如下所示:&lt;/p&gt; &lt;pre&gt;&lt;code&gt; // Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000:
        || `[0x10007fff8000, 0x7fffffffffff]` || HighMem ||
        || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow ||
        || `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap ||
        || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow ||
        || `[0x000000000000, 0x00007fff7fff]` || LowMem ||
        &lt;/code&gt;&lt;/pre&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;给定 application memory 地址 &lt;code&gt;addr&lt;/code&gt;,计算其对应的 shadow memory 地址的公式如下:&lt;/p&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;pre&gt;&lt;code&gt;uptr MemToShadow(uptr addr) { return (addr &amp;gt;&amp;gt; 3) + 0x7fff8000; }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;HWASAN: dynamic shadow mapping。根据 MaxUserVirtualAddress 计算 shadow memory 所需要的总大小 shadow_size,通过 mmap(shadow_size) 得到 shadow memory 区间,再具体划分 HighMem, HighShadow, ShadowGap, LowShadow, LowMem 的地址区间。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;伪算法如下(未考虑对齐):&lt;/p&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;pre&gt;&lt;code&gt; kHighMemEnd = GetMaxUserVirtualAddress();
        shadow_size = MemToShadowSize(kHighMemEnd);
        __hwasan_shadow_memory_dynamic_address = mmap(shadow_size);
        // Place the low memory first.
        kLowMemEnd = __hwasan_shadow_memory_dynamic_address - 1;
        kLowMemStart = 0;
        // Define the low shadow based on the already placed low memory.
        kLowShadowEnd = MemToShadow(kLowMemEnd);
        kLowShadowStart = __hwasan_shadow_memory_dynamic_address;
        // High shadow takes whatever memory is left up there.
        kHighShadowEnd = MemToShadow(kHighMemEnd);
        kHighShadowStart = Max(kLowMemEnd, MemToShadow(kHighShadowEnd)) + 1;
        // High memory starts where allocated shadow allows.
        kHighMemStart = ShadowToMem(kHighShadowStart);
        &lt;/code&gt;&lt;/pre&gt;
        &lt;pre&gt;&lt;code&gt;uptr MemToShadow(uptr untagged_addr) {
        return (untagged_addr &amp;gt;&amp;gt; 4) + __hwasan_shadow_memory_dynamic_address;
        }
        uptr ShadowToMem(uptr shadow_addr) {
        return (shadow_addr - __hwasan_shadow_memory_dynamic_address) &amp;lt;&amp;lt; 4;
        }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;Linux/AArch64 下 HWASAN 的某种 shadow mapping 如下所示:&lt;/p&gt; &lt;pre&gt;&lt;code&gt; // Typical mapping on Linux/AArch64
        // with dynamic shadow mapped: [0xefff00000000, 0xffff00000000]:
        || [0xffff00000000, 0xffffffffffff] || HighMem ||
        || [0xfffef0000000, 0xfffeffffffff] || HighShadow ||
        || [0xfefef0000000, 0xfffeefffffff] || ShadowGap ||
        || [0xefff00000000, 0xfefeefffffff] || LowShadow ||
        || [0x000000000000, 0xeffeffffffff] || LowMem ||
        &lt;/code&gt;&lt;/pre&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;h3&gt;tagging&lt;/h3&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;stack 内存对象:在编译时插桩阶段,HWASAN 会插入代码来实现对 stack 内存对象的 tagging,将生成的随机 tag 保存在 stack 内存对象指针的 top byte,同时将随机 tag 保存这些 stack 内存对象对应的 shadow memory 中。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;每个 stack 内存对象的 tag 是通过 &lt;code&gt;stack_base_tag ^ RetagMask(AllocaNo)&lt;/code&gt; 计算得到的。&lt;code&gt;stack_base_tag&lt;/code&gt; 对于不同的 stack frame 是不同的值,&lt;code&gt;RetagMask(AllocaNo)&lt;/code&gt; 的实现如下(AllocaNo 可以看作是 stack 内存对象的序号)。&lt;/p&gt; &lt;pre&gt;&lt;code&gt; static unsigned RetagMask(unsigned AllocaNo) {
        // A list of 8-bit numbers that have at most one run of non-zero bits.
        // x = x ^ (mask &amp;lt;&amp;lt; 56) can be encoded as a single armv8 instruction for these
        // masks.
        // The list does not include the value 255, which is used for UAR.
        //
        // Because we are more likely to use earlier elements of this list than later
        // ones, it is sorted in increasing order of probability of collision with a
        // mask allocated (temporally) nearby. The program that generated this list
        // can be found at:
        // https://github.com/google/sanitizers/blob/master/hwaddress-sanitizer/sort_masks.py
        static unsigned FastMasks[] = {0, 128, 64, 192, 32, 96, 224, 112, 240,
        48, 16, 120, 248, 56, 24, 8, 124, 252,
        60, 28, 12, 4, 126, 254, 62, 30, 14,
        6, 2, 127, 63, 31, 15, 7, 3, 1};
        return FastMasks[AllocaNo % std::size(FastMasks)];
        }
        &lt;/code&gt;&lt;/pre&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;heap 内存对象:随机 tag 是在运行时申请 heap 内存对象时由 HWASAN 的内存分配器生成的。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;当程序调用 malloc 或者 operator new 申请内存时,HWASAN 的内存分配器会将随机生成的 tag 保存在 malloc 或者 operator new 返回的指针的 top byte 中,同时将随机 tag 保存在这些 heap 内存对象对应的 shadow memory 中。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;当程序调用 free 或者 operator delete 释放内存时,HWASAN 的内存分配器会再次随机生成 tag ,将新生成的随机 tag 保存在正释放的 heap 内存对象对应的 shadow memory 中。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;global 内存对象:对于每个编译单元内的 global 内存对象,在编译时插桩阶段都会生成对应的 tag,编译单元内的第一个 global 内存对象的 tag 是对当前编译单元文件路径计算 MD5 哈希值然后取第一个字节得到的,编译单元内的后续 global 内存对象的 tag 是基于之前 global 内存对象的 tag 递增得到的。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;在编译时插桩阶段 HWASAN 会插入代码来将生成的随机 tag 保存在 global 内存对象指针的 top byte,而将随机 tag 保存到这些 global 内存对象对应的 shadow memory 中则是由 HWASAN runtime 在程序启动阶段做的。对于每一个 global 内存对象,HWASAN 在编译插桩阶段都会创建一个 descriptor 保存在 &quot;hwasan_globals&quot; section 中,descriptor 中保存了 global 内存对象的大小以及 tag 等信息,程序启动时 HWASAN runtime 会遍历 &quot;hwasan_globals&quot; section 中所有的 descriptor 来设置每个 global 内存对象对应的 shadow memory tag。&lt;/p&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;h3&gt;short granules&lt;/h3&gt;
        &lt;p&gt;每个 heap/stack/global 内存对象都会被对齐到 16-bytes,heap/stack/global 内存对象原本大小记作 size,如果 &lt;code&gt;size % 16 != 0&lt;/code&gt;,那么就需要 padding,heap/stack/global 内存对象最后不足 16-bytes 的部分就被称为 short granule。此时会将 tag 存储到 padding 的最后一个字节,而 padding 所在的 16-bytes 内存对应的 1-byte shadow memory 中存储的则是 short granule size 即 &lt;code&gt;size % 16&lt;/code&gt;&lt;/p&gt;
        &lt;p&gt;举例如下:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;uint8_t buf\[20\];
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;&lt;code&gt;uint8_t buf[20]&lt;/code&gt; 开启 HWASAN 后会变为:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;uint8_t buf[32]; // 20-bytes aligned to 16-bytes -&amp;gt; 32-bytes
        uint8_t tag = __hwasan_generate_tag();
        buf[31] = tag;
        *(char *)MemToShadow(buf) = tag;
        *((char *)MemToShadow(buf)+1) = 20 % 16;
        uint8_t *tagged_buf = reinterpret_cast&amp;lt;int8_t *&amp;gt;(
        reinterpret_cast&amp;lt;uintptr_t&amp;gt;(buf) | (tag &amp;lt;&amp;lt; 56));
        // Replace all uses of `buf` with `tagged_buf`
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;&lt;code&gt;uint_t buf[20]&lt;/code&gt; 的最后 4-bytes 就是 short granule,short granule size 即 20 % 16 = 4。&lt;/p&gt;
        &lt;p&gt;因为 short granules 的存在,所以在比较保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 是否一致时,需要考虑如下两种可能:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt; &lt;p&gt;保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 相同。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;保存在 shadow memory 中的 tag 实际上是 short granule size,保存在指针 top byte 的 tag 等于保存在指针指向的内存所在的 16-bytes 内存的最后一个字节的 tag。&lt;/p&gt; &lt;/li&gt;
        &lt;/ol&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-941c89ebb18a8a02d080ece2e362744cf45.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;为什么需要 short granules ?&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;考虑 &lt;code&gt;uint8_t buf[20]&lt;/code&gt;,假设代码中存在访问 &lt;code&gt;buf[22]&lt;/code&gt; 导致的 buffer-overflow。因为 HWASAN 会将 heap/stack/global 内存对象对齐到 16-bytes,所以实际为 &lt;code&gt;uint8_t buf[20]&lt;/code&gt; 申请的空间是 &lt;code&gt;uint8_t buf[32]&lt;/code&gt;&lt;/p&gt;
        &lt;p&gt;如果没有 short granules,那么保存在 &lt;code&gt;buf&lt;/code&gt; 指针 top byte 的 tag 为 0xa1,保存在 &lt;code&gt;buf[22]&lt;/code&gt; 对应的 shadow memory 中的 tag 为 0xa1,尽管访问 &lt;code&gt;buf[22]&lt;/code&gt; 时发生了 buffer-overflow,此时 HWASAN 也检测不到,因为保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 是否一致的。&lt;/p&gt;
        &lt;p&gt;有了 short granules,保存在 &lt;code&gt;buf&lt;/code&gt; 指针 top byte 的 tag 为 0xa1,保存在 &lt;code&gt;buf[22]&lt;/code&gt; 对应的 shadow memory 中的 tag 则是 short granule size 即 20 % 16 = 4。访问 &lt;code&gt;buf[22]&lt;/code&gt; 时,HWASAN 发现保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 不一致,保存在 &lt;code&gt;buf[22]&lt;/code&gt; 对应的 shadow memory 中的 tag 是 short granule size 为 4,这意味着 &lt;code&gt;buf[22]&lt;/code&gt; 所在的 16-bytes 内存只有前 4-bytes 是可以合法访问的,而 &lt;code&gt;buf[22]&lt;/code&gt; 访问的却是其所在 16-bytes 内存的第 7 个 byte,说明访问 &lt;code&gt;buf[22]&lt;/code&gt; 时发生了 buffer-overflow!&lt;/p&gt;
        &lt;h3&gt;hwasan check memaccess&lt;/h3&gt;
        &lt;p&gt;本节说明 HWASAN 如何在每一处内存读写之前通过插桩来比较保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 是否一致的。&lt;/p&gt;
        &lt;p&gt;开启 HWASAN 后,HWAddressSanitizer instrumentation pass 会在 LLVM IR 层面进行插桩。默认情况下,HWASAN 在每一处内存读写之前添加对 &lt;code&gt;llvm.hwasan.check.memaccess.shortgranules&lt;/code&gt; intrinsic 的调用。该 &lt;code&gt;llvm.hwasan.check.memaccess.shortgranules&lt;/code&gt; intrinsic 会在生成汇编代码时转换为对相应函数的调用。&lt;/p&gt;
        &lt;p&gt;还是以如下代码为例说明:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;// cat test.c #include &amp;lt;stdlib.h&amp;gt; int main() { int * volatile x = (int *)malloc(sizeof(int)*10); x\[10\] = 0; free(x); }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;上述代码 &lt;code&gt;clang -O1 -fsanitize=hwaddress test.c -S -emit-llvm&lt;/code&gt; 开启 HWASAN 生成的 LLVM IR 如下:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;%5 = alloca ptr, align 8
        %6 = call noalias ptr @malloc(i64 noundef 40)
        store ptr %6, ptr %5, align 8
        %7 = load volatile ptr, ptr %5, align 8
        %8 = getelementptr inbounds i32, ptr %7, i64 10
        call void @llvm.hwasan.check.memaccess.shortgranules(ptr %__hwasan_shadow_memory_dynamic_address, ptr %8, i32 18)
        store i8 0, ptr %8, align 4
        %9 = load volatile ptr, ptr %5, align 8
        call void @free(ptr noundef %9)
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;&lt;code&gt;llvm.hwasan.check.memaccess.shortgranules&lt;/code&gt; intrinsic 有三个参数:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt;__hwasan_shadow_memory_dynamic_address&lt;/li&gt;
        &lt;li&gt;本次 memory access 访问的内存地址&lt;/li&gt;
        &lt;li&gt;常数 AccessInfo,编码了本次 memory access 的相

@github-actions github-actions bot added the Auto: Route Test Complete Auto route test has finished on given PR label Dec 28, 2024
Copy link
Contributor

Successfully generated as following:

http://localhost:1200/oschina/u/6150560 - Success ✔️
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>字节跳动SYS Tech的博客</title>
    <link>https://my.oschina.net/u/6150560</link>
    <atom:link href="http://localhost:1200/oschina/u/6150560" rel="self" type="application/rss+xml"></atom:link>
    <description>聚焦系统技术领域,分享前沿技术动态、技术创新与实践、行业技术热点分析。 - Powered by RSSHub</description>
    <generator>RSSHub</generator>
    <webMaster>[email protected] (RSSHub)</webMaster>
    <language>en</language>
    <lastBuildDate>Sat, 28 Dec 2024 04:41:16 GMT</lastBuildDate>
    <ttl>5</ttl>
    <item>
      <title>字节跳动开源Linux内核网络抓包工具netcap</title>
      <description>&lt;div class=&quot;content&quot;&gt;
        &lt;div class=&quot;ad-wrap&quot; style=&quot;margin-bottom: 8px;&quot;&gt;
        &lt;div data-traceid=&quot;news_comment_top_ad&quot; data-tracepid=&quot;news_comment_top&quot; style=&quot;text-align: center;&quot;&gt;
        &lt;a style=&quot;color:#A00;font-weight:bold;&quot; href=&quot;https://www.oschina.net/live/&quot; target=&quot;_blank&quot;&gt;
        本周六,「源创会年终盛典」珠海站再次回归!错过等一年&lt;img src=&quot;https://www.oschina.net/img/hot3.png&quot; align=&quot;absmiddle&quot; style=&quot;max-height: 32px;max-width: 32px;margin-top: -4px;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;
        &lt;/div&gt;
        &lt;/div&gt;
        &lt;blockquote&gt;
        &lt;p&gt;文章转载至【字节跳动SYS Tech】公众号。原文链接点击:&lt;a href=&quot;https://my.oschina.net/u/6150560/blog/%22https://mp.weixin.qq.com/s?__biz=Mzg3Mjg2NjU4NA==&amp;amp;mid=2247485050&amp;amp;idx=1&amp;amp;sn=6b18af60f6dd6c890da8721368cb6525edd2a75050%22&quot;&gt;字节跳动开源Linux内核网络抓包工具netcap&lt;/a&gt;&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;h1&gt;背景介绍&lt;/h1&gt;
        &lt;p&gt;在 Linux 内核网络开发过程中,网络丢包问题是一个常见的挑战。传统的网络抓包工具(如 tcpdump)虽然能够帮助开发者定位问题,但其效率较低,且在深度网络问题定位方面能力有限。随着 eBPF 技术的快速发展,出现了更高级的问题跟踪能力。&lt;strong&gt;字节跳动 STE 团队&lt;/strong&gt;基于此技术开发了&lt;strong&gt;下一代内核网络抓包工具&lt;/strong&gt;&lt;strong&gt;netcap&lt;/strong&gt; (net capture,内部原名:xcap),并正式对外开源:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fbytedance%2Fnetcap&quot; target=&quot;_blank&quot;&gt;GitHub - bytedance/netcap&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;与 tcpdump 工具只能作用于内核网络协议栈准备发包和收包的固定点相比,netcap 可以几乎跟踪整个内核网络协议栈(有skb作为参数的函数)。字节跳动 STE 团队使用 tcpdump 语法作为过滤条件,以 skb(socket buffer)为上下文,可以轻松掌握整个报文在内核网络协议栈的完整踪迹,从而帮助开发者大大提高内核网络丢包问题的定位效率。&lt;/p&gt;
        &lt;h1&gt;使用举例&lt;/h1&gt;
        &lt;p&gt;使用方式举例:&lt;/p&gt;
        &lt;p&gt;例1:查看 ip 10.227.0.45 的 icmp 包是否到达内核预期的函数调用点, 这样做的好处是:在定位排查网络问题的时候,可以方便的缩小怀疑范围,提高效率。&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;netcap skb -f icmp_rcv@1 -i eth0 -e &quot;host 10.227.0.45&quot; -t &quot;-nnv&quot;
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;其中 -f 后面的参数是 kprobe 或者 tracepoint 的具体函数(默认是kprobe),并且需要告诉 netcap,skb 在这个函数(本例是 icmp_rcv )的第几个参数(从1开始),本例是第1个。&lt;/p&gt;
        &lt;p&gt;-i 后面是指skb的dev参数对应的网卡,这里要谨慎使用,因为有些函数的 skb 是没有设置 dev 的。&lt;/p&gt;
        &lt;p&gt;-e 的参数是 tcpdump 的过滤语法。&lt;/p&gt;
        &lt;p&gt;-t 的参数是 tcpdump 的显示方式,netcap 并没有自己显示数据包内容,而是借用了 tcpdump 的显示方式。&lt;/p&gt;
        &lt;p&gt;例2:查看内核对于 tcp 端口 9000 的报文的丢包位置:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;netcap skb -f tracepoint:skb:kfree_skb -e &quot;tcp port 9000&quot; -S
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;其中 -f 后面的参数是 kprobe 或者 tracepoint 的具体函数,tracepoint 不需要传递 skb 是第几个参数。&lt;/p&gt;
        &lt;p&gt;-S 表示连带着打印出此调用的 stack,本例中通过 stack 可以看到是哪里丢包的。&lt;/p&gt;
        &lt;p&gt;举个例子,在机器上配置一个丢包的 iptables 规则把来访的 tcp 9000 的包丢掉,如下图所示:&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;iptables -A INPUT -p tcp --dport 9000 -j DROP
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;然后使用 netcap上面的命令观察丢包情况: &lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a77077a6f9b2cc00ea7c5307fc4b02e2fc7.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;其它命令行参数可以通过阅读开源代码的 &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fbytedance%2Fnetcap&quot; target=&quot;_blank&quot;&gt;README&lt;/a&gt; 或命令 &lt;code&gt;netcap help skb&lt;/code&gt; 来详细了解。&lt;/p&gt;
        &lt;ol start=&quot;3&quot;&gt;
        &lt;li&gt; &lt;h1&gt;设计与实现&lt;/h1&gt; &lt;/li&gt;
        &lt;/ol&gt;
        &lt;h2&gt;3.1 主体框架&lt;/h2&gt;
        &lt;p&gt;netcap 通过 kprobe / tracepoint 方式实现函数的 hook,通过函数参数获取 skb 和 sock 关键结构体,拿到网络包的数据,通过 bpf map 和用户态进行数据传递。 &lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-4844dc0042ab0494bf826805977fb7bbee5.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h2&gt;3.2 实现原理&lt;/h2&gt;
        &lt;p&gt;netcap 的 工作原理大体如下:在 eBPF 程序中完成数据包的过滤,找出 tcpdump 语法过滤的包,然后把这个包给到 netcap 应用程序,netcap 应用程序再把这个包发去给 tcpdump 显示,或者直接输出 pcap 文件。如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-4c19c2e4e3a9f3b6b9356db687173d92d73.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h3&gt;3.2.1 如何按 tcpdump 语法过滤&lt;/h3&gt;
        &lt;p&gt;tcpdump 的过滤语法是基于 cBPF的,使用开源库:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fcloudflare%2Fcbpfc&quot; target=&quot;_blank&quot;&gt;https://github.com/cloudflare/cbpfc&lt;/a&gt; 这里可以把 tcpdump 的过滤语法转化成一个 C 函数,这个 C 函数可以嵌入到 netcap 的 eBPF 的程序中。转成 C 函数的基本原理如下:先利用 libpcap 库把 tcpdump 过滤语法转成 cBPF 指令码,然后基于此指令码转化成 C 语言的函数。如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-4bd1fd4c8478e2ebcbbd57c5bb72e5b96a8.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h3&gt;3.2.2 如何把数据包内容用 tcpdump 显示出来&lt;/h3&gt;
        &lt;p&gt;netcap 程序启动后,也会启动一个 tcpdump 的程序,tcpdump 的标准输入接收 pcap 格式的输入流,然后以不同的参数(例如 -e 是显示 mac 地址)从其标准输出打印出解析后的格式。如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-42ea60c0b8a18bed59d923d13f95db480c4.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h3&gt;3.2.3 如何找到数据包的内容&lt;/h3&gt;
        &lt;p&gt;在内核中,是用 skb 来描述数据包的,找到 skb 中所指定的不同 header 的位置,就可以找到整个数据包,skb 的结构大体如下所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a26e8c31e9a13cff85f6a9cbce913157710.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h3&gt;3.2.4 发送方向数据包不完整,如何过滤数据包&lt;/h3&gt;
        &lt;p&gt;在发送数据包的时候,例如 __ip_finish_output 函数,有时未填充完整的 eth头、ip 头、tcp 头,那么是怎么得到完整的包呢?&lt;/p&gt;
        &lt;p&gt;netcap 会尽力根据 skb 的 sock 结构来推导,还原数据包,此时抓出来的包有些非关键信息会与实际情况不一致(比如 ip 头的 id 字段)。skb 通过sock来推导数据包内容的逻辑大体如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-1437be41caa807a9c1bf0531365bedd6b72.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h1&gt;4.其他用法及扩展&lt;/h1&gt;
        &lt;h2&gt;4.1 多Trace点汇总分析&lt;/h2&gt;
        &lt;p&gt;netcap 可以统计数据包经过多个点的时间,然后汇总输出,从而分析性能,举个例子,使用下面的命令:&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;netcap skb -f tracepoint:net:netif_receive_skb,ip_local_deliver@1,ip_local_deliver_finish@3,icmp_rcv@1 -e &quot;host 10.227.0.72 and icmp&quot; -i eth0 --gather --gather-output-color cyan
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;可以观察到输出如下,根据到达 trace 点的时间,就能够分析出数据包性能损耗在哪里,或者在哪里可能引入了延迟。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-54fbaba29a3f94b7324e4819fb6c5de87ae.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h2&gt;4.2 扩展功能&lt;/h2&gt;
        &lt;p&gt;用户可以自定义自己的过滤函数和输出函数,这里举例如下,&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;netcap skb -f icmp_rcv@1 -e &quot;host 10.227.0.72&quot; -i eth0 --user-filter skb_user_filter.c --user-action skb_user_action.c --user-output-color green
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;其中扩展过滤文件 skb_user_filter.c 如下:&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;// Return 0 means it&#39;s not need, pls filter out it.
        static inline int xcap_user_filter(void *ctx, void *pkt, u16 trace_index)
        {
        return 1;
        }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;这个扩展函数的返回值如果是 0,表示在 tcpdump 语法的过滤后,再进行一次用户自定义过滤,比如可以方便的写几行脚本,然后按照 skb-&amp;gt;mark 来过滤。&lt;/p&gt;
        &lt;p&gt;其中扩展输出文件 skb_user_action.c 如下:&lt;/p&gt;
        &lt;pre&gt;&lt;code class=&quot;language-Bash&quot;&gt;struct xcap_user_extend {
        int a; // format: 0x%x
        uint32_t b; //
        int64_t c;
        uint8_t x1; // format: %c
        uint8_t x2; // format: 0x%x
        uint16_t x3; // format: 0x%x
        };
        // Return 0 means not need to ouput
        static inline int xcap_user_action(void *ctx, void *pkt, u32 pkt_len, struct xcap_user_extend *user, u16 trace_index)
        {
        user-&amp;gt;a = 0x12345678;
        user-&amp;gt;b = 1000;
        user-&amp;gt;c = 2002;
        user-&amp;gt;x1 = &#39;M&#39;;
        user-&amp;gt;x2 = 0x11;
        user-&amp;gt;x3 = 0xabcd;
        return 1;
        }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;其中 struct xcap_user_extend 是用户自定义的结构体,想输出什么信息,就在这个结构体定义并赋值即可。结构体可支持的类型如下 (注:不支持指针,也不支持数组):int8_t, uint8_t, char, int16_t, uint16_t, int, uint32_t,int64_t, uint64_t 。&lt;/p&gt;
        &lt;p&gt;这样就可以附带一些信息输出了,如下图:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a3521e3550363e78ebd2e64a3618df6b622.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;h1&gt;未来展望&lt;/h1&gt;
        &lt;p&gt;在开发者的日常工作中,网络抓包工具成为了网络工程师、测试工程师等必备的技能之一,字节跳动 STE 团队开源的 netcap 网络抓包工具,期望能够帮助大家提高定位内核网络丢包问题的效率,非常欢迎开发者们一起加入并贡献 PR,共同推进开源项目发展。未来我们也将在以下几个方向进行优化,敬请关注。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;对 DPDK 的进一步支持,由于 usdt 的上游库存在问题,故无法支持应用程序的 usdt,有兴趣的读者可以修改支持。&lt;/li&gt;
        &lt;li&gt;对多内核版本的统一支持。&lt;/li&gt;
        &lt;li&gt;在自定义输出的时候,数据包较多的情况下,会出现打印错乱,原因是 tcpdump 的输出信息和用户自定义的输出信息共同使用了标准输出,未来也将针对该问题做后续优化。&lt;/li&gt;
        &lt;/ul&gt;
        &lt;/div&gt;
      </description>
      <link>https://my.oschina.net/u/6150560/blog/15154212</link>
      <guid isPermaLink="false">https://my.oschina.net/u/6150560/blog/15154212</guid>
      <pubDate>Mon, 05 Aug 2024 06:23:00 GMT</pubDate>
      <author>字节跳动SYS Tech</author>
    </item>
    <item>
      <title>字节跳动新型网络装机方案HTTPBOOT(Linux),轻松解决运维困扰</title>
      <description>&lt;div class=&quot;content&quot;&gt;
        &lt;div class=&quot;ad-wrap&quot; style=&quot;margin-bottom: 8px;&quot;&gt;
        &lt;div data-traceid=&quot;news_comment_top_ad&quot; data-tracepid=&quot;news_comment_top&quot; style=&quot;text-align: center;&quot;&gt;
        &lt;a style=&quot;color:#A00;font-weight:bold;&quot; href=&quot;https://www.oschina.net/live/&quot; target=&quot;_blank&quot;&gt;
        本周六,「源创会年终盛典」珠海站再次回归!错过等一年&lt;img src=&quot;https://www.oschina.net/img/hot3.png&quot; align=&quot;absmiddle&quot; style=&quot;max-height: 32px;max-width: 32px;margin-top: -4px;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;
        &lt;/div&gt;
        &lt;/div&gt;
        &lt;blockquote&gt;
        &lt;p&gt;文章转载至【字节跳动SYS Tech】公众号。原文链接点击:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg3Mjg2NjU4NA%3D%3D%26mid%3D2247484773%26idx%3D1%26sn%3D173de6fddd2216fb7376a6edd2a75050&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;字节跳动新型网络装机方案HTTPBOOT(Linux),轻松解决运维困扰&lt;/a&gt;&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;p&gt;HTTPBOOT 是由字节跳动 STE 团队和 ICS 团队合作开发的新型网络装机方案,用于解决日常字节服务器装机运维中出现的一系列问题,目前已在字节机房大批量上线使用,帮助节省了大量的运维人力。&lt;/p&gt;
        &lt;p&gt;本文主要分为三个部分:HTTPBOOT Overview,HTTPBOOT 实践和 HTTPBOOT 成果与规划。通过阅读此文,能够对 HTTPBOOT 的需求背景,原理优势,发展现状以及未来方向有更清晰的了解。&lt;/p&gt;
        &lt;span id=&quot;OSC_h1_1&quot;&gt;&lt;/span&gt;
        &lt;h1&gt;HTTPBOOT Overview&lt;/h1&gt;
        &lt;p&gt;在本节中,将介绍目前装机上存在的问题与需求,为什么选择 HTTPBOOT,以及 HTTPBOOT 和其他装机方案相比有哪些优势。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_2&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;一、目前存在的装机问题和需求&lt;/h2&gt;
        &lt;p&gt;在使用 HTTPBOOT 装机之前,装机大多采用 UEFI+PXE 方式,其 IPV4 和 IPV6 装机方式流程图如下:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-edb3e2091a78c696da0d537ac301ff0b728.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;该方式存在两个不足:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt;&lt;strong&gt;对 MAC 地址存在强依赖:&lt;/strong&gt; 在 PXE 方式下,如果运维团队想要识别一台服务器的身份,只能通过 MAC 地址和 DUID 实现,但是使用 MAC 地址作为身份识别 ID 来对服务器装机进行管控不仅不可靠,还会带来一些问题。例如:如果手动更改 MAC 地址,或者更换网卡后,会使机器无法被正确的识别,导致装机失败。运维一般会有专门的人力去维护 MAC 地址和机器的对应关系,但人工有时也会出现错漏,还会占用更多人力去 处理问题,这是一个痛点。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;基于 TFTP / UDP 的传输不稳定:&lt;/strong&gt; PXE 使用基于 UDP 的 TFTP 协议进行装机程序传输,由于装机网络的环境复杂,网络波动较大。在程序下载的过程中经常由于网络质量差导致传输失败,十分影响整体的机器交付时效。&lt;/li&gt;
        &lt;/ol&gt;
        &lt;p&gt;同时,UEFI 的生态模式还带来了另一个问题:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt;&lt;strong&gt;部分装机问题依赖厂商发版解决:&lt;/strong&gt; 如果发现问题出现在 UEFI 固件上,比如网卡驱动有问题,或者需要做驱动适配,往往需要等厂商去解决,但是受厂商解决问题发布版本的时效影响,这些问题往往不能很快得到响应和及时解决。&lt;/li&gt;
        &lt;/ol&gt;
        &lt;p&gt;除此之外,ICS 团队也在积极改善和优化服务器装机交付流程,以提高效率,这些改造也带来了新的需求:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt;&lt;strong&gt;支持服务器自由上架:&lt;/strong&gt; 以往我们在采购服务器时,会提前为每台机器在机房预留固定的机位和IP,并在服务器到货后需要把机器安装到固定的位置上。不仅流程繁琐且需要人力去维护与核对机位状态。于是我们设计了自由上架规则,无需预先对机位进行预留,机器在上架后线上进行动态绑定,启用该规则需要固件侧进行适配。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;定制化引导 API 接口兼容:&lt;/strong&gt; 为了加强对服务器装机行为的管控,以及灵活适配不同的机型,支持多种装机方案,我们开发了一个引导 API,支持根据机型和状态下发不同的指令或装机配置,但该API 需要固件支持。&lt;/li&gt;
        &lt;/ol&gt;
        &lt;p&gt;而现有的 UEFI+PXE 装机方案不能解决上述问题和需求,我们迫切需要一款&lt;strong&gt;高稳定性、高兼容度、高可靠性、高灵活度&lt;/strong&gt;的装机引导程序。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_3&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;二、为什么选择 HTTPBOOT&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_4&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;1. HTTPBOOT 合作背景&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_5&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;(1)HTTPBOOT 由来&lt;/h4&gt;
        &lt;p&gt;HTTPBOOT 最初为 STE 自研固件 CloudFirmware 中的一个规划功能。因为 PXE 装机问题一直存在。在自研服务器交付或者重装到时候,常因为装机问题影响交付,所以在开发自研固件时,将装机问题作为一个重点关注的问题去解决,进行了一些先验合作。后来 CloudFirmware 上线后,装机问题得到了有效解决;ICS 团队同学也在寻找更好的装机解决方案。于是在这些基础上开展了后续的合作。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-9ca0086e31e34831023429adff519329086.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_6&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;(2)HTTPBOOT 是什么?&lt;/h4&gt;
        &lt;p&gt;上文提到 HTTPBOOT 来源于 CloudFirmware,那么 CloudFirmware 又是什么?HTTPBOOT 和它有哪些联系?这里简单介绍一下。&lt;/p&gt;
        &lt;p&gt;CloudFirmware 是字节跳动STE团队自定义的适用于云厂商的固件解决方案,目前已经被 OCP,OSFF 等社区接受并推广。它将整个固件分为三层:Silicon 代码,coreboot 和 LinuxBoot。其中 Silicon 代码是 Vendor 提供的,用于 Silicon 初始化的代码。coreboot 则是平台相关的,它会调用 Silicon 代码做初始化工作,同时也对外提供一些抽象硬件接口如 ACPI,SMBIOS。coreboot 追求轻量化,希望程序越小越好,有些类似嵌入式使用的 Uboot,这点和 UEFI 是不一样的。LinuxBoot 则是一个小型的 Linux 系统,由 Kernel 和 U-ROOT 文件系统组成。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-2a9f9991bfb4e6cdf4386d990345d8b90f4.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;对于互联网云厂商而言,整个解决方案中,主要的交互方集中在 LinuxBoot 这一层,这样架构也具有更好的灵活性、定制性、可控性,也更适合。为什么这么说?Silicon 代码层由供应商提供,无需操心,coreboot 结构简单,大多是一些流程和框架,操作空间有限。如果需要解决固件问题或者对自研机器提供更好的底层支持,大多在 LinuxBoot 中进行开发工作。而这也是我们的优势所在,因为我们的 Linux 工程师数量远远多于 UEFI 工程师,可以又快又好的在 LinuxBoot 上解决固件问题。&lt;/p&gt;
        &lt;p&gt;而 HTTPBOOT,其实就是 CloudFirmware 将 LinuxBoot 抽离出来,只保留装机相关功能形成的工具。它不但继承了 CloudFirmware 的优点,还是一个跨平台的工具,得益于 Linux 的开源生态和 GO 语言的 U-ROOT,这使得 HTTPBOOT 在移植到不同架构和型号的机器时十分容易。&lt;/p&gt;
        &lt;p&gt;回归到装机本身来说,HTTPBOOT 的这种做法和其他装机方案以及开源装机工具相比,有哪些优势?&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_7&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;2.HTTPBOOT 优势&lt;/h3&gt;
        &lt;p&gt;这里我们从技术方案和开源产品两个层面来进行对比,首先目前主流的几种装机技术方案如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-ccace028aad81bda6b264517b6f1a8044f7.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;该图上下部分,分别是选择 UEFI 和 Linux 作为载体的方案。两者相比,从产品角度来看,使用 Linux 可以直接基于开源技术产品化,而采用 UEFI 则需要通过 IBV,很难进行产品化。从技术角度来看,Linux 拥有更强大的网络驱动和更健壮的代码,更重要的是 Linux 的开源生态可以让我们不必依赖其他厂商,直接入手解决问题。从左右对比,分别是使用 PXE 和使用 HTTP 的方法,这个更不必说,PXE 从诞生至今近20年了,它使用的 TFTP 协议相比当下主流的HTTP协议,并不能提供类似断点续传,安全加密,灵活拓展等特性。&lt;/p&gt;
        &lt;p&gt;所以综上所述,HTTPBOOT 采用的基于 Linux 平台的 HTTP 启动方案是当下最好的技术方案。同时我们也调研了其他的开源装机产品,和其他开源装机工具对比:&lt;/p&gt;
        &lt;table&gt;
        &lt;tbody&gt;
        &lt;tr&gt;
        &lt;th&gt;装机工具&lt;/th&gt;
        &lt;th&gt;支持HTTP&lt;/th&gt;
        &lt;th&gt;代码维护&lt;/th&gt;
        &lt;th&gt;健壮度&lt;/th&gt;
        &lt;/tr&gt;
        &lt;/tbody&gt;
        &lt;tbody&gt;
        &lt;tr&gt;
        &lt;td&gt;syslinux&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;依赖厂商&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
        &lt;td&gt;bootnet64&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;依赖厂商&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
        &lt;td&gt;ipxe&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;依赖厂商&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
        &lt;td&gt;HTTPBOOT&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;开源社区,无依赖&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;/tbody&gt;
        &lt;/table&gt;
        &lt;p&gt;其中 ICS 同学对 ipxe 工具做过灰度测试,发现存在问题: 1. 开源版本对 IPV6 网络的支持不够好 2. 部分机型上存在适配问题。而和原有 PXE 方案对比,HTTPBOOT 在稳定性,可靠性,灵活性和兼容性上都更加出色。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-618c21abf90373e6e5fb9fcbf1ce68c7788.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;毫无疑问的,HTTPBOOT 成为了我们选择的最终装机方案。&lt;/p&gt;
        &lt;span id=&quot;OSC_h1_8&quot;&gt;&lt;/span&gt;
        &lt;h1&gt;HTTPBOOT 实践&lt;/h1&gt;
        &lt;p&gt;在本小节中将介绍 HTTPBOOT 的发展历程及工作原理,并结合较为实际的例子说明 HTTPBOOT 是如何解决第一节提出的装机问题和需求。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_9&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;一、HTTPBOOT Journey in Bytedance&lt;/h2&gt;
        &lt;p&gt;HTTPBOOT的实践发展历程分为四个阶段。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;2021年:HTTPBOOT 作为 CloudFirmware 的 Feature,随 CloudFirmware 一同上线。在装机方面取得突出表现,和ICS进行了一些合作测试。&lt;/li&gt;
        &lt;li&gt;2022年:HTTPBOOT 除了在 CloudFirmware 机器上使用之外,在小部分机房进行使用。多数情况下作为UEFI 装机失败后的修复工具。&lt;/li&gt;
        &lt;li&gt;2023年:ICS 对装机网络进行改造,HTTPBOOT 作为主推装机方案在内场机房大批量上线使用,给运维节省了大量的人力。&lt;/li&gt;
        &lt;li&gt;2024年:一些非服务器形态的板卡项目开始使用 HTTPBOOT 作为他们的装机解决方案。&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;经过四年的实践运行,HTTPBOOT 积累了大量的经验和好评,成为字节内场主推的装机解决方案。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_10&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;二、HTTPBOOT 工作原理&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_11&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;1. HTTPBOOT 装机流程&lt;/h3&gt;
        &lt;p&gt;引入 HTTPBOOT 后,新装机方案流程如下:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-c8010723a22c2040965f2f9a4c7ce2698d4.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;其中,对于UDP装机不稳定的问题,在搭载 CloudFirmware 的机器上得到了完全解决。对于搭载 UEFI 的机器,仍然需要通过 TFTP/UDP 下载一个 HTTPBOOT 工具到本地,问题依旧存在,但是相比于之前同 PXE 下载一个接近40M的最小镜像,下载一个5.8M的 HTTPBOOT 大大减少了下载失败的概率。&lt;/p&gt;
        &lt;p&gt;HTTPBOOT在本地运行流程如下:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-3ebc45c4aff05b9fd3b7e7eb79e3d3863fe.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_12&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;2. HTTPBOOT 关键步骤&lt;/h3&gt;
        &lt;p&gt;下面将通过一些关键步骤展示 HTTPBOOT 是如何适配和解决问题的:&lt;/p&gt;
        &lt;p&gt;对自定义 API 适配分为两部分:接入和执行。接入部分的适配如下:DHCP 服务端通过识别 DHCP 请求中的Option 61 判断客户端是否为HTTPBOOT,如果是,则将 API 的 URL 回发给客户端。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-c85525110a60f78e98075848cc050e2cf26.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;图1 接入自定义API&lt;/p&gt;
        &lt;p&gt;HTTPBOOT 收到 DHCP 返回的 API 后,会附上本机的产品序列号 SN,通过 API 上传到远端服务器,服务器根据 SN 号进行一个 IP 地址的动态的绑定。如此一来,解除了对 MAC 地址强依赖,也无需预先对服务器进行 IP 和机位的分配,实现了服务器的自由上架,节省了大量的人力。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-044c40e753077bc3de928897b0e67f5f568.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;图2 解决对MAC地址依赖,支持自由上架&lt;/p&gt;
        &lt;p&gt;同时后台接收到上传的机器 SN 号信息后,根据后台记录的机器状态下发不同的指令 ,若机器为非装机设备,则会返回如下命令,机器从本地磁盘启动。&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;#!ipxe
        sanboot --no-describe --drive 0x80
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;而在正常状态下,会返回装机配置文件的 URL ,里面包含需要安装的 OS 文件地址,HTTPBOOT 会下载相应的 OS 文件并启动。&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;#!ipxe
        http://[XXXX:XXXX::X:X:X:XX]:88/httpboot.cfg/?IP=fdbddc01002b0f1c0000000000000028
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;HTTPBOOT 可以支持多种指令来对不同的机型和情况进行灵活处理,至此完成对自定义 API 执行部分的适配。&lt;/p&gt;
        &lt;span id=&quot;OSC_h1_13&quot;&gt;&lt;/span&gt;
        &lt;h1&gt;HTTPBOOT成果和规划&lt;/h1&gt;
        &lt;p&gt;上一小节讲述了 HTTPBOOT 的工作原理及如何解决问题,本节讲述 HTTPBOOT 成果规划。&lt;/p&gt;
        &lt;p&gt;目前 HTTPBOOT 已经在系统研发阶段闭环了相关使能和测试流程。并在字节内场被广泛应用,完成了十万次数量级的装机交付,其直观收益体现在以下几个方面:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-2129cf48f16b2e5fe892228a615ca3fb80f.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;在未来 HTTPBOOT 将在字节内部进行进一步的推广和应用,特别感谢 OPC 、OSFF 、LinuxBoot 、U-ROOT 等开源社区及字节跳动相关部门同学在项目过程中提供的帮助及付出的努力,未来将继续和大家并肩携手,共同成长,收获共赢。最后欢迎对 LinuxBoot 开源项目感兴趣的小伙伴一起交流 :&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Flinuxboot%2Flinuxboot&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;https://github.com/linuxboot/linuxboot&lt;/a&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h1_14&quot;&gt;&lt;/span&gt;
        &lt;h1&gt;热门招聘&lt;/h1&gt;
        &lt;p&gt;金三银四的季节,字节跳动STE团队诚邀您的加入!团队长期招聘,&lt;strong&gt;北京、上海、深圳、杭州、US、UK&lt;/strong&gt;&amp;nbsp;均设岗位,点击 &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fjobs.bytedance.com%2Fexperienced%2Fposition%3Fcategory%3D%26current%3D5%26functionCategory%3D%26job_hot_flag%3D%26keywords%3D%25E7%25B3%25BB%25E7%25BB%259F-STE%26limit%3D10%26location%3D%26project%3D%26type%3D&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;招聘详情&lt;/a&gt; 即可查看近期的最新招聘职位信息,有意向者可直接投递简历或咨询小助手微信:sys_tech,期待与你早日相遇,在字节共赴星辰大海!岗位多多,快来砸简历吧!&lt;/p&gt;
        &lt;/div&gt;
      </description>
      <link>https://my.oschina.net/u/6150560/blog/11047367</link>
      <guid isPermaLink="false">https://my.oschina.net/u/6150560/blog/11047367</guid>
      <pubDate>Thu, 14 Mar 2024 07:48:00 GMT</pubDate>
      <author>字节跳动SYS Tech</author>
    </item>
    <item>
      <title>ByteFUSE分布式文件系统的演进与落地</title>
      <description>&lt;div class=&quot;content&quot;&gt;
        &lt;div class=&quot;ad-wrap&quot; style=&quot;margin-bottom: 8px;&quot;&gt;
        &lt;div data-traceid=&quot;news_comment_top_ad&quot; data-tracepid=&quot;news_comment_top&quot; style=&quot;text-align: center;&quot;&gt;
        &lt;a style=&quot;color:#A00;font-weight:bold;&quot; href=&quot;https://www.oschina.net/live/&quot; target=&quot;_blank&quot;&gt;
        本周六,「源创会年终盛典」珠海站再次回归!错过等一年&lt;img src=&quot;https://www.oschina.net/img/hot3.png&quot; align=&quot;absmiddle&quot; style=&quot;max-height: 32px;max-width: 32px;margin-top: -4px;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;
        &lt;/div&gt;
        &lt;/div&gt;
        &lt;blockquote&gt;
        &lt;p&gt;原文链接:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg3Mjg2NjU4NA%3D%3D%26mid%3D2247484577%26idx%3D1%26sn%3Dacf23f871cb1805b8fbdecfd2ddb7ab9&quot; target=&quot;_blank&quot;&gt;ByteFUSE分布式文件系统的演进与落地&lt;/a&gt;&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;p&gt;导语:ByteFUSE是字节ByteNAS团队和STE团队合作研发的一个项目,因其具有高可靠性、极致的性能、兼容Posix语义以及支持丰富的使用场景等优点而被业务广泛使用。目前承接了在线业务ES,AI训练业务,系统盘业务,数据库备份业务,消息队列业务,符号表业务以及编译业务等,字节内部部署机器和日常挂载点均已达到万级规模,总吞吐近百GB/s,容量十几PB,其性能与稳定性能够满足业务需求。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_1&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;背景&lt;/h2&gt;
        &lt;p&gt;ByteNAS是一款全自研、高性能、高扩展,多写多读、低时延并且完全兼容Posix语义的分布式文件系统,目前支撑了字节内部AI训练,数据库备份,在线ES等多个关键业务,也是未来云上NAS主打的产品形态。早期ByteNAS对外提供服务使用的是NFS协议,其依赖TTGW四层负载均衡器将外部流量以TCP连接的粒度均衡到连接的多台Proxy,用户使用TTGW提供的VIP并进行挂载即可与多台Proxy中一台进行通信。如果当前通信的Proxy因为机器宕机等原因挂掉后,TTGW内部探测心跳超时会触发Failover机制,自动将来自该Client的请求Redirect到新的活着的Proxy,该机制对客户端是完全透明的。但是使用TTGW具有以下缺点:&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;无法支持大吞吐场景:用户的吞吐不仅受限于TTGW集群本身吞吐的限制,而且受限于NFS协议单次读写1MB的限制。另外NFS是单TCP连接,同时内核slot并发请求也有限制,这会导致吞吐受限以及元数据和数据相互影响&lt;/li&gt;
        &lt;li&gt;额外的网络延迟:用户访问ByteNAS多两跳网络(用户侧NFS Client -&amp;gt; TTGW -&amp;gt; Proxy -&amp;gt; ByteNAS)&lt;/li&gt;
        &lt;li&gt;额外的机器成本:需要TTGW以及Proxy等机器资源&lt;/li&gt;
        &lt;li&gt;定制化业务需求以及性能优化比较困难:受限于内核NFS Client,NFS协议以及TTGW的影响,其定制化需求以及性能优化比较困难&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;为了解决以上问题,ByteFUSE应运而生。ByteFUSE是一套基于用户态文件系统(FUSE)框架接入ByteNAS的解决方案,通过ByteNAS SDK直连ByteNAS集群,不仅满足了低延迟的目标,同时也解决了协议吞吐受限的问题。除此之外,由于部分文件系统逻辑上移到了用户态,对于问题排查,功能扩展以及性能优化都会变得非常方便。用户使用ByteFUSE和NFS两种协议访问ByteNAS的流程如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-8a4d6ec8bcbbcf24c56d709782620b9599f.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_2&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;目标&lt;/h2&gt;
        &lt;ul&gt;
        &lt;li&gt;高性能、低延迟,对业务友好的架构模型设计&lt;/li&gt;
        &lt;li&gt;完全兼容Posix语义&lt;/li&gt;
        &lt;li&gt;支持一写多读/多写多读&lt;/li&gt;
        &lt;li&gt;自研以及可维护性强,提供定制化特性能力支持&lt;/li&gt;
        &lt;/ul&gt;
        &lt;span id=&quot;OSC_h2_3&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;演进路线&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_4&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;1. ByteFUSE 1.0 — 基础功能完备,云原生化部署支持&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_5&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;通过原生FUSE接入ByteNAS&lt;/h4&gt;
        &lt;p&gt;原生FUSE对接ByteNAS的整体架构图如下所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-e67e94f6abe33f340da622997a5327f6df9.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;ByteFUSE Daemon:&amp;nbsp;集成了ByteNAS SDK的FUSE Daemon,用户的文件系统请求会通过FUSE协议转发给ByteFUSE Daemon,然后,通过ByteNAS SDK被转发到后端存储集群。&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_6&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;云原生化部署支持&lt;/h4&gt;
        &lt;p&gt;ByteFUSE基于K8S CSI接口规范 [1] 开发了CSI插件,以支持在K8S集群中使用ByteFUSE访问ByteNAS集群,其架构如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-27c09c691468983ea14670ff19cff8b126d.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;CSI-Driver:ByteFUSE的云原生架构目前只支持静态卷,Mount/Umount操作会在CSI-Dirver中启动/销毁FUSE Client,CSI-Driver会记录每个挂载点的状态,当CSI-Drvier异常退出重启时会recover所有挂载点来保证高可用性。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;FUSE Client:即上面提到的ByteFUSE Daemon,在1.0架构下,针对每个挂载点,CSI-Driver都会启动一个FUSE Client来提供服务。&lt;/p&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;span id=&quot;OSC_h3_7&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;2. ByteFUSE 2.0 — 云原生架构升级,一致性、可用性和可运维性提升&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_8&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;业务需求和挑战&lt;/h4&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;FUSE Client资源占用不可控以及无法复用&lt;/strong&gt;:多FUSE Client模式下,一个挂载点对应一个FUSE Client进程,FUSE Client的资源占用与挂载点个数强相关,这导致FUSE Client资源占用不可控。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;FUSE Client与CSI-Driver强耦合导致CSI-Driver无法平滑升级&lt;/strong&gt;:FUSE Client进程的生命周期与CSI-Driver关联,当需要升级CSI时,FUSE Client也需要跟随重建,导致业务I/O也会受影响,同时,这个影响时长与CSI-Driver的升级时长(秒级)强相关。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;部分业务希望在Kata容器场景接入ByteFUSE&lt;/strong&gt;:云原生场景下,有部分业务会以Kata容器的方式来运行,为了满足这部分业务接入ByteFUSE的需求,CSI-Driver需要支持kata这种容器运行时,即在kata虚机内能够通过ByteFUSE访问ByteNAS服务。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;原生FUSE一致性模型无法满足某些业务需求&lt;/strong&gt;:某些业务是典型的一写多读场景,对读写吞吐,数据可见性以及尾延迟的要求极高,但原生FUSE在开启内核缓存的情况下,无法提供像CTO (Close-to-Open) 这样的一致性模型。&lt;/li&gt;
        &lt;li&gt;&lt;strong&gt;原生FUSE可用性/可运维性能力较弱,无法适用于大规模生产环境&lt;/strong&gt;:原生FUSE对高可用、热升级等能力的支持较弱,当出现FUSE进程crash或者内核模块有bug需要升级等情况时,往往需要知会业务重启Pod、甚至重启整个物理节点,这对于大部分业务都是不可接受的。&lt;/li&gt;
        &lt;/ul&gt;
        &lt;span id=&quot;OSC_h4_9&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;云原生架构升级&lt;/h4&gt;
        &lt;p&gt;&lt;strong&gt;FUSE Client架构升级:单Daemon化&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;针对上述业务需求和挑战,我们对架构进行了升级,支持了单FUSE Daemon模式来解决资源不可控以及资源无法复用的问题,采用FUSE Daemon和CSI-Driver的分离解决CSI-Driver无法平滑升级的问题,其架构如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-821c30978fef326de02fb746dc121a4dc2b.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;AdminServer:监控Mountpoint/FUSE Daemon状态,升级FUSE Daemon以及统计集群信息等。&lt;/p&gt;
        &lt;p&gt;FUSE Daemon:管理ByteNAS集群所有的挂载点以及处理读写请求,重启后recover所有的挂载点,恢复时间为ms级别。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Kata Containers 场景支持&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;为了提供Kata场景的支持,同时,解决原生FUSE的高可用和性能可扩展性问题,我们在2.0架构中引入了VDUSE[2]这个字节自主研发的技术框架来实现ByteFUSE Daemon。VDUSE利用了virtio这套成熟的软件框架,使ByteFUSE Daemon能够同时支持从虚机或者宿主机(容器)挂载。同时,相较于传统的FUSE框架,基于VDUSE实现的FUSE Daemon不再依赖/dev/fuse这个字符设备,而是通过共享内存机制来和内核通信,这种方式一方面对后续的性能优化大有裨益,另一方面也很好地解决了Crash Recovery问题。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-927aa354ea97a1899ca26fc9f1dea35fe4c.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_10&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;一致性、可用性和可运维性提升&lt;/h4&gt;
        &lt;p&gt;&lt;strong&gt;一致性模型增强&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;性能和一致性是分布式系统设计中的一对根本性矛盾 —— 保持一致性意味着更多节点的通信,而更多节点的通信意味着性能的下降。为了满足相关业务需求,我们在FUSE原生缓存模式的基础上不断的取舍性能与一致性,实现了 FUSE CTO (Close-to-Open) 一致性模型 [4],并将这些一致性模型根据不同配置抽象成以下五种: &lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a6b41784b8b959f18fea19427941ae91c3e.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Daemon高可用&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;由于ByteFUSE 2.0架构引入了VDUSE [2]这个技术框架,因此支持使用基于共享内存的Virtio协议作为传输层,Virtio协议内置的inflight I/O追踪特性可以将 ByteFUSE 正在处理的请求实时持久化,并在 ByteFUSE 恢复时重新处理未完成请求,这弥补了原生 libfuse 中使用字符设备 /dev/fuse 作为传输层时状态保存功能的缺失。基于该inflight I/O 追踪特性,ByteFUSE 进一步考虑了文件系统状态在恢复前后的一致性和幂等性,实现了用户无感的崩溃恢复 [3],同时基于崩溃恢复实现了Daeamon的热升级。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;内核模块热升级&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;ByteFUSE 在使用定制化内核模块来获得更好的性能、可用性和一致性的同时,也对这些定制化内核模块的升级维护提出挑战。为了解决二进制内核模块无法随内核升级的问题,我们通过 DKMS 来部署定制化内核模块,让内核模块随内核升级而自动重新编译部署。为了解决内核模块自身热升级的问题,我们通过将内核模块所导出的符号名或设备号与版本号绑定的方式实现了同一内核模块的多版本共存。新的 ByteFUSE 挂载将自动使用新的内核模块;旧的 ByteFUSE 挂载延续使用旧内核模块。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-960ff15127049645b8b135632942c7dd265.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;目前,通过上述 DKMS 技术及“多版本共存”技术,我们将ByteFUSE内核模块的升级与内核及ByteFUSE Daemon进行了解耦;未来,我们将进一步实现ByteFUSE内核模块热升级功能,以支持线上运行的存量ByteFUSE卷的热升级功能。&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_11&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;3. ByteFUSE 3.0 — 极致性能优化,打造业界一流的高性能文件存储系统&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_12&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;业务需求和挑战&lt;/h4&gt;
        &lt;p&gt;&lt;strong&gt;大模型训练场景对存储系统的性能需求&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;大模型训练场景下,训练巨量模型需要巨大的算力,但随着数据集和模型规模不断增加,应用程序载入数据所花费的时间变得越长,进而影响了应用程序的性能,缓慢的 I/O 严重拖累GPU 的强大算力。于此同时,模型的评估 &amp;amp; 部署需要并行读取大量模型,要求存储能够提供超高吞吐。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;云原生高密部署的场景,需要进一步降低资源占用开销&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;云原生高密部署场景下,随着ByteFUSE卷的数量级增加,对ByteFUSE单机侧的资源(CPU &amp;amp; Memory)占用及隔离提出了新的要求。&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_13&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;极致性能优化&lt;/h4&gt;
        &lt;p&gt;ByteFUSE 3.0从线程模型,数据拷贝,内核侧以及协议栈进行了全链路的性能优化,性能提高2.5倍,2个core即可打满百Gb网卡。其优化方向如下所示:&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;Run-to-Completion线程模型&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;2.0 版本的一次Read/Write请求会有4次线程切换,接入Run-to-Completion(RTC)能够节省这四次线程切换带来的开销。为了做到Run-to-Completion,我们对ByteFUSE和ByteNAS SDK进行了shared-nothing的设计和锁的非阻塞化改造,其目的是保证RTC线程不会被阻塞,避免影响请求的延迟。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-30294a97bd02da03a0a1dd9ee87a635491b.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;RDMA &amp;amp; 用户态协议栈&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;3.0架构相较于2.0,在网络传输这块也做了较大的改进,主要体现在引入了RDMA和用户态协议栈(Tarzan)来替换传统的内核TCP/IP协议栈,相较于内核TCP/IP协议栈,RDMA/Tarzan能够节省用户态与内核态的切换和数据拷贝带来的延迟,并进一步降低CPU占用。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;全链路零拷贝&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;引入RDMA/Tarzan之后,ByteFUSE在网络传输这块的拷贝被成功消除了,但FUSE接入这块,仍然存在Page Cache到Bounce Buffer,Bounce Buffer到RDMA/Tarzan DMA Buffer两次拷贝。为了降低这部分的拷贝开销(经统计1M数据的拷贝消耗100us左右),3.0架构引入了VDUSE umem [5] 特性,通过将RDMA/Tarzan DMA Buffer注册给VDUSE内核模块,减少了其中的一次拷贝。未来,我们还会进一步实现FUSE PageCache Extension特性,来达成全链路零拷贝这个优化目标。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-aab67681860a71918b95eca82fba7620455.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;FUSE内核优化&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;(1)多队列&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;在原生FUSE/viritofs内核模块中,FUSE 请求的处理路径有很多单队列设计:如每个 FUSE 挂载只有一个 IQ (input queue)、一个 BGQ (background queue)、virtiofs 设备使用单队列模型发送 FUSE 请求等。为了减少单队列模型带来的锁竞争、提高可扩展性,我们在 FUSE/virtiofs 的请求路径中支持了 per-cpu 的 FUSE 请求队列和可配置的 virtiofs virtqueue 数量。基于 FUSE 多队列特性的支持,ByteFUSE 可以根据不同部署环境配置不同的 CPU 亲和性策略来减少核间通信或平衡核间负载。ByteFUSE 工作线程也可以打开 FUSE 多队列特性所提供的负载均衡调度来缓解核间请求不均情况下的局部请求排队现象。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-75e690e573177cae56531a666488021e2d0.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;(2)huge块支持&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;为了满足高吞吐场景的性能需求,ByteFUSE 3.0 版本支持定制化的FUSE内核模块参数。Linux 内核原生的FUSE 模块中存在一些对数据传输的硬编码,如单次最大数据传输单元为 1 MB,单次最大目录树读取单元为 4 KB。而在ByteFUSE内核模块中,我们将单次最大数据传输上调为 8 MB, 单次最大目录读取单元上调为 32 KB。在数据库备份场景下,将单次写下刷改成8MB,单机吞吐能提高约20%。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_14&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;演进收益&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_15&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;收益总览&lt;/h3&gt;
        &lt;span id=&quot;OSC_h4_16&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;1.0 -&amp;gt; 2.0&lt;/h4&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;降低资源占用,便于资源控制&lt;/strong&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;单FUSE Daemon和多FUSE Client相比,多个挂载点之间的线程、内存、连接等资源可以复用,可以有效降低资源占用。除此之外,将FUSE Daemon单独运行于Pod内能够更好地适应Kubernetes生态,保证其在 Kubernetes 的管控内,用户可以直接在集群中观察到FUSE Daemon的Pod,可观测性强。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;CSI-Driver与FUSE Daemon解耦合&lt;/strong&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;CSI-Driver与FUSE Daemon作为两个独立部署的服务,其部署、升级都可以独立进行而不影响彼此,进一步降低了运维工作对业务的影响。除此之外,我们支持POD内热升级FUSE Daemon,整个升级对业务是无感的。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;支持内核模块热升级&lt;/strong&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;可以在业务无感的情况下,支持对ByteFUSE增量卷进行热升级,修复内核模块的已知bug,降低线上风险。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;&lt;strong&gt;支持统一的监控以及管控平台,方便可视化管理&lt;/strong&gt;&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;AdminServer监控一个Region内所有FUSE Daemon &amp;amp; 挂载点的状态,支持远程恢复异常挂载点,支持Pod内热升级FUSE Daemon,支持远程挂载点异常探测以及报警等。&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_17&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;2.0 -&amp;gt; 3.0&lt;/h4&gt;
        &lt;p&gt;整个架构实现了Run-to-Complete的线程模型,减少了锁以及上下文切换带来的性能损耗。除此之外,我们将内核态TCP换成用户态TCP,bypass内核以及采用内存注册到内核的方式实现全链路zero-copy进一步的提升性能。对于1MB的写请求,FUSE Daemon侧可以节省百us。&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_18&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;性能对比&lt;/h3&gt;
        &lt;p&gt;FUSE Daemon 机器规格:&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;CPU: 32物理核,64逻辑核&lt;/li&gt;
        &lt;li&gt;内存:251.27GB&lt;/li&gt;
        &lt;li&gt;NIC: 100Gbps&lt;/li&gt;
        &lt;/ul&gt;
        &lt;span id=&quot;OSC_h4_19&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;元数据性能对比&lt;/h4&gt;
        &lt;p&gt;使用mdtest进行性能测试,测试命令&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;mdtest &#39;-d&#39; &#39;/mnt/mdtest/&#39; &#39;-b&#39; &#39;6&#39; &#39;-I&#39; &#39;8&#39; &#39;-z&#39; &#39;4&#39; &#39;-i&#39; &#39;30
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;,性能差异如下所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-5192b79e65e3c29b96c61e598cbcaf686ec.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_20&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;结论&lt;/h3&gt;
        &lt;p&gt;3.0架构相比1.0架构元数据性能大约提高了25%左右。&lt;/p&gt;
        &lt;span id=&quot;OSC_h4_21&quot;&gt;&lt;/span&gt;
        &lt;h4&gt;数据性能对比&lt;/h4&gt;
        &lt;p&gt;FIO采用4线程,其性能如下图所示:&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-a6d3f30bde68c9bf55efbbb6c474ed4f83d.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;此外,测试ByteFUSE 3.0 polling线程个数对性能的影响。对于写,2个polling线程基本打满百G网卡,而对于读,则需要4个polling线程(比写操作多一次数据拷贝)。未来,我们将改造用户态协议栈Tarzan,节省读的一次数据拷贝,做到读写zero-copy。&lt;/p&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-2a407c914dc1de674301542ff786183d58d.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_22&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;业务落地&lt;/h2&gt;
        &lt;span id=&quot;OSC_h3_23&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;ES存算分离架构场景的落地&lt;/h3&gt;
        &lt;p&gt;&lt;strong&gt;场景描述&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;ES的Shared Storage架构,让ES 的多个分片副本使用同一份数据,来解决在 Shared Nothing 架构下的扩容生效缓慢,分片迁移慢,搜索打分振荡以及存储成本高等问题。底层存储使用ByteNAS来共享主副分片的数据以及使用ByteFUSE作为接入协议来满足高性能,高吞吐以及低延迟的要求。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;收益&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;ES存算分离架构的落地,每年节省存储成本近千万&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_24&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;Ai训练场景的落地&lt;/h3&gt;
        &lt;p&gt;&lt;strong&gt;场景描述&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;AI 类 Web IDE场景下使用块存储 + NFS的方式共享根文件系统无法解决由于NFS断联进程进入D状态以及NFS断联触发内核Bug导致mount功能不可用等问题。除此之外,AI训练场景下受限负载均衡的吞吐以及NFS协议性能的影响无法满足训练任务高吞吐 &amp;amp; 低延迟的需求,而ByteNAS提供共享文件系统,超大吞吐以及低延迟满足模型训练。&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;收益&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;满足AI训练对高吞吐,低延迟的需求&lt;/p&gt;
        &lt;span id=&quot;OSC_h3_25&quot;&gt;&lt;/span&gt;
        &lt;h3&gt;其他业务场景的落地&lt;/h3&gt;
        &lt;p&gt;受限于TTGW吞吐以及稳定性的原因,数据库备份业务,消息队列业务,符号表业务以及编译业务从NFS切换到ByteFUSE。&lt;/p&gt;
        &lt;span id=&quot;OSC_h2_26&quot;&gt;&lt;/span&gt;
        &lt;h2&gt;未来展望&lt;/h2&gt;
        &lt;p&gt;ByteFUSE 3.0架构已经可以满足绝大部分业务的需求,但是,为了追求更极致的性能以及满足更多的业务场景,未来我们还有不少工作有待展开:&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;ByteFUSE推广到ToB场景;满足云上业务超低延迟,超高吞吐的需求&lt;/li&gt;
        &lt;li&gt;支持非Posix语义;定制化接口满足上层应用的需求,譬如 IO fencing语意&lt;/li&gt;
        &lt;li&gt;FUSE PageCache Extension;FUSE支持Page Cache用户态扩展,FUSE Daemon能够直接读写Page Cache&lt;/li&gt;
        &lt;li&gt;支持内核模块热升级;支持用户无感情况下,升级存量及增量ByteFUSE卷的内核模块&lt;/li&gt;
        &lt;li&gt;支持GPU Direct Storage[6];数据直接在RDMA网卡和GPU之间进行传输,bypaas主机内存和CPU&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;[1] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fkubernetes-csi.github.io%2Fdocs%2F&quot; target=&quot;_blank&quot;&gt;https://kubernetes-csi.github.io/docs/&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[2] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.redhat.com%2Fen%2Fblog%2Fintroducing-vduse-software-defined-datapath-virtio&quot; target=&quot;_blank&quot;&gt;https://www.redhat.com/en/blog/introducing-vduse-software-defined-datapath-virtio&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[3] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fjuejin.cn%2Fpost%2F7171280231238467592&quot; target=&quot;_blank&quot;&gt;https://juejin.cn/post/7171280231238467592&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[4] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Flore.kernel.org%2Flkml%2F20220624055825.29183-1-zhangjiachen.jaycee%40bytedance.com%2F&quot; target=&quot;_blank&quot;&gt;https://lore.kernel.org/lkml/[email protected]/&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[5] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Flwn.net%2FArticles%2F900178%2F&quot; target=&quot;_blank&quot;&gt;https://lwn.net/Articles/900178/&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;[6] &lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fdocs.nvidia.com%2Fgpudirect-storage%2Foverview-guide%2Findex.html&quot; target=&quot;_blank&quot;&gt;https://docs.nvidia.com/gpudirect-storage/overview-guide/index.html&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;热门招聘&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;字节跳动STE团队诚邀您的加入!团队长期招聘,&lt;strong&gt;北京、上海、深圳、杭州、US、UK&lt;/strong&gt;&amp;nbsp;均设岗位,以下为近期的招聘职位信息,有意向者可直接扫描海报二维码投递简历,期待与你早日相遇,在字节共赴星辰大海!若有问题可咨询小助手微信:sys_tech,岗位多多,快来砸简历吧!&lt;/p&gt;
        &lt;/div&gt;
      </description>
      <link>https://my.oschina.net/u/6150560/blog/10098437</link>
      <guid isPermaLink="false">https://my.oschina.net/u/6150560/blog/10098437</guid>
      <pubDate>Fri, 18 Aug 2023 03:24:00 GMT</pubDate>
      <author>字节跳动SYS Tech</author>
    </item>
    <item>
      <title>深入分析HWASAN检测内存错误原理</title>
      <description>&lt;div class=&quot;content&quot;&gt;
        &lt;div class=&quot;ad-wrap&quot; style=&quot;margin-bottom: 8px;&quot;&gt;
        &lt;div data-traceid=&quot;news_comment_top_ad&quot; data-tracepid=&quot;news_comment_top&quot; style=&quot;text-align: center;&quot;&gt;
        &lt;a style=&quot;color:#A00;font-weight:bold;&quot; href=&quot;https://www.oschina.net/live/&quot; target=&quot;_blank&quot;&gt;
        本周六,「源创会年终盛典」珠海站再次回归!错过等一年&lt;img src=&quot;https://www.oschina.net/img/hot3.png&quot; align=&quot;absmiddle&quot; style=&quot;max-height: 32px;max-width: 32px;margin-top: -4px;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/a&gt;
        &lt;/div&gt;
        &lt;/div&gt;
        &lt;blockquote&gt;
        &lt;p&gt;原文链接:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg3Mjg2NjU4NA%3D%3D%26mid%3D2247484531%26idx%3D1%26sn%3D57acfda58d6cc0dffb9fa5e5c6f488bb&quot; title=&quot;深入分析HWASAN检测内存错误原理&quot; target=&quot;_blank&quot;&gt;深入分析HWASAN检测内存错误原理&lt;/a&gt;&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;p&gt;导语:ASAN(AddressSanitizer) 是 C/C++开发者常用的内存错误检测工具,主要用于检测缓冲区溢出、访问已释放的内存等内存错误。 AArch64 上提供了 Top-Byte-Ingore 硬件特性,HWASan(HardWare-assisted AddressSanitizer) 就是利用 Top-Byte-Ignore 特性实现的增强版 ASan,与 ASAN 相比 HWASan 的内存开销更低,检测到的内存错误范围更大。因此在 AArch64 平台,建议使用 HWASAN。本篇文章将深入分析 HWASAN 检测内存错误的原理,帮助大家更好地理解和使用 HWASan 来排查程序中存在的疑难内存错误。&lt;/p&gt;
        &lt;h2&gt;前言&lt;/h2&gt;
        &lt;p&gt;在字节跳动,C++语言被广泛应用在各个业务中,由于C++语言的特性,导致 C++ 程序很容易出现内存问题。ASAN 等内存检测工具在字节跳动内部已经取得了可观的收益和效果(更多内容请查看:&lt;a href=&quot;https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.bilibili.com%2Fvideo%2FBV1YT411Q7BU%2F&quot; target=&quot;_blank&quot;&gt;Sanitizer 在字节跳动 C C++ 业务中的实践&lt;/a&gt;),服务于60个业务线,近一年协助修复上百个内存缺陷。但是仍然有很大的提升空间,特别是在性能开销方面。随着 ARM 进入服务器芯片市场,ARM架构下的一些硬件特性可以用来缓解 ASAN 工具的性能问题,利用这些硬件特性研发的 HWASAN 检测工具在超大型 C++ 服务上的检测能力还有待确认。&lt;/p&gt;
        &lt;p&gt;为此,STE 团队对 HWASAN 进行了深入分析,并在字节跳动 C++ 核心服务上进行了落地验证。在落地 HWASAN 过程中,修复了 HWASAN 实现中的一些关键 bug,并对易用性进行了提升。相关 patch 已经贡献到LLVM开源社区(详情请查看文末链接)。本篇文章将深入分析 HWASAN 检测内存错误的原理,帮助大家更好地理解和使用 HWASan 来排查程序中存在的疑难内存错误。&lt;/p&gt;
        &lt;h2&gt;概述&lt;/h2&gt;
        &lt;p&gt;&lt;strong&gt;HWASAN&lt;/strong&gt;: &lt;strong&gt;H&lt;/strong&gt;ard&lt;strong&gt;W&lt;/strong&gt;are-assisted &lt;strong&gt;A&lt;/strong&gt;ddress&lt;strong&gt;San&lt;/strong&gt;itizer, a tool similar to AddressSanitizer, but based on partial hardware assistance and consumes much less memory.&lt;/p&gt;
        &lt;p&gt;这里所谓的 &quot;partial hardware assistance&quot; 就是指 AArch64 的 &lt;strong&gt;TBI&lt;/strong&gt; (Top Byte Ignore) 特性。&lt;/p&gt;
        &lt;blockquote&gt;
        &lt;p&gt;TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address translation and can be used to store a tag.&lt;/p&gt;
        &lt;/blockquote&gt;
        &lt;p&gt;以如下代码举例,Linux/AArch64 下将指针 x 的 top byte 设置为 0xfe,不影响程序执行:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;// $ cat tbi.cpp
        int main(int argc, char **argv) {
        int * volatile x = (int *)malloc(sizeof(int));
        *x = 666;
        printf(&quot;address: %p, value: %d\n&quot;, x, *x);
        x = reinterpret_cast&amp;lt;int*&amp;gt;(reinterpret_cast&amp;lt;uintptr_t&amp;gt;(x) | (0xfeULL &amp;lt;&amp;lt; 56));
        printf(&quot;address: %p, value: %d\n&quot;, x, *x);
        free(x);
        return 0;
        }
        // $ clang++ tbi.cpp &amp;amp;&amp;amp; ./a.out
        address: 0xaaab1845fe70, value: 666
        address: 0xfe00aaab1845fe70, value: 666
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;AArch64 的 TBI 特性使得软件可以在 64-bit 虚拟地址的最高字节中存储任意数据,HWASAN 正是基于 TBI 这一特性设计并实现的内存错误检测工具。&lt;/p&gt;
        &lt;p&gt;举个例子,以下代码中存在 heap-buffer-overflow bug:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;// cat test.c
        #include &amp;lt;stdlib.h&amp;gt;
        int main() {
        int * volatile x = (int *)malloc(sizeof(int)*10);
        x[10] = 0;
        free(x);
        }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;使用 HWASAN 检测上述代码中的 heap-buffer-overflow bug:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;$ clang -fuse-ld=lld -g -fsanitize=hwaddress ./test.c &amp;amp;&amp;amp; ./a.out
        ==3581920==ERROR: HWAddressSanitizer: tag-mismatch on address 0xec2bfffe0028 at pc 0xaaad830db1a4
        WRITE of size 4 at 0xec2bfffe0028 tags: 69/08(69) (ptr/mem) in thread T0
        #0 0xaaad830db1a4 in main ./test.c:4:11
        #1 0xfffd07350da0 in __libc_start_main libc-start.c:308:16
        #2 0xaaad83090820 in _start (./a.out+0x40820)
        [0xec2bfffe0000,0xec2bfffe0030) is a small allocated heap chunk; size: 48 offset: 40
        Cause: heap-buffer-overflow
        0xec2bfffe0028 is located 0 bytes after a 40-byte region [0xec2bfffe0000,0xec2bfffe0028)
        allocated by thread T0 here:
        #0 0xaaad83099248 in __sanitizer_malloc.part.13 llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:151:3
        #1 0xaaad830db17c in main ./test.c:3:31
        #2 0xfffd07350da0 in __libc_start_main libc-start.c:308:16
        #3 0xaaad83090820 in _start (/a.out+0x40820)
        Thread: T0 0xeffc00002000 stack: [0xffffc3a10000,0xffffc4210000) sz: 8388608 tls: [0xfffd076a5030,0xfffd076a5e70)
        Memory tags around the buggy address (one tag corresponds to 16 bytes):
        0xec2bfffdf800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdf900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfa00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfb00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfc00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfd00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdfe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffdff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        =&amp;gt;0xec2bfffe0000: 69 69 [08] 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0xec2bfffe0800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        Tags for short granules around the buggy address (one tag corresponds to 16 bytes):
        0xec2bfffdff00: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
        =&amp;gt;0xec2bfffe0000: .. .. [69] .. .. .. .. .. .. .. .. .. .. .. .. ..
        0xec2bfffe0100: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
        See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags
        Registers where the failure occurred (pc 0xaaad830db1a4):
        x0 a100ffffc4201580 x1 6900ec2bfffe0028 x2 0000000000000000 x3 0000000000000000
        x4 0000000000000020 x5 0000000000000000 x6 0000000000100000 x7 fffffffffff00005
        x8 6900ec2bfffe0000 x9 6900ec2bfffe0000 x10 0030f15d14c79f97 x11 00ffffffffffffff
        x12 00001f0d780b69d2 x13 0000000000000001 x14 0000ffffc4200b60 x15 0000000000000696
        x16 0000aaad830a3540 x17 000000000000000b x18 0000000000000100 x19 0000aaad830db600
        x20 0200effd00000000 x21 0000aaad830907f0 x22 0000000000000000 x23 0000000000000000
        x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
        x28 0000000000000000 x29 0000ffffc4201590 x30 0000aaad830db1a8 sp 0000ffffc4201550
        SUMMARY: HWAddressSanitizer: tag-mismatch ./test.c:4:11 in main
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;如上所示,HWASAN 与 ASAN 相比不管是用法 (&lt;code&gt;-fsanitize=hwaddress&lt;/code&gt; v.s. &lt;code&gt;-fsanitize=address&lt;/code&gt;) 还是检测到错误后的报告都很相似。&lt;/p&gt;
        &lt;p&gt;下面对比分析 ASAN 与 HWASAN 检测内存错误的技术原理:&lt;/p&gt;
        &lt;p&gt;ASAN (AddressSanitizer):&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;使用 shadow memory 技术,每 8-bytes 的 application memory 对应 1-byte 的 shadow memory。&lt;/li&gt;
        &lt;li&gt;使用 redzone 来检测 buffer-overflow。不管是栈内存还是堆内存,在申请内存时都会在原本内存的两侧额外申请一定大小的内存作为 redzone,一旦访问到了 redzone 则说明发生了缓冲区溢出。&lt;/li&gt;
        &lt;li&gt;使用 quarantine 检测 use-after-free。应用程序执行 delete 或 free 释放内存时,并不真正释放,而是放在一个暂存区 (quarantine) 中,一旦访问了位于 quarantine 中的内存则说明访问了已释放的内存。&lt;/li&gt;
        &lt;li&gt;每 1-byte 的 shadow memory 编码表示对应的 8-byte application memory 的信息,每次访问 application memory 之前 ASAN 都会检查对应的 shadow memory 判断本次内存访问是否合法。例如:shadow memory 的值为 0xfd 表示对应的 8-bytes application memory 是 freed memory,所以当访问的 application memory 其 shadow memory byte 为 0xfd 就说明此时访问的是已经释放的内存了即 use-after-free;shadow memory 为 0xfa 表示相应的 application memory 是堆内存的 redzone,所以当访问到的 appllcation memory 其 shadow memory 为 0xfa 就说明此时访问的是堆内存附近的 redzone 发生了堆缓冲区溢出错误即 heap-buffer-overflow&lt;/li&gt;
        &lt;/ul&gt;
        &lt;p&gt;HWASAN (HardWare-assisted AddressSanitizer)&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;同样使用 shadow memory 技术,不过与 ASAN 不同的是:HWASAN 每 16-bytes 的 application memory 对应 1-byte 的 shadow memory。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;不依赖 redzone 检测 buffer-overflow,不依赖 quarantine 检测 use-after-free,仅基于 TBI 特性就能检测 buffer-overflow 和 use-after-free。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;举例说明 HWASAN 检测内存错误的原理&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;因为 HWASAN 会将每 16-bytes 的 application memory 都对应 1-byte 的 shadow tag,所以 HWASAN 会将申请的内存都对齐到 16-bytes,因此下图中 new char[20] 实际申请的内存是 32-bytes。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;HWASAN 会生成一个随机 tag 保存在 operator new 返回的指针 p 的 top byte 中,同时会将 tag 保存在 p 指向的内存对应 shadow memory 中。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;为了方便说明,下图中用不同的颜色表示不同的 tag,绿色表示 tag 0xa,蓝色表示 tag 0xb,紫色表示 tag 0xc。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;&lt;strong&gt;检测 heap-buffer-overflow&lt;/strong&gt;&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;假设 HWASAN 为 &lt;code&gt;new char[20]&lt;/code&gt; 生成的 tag 为 0xa 即绿色,所以指针 p 的 top byte 为 0xa。在通过 &lt;code&gt;p[32]&lt;/code&gt; 访问内存时,HWASAN 会检查保存在指针 p 的 tag 与 &lt;code&gt;p[32]&lt;/code&gt; 指向的内存所对应的 shadow memory 中保存的 tag 是否一致。显然保存在指针 p 的 tag 是绿色 而&lt;code&gt;p[32]&lt;/code&gt; 指向的内存所对应的 shadow memory 中保存的 tag 是蓝色,即 tag 是不匹配的,这说明访问 &lt;code&gt;p[32]&lt;/code&gt; 时存在内存错误。 &lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-6a016a9327a452214e4a1ef63998c5faab1.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/li&gt;
        &lt;/ul&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;&lt;strong&gt;检测 use-after-free&lt;/strong&gt;&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt;假设 HWASAN 为 &lt;code&gt;new char[20]&lt;/code&gt; 生成的 tag 为 0xa 即绿色,所以指针 p 的 top byte 为 0xa。执行 &lt;code&gt;delete[] p&lt;/code&gt; 释放内存时,HWASAN 将这块释放的内存 retag 为紫色,即将这块释放的内存对应的 shadow memory 从绿色修改为紫色。在通过 &lt;code&gt;p[0]&lt;/code&gt; 访问内存时,HWASAN 会检查保存在指针 p 的 tag 与 &lt;code&gt;p[0]&lt;/code&gt; 指向的内存所对应的 shadow memory 中保存的 tag 是否一致。显然保存在指针 p 的 tag 是绿色 而&lt;code&gt;p[0]&lt;/code&gt; 指向的内存所对应的 shadow memory 中保存的 tag 是紫色,即 tag 是不匹配的,这说明访问 &lt;code&gt;p[0]&lt;/code&gt; 时存在内存错误。&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-75a2820861bf87b77c36f17f25b42329c8c.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/li&gt;
        &lt;/ul&gt; &lt;/li&gt;
        &lt;/ul&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;h2&gt;算法&lt;/h2&gt;
        &lt;ul&gt;
        &lt;li&gt;shadow memory:每 16-bytes 的 application memory 对应 1-byte 的 shadow memory。&lt;/li&gt;
        &lt;li&gt;每个 heap/stack/global 内存对象都被对齐到 16-bytes,这样每个 heap/stack/global 内存对象至少对应的 1-byte shadow memory。&lt;/li&gt;
        &lt;li&gt;为每一个 heap/stack/global 内存对象生成一个 1-byte 的随机 tag,将该随机 tag 保存到指向这些内存对象的指针的 top byte 中,同样将该随机 tag 保存到这些内存对象对应的 shadow memory 中。&lt;/li&gt;
        &lt;li&gt;在每一处内存读写之前插桩:比较保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 是否一致,如果不一致则报错。&lt;/li&gt;
        &lt;/ul&gt;
        &lt;h2&gt;实现&lt;/h2&gt;
        &lt;h3&gt;shadow mapping&lt;/h3&gt;
        &lt;p&gt;HWASAN 与 ASAN 一样都使用了 shadow memory 技术。ASAN 默认使用 static shadow mapping,只有对 IOS 和 32-bit Android 平台才使用 dynamic shadow mapping。而 HWASAN 则总是使用 dynamic shadow mapping。&lt;/p&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;ASAN: static shadow mapping。在 llvm-project/compiler-rt/lib/asan/asan_mapping.h 中预定义了不同平台下 shadow memory 的布局:HighMem, HighShadow, ShadowGap, LowShadow, LowMem 的地址区间。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;Linux/x86_64 下 ASAN 的 shadow mapping 如下所示:&lt;/p&gt; &lt;pre&gt;&lt;code&gt; // Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000:
        || `[0x10007fff8000, 0x7fffffffffff]` || HighMem ||
        || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow ||
        || `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap ||
        || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow ||
        || `[0x000000000000, 0x00007fff7fff]` || LowMem ||
        &lt;/code&gt;&lt;/pre&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;给定 application memory 地址 &lt;code&gt;addr&lt;/code&gt;,计算其对应的 shadow memory 地址的公式如下:&lt;/p&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;pre&gt;&lt;code&gt;uptr MemToShadow(uptr addr) { return (addr &amp;gt;&amp;gt; 3) + 0x7fff8000; }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;HWASAN: dynamic shadow mapping。根据 MaxUserVirtualAddress 计算 shadow memory 所需要的总大小 shadow_size,通过 mmap(shadow_size) 得到 shadow memory 区间,再具体划分 HighMem, HighShadow, ShadowGap, LowShadow, LowMem 的地址区间。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;伪算法如下(未考虑对齐):&lt;/p&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;pre&gt;&lt;code&gt; kHighMemEnd = GetMaxUserVirtualAddress();
        shadow_size = MemToShadowSize(kHighMemEnd);
        __hwasan_shadow_memory_dynamic_address = mmap(shadow_size);
        // Place the low memory first.
        kLowMemEnd = __hwasan_shadow_memory_dynamic_address - 1;
        kLowMemStart = 0;
        // Define the low shadow based on the already placed low memory.
        kLowShadowEnd = MemToShadow(kLowMemEnd);
        kLowShadowStart = __hwasan_shadow_memory_dynamic_address;
        // High shadow takes whatever memory is left up there.
        kHighShadowEnd = MemToShadow(kHighMemEnd);
        kHighShadowStart = Max(kLowMemEnd, MemToShadow(kHighShadowEnd)) + 1;
        // High memory starts where allocated shadow allows.
        kHighMemStart = ShadowToMem(kHighShadowStart);
        &lt;/code&gt;&lt;/pre&gt;
        &lt;pre&gt;&lt;code&gt;uptr MemToShadow(uptr untagged_addr) {
        return (untagged_addr &amp;gt;&amp;gt; 4) + __hwasan_shadow_memory_dynamic_address;
        }
        uptr ShadowToMem(uptr shadow_addr) {
        return (shadow_addr - __hwasan_shadow_memory_dynamic_address) &amp;lt;&amp;lt; 4;
        }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;Linux/AArch64 下 HWASAN 的某种 shadow mapping 如下所示:&lt;/p&gt; &lt;pre&gt;&lt;code&gt; // Typical mapping on Linux/AArch64
        // with dynamic shadow mapped: [0xefff00000000, 0xffff00000000]:
        || [0xffff00000000, 0xffffffffffff] || HighMem ||
        || [0xfffef0000000, 0xfffeffffffff] || HighShadow ||
        || [0xfefef0000000, 0xfffeefffffff] || ShadowGap ||
        || [0xefff00000000, 0xfefeefffffff] || LowShadow ||
        || [0x000000000000, 0xeffeffffffff] || LowMem ||
        &lt;/code&gt;&lt;/pre&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;h3&gt;tagging&lt;/h3&gt;
        &lt;ul&gt;
        &lt;li&gt; &lt;p&gt;stack 内存对象:在编译时插桩阶段,HWASAN 会插入代码来实现对 stack 内存对象的 tagging,将生成的随机 tag 保存在 stack 内存对象指针的 top byte,同时将随机 tag 保存这些 stack 内存对象对应的 shadow memory 中。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;每个 stack 内存对象的 tag 是通过 &lt;code&gt;stack_base_tag ^ RetagMask(AllocaNo)&lt;/code&gt; 计算得到的。&lt;code&gt;stack_base_tag&lt;/code&gt; 对于不同的 stack frame 是不同的值,&lt;code&gt;RetagMask(AllocaNo)&lt;/code&gt; 的实现如下(AllocaNo 可以看作是 stack 内存对象的序号)。&lt;/p&gt; &lt;pre&gt;&lt;code&gt; static unsigned RetagMask(unsigned AllocaNo) {
        // A list of 8-bit numbers that have at most one run of non-zero bits.
        // x = x ^ (mask &amp;lt;&amp;lt; 56) can be encoded as a single armv8 instruction for these
        // masks.
        // The list does not include the value 255, which is used for UAR.
        //
        // Because we are more likely to use earlier elements of this list than later
        // ones, it is sorted in increasing order of probability of collision with a
        // mask allocated (temporally) nearby. The program that generated this list
        // can be found at:
        // https://github.com/google/sanitizers/blob/master/hwaddress-sanitizer/sort_masks.py
        static unsigned FastMasks[] = {0, 128, 64, 192, 32, 96, 224, 112, 240,
        48, 16, 120, 248, 56, 24, 8, 124, 252,
        60, 28, 12, 4, 126, 254, 62, 30, 14,
        6, 2, 127, 63, 31, 15, 7, 3, 1};
        return FastMasks[AllocaNo % std::size(FastMasks)];
        }
        &lt;/code&gt;&lt;/pre&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;heap 内存对象:随机 tag 是在运行时申请 heap 内存对象时由 HWASAN 的内存分配器生成的。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;当程序调用 malloc 或者 operator new 申请内存时,HWASAN 的内存分配器会将随机生成的 tag 保存在 malloc 或者 operator new 返回的指针的 top byte 中,同时将随机 tag 保存在这些 heap 内存对象对应的 shadow memory 中。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;当程序调用 free 或者 operator delete 释放内存时,HWASAN 的内存分配器会再次随机生成 tag ,将新生成的随机 tag 保存在正释放的 heap 内存对象对应的 shadow memory 中。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;global 内存对象:对于每个编译单元内的 global 内存对象,在编译时插桩阶段都会生成对应的 tag,编译单元内的第一个 global 内存对象的 tag 是对当前编译单元文件路径计算 MD5 哈希值然后取第一个字节得到的,编译单元内的后续 global 内存对象的 tag 是基于之前 global 内存对象的 tag 递增得到的。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;在编译时插桩阶段 HWASAN 会插入代码来将生成的随机 tag 保存在 global 内存对象指针的 top byte,而将随机 tag 保存到这些 global 内存对象对应的 shadow memory 中则是由 HWASAN runtime 在程序启动阶段做的。对于每一个 global 内存对象,HWASAN 在编译插桩阶段都会创建一个 descriptor 保存在 &quot;hwasan_globals&quot; section 中,descriptor 中保存了 global 内存对象的大小以及 tag 等信息,程序启动时 HWASAN runtime 会遍历 &quot;hwasan_globals&quot; section 中所有的 descriptor 来设置每个 global 内存对象对应的 shadow memory tag。&lt;/p&gt; &lt;/li&gt;
        &lt;/ul&gt;
        &lt;h3&gt;short granules&lt;/h3&gt;
        &lt;p&gt;每个 heap/stack/global 内存对象都会被对齐到 16-bytes,heap/stack/global 内存对象原本大小记作 size,如果 &lt;code&gt;size % 16 != 0&lt;/code&gt;,那么就需要 padding,heap/stack/global 内存对象最后不足 16-bytes 的部分就被称为 short granule。此时会将 tag 存储到 padding 的最后一个字节,而 padding 所在的 16-bytes 内存对应的 1-byte shadow memory 中存储的则是 short granule size 即 &lt;code&gt;size % 16&lt;/code&gt;&lt;/p&gt;
        &lt;p&gt;举例如下:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;uint8_t buf\[20\];
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;&lt;code&gt;uint8_t buf[20]&lt;/code&gt; 开启 HWASAN 后会变为:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;uint8_t buf[32]; // 20-bytes aligned to 16-bytes -&amp;gt; 32-bytes
        uint8_t tag = __hwasan_generate_tag();
        buf[31] = tag;
        *(char *)MemToShadow(buf) = tag;
        *((char *)MemToShadow(buf)+1) = 20 % 16;
        uint8_t *tagged_buf = reinterpret_cast&amp;lt;int8_t *&amp;gt;(
        reinterpret_cast&amp;lt;uintptr_t&amp;gt;(buf) | (tag &amp;lt;&amp;lt; 56));
        // Replace all uses of `buf` with `tagged_buf`
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;&lt;code&gt;uint_t buf[20]&lt;/code&gt; 的最后 4-bytes 就是 short granule,short granule size 即 20 % 16 = 4。&lt;/p&gt;
        &lt;p&gt;因为 short granules 的存在,所以在比较保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 是否一致时,需要考虑如下两种可能:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt; &lt;p&gt;保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 相同。&lt;/p&gt; &lt;/li&gt;
        &lt;li&gt; &lt;p&gt;保存在 shadow memory 中的 tag 实际上是 short granule size,保存在指针 top byte 的 tag 等于保存在指针指向的内存所在的 16-bytes 内存的最后一个字节的 tag。&lt;/p&gt; &lt;/li&gt;
        &lt;/ol&gt;
        &lt;p&gt;&lt;img src=&quot;https://oscimg.oschina.net/oscnet/up-941c89ebb18a8a02d080ece2e362744cf45.png&quot; alt=&quot;&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
        &lt;p&gt;&lt;strong&gt;为什么需要 short granules ?&lt;/strong&gt;&lt;/p&gt;
        &lt;p&gt;考虑 &lt;code&gt;uint8_t buf[20]&lt;/code&gt;,假设代码中存在访问 &lt;code&gt;buf[22]&lt;/code&gt; 导致的 buffer-overflow。因为 HWASAN 会将 heap/stack/global 内存对象对齐到 16-bytes,所以实际为 &lt;code&gt;uint8_t buf[20]&lt;/code&gt; 申请的空间是 &lt;code&gt;uint8_t buf[32]&lt;/code&gt;&lt;/p&gt;
        &lt;p&gt;如果没有 short granules,那么保存在 &lt;code&gt;buf&lt;/code&gt; 指针 top byte 的 tag 为 0xa1,保存在 &lt;code&gt;buf[22]&lt;/code&gt; 对应的 shadow memory 中的 tag 为 0xa1,尽管访问 &lt;code&gt;buf[22]&lt;/code&gt; 时发生了 buffer-overflow,此时 HWASAN 也检测不到,因为保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 是否一致的。&lt;/p&gt;
        &lt;p&gt;有了 short granules,保存在 &lt;code&gt;buf&lt;/code&gt; 指针 top byte 的 tag 为 0xa1,保存在 &lt;code&gt;buf[22]&lt;/code&gt; 对应的 shadow memory 中的 tag 则是 short granule size 即 20 % 16 = 4。访问 &lt;code&gt;buf[22]&lt;/code&gt; 时,HWASAN 发现保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 不一致,保存在 &lt;code&gt;buf[22]&lt;/code&gt; 对应的 shadow memory 中的 tag 是 short granule size 为 4,这意味着 &lt;code&gt;buf[22]&lt;/code&gt; 所在的 16-bytes 内存只有前 4-bytes 是可以合法访问的,而 &lt;code&gt;buf[22]&lt;/code&gt; 访问的却是其所在 16-bytes 内存的第 7 个 byte,说明访问 &lt;code&gt;buf[22]&lt;/code&gt; 时发生了 buffer-overflow!&lt;/p&gt;
        &lt;h3&gt;hwasan check memaccess&lt;/h3&gt;
        &lt;p&gt;本节说明 HWASAN 如何在每一处内存读写之前通过插桩来比较保存在指针 top byte 的 tag 和保存在 shadow memory 中的 tag 是否一致的。&lt;/p&gt;
        &lt;p&gt;开启 HWASAN 后,HWAddressSanitizer instrumentation pass 会在 LLVM IR 层面进行插桩。默认情况下,HWASAN 在每一处内存读写之前添加对 &lt;code&gt;llvm.hwasan.check.memaccess.shortgranules&lt;/code&gt; intrinsic 的调用。该 &lt;code&gt;llvm.hwasan.check.memaccess.shortgranules&lt;/code&gt; intrinsic 会在生成汇编代码时转换为对相应函数的调用。&lt;/p&gt;
        &lt;p&gt;还是以如下代码为例说明:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;// cat test.c #include &amp;lt;stdlib.h&amp;gt; int main() { int * volatile x = (int *)malloc(sizeof(int)*10); x\[10\] = 0; free(x); }
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;上述代码 &lt;code&gt;clang -O1 -fsanitize=hwaddress test.c -S -emit-llvm&lt;/code&gt; 开启 HWASAN 生成的 LLVM IR 如下:&lt;/p&gt;
        &lt;pre&gt;&lt;code&gt;%5 = alloca ptr, align 8
        %6 = call noalias ptr @malloc(i64 noundef 40)
        store ptr %6, ptr %5, align 8
        %7 = load volatile ptr, ptr %5, align 8
        %8 = getelementptr inbounds i32, ptr %7, i64 10
        call void @llvm.hwasan.check.memaccess.shortgranules(ptr %__hwasan_shadow_memory_dynamic_address, ptr %8, i32 18)
        store i8 0, ptr %8, align 4
        %9 = load volatile ptr, ptr %5, align 8
        call void @free(ptr noundef %9)
        &lt;/code&gt;&lt;/pre&gt;
        &lt;p&gt;&lt;code&gt;llvm.hwasan.check.memaccess.shortgranules&lt;/code&gt; intrinsic 有三个参数:&lt;/p&gt;
        &lt;ol&gt;
        &lt;li&gt;__hwasan_shadow_memory_dynamic_address&lt;/li&gt;
        &lt;li&gt;本次 memory access 访问的内存地址&lt;/li&gt;
        &lt;li&gt;常数 AccessInfo,编码了本次 memory access 的相

@TonyRL TonyRL merged commit a5274c2 into DIYgod:master Dec 28, 2024
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Auto: Route Test Complete Auto route test has finished on given PR Route
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants