<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>deepin 社区博客</title>
    <link>https://blog.deepin.org/</link>
    <description>deepin 社区博客</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-cn</language>
    <lastBuildDate>Mon, 18 Mar 2024 13:50:31 +0800</lastBuildDate>
    
    <atom:link href="https://blog.deepin.org/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item xml:base="https://blog.deepin.org/posts/zram/">
      <title>linux内存压缩技术</title>
      <link>https://blog.deepin.org/posts/zram/</link>
      <pubDate>Mon, 18 Mar 2024 13:50:31 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/zram/</guid>
      <description>&lt;h1 id=&#34;技术背景&#34; &gt;技术背景
&lt;span&gt;
    &lt;a href=&#34;#%e6%8a%80%e6%9c%af%e8%83%8c%e6%99%af&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;顾名思义，内存压缩就是压缩内存，节省内存空间。相信对于搞技术的人来说，压缩这个词并不陌生，一想到这个词，我们首先想到的是压缩可以降低占用空间，使同样的空间可以存放更多的东西。
内存无论多大，总是会有不够用的时候，或者说我们总是想在尽量低的成本控制下，达到系统最优，这个时候还是有必要引入诸如内存压缩的功能来优化系统内存占用。当系统内存紧张的时候，会将文件页丢弃或回写回磁盘（如果是脏页），还可能会触发LMK杀进程进行内存回收。这些被回收的内存如果再次使用都需要重新从磁盘读取，而这个过程涉及到较多的IO操作。
就目前的技术而言，IO的速度远远慢于这RAM操作速度。因此，如果频繁地做IO操作，不仅影响flash使用寿命，还严重影响系统性能。内存压缩是一种让IO过程平滑过渡的做法, 即尽量减少由于内存紧张导致的IO，提升性能。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/c6907def-2d6d-4d90-bb98-3acfd3f9ed43&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;图：内存管理大体框架（内存压缩技术处于内存回收memory reclaim部分中）&lt;/p&gt;
&lt;h1 id=&#34;主流内存压缩技术&#34; &gt;主流内存压缩技术
&lt;span&gt;
    &lt;a href=&#34;#%e4%b8%bb%e6%b5%81%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e6%8a%80%e6%9c%af&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;目前linux内核主流的内存压缩技术主要有3种：zSwap, zRAM, zCache。下面我们依次对几种方式进行一个简要的说明&lt;/p&gt;
&lt;h2 id=&#34;zswap&#34; &gt;zSwap
&lt;span&gt;
    &lt;a href=&#34;#zswap&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;zSwap是在memory与flash之间的一层“cache”,当内存需要swap出去磁盘的时候，先通过压缩放到zSwap中去，zSwap空间按需增长。达到一定程度后则会按照LRU的顺序(前提是使用的内存分配方法需要支持LRU)将就最旧的page解压写入磁盘swap device，之后将当前的page压缩写入zSwap。&lt;/p&gt;
&lt;p&gt;zswap本身存在一些缺陷或问题:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果开启当zswap满交换出backing store的功能, 由于需要将zswap里的内存按LRU顺序解压再swap out, 这就要求内存分配器支持LRU功能。&lt;/li&gt;
&lt;li&gt;如果不开启当zswap满交换出backing store的功能, 和zRam是类似的。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;zrram&#34; &gt;zRram
&lt;span&gt;
    &lt;a href=&#34;#zrram&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;zRram即压缩的内存， 使用内存模拟block device的做法。实际不会写到块设备中去，只会压缩后写到模拟的块设备中，其实也就是还是在RAM中，只是通过压缩了。由于压缩和解压缩的速度远比读写IO好，因此在移动终端设备广泛被应用。zRam是基于RAM的block device, 一般swap priority会比较高。只有当其满，系统才会考虑其他的swap devices。当然这个优先级用户可以配置。&lt;/p&gt;
&lt;p&gt;zRram本身存在一些缺陷或问题:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;zRam大小是可灵活配置的, 那是不是配置越大越好呢? 如果不是,配置多大是最合适的呢?&lt;/li&gt;
&lt;li&gt;使用zRam可能会在低内存场景由于频繁的内存压缩导致kswapd进程占CPU高, 怎样改善?&lt;/li&gt;
&lt;li&gt;增大了zRam配置,对系统内存碎片是否有影响?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;要利用好zRam功能, 并不是简单地配置了就OK了, 还需要对各种场景和问题都做好处理, 才能发挥最优的效果。&lt;/p&gt;
&lt;h2 id=&#34;zcache&#34; &gt;zCache
&lt;span&gt;
    &lt;a href=&#34;#zcache&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;zCache是oracle提出的一种实现文件页压缩技术，也是memory与block dev之间的一层“cache”,与zswap比较接近，但zcache目前压缩的是文件页，而zSwap和zRAM压缩是匿名页。&lt;/p&gt;
&lt;p&gt;zcache本身存在一些缺陷或问题:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;有些文件页可能本身是压缩的内容, 这时可能无法再进行压缩了；&lt;/li&gt;
&lt;li&gt;zCache目前无法使用zsmalloc, 如果使用zbud,压缩率较低；&lt;/li&gt;
&lt;li&gt;使用的zbud/z3fold分配的内存是不可移动的, 需要关注内存碎片问题；&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;内存压缩主流的内存分配器&#34; &gt;内存压缩主流的内存分配器
&lt;span&gt;
    &lt;a href=&#34;#%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e4%b8%bb%e6%b5%81%e7%9a%84%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e5%99%a8&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;h2 id=&#34;zsmalloc&#34; &gt;Zsmalloc
&lt;span&gt;
    &lt;a href=&#34;#zsmalloc&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;zsmalloc是为ZRAM设计的一种内存分配器。内核已经有slub了，为什么还需要zsmalloc内存分配器？这是由内存压缩的场景和特点决定的。zsmalloc内存分配器期望在低内存的场景也能很好地工作，事实上，当需要压缩内存进行zsmalloc内存分配时，内存一般都比较紧张且内存碎片都比较严重了。如果使用slub分配， 很可能由于高阶内存分配不到而失败。另外，slub也可能导致内存碎片浪费比较严重，最坏情况下，当对象大小略大于PAGE_SIZE/2时，每个内存页接近一半的内存将被浪费。&lt;/p&gt;
&lt;p&gt;实测发现，anon pages的平均压缩比大约在1:3左右，所以compressed anon page size很多在1.2K左右。如果是Slub，为了分配大量1.2K的内存，可能内存浪费严重。zsmalloc分配器尝试将多个相同大小的对象存放在组合页（称为zspage）中，这个组合页不要求物理连续，从而提高内存的使用率。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://wiki.deepin.org/2024-3-14_56580.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;需要注意的是, 当前zsmalloc不支持LRU功能, 旧版本内核分配的不可移动的页, 对内存碎片影响严重, 但最新版本内核已经是支持分配可移动类型内存了。&lt;/p&gt;
&lt;h2 id=&#34;zbud&#34; &gt;Zbud
&lt;span&gt;
    &lt;a href=&#34;#zbud&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;zbud是一个专门为存储压缩page而设计的内存分配器。用于将2个objects存到1个单独的page中。zbud是可以支持LRU的, 但分配的内存是不可移动的。&lt;/p&gt;
&lt;h2 id=&#34;z3fold&#34; &gt;Z3fold
&lt;span&gt;
    &lt;a href=&#34;#z3fold&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;z3fold是一个较新的内存分配器, 与zbud不同的是, 将3个objects存到1个单独的page中,也就是zbud内存利用率极限是1:2, z3fold极限是1:3。同样z3fold是可以支持LRU的, 但分配的内存是不可移动的。&lt;/p&gt;
&lt;h1 id=&#34;内存压缩技术与内存分配器组合&#34; &gt;内存压缩技术与内存分配器组合
&lt;span&gt;
    &lt;a href=&#34;#%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e6%8a%80%e6%9c%af%e4%b8%8e%e5%86%85%e5%ad%98%e5%88%86%e9%85%8d%e5%99%a8%e7%bb%84%e5%90%88&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;结合上面zSwap / zRam /zCache的介绍, 与zsmalloc/zbud/z3fold分别怎样组合最合适呢?
下面总结了一下, 具体原因可以看上面介绍的时候各类型的特点。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;对比项&lt;/th&gt;
&lt;th&gt;zsmalloc&lt;/th&gt;
&lt;th&gt;zbud&lt;/th&gt;
&lt;th&gt;z3fold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;zSwap（有实际swap device）&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;zSwap（无实际swap device）&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;zRam&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;zCache&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;h1 id=&#34;zram技术原理&#34; &gt;zRAM技术原理
&lt;span&gt;
    &lt;a href=&#34;#zram%e6%8a%80%e6%9c%af%e5%8e%9f%e7%90%86&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;zRam内存压缩技术是目前移动终端广泛使用的内存压缩技术。&lt;/p&gt;
&lt;h2 id=&#34;软件框架&#34; &gt;软件框架
&lt;span&gt;
    &lt;a href=&#34;#%e8%bd%af%e4%bb%b6%e6%a1%86%e6%9e%b6&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;下图展示了内存管理大体的框架， 内存压缩技术处于内存回收memory reclaim部分中。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/c85953a2-c979-4b37-b87f-34148b61df6b&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;再具体到zRam, 它的软件架构可以分为3部分：数据流操作，内存压缩算法 ，zram驱动。
&lt;img src=&#34;https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/447b5c2e-1234-48e8-8af6-a979db5a14a8&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;实现原理&#34; &gt;实现原理
&lt;span&gt;
    &lt;a href=&#34;#%e5%ae%9e%e7%8e%b0%e5%8e%9f%e7%90%86&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;Zram内存压缩技术本质上就是以时间换空间。通过CPU压缩、解压缩的开销换取更大的可用内存空间。&lt;/p&gt;
&lt;p&gt;我们主要描述清楚下面这2个问题：
1.什么时候会进行内存压缩？
2.进行内存压缩/解压缩的流程是怎样的？&lt;/p&gt;
&lt;p&gt;进行内存压缩的时机：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Kswapd场景：kswapd是内核内存回收线程， 当内存watermark低于low水线时会被唤醒工作， 其到内存watermark不小于high水线。&lt;/li&gt;
&lt;li&gt;Direct reclaim场景：内存分配过程进入slowpath, 进行直接行内存回收。
&lt;img src=&#34;https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/289527b7-9055-4091-99c1-9363486d738e&#34; alt=&#34;image&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下面是基于4.4内核理出的内存压缩、解压缩流程。
内存回收过程路径进行内存压缩。会将非活跃链表的页进行shrink, 如果是匿名页会进行pageout, 由此进行内存压缩存放到ZRAM中， 调用路径如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/87b89832-6d46-4990-9f12-a815bd050cbe&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;在匿名页换出到swap设备后， 访问页时， 产生页访问错误, 当发现“页表项不为空， 但页不在内存中”， 该页就是已换到swap区中，由此会开始将该页从swap区中重新读取， 如果是ZRAM， 则是解压缩的过程。调用路径如下：
&lt;img src=&#34;https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/63700e9c-18cd-4dc2-843c-5d46eb3dbf81&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;内存压缩算法&#34; &gt;内存压缩算法
&lt;span&gt;
    &lt;a href=&#34;#%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e7%ae%97%e6%b3%95&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;目前比较主流的内存算法主要为LZ0, LZ4, ZSTD等。下面截取了几种算法在x86机器上的表现。各算法有各自特点， 有以压缩率高的， 有压缩/解压快的等， 具体要结合需求场景选择使用。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://github.com/zhangke1990616/.blog.deepin.org/assets/3860068/1dfeb35c-f283-4a91-8c67-730737ab9120&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;zram技术应用&#34; &gt;zRAM技术应用
&lt;span&gt;
    &lt;a href=&#34;#zram%e6%8a%80%e6%9c%af%e5%ba%94%e7%94%a8&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;本节描述一下在使用ZRAM常遇到的一些使用或配置，调试的方法。&lt;/p&gt;
&lt;h2 id=&#34;如何配置开启zram&#34; &gt;如何配置开启zRAM
&lt;span&gt;
    &lt;a href=&#34;#%e5%a6%82%e4%bd%95%e9%85%8d%e7%bd%ae%e5%bc%80%e5%90%afzram&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;配置内存压缩算法&#34; &gt;配置内存压缩算法
&lt;span&gt;
    &lt;a href=&#34;#%e9%85%8d%e7%bd%ae%e5%86%85%e5%ad%98%e5%8e%8b%e7%bc%a9%e7%ae%97%e6%b3%95&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;下面例子配置压缩算法为lz4&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo lz4 &amp;gt; /sys/block/zram0/comp_algorithm
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;配置zram大小&#34; &gt;配置ZRAM大小
&lt;span&gt;
    &lt;a href=&#34;#%e9%85%8d%e7%bd%aezram%e5%a4%a7%e5%b0%8f&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;下面例子配置zram大小为2GB&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo 2147483648 &amp;gt; /sys/block/zram0/disksize
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;使能zram&#34; &gt;使能zram
&lt;span&gt;
    &lt;a href=&#34;#%e4%bd%bf%e8%83%bdzram&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;mkswap /dev/zram0&lt;/p&gt;
&lt;p&gt;swapon /dev/zram0&lt;/p&gt;
&lt;h3 id=&#34;zram块设备个数设定&#34; &gt;zRAM块设备个数设定
&lt;span&gt;
    &lt;a href=&#34;#zram%e5%9d%97%e8%ae%be%e5%a4%87%e4%b8%aa%e6%95%b0%e8%ae%be%e5%ae%9a&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;如果是编译为内核模块，那么可以在内核模块加载的时候，添加参数：insmod zram.ko num_devices=4&lt;/p&gt;
&lt;p&gt;也可直接修改内核源代码，代码地址为：
/drivers/block/zram/zram_drv.c&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Module params (documentation at end) */
static unsigned int num_devices = 1;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;修改num_devices为你想要的zram个数即可&lt;/p&gt;
&lt;h3 id=&#34;压缩流的最大个数设定&#34; &gt;压缩流的最大个数设定
&lt;span&gt;
    &lt;a href=&#34;#%e5%8e%8b%e7%bc%a9%e6%b5%81%e7%9a%84%e6%9c%80%e5%a4%a7%e4%b8%aa%e6%95%b0%e8%ae%be%e5%ae%9a&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;这个是 3.15 版本及以后的 kernel 新加入的功能，3.15 版本之前的 zram 压缩都是使用一个压缩流（缓存 buffer 和算法私有部分）实现，每个写（压缩）操作都会独享压缩流，但是单压缩流如果出现数据奔溃或者卡住的现象，所有的写（压缩）操作将一直处于等待状态，这样效率非常低；而多压缩流的架构会让写（压缩）操作可以并行去执行，大大提高了压缩的效率和稳定性。&lt;/p&gt;
&lt;p&gt;查看压缩流个数：默认是1，可以直接向proc文件写入，也可以直接更改代码方式来改变默认压缩流个数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat /sys/block/zram0/max_comp_streams
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;设定压缩流个数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo 3 &amp;gt; /sys/block/zram0/max_comp_streams
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;其他参数&#34; &gt;其他参数
&lt;span&gt;
    &lt;a href=&#34;#%e5%85%b6%e4%bb%96%e5%8f%82%e6%95%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Access&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;disksize&lt;/td&gt;
&lt;td&gt;RW&lt;/td&gt;
&lt;td&gt;显示和设置该块设备的内存大小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;initstate&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;显示设备的初始化状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reset&lt;/td&gt;
&lt;td&gt;WO&lt;/td&gt;
&lt;td&gt;重置设备&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;num_reads&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;读数据的个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;failed_reads&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;读数据失败的个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;num_write&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;写数据的个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;failed_writes&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;写数据失败的个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;invalid_io&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;非页面大小对齐的I/O请求的个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;max_comp_streams&lt;/td&gt;
&lt;td&gt;RW&lt;/td&gt;
&lt;td&gt;最大可能同时执行压缩操作的个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;comp_algorithm&lt;/td&gt;
&lt;td&gt;RW&lt;/td&gt;
&lt;td&gt;显示和设置压缩算法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;notify_free&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;空闲内存的通知个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zero_pages&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;写入该块设备的全为的页面的个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;orig_data_size&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;保存在该块设备中没有被压缩的数据的大小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;compr_data_size&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;保存在该块设备中已被压缩的数据的大小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mem_used_total&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;分配给该块设备的总内存大小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mem_used_max&lt;/td&gt;
&lt;td&gt;RW&lt;/td&gt;
&lt;td&gt;该块设备已用的内存大小，可以写 1 重置这个计数参数到当前真实的统计值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mem_limit&lt;/td&gt;
&lt;td&gt;RW&lt;/td&gt;
&lt;td&gt;zram 可以用来保存压缩数据的最大内存&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pages_compacted&lt;/td&gt;
&lt;td&gt;RO&lt;/td&gt;
&lt;td&gt;在压缩过程中可用的空闲页面的个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;compact&lt;/td&gt;
&lt;td&gt;WO&lt;/td&gt;
&lt;td&gt;触发内存压缩&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;swappiness含义简述&#34; &gt;swappiness含义简述
&lt;span&gt;
    &lt;a href=&#34;#swappiness%e5%90%ab%e4%b9%89%e7%ae%80%e8%bf%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;swappiness参数是内核倾向于回收匿名页到swap（使用的ZRAM就是swap设备）的积极程度， 原生内核范围是0~100， 参数值越大， 表示回收匿名页到swap的比例就越大。如果配置为0， 表示仅回收文件页，不回收匿名页。默认值为60。可以通过节点“/proc/sys/vm/swappiness”配置。&lt;/p&gt;
&lt;h2 id=&#34;zram相关的技术指标&#34; &gt;zRam相关的技术指标
&lt;span&gt;
    &lt;a href=&#34;#zram%e7%9b%b8%e5%85%b3%e7%9a%84%e6%8a%80%e6%9c%af%e6%8c%87%e6%a0%87&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;zram大小及剩余空间&#34; &gt;zRAM大小及剩余空间
&lt;span&gt;
    &lt;a href=&#34;#zram%e5%a4%a7%e5%b0%8f%e5%8f%8a%e5%89%a9%e4%bd%99%e7%a9%ba%e9%97%b4&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;Proc/meminfo 中可以查看相关信息
SwapTotal：swap 总大小, 如果配置为ZRAM, 这里就是ZRAM总大小
SwapFree：swap 剩余大小, 如果配置为ZRAM, 这里就是ZRAM剩余大小&lt;/p&gt;
&lt;p&gt;当然， 节点 /sys/block/zram0/disksize 是最直接的。&lt;/p&gt;
&lt;h3 id=&#34;zram压缩率&#34; &gt;zRAM压缩率
&lt;span&gt;
    &lt;a href=&#34;#zram%e5%8e%8b%e7%bc%a9%e7%8e%87&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;/sys/block/zram/mm_stat 中有压缩前后的大小数据， 由此可以计算出实际的压缩率
orig_data_size：压缩前数据大小， 单位为bytes
compr_data_size ：压缩后数据大小， 单位为bytes&lt;/p&gt;
&lt;h3 id=&#34;换出换入swap区的总量&#34; &gt;换出/换入swap区的总量
&lt;span&gt;
    &lt;a href=&#34;#%e6%8d%a2%e5%87%ba%e6%8d%a2%e5%85%a5swap%e5%8c%ba%e7%9a%84%e6%80%bb%e9%87%8f&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;proc/vmstat 中中有相关信息
pswpin:换入总量， 单位为page
pswout:换出总量， 单位为page&lt;/p&gt;
&lt;h2 id=&#34;zram相关优化&#34; &gt;zRam相关优化
&lt;span&gt;
    &lt;a href=&#34;#zram%e7%9b%b8%e5%85%b3%e4%bc%98%e5%8c%96&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;上面提到zRam的一些缺陷, 怎么去改善呢?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;zRam大小是可灵活配置的, 那是不是配置越大越好呢? 如果不是配置多大是最合适的呢?
zRam大小的配置比较灵活, 如果zRam配置过大, 后台缓存了应用过多, 这也是有可能会影响前台应用使用的流畅度。另外, zRam配置越大, 也需要关注系统的内存碎片化情。因此zRam并不是配置越大越好,具体的大小需要根据内存总大小及系统负载情况考虑及实测而定。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用zRam,可能会存在低内存场景由于频繁的内存压缩导致kswapd进程占CPU高, 怎样改善?
zRam本质就是以时间换空间, 在低内存的情况下, 肯定会比较频繁地回收内存, 这时kswapd进程是比较活跃的, 再加上通过压缩内存, 会更加消耗CPU资源。改善这种情况方法也比较多, 比如, 可以使用更优的压缩算法, 区别使用场景, 后台不影响用户使用的场景异步进行深度内存压缩, 与用户体验相关的场景同步适当减少内存压缩, 通过增加文件页的回收比例加快内存回收等等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;增大了zRam配置,对系统内存碎片是否有影响?
使用zRam是有可能导致系统内存碎片变得更严重的, 特别是zsmalloc分配不支持可移动内存类型的时候。新版的内核zsmalloc已经支持可移动类型分配的， 但由于增大了zRam,结合android手机的使用特点, 仍然会有可能导致系统内存碎片较严重的情况,因些内存碎片问题也是需要重点关注的。解决系统内存碎片的方法也比较多, 可以结合具体的原因及场景进行优化。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;参考资料&#34; &gt;参考资料
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;&lt;a href=&#34;https://cclinuxer.github.io/2020/09/Linux%E5%86%85%E5%AD%98%E5%8E%8B%E7%BC%A9%E6%B5%85%E6%9E%90%E4%B9%8B%E5%8E%9F%E7%90%86/&#34;&gt;Linux内存压缩浅析之原理&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://www.wowotech.net/memory_management/zram.html&#34;&gt;zRAM内存压缩技术原理与应用&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/tlp-power-management/">
      <title>TLP 电源管理简述</title>
      <link>https://blog.deepin.org/posts/tlp-power-management/</link>
      <pubDate>Thu, 07 Dec 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/tlp-power-management/</guid>
      <description>&lt;h2 id=&#34;简述&#34; &gt;简述
&lt;span&gt;
    &lt;a href=&#34;#%e7%ae%80%e8%bf%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;在上一篇&lt;a href=&#34;https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/&#34;&gt;浅析Linux电源配置&lt;/a&gt;之后，我们一直在深入探索如何进一步优化我们系统的续航和性能表现，今天它来了：&lt;/p&gt;
&lt;p&gt;TLP 是适用于 Linux 的功能丰富的命令行实用程序，无需深入研究技术细节即可节省笔记本电脑电池电量。之前我们的系统使用的laptopmode,但是相较于TLP还有有部分劣势：比如tlp脚本是被动唤醒，可以以较小的开销完成电源管理相关内容。而且TLP文档支持非常完善，所以可以方便用户自行调整相关配置。以下是TLP官方文档内容的和我自己的理解的结合，各位系统用户可以结合自己的实际情况diy自己的电源策略文件，也可以将好的电源配置在&lt;a href=&#34;https://bbs.deepin.org/&#34;&gt;deepin 论坛&lt;/a&gt;中分享。&lt;/p&gt;
&lt;h2 id=&#34;工作原理&#34; &gt;工作原理
&lt;span&gt;
    &lt;a href=&#34;#%e5%b7%a5%e4%bd%9c%e5%8e%9f%e7%90%86&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;TLP 基本上所做的是调整影响功耗的内核设置，内核态的配置文件存储在RAM中，所以并不具备持久性。TLP将配置存储在用户态中，在内核启动时对其进行配置&lt;/li&gt;
&lt;li&gt;TLP 处理的大多数内核设置都作为 sysfs 节点导出到用户空间，即 /sys/ 下的文件。&lt;code&gt;tlp-stat&lt;/code&gt; 的输出将显示路径。&lt;/li&gt;
&lt;li&gt;TLP 提供两组独立的设置，称为配置文件，一组用于电池 （BAT），另一组用于交流操作。这意味着 TLP 不仅在启动时，而且在每次电源更改时都必须应用适当的配置文件（可以据此实现AC BT切换电源调度状态）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;tlp触发事件信号&#34; &gt;TLP触发事件（信号）
&lt;span&gt;
    &lt;a href=&#34;#tlp%e8%a7%a6%e5%8f%91%e4%ba%8b%e4%bb%b6%e4%bf%a1%e5%8f%b7&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;充电器插入（交流供电）：应用AC配置文件&lt;/li&gt;
&lt;li&gt;充电器已拔下（电池供电）： 应用BAT配置文件&lt;/li&gt;
&lt;li&gt;已插入 USB 设备：激活设备的 USB 自动挂起模式（可以在配置文件设置例外或拒绝连接）&lt;/li&gt;
&lt;li&gt;系统启动（boot）：应用与当前电源 AC/BAT 相对应的设置配置文件。应用充电阈值并根据您的个人设置切换蓝牙、Wi-Fi 和 WWAN 设备（在默认配置中禁用）&lt;/li&gt;
&lt;li&gt;系统关机 (power off)：保存或切换蓝牙、Wi-Fi 和 WWAN 设备状态，并根据您的个人设置禁用 USB 自动挂起（在默认配置中禁用）&lt;/li&gt;
&lt;li&gt;系统重启： 相当于关机再启动&lt;/li&gt;
&lt;li&gt;系统挂起到 ACPI 睡眠状态 S0ix（空闲待机）、S3（挂起到 RAM）或 S4（挂起到磁盘）：保存蓝牙、Wi-Fi 和 WWAN 设备状态，并根据您的个人设置关闭可移动光盘驱动器的电源（在默认配置中禁用）。&lt;/li&gt;
&lt;li&gt;系统从 ACPI 睡眠状态 S0ix（空闲待机）、S3（挂起到 RAM）或 S4（挂起到磁盘）恢复： 应用与当前电源 AC/BAT 相对应的设置配置文件。恢复充电阈值以及蓝牙、Wi-Fi 和 WWAN 设备状态，具体取决于您的个人设置（在默认配置中禁用）。&lt;/li&gt;
&lt;li&gt;LAN、Wi-Fi、WWAN 连接/断开连接或笔记本电脑插接/未插接：根据您的个人设置启用或禁用内置蓝牙、Wi-Fi 和 WWAN 设备（在默认配置中禁用）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;除了上述事件之外，TLP 不会对设置进行动态或自适应更改
特别是，TLP 绝不会因 CPU 负载、电池电量或其他原因而调整设置（如果我们需要去实现这一部分，则可以，则可以通过添加一个信号的方式来实现）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;安装&#34; &gt;安装
&lt;span&gt;
    &lt;a href=&#34;#%e5%ae%89%e8%a3%85&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;sudo apt install tlp&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;使用&#34; &gt;使用
&lt;span&gt;
    &lt;a href=&#34;#%e4%bd%bf%e7%94%a8&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;启动&#34; &gt;启动
&lt;span&gt;
    &lt;a href=&#34;#%e5%90%af%e5%8a%a8&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;安装后TLP将在系统启动的时候自动启动，如果你不想重启系统，可以使用&lt;code&gt;sudo tlp start&lt;/code&gt;来启动tlp，也可以使用此命令来应用更改。&lt;/p&gt;
&lt;h3 id=&#34;状态&#34; &gt;状态
&lt;span&gt;
    &lt;a href=&#34;#%e7%8a%b6%e6%80%81&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;tlp-stat -s&lt;/code&gt;  TLP是bash脚本，所以不存于daemon进程&lt;/p&gt;
&lt;h3 id=&#34;命令行&#34; &gt;命令行
&lt;span&gt;
    &lt;a href=&#34;#%e5%91%bd%e4%bb%a4%e8%a1%8c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;h4 id=&#34;tlp&#34; &gt;TLP：
&lt;span&gt;
    &lt;a href=&#34;#tlp&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;&lt;code&gt;sudo tlp bat&lt;/code&gt; 应用电池配置文件并进入手动模式  手动模式意味着对电源的更改将被忽略，直到下一次重新启动或发出 tlp start 以恢复自动模式&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp ac&lt;/code&gt;应用交流配置文件并进入手动模式&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp usb&lt;/code&gt; 对所有的ubs设备应用自动挂起&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp bayoff&lt;/code&gt; 关闭 MediaBay/Ultrabay 中的光驱电源&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp setcharge [&amp;lt;START_CHARGE_THRESH&amp;gt; &amp;lt;STOP_CHARGE_THRESH&amp;gt;] [BAT0|BAT1|BAT&amp;lt;x&amp;gt;|CMB0|CMB1]&lt;/code&gt; 可以设定对指定电池开始充电百分比和结束充电的百分比，以达到养护电池的目的（如果不带参数 会重置电池管理方案）（命令只能暂时更改，如果需要持久化更改 需要修改配置文件）&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp fullcharge [BAT0|BAT1|BAT&amp;lt;x&amp;gt;|CMB0|CMB1]&lt;/code&gt; 设定电池充满&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tlp diskid&lt;/code&gt; 显示已经配置驱动器的磁盘ID&lt;/p&gt;
&lt;p&gt;以下部分为ThinkPad专属&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp chargeonce [BAT0|BAT1]&lt;/code&gt; 将电池充电至停止充电阈值一次，这个阈值是使用setcharge设置的&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp discharge [BAT0|BAT1]&lt;/code&gt; 让电池在交流电源下完全放电&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp recalibrate [BAT0|BAT1]&lt;/code&gt;校准电池&lt;/p&gt;
&lt;h4 id=&#34;tlp-rdw&#34; &gt;TLP-RDW
&lt;span&gt;
    &lt;a href=&#34;#tlp-rdw&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;&lt;code&gt;sudo tlp-rdw [ enable | disable ]&lt;/code&gt; 启用或关闭无线电管理功能&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;bluetooth &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; on | off | toggle &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
nfc &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; on | off | toggle &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
wifi &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; on | off | toggle &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
wwan &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; on | off | toggle &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启用、禁用、切换或检查内置蓝牙、NFC、Wi-Fi 和 WWAN（3G/UMTS、4G/LTE 或 5G）无线电的状态，如果不带参数则为当前硬件状态（硬件需要支持rfkill）&lt;/p&gt;
&lt;h4 id=&#34;tlp-stat&#34; &gt;TLP-STAT
&lt;span&gt;
    &lt;a href=&#34;#tlp-stat&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;&lt;code&gt;sudo tlp-stat&lt;/code&gt; 查看TLP配置信息，系统信息和内核省电设置以及电池数据&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp-stat [-b /--battery]&lt;/code&gt; 查看电池信息，部分电池加&lt;code&gt;-v&lt;/code&gt;参数可以查看电压&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp-stat [-c /--config]&lt;/code&gt;查看配置信息&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp-stat --cdiff&lt;/code&gt; 查看默认配置和用户配置之间的差异&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp-stat [-d /--disk]&lt;/code&gt; 查看硬盘配置信息&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo tlp-stat [-e/ --pcie]&lt;/code&gt; 查看Pcie配置信息&lt;/p&gt;
&lt;h2 id=&#34;配置&#34; &gt;配置
&lt;span&gt;
    &lt;a href=&#34;#%e9%85%8d%e7%bd%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;TLP最重要的就是其配置文件，可以说，TLP是否节电的关键。 TLP 使用两个根据电源自动应用的设置配置文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;以&lt;code&gt;_AC&lt;/code&gt;结尾的参数在连接交流电源的时候生效&lt;/li&gt;
&lt;li&gt;以&lt;code&gt;_BAT&lt;/code&gt;结尾的参数在使用电池的时候有效&lt;/li&gt;
&lt;li&gt;既不以&lt;code&gt; _AC &lt;/code&gt;结尾也不以 &lt;code&gt;_BAT&lt;/code&gt; 结尾的参数适用于这两个配置文件&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;配置文件&#34; &gt;配置文件
&lt;span&gt;
    &lt;a href=&#34;#%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;按指定顺序从以下文件中读取设置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Intrinsic defaults 固有默认值（这个配置为TLP自带，不可被更改）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/tlp.d/*.conf&lt;/code&gt;：插入式自定义片段，按词法（字母顺序）顺序读取，不过建议可以使用一般配置命名方法（00_xxxx.conf)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/tlp.conf&lt;/code&gt;：用户配置&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;如果多个参数相同，但在同一文件中也存在相同的参数，则最后一个匹配项优先，这也意味着，&lt;code&gt;/etc/tlp.conf&lt;/code&gt; 中的参数将覆盖其他任何内容，因为它是最后读取的
默认的&lt;code&gt;/etc/tlp.conf &lt;/code&gt;中的所有参数都被禁用，删除前导 # 以激活您的更改
/etc/tlp.d/ 目录中的配置文件由用户创建：
* 文件名必须以 .conf 结尾，否则文件将被忽略
* 00-template.conf 作为示例提供&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;参数默认值&#34; &gt;参数默认值
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%82%e6%95%b0%e9%bb%98%e8%ae%a4%e5%80%bc&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;配置中有两种参数，一种是具有默认值的，会在本文档中说明，并且在&lt;code&gt;/etc/tlp.conf&lt;/code&gt;中有&lt;code&gt;Default&lt;/code&gt;前缀。还有一种没有默认值的。&lt;/p&gt;
&lt;h3 id=&#34;参数语法&#34; &gt;参数语法
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%82%e6%95%b0%e8%af%ad%e6%b3%95&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;配置文件由参数和注释行组成。&lt;/p&gt;
&lt;h4 id=&#34;参数行&#34; &gt;参数行
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%82%e6%95%b0%e8%a1%8c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;PARAMETER&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;value
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果value包含空格，则需要使用双引号&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;111 1111 1111&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;注释行&#34; &gt;注释行
&lt;span&gt;
    &lt;a href=&#34;#%e6%b3%a8%e9%87%8a%e8%a1%8c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;以&lt;code&gt;#&lt;/code&gt;开头，在1.6版本后可以在参数行后接&lt;code&gt;#&lt;/code&gt;作为注释&lt;/p&gt;
&lt;h4 id=&#34;禁用功能&#34; &gt;禁用功能
&lt;span&gt;
    &lt;a href=&#34;#%e7%a6%81%e7%94%a8%e5%8a%9f%e8%83%bd&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;没有默认值：使用注释或者删除即可&lt;/li&gt;
&lt;li&gt;有默认值： 赋空值即可 eg：&lt;code&gt;key=&amp;quot;&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;使用追加配置&#34; &gt;使用&lt;code&gt;+=&lt;/code&gt;追加配置
&lt;span&gt;
    &lt;a href=&#34;#%e4%bd%bf%e7%94%a8%e8%bf%bd%e5%8a%a0%e9%85%8d%e7%bd%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;和bash的环境变量一样，支持使用&lt;code&gt;+=&lt;/code&gt;作为追加配置&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用root权限编辑配置文件，在保存更改后可以使用重启，拔插ac电源或者使用&lt;code&gt;sudo tlp start&lt;/code&gt;命令激活配置&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;配置详解&#34; &gt;配置详解
&lt;span&gt;
    &lt;a href=&#34;#%e9%85%8d%e7%bd%ae%e8%af%a6%e8%a7%a3&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;h4 id=&#34;基础操作&#34; &gt;基础操作
&lt;span&gt;
    &lt;a href=&#34;#%e5%9f%ba%e7%a1%80%e6%93%8d%e4%bd%9c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TLP_ENABLE&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;设置为0可禁用TLP（需要重新启动）。未配置时的默认值：1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLP_WARN_LEVEL&lt;/td&gt;
&lt;td&gt;/&lt;/td&gt;
&lt;td&gt;控制如何发出有关无效设置的警告：
0 - 禁用
1 - 向系统日志/日志报告后台任务（启动、恢复、电源更改）
2 - 外壳命令向终端报告（标准）
3 - 1和2的组合。未配置时的默认值：3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLP_DEFAULT_MODE&lt;/td&gt;
&lt;td&gt;/&lt;/td&gt;
&lt;td&gt;定义TLP的默认操作模式（AC或BAT），以防无法检测到电源。仅涉及某些台式机和嵌入式硬件。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLP_PERSISTENT_DEFAULT&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;选择如何确定操作模式：
0 – 根据实际电源应用设置配置文件（默认）
1 – 始终使用TLP_DEFAULT_MODE设置。未配置时的默认值：0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLP_PS_IGNORE&lt;/td&gt;
&lt;td&gt;/&lt;/td&gt;
&lt;td&gt;确定工作模式时要忽略的电源等级：（用作错误检测到操作模式 AC 或 BAT 的笔记本电脑的解决方法）
AC
BAT
USB - 仅限版本 1.4 及更高版本。仅限版本 1.4 及更高版本：输入多个类，以空格分隔。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;音频&#34; &gt;音频
&lt;span&gt;
    &lt;a href=&#34;#%e9%9f%b3%e9%a2%91&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SOUND_POWER_SAVE_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;设置为0可禁用音频省电模式（需要重新启动）。未配置时的默认值：1（AC），1（BAT）- 版本 1.4 及更高版本，0（AC），1（BAT）- 版本 1.3。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SOUND_POWER_SAVE_CONTROLLER&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;Y – 关闭控制器和声音芯片的电源
N – 控制器保持活动状态。未配置时的默认值：Y。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;注释： &lt;code&gt;SOUND_POWER_SAVE_ON_AC/BAT&lt;/code&gt; 指的是&lt;code&gt;SOUND_POWER_SAVE_ON_AC&lt;/code&gt; 和 &lt;code&gt;SOUND_POWER_SAVE_ON_BAT&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&#34;电池保养&#34; &gt;电池保养
&lt;span&gt;
    &lt;a href=&#34;#%e7%94%b5%e6%b1%a0%e4%bf%9d%e5%85%bb&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;START_CHARGE_THRESH_BAT&amp;lt;x&amp;gt;&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;电池充电水平低于该水平，连接充电器时将开始充电。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;STOP_CHARGE_THRESH_BAT&amp;lt;x&amp;gt;&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;电池充电水平，超过该水平，充电器连接时充电将停止。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些参数用于设置笔记本电脑主/内部电池（BAT0）和辅助电池（BAT1）的充电阈值。启动充电阈值表示在连接充电器时，电池充电水平低于该值时将开始充电。停止充电阈值表示在充电器连接时，电池充电水平超过该值时将停止充电。这些阈值始终具有较低的可用电池容量，因此默认情况下禁用这些设置，并且必须通过删除前导 # 来显式启用这些设置。&lt;/p&gt;
&lt;h4 id=&#34;光驱&#34; &gt;光驱
&lt;span&gt;
    &lt;a href=&#34;#%e5%85%89%e9%a9%b1&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BAY_POWEROFF_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;控制光驱在交流电源和电池供电时是否关闭电源。
1：保持光驱开启状态
0：关闭光驱电源&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BAY_DEVICE&lt;/td&gt;
&lt;td&gt;sr0&lt;/td&gt;
&lt;td&gt;指定光驱设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;硬盘&#34; &gt;硬盘
&lt;span&gt;
    &lt;a href=&#34;#%e7%a1%ac%e7%9b%98&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DISK_DEVICES&lt;/td&gt;
&lt;td&gt;&amp;ldquo;nvme0n1 sda&amp;rdquo;&lt;/td&gt;
&lt;td&gt;定义参数作用的磁盘设备。多个设备用空白分隔。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DISK_APM_LEVEL_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;&amp;ldquo;254 254&amp;rdquo;（AC）
&amp;ldquo;128 128&amp;rdquo; (BAT)&lt;/td&gt;
&lt;td&gt;设置“高级电源管理级别”。可能的值介于1和255之间。
1 – 最大省电/最低性能 – 重要提示：此设置可能会导致磁盘驱动器磨损增加，因为读写磁头卸载过多
128 – 省电和磨损之间的折衷（电池的 TLP 标准设置）
192 – 防止某些 HDD 的磁头过度卸载
254 – 最小省电/最大性能（交流电的 TLP 标准设置）
255 – 禁用 APM（某些磁盘型号不支持）
keep – 用于跳过特定磁盘的此设置的特殊值（同义词：&lt;code&gt;_&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DISK_APM_CLASS_DENYLIST&lt;/td&gt;
&lt;td&gt;&amp;ldquo;usb ieee1394&amp;rdquo;&lt;/td&gt;
&lt;td&gt;从高级电源管理（APM）中排除磁盘类。可能的值：sata、ata、usb、ieee1394。默认为“usb ieee1394”。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DISK_SPINDOWN_TIMEOUT_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;&amp;ldquo;0 0&amp;rdquo;&lt;/td&gt;
&lt;td&gt;磁盘空闲时主轴电机停止的超时值。有效设置：0（已禁用）、1..240（5秒到20分钟）、241..251（30分钟到5.5小时）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DISK_IOSCHED&lt;/td&gt;
&lt;td&gt;&amp;ldquo;keep keep&amp;rdquo;&lt;/td&gt;
&lt;td&gt;两个参数为
多队列 （blk-mq） 调度器：&lt;code&gt;mq-deadline&lt;/code&gt; 、&lt;code&gt;none&lt;/code&gt;、&lt;code&gt;kyber&lt;/code&gt;、&lt;code&gt;bfq&lt;/code&gt;、&lt;code&gt;keep&lt;/code&gt;
单队列调度程序：&lt;code&gt;deadline&lt;/code&gt;、&lt;code&gt;cfq&lt;/code&gt;、&lt;code&gt;bfq&lt;/code&gt;、&lt;code&gt;noop&lt;/code&gt;、&lt;code&gt;keep&lt;/code&gt;
如果未配置，默认情况下所有磁盘将使用内核的默认调度程序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SATA_LINKPWR_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;&amp;ldquo;med_power_with_dipm&amp;rdquo;&lt;/td&gt;
&lt;td&gt;设置SATA链路的电源管理模式。可能的值包括：max_performance、medium_power、med_power_with_dipm、min_power。默认为med_power_with_dipm。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SATA_LINKPWR_DENYLIST&lt;/td&gt;
&lt;td&gt;&amp;ldquo;host1&amp;rdquo;&lt;/td&gt;
&lt;td&gt;从AHCI链路电源管理（ALPM）中排除SATA磁盘的主机列表。默认为空。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AHCI_RUNTIME_PM_ON_AC&lt;/td&gt;
&lt;td&gt;&amp;ldquo;on&amp;rdquo;&lt;/td&gt;
&lt;td&gt;控制NVMe、SATA、ATA和USB磁盘以及SATA端口的运行时电源管理。可能的值包括：auto（启用）、on（禁用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AHCI_RUNTIME_PM_ON_BAT&lt;/td&gt;
&lt;td&gt;&amp;ldquo;auto&amp;rdquo;&lt;/td&gt;
&lt;td&gt;同上&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AHCI_RUNTIME_PM_TIMEOUT&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;磁盘或端口挂起前的不活动时间（秒）。仅在激活AHCI_RUNTIME_PM_ON_AC/BAT时有效。默认为15。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;注释：DISK_IOSCHED 如果使用是NVME设备时，最好使用无IO调度程序来减少CPU开销（none和noop）&lt;/p&gt;
&lt;h4 id=&#34;文件系统&#34; &gt;文件系统
&lt;span&gt;
    &lt;a href=&#34;#%e6%96%87%e4%bb%b6%e7%b3%bb%e7%bb%9f&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DISK_IDLE_SECS_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;0 (AC), 2 (battery)&lt;/td&gt;
&lt;td&gt;笔记本电脑模式等待磁盘空闲的秒数，然后再次将脏缓存块从 RAM 同步到磁盘。值大于0将激活内核笔记本电脑模式。请勿更改此设置。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MAX_LOST_WORK_SECS_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;15 (AC), 60 (battery)&lt;/td&gt;
&lt;td&gt;将文件系统缓冲区中未保存的数据写入磁盘的超时时间（秒）。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;图形显卡&#34; &gt;图形显卡
&lt;span&gt;
    &lt;a href=&#34;#%e5%9b%be%e5%bd%a2%e6%98%be%e5%8d%a1&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;INTEL_GPU_MIN_FREQ_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;设置 Intel GPU 的最小频率。可能的值取决于硬件。通过运行 &lt;code&gt;tlp-stat -g&lt;/code&gt; 命令查看可用频率。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INTEL_GPU_MAX_FREQ_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;设置 Intel GPU 的最大频率。可能的值取决于硬件。通过运行 &lt;code&gt;tlp-stat -g&lt;/code&gt;命令查看可用频率。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INTEL_GPU_BOOST_FREQ_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;设置 Intel GPU 的睿频频率。可能的值取决于硬件。通过运行 &lt;code&gt;tlp-stat -g&lt;/code&gt; 命令查看可用频率。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RADEON_DPM_PERF_LEVEL_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;auto&lt;/td&gt;
&lt;td&gt;控制 AMD GPU 的动态电源管理（DPM）性能级别。支持 amdgpu（仅限 TLP 版本 1.4 及更高版本）和 radeon 驱动程序。可能的值包括 auto、low、high。默认值：auto。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RADEON_DPM_STATE_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;performance (AC), battery (BAT)&lt;/td&gt;
&lt;td&gt;控制 AMD GPU 的电源管理方法。可能的值包括 battery、balanced、performance。默认值：performance（AC）、battery（BAT）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RADEON_POWER_PROFILE_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;控制 AMD GPU 的时钟。仅在旧版 ATI 硬件上受 radeon 驱动程序支持（DPM 不可用）。可能的值包括 low、mid、high、auto、default。默认值：default。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些参数允许用户调整 Intel GPU 和 AMD GPU 在交流电和电池模式下的性能和电源管理行为。在配置这些参数时，建议参考硬件规格和运行 &lt;code&gt;tlp-stat -g&lt;/code&gt; 查看可用频率。&lt;/p&gt;
&lt;h4 id=&#34;kernel&#34; &gt;kernel
&lt;span&gt;
    &lt;a href=&#34;#kernel&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NMI_WATCHDOG&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;激活内核 NMI 看门狗定时器。设置为 0 表示禁用，有助于节省电源。设置为 1 表示启用，对于内核调试和看门狗守护程序是相关的。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;不建议关闭watchdog 否则可能导致内核崩溃后无法自动重启和内核调试&lt;/p&gt;
&lt;h4 id=&#34;网络&#34; &gt;网络
&lt;span&gt;
    &lt;a href=&#34;#%e7%bd%91%e7%bb%9c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WIFI_PWR_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;off (AC),&lt;/td&gt;
&lt;td&gt;设置 Wi-Fi 的电源保存模式。可能的值包括 off（禁用）和 on（启用）。默认值：off（AC）、on（BAT）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;on (BAT)&lt;/td&gt;
&lt;td&gt;提示：支持已弃用的配置值 1=off/5=on，以实现向后兼容性。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WOL_DISABLE&lt;/td&gt;
&lt;td&gt;Y&lt;/td&gt;
&lt;td&gt;控制是否禁用 Wake-on-LAN（LAN 唤醒）。可能的值包括 Y（禁用）和 N（不禁用，保持 BIOS 默认）。默认值：Y。
注意：更改为 WOL_DISABLE=N 后，需要重新启动才能使新设置生效（或在 shell 中使用 &lt;code&gt;sudo ethtool -s wol g&lt;/code&gt;）。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些参数允许用户配置Wi-Fi的电源保存模式和控制Wake-on-LAN（LAN唤醒）功能。&lt;/p&gt;
&lt;h4 id=&#34;平台&#34; &gt;平台
&lt;span&gt;
    &lt;a href=&#34;#%e5%b9%b3%e5%8f%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PLATFORM_PROFILE_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;performance&lt;/td&gt;
&lt;td&gt;选择平台配置文件以控制系统的功率/性能级别、散热和风扇速度的运行特性。可能的值包括 performance、balanced、low-power。默认值：performance（AC）、low-power（BAT）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEM_SLEEP_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;s2idle&lt;/td&gt;
&lt;td&gt;选择系统挂起模式。可能的值包括 s2idle（空闲待机）和 deep（挂起到 RAM）。注意：更改挂起模式可能导致系统不稳定和数据丢失。请使用 tlp-stat -s 检查系统上不同模式的可用性。如果不确定，请坚持使用系统默认值。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;其实如果能使用S3休眠那就更好，不过现在很多厂商并不支持S3,所以如果能用S2那就用S2吧。&lt;/p&gt;
&lt;h4 id=&#34;处理器&#34; &gt;处理器
&lt;span&gt;
    &lt;a href=&#34;#%e5%a4%84%e7%90%86%e5%99%a8&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CPU_DRIVER_OPMODE_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;active (amd-pstate), active (intel_pstate)&lt;/td&gt;
&lt;td&gt;选择 CPU 缩放驱动程序操作模式。配置取决于活动驱动程序：对于 amd-pstate（Active 模式），可能的值为 active 和 passive；对于 intel_pstate（Active 模式），可能的值为 active、passive 和 guided。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU_SCALING_GOVERNOR_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;powersave&lt;/td&gt;
&lt;td&gt;选择用于自动频率缩放的 CPU 缩放调节器。配置取决于活动驱动程序。可能的值包括 performance、powersave、conservative、ondemand、userspace 和 schedutil。默认值：powersave（AC）、powersave（BAT）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU_SCALING_MIN/MAX_FREQ_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;0, 9999999&lt;/td&gt;
&lt;td&gt;设置可用于缩放调控器的最小/最大频率。可能的值取决于您的 CPU。请查阅&lt;code&gt;tlp-stat -p&lt;/code&gt;的输出以获取可用频率。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU_ENERGY_PERF_POLICY_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;balance_performance&lt;/td&gt;
&lt;td&gt;设置 CPU 能耗/性能策略。可能的值包括 performance、balance_performance、default、balance_power 和 power。默认值：balance_performance（AC）、balance_power（BAT）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU_MIN/MAX_PERF_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;0, 100&lt;/td&gt;
&lt;td&gt;定义 Intel CPU 的最小/最大 P 状态，表示为总可用处理器性能的百分比。建议仅用于限制 CPU 的功耗。可能的值在 0 到 100 之间。默认值：0 到 100（AC）、0 到 30（BAT）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU_BOOST_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;配置 CPU “turbo boost”（Intel）或“turbo core”（AMD）功能。可能的值为 0（禁用）和 1（允许）。请注意，值为 1 不会激活提升，只是允许它。默认值：1（AC）、0（BAT）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU_HWP_DYN_BOOST_ON_AC/BAT&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;配置 Intel CPU HWP 动态提升功能。可能的值为 0（禁用）和 1（启用）。要求 Intel Core i 第 6 代（“Skylake”）或更新的 CPU，在活动模式下具有 intel_pstate 扩展驱动程序。默认值：1（AC）、0（BAT）。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些参数允许用户配置 CPU 的性能和功耗特性，包括缩放驱动程序操作模式、调节器、频率范围、能耗/性能策略、P 状态范围、提升功能以及 HWP 动态提升功能。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;部分电脑的BIOS会干预PState 所以需要检查自己的CPU是否支持&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;无线设备&#34; &gt;无线设备
&lt;span&gt;
    &lt;a href=&#34;#%e6%97%a0%e7%ba%bf%e8%ae%be%e5%a4%87&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RESTORE_DEVICE_STATE_ON_STARTUP&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;在启动时从上次关机中恢复无线电设备状态。可能的值为 0（禁用）和 1（启用）。默认值：0。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_DISABLE_ON_STARTUP&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;在启动时禁用内置无线电设备。可能的值包括 bluetooth、wifi 和 wwan，多个设备用空白分隔。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_ENABLE_ON_STARTUP&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;在启动时启用内置无线电设备。可能的值与上述相同，用于启用在默认情况下禁用的设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_ENABLE_ON_AC&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;插入交流电源时启用内置无线电设备。可能的值与上述相同。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_DISABLE_ON_BAT&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;在更改为电池电源时禁用内置无线电设备，无论其连接状态如何。可能的值与上述相同。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;在更改为电池电源时禁用未连接的内置无线电设备。可能的值与上述相同。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些参数允许用户配置在系统启动、关闭或更改电源状态时如何处理内置的蓝牙、Wi-Fi 和 WWAN 设备。可通过设置禁用或启用这些设备，以及在何种条件下执行这些操作。&lt;/p&gt;
&lt;h4 id=&#34;无线配置向导自动化配置&#34; &gt;无线配置向导（自动化配置）
&lt;span&gt;
    &lt;a href=&#34;#%e6%97%a0%e7%ba%bf%e9%85%8d%e7%bd%ae%e5%90%91%e5%af%bc%e8%87%aa%e5%8a%a8%e5%8c%96%e9%85%8d%e7%bd%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;参考参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_DISABLE_ON_LAN_CONNECT&lt;/td&gt;
&lt;td&gt;&amp;ldquo;wifi wwan&amp;rdquo;&lt;/td&gt;
&lt;td&gt;当建立 LAN 连接时，禁用蓝牙、Wi-Fi 和 WWAN 设备。多个设备用空白分隔。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_DISABLE_ON_WIFI_CONNECT&lt;/td&gt;
&lt;td&gt;&amp;ldquo;wwan&amp;rdquo;&lt;/td&gt;
&lt;td&gt;当建立 Wi-Fi 连接时，禁用 WWAN 设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_DISABLE_ON_WWAN_CONNECT&lt;/td&gt;
&lt;td&gt;&amp;ldquo;wifi&amp;rdquo;&lt;/td&gt;
&lt;td&gt;当建立 WWAN 连接时，禁用 Wi-Fi 设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_ENABLE_ON_LAN_DISCONNECT&lt;/td&gt;
&lt;td&gt;&amp;ldquo;wifi wwan&amp;rdquo;&lt;/td&gt;
&lt;td&gt;当断开 LAN 连接时，启用蓝牙、Wi-Fi 和 WWAN 设备。多个设备用空白分隔。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_ENABLE_ON_WIFI_DISCONNECT&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;当断开 Wi-Fi 连接时，启用所有设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_ENABLE_ON_WWAN_DISCONNECT&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;当断开 WWAN 连接时，启用所有设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_ENABLE_ON_DOCK&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;在对接后，启用所有设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_DISABLE_ON_DOCK&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;在对接后，禁用所有设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_ENABLE_ON_UNDOCK&lt;/td&gt;
&lt;td&gt;&amp;ldquo;wifi&amp;rdquo;&lt;/td&gt;
&lt;td&gt;在取消对接后，启用 Wi-Fi 设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEVICES_TO_DISABLE_ON_UNDOCK&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;在取消对接后，禁用所有设备。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些参数允许用户配置在特定事件触发时如何处理内置的蓝牙、Wi-Fi 和 WWAN 设备。用户可以根据 LAN、Wi-Fi 或 WWAN 的连接状态、对接或取消对接等事件来启用或禁用这些设备。&lt;/p&gt;
&lt;h4 id=&#34;pcie电源配置&#34; &gt;PCIE电源配置
&lt;span&gt;
    &lt;a href=&#34;#pcie%e7%94%b5%e6%ba%90%e9%85%8d%e7%bd%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RUNTIME_PM_ON_AC&lt;/td&gt;
&lt;td&gt;on&lt;/td&gt;
&lt;td&gt;控制 PCIe 设备的运行时电源管理。可能的值：auto（启用）或 on（禁用）。未配置时的默认值：on（AC）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RUNTIME_PM_ON_BAT&lt;/td&gt;
&lt;td&gt;auto&lt;/td&gt;
&lt;td&gt;控制 PCIe 设备的运行时电源管理。可能的值：auto（启用）或 on（禁用）。未配置时的默认值： auto（BAT）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RUNTIME_PM_DENYLIST&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;从运行时电源管理中排除列出的 PCIe 设备地址。使用 lspci 查找地址。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RUNTIME_PM_DRIVER_DENYLIST&lt;/td&gt;
&lt;td&gt;&amp;ldquo;mei_me nouveau radeon&amp;rdquo;&lt;/td&gt;
&lt;td&gt;从运行时电源管理中排除分配给所列驱动程序的 PCIe 设备。使用 tlp-stat -e 查找驱动程序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RUNTIME_PM_ENABLE&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;为列表中的 PCI（e） 设备地址永久启用（自动）运行时 PM。这优先于所有先前的运行时 PM 设置。使用 lspci 获取地址。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RUNTIME_PM_DISABLE&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;为列表中的 PCI（e） 设备地址永久禁用（on）运行时 PM。与 RUNTIME_PM_ENABLE 类似，不过是禁用。使用 lspci 获取地址。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PCIE_ASPM_ON_AC&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;设置 PCIe ASPM 省电模式。可能的值：default（推荐）、performance（性能）、powersave（省电）和 powersupersave（PowerSuperSave，超级省电）。未配置时的默认值：default。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PCIE_ASPM_ON_BAT&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;td&gt;设置 PCIe ASPM 省电模式。可能的值：default（推荐）、performance（性能）、powersave（省电）和 powersupersave（PowerSuperSave，超级省电）。未配置时的默认值：default。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些参数允许用户配置与 PCIe 设备相关的运行时电源管理和 ASPM 等功能。用户可以根据电源来源、设备地址、驱动程序等来调整这些设置，以实现更好的功耗管理。（建议不要对nvidia驱动进行调整，可能会引发意外）&lt;/p&gt;
&lt;h4 id=&#34;usb&#34; &gt;USB
&lt;span&gt;
    &lt;a href=&#34;#usb&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数名称&lt;/th&gt;
&lt;th&gt;默认参数值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;USB_AUTOSUSPEND&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;在启动时和插入时为 USB 设备设置自动挂起模式。可能的值：1（启用）或 0（禁用）。未配置时的默认值：1。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB_DENYLIST&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;从自动挂起模式中排除 USB 设备 ID。使用 tlp-stat -u 查找 ID。多个 ID 用空格分隔。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB_EXCLUDE_AUDIO&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;从自动挂起模式中排除音频设备：1（排除）或 0（不排除）。未配置时的默认值：1。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB_EXCLUDE_BTUSB&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;从自动挂起模式中排除蓝牙设备：1（排除）或 0（不排除）。未配置时的默认值：0。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB_EXCLUDE_PHONE&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;将智能手机从自动挂起模式中排除以启用充电：1（排除）或 0（不排除）。未配置时的默认值：0。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB_EXCLUDE_PRINTER&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;从自动挂起模式中排除打印机：1（排除）或 0（不排除）。未配置时的默认值：1。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB_EXCLUDE_WWAN&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;从自动挂起模式中排除内置 WWAN 设备：1（排除）或 0（不排除）。未配置时的默认值：0。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB_ALLOWLIST&lt;/td&gt;
&lt;td&gt;&amp;quot;&amp;quot;&lt;/td&gt;
&lt;td&gt;为已被上述任何设置排除的 USB 设备 ID 重新启用自动挂起模式。使用 &lt;code&gt;tlp-stat -u&lt;/code&gt; 查找 ID。多个 ID 用空格分隔。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;在系统关闭时禁用 USB 自动挂起模式：1（启用）或 0（禁用）。未配置时的默认值：0。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;trace-mode&#34; &gt;Trace Mode
&lt;span&gt;
    &lt;a href=&#34;#trace-mode&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;&lt;code&gt;TLP_DEBUG=&amp;quot;arg bat disk lock nm path pm ps rf run sysfs udev usb&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;结语&#34; &gt;结语
&lt;span&gt;
    &lt;a href=&#34;#%e7%bb%93%e8%af%ad&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;我们对于系统的优化不仅于此，现阶段tlp的配置策略仅对于部分有能力的用户公开，后续经过充分的测试和调优之后，会提供几份默认的配置给普通用户使用。并将来将这些配置文件GUI化，集成于深度定制项目中，为用户提供更为方便直观的操作体验。&lt;/p&gt;
&lt;p&gt;从这一阶段对于电源优化的探索可以看出，deepin系统的电源管理方案优化不仅是为了解决用户反馈的问题，更是一种对用户需求的回应和尊重。在未来，deepin系统将继续秉持用户至上的原则，不断提升系统的性能和用户体验，为广大用户提供更加优秀的操作系统产品。&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/">
      <title>关于 DDUC2023 和 QtWS23 分享内容的一些想法</title>
      <link>https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/</link>
      <pubDate>Thu, 30 Nov 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/deepin%E4%B8%8Eqt/</guid>
      <description>&lt;p&gt;2023 年 11 月 21 日，Qt Group 在上海成功举办了“Qt 全球峰会 2023 中国站”，本次大会上发布了关于 Qt 开放框架与开发工具的战略，集中展示了最新的产品、解决方案，阐述了它们在优化软件跨平台开发全流程、提升研发团队跨职能协作效率以及加速产品迭代速度等方面的强大优势，同时大会上也分享了对于人工智能如何影响软件开发和测试的思考。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./1.png&#34; alt=&#34;pic1&#34;&gt;&lt;/p&gt;
&lt;p&gt;Qt 作为 Linux 上最重要的开发工具之一，为中国信创产业的发展和创新提供了更多的可能性和选择。Qt Group 中国区负责人许晟在开幕致辞中表示，“Qt 中国作为 Qt Group 在中国的本地化团队，不仅通过源代码交付帮助本地企业实现软件的自主创新，还基于强大的跨平台能力支持针对本地操作系统和主流芯片的开发，为中国信创产业的发展和创新提供了更多的可能性和选择。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./2.png&#34; alt=&#34;pic2&#34;&gt;&lt;/p&gt;
&lt;p&gt;值得一提的是，deepin 已为 Qt社区贡献 100000+ 行代码，在贡献者中名列前茅。此外，deepin 社区也基于 Qt 开发了一整套简单且实用的通用开发框架 Development ToolKit（简称DTK），DTK 处于deepin系统中的核心位置。DDE 中超过30+组件，如浏览器、音乐、邮件等40余款原生应用全部使用 DTK 开发。为此，deepin（深度）社区在&lt;a href=&#34;docs.deepin.org&#34;&gt; deepin 开发者平台&lt;/a&gt;上专门提供了在 deepin 上进行 Qt 开发的文档指导。&lt;/p&gt;
&lt;p&gt;2023 年 11 月 18 日，deepin（深度）社区在北京798艺术中心举办了第十三届深度开发者与用户大会（Deepin Developer&amp;amp;User Conference，简称DDUC），在本次大会上，deepin社区也是公开了近一年在 DTK 和 Qt 这块的建设成果，目前 DTK 已正式适配 Qt6(6.4.2)，实现全面升级，相关介绍可以查看&lt;a href=&#34;https://www.deepin.org/zh/2023-9-7/&#34;&gt;《deepin（深度）宣布 deepin DTK 已完成基于 Qt6 的全面升级！》&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./3.png&#34; alt=&#34;pic3&#34;&gt;&lt;/p&gt;
&lt;p&gt;在本次 QtWS23 峰会上，Qt Group 资深开发工程师齐亮介绍了Qt Wayland 的最新进展。近些年 Linux 桌面发行版迁移到 Wayland 的趋势愈发明显，大有替代  X11 之势。Ubuntu 目前已经默认 Wayland 会话，GNOME 在 Wayland 支持这块一直走在 Linux 发行版的前列，且动作非常激进，目前 GNOME 桌面已经对外宣布将移除对 &lt;a href=&#34;http://x.org/&#34;&gt;X.Org&lt;/a&gt; 会话支持，默认使用 Wayland。X11 诞生于 1984 年，其设计符合当时的硬件环境、用户需求。随着技术的发展，出现了很多问题，特别是在桌面环境里面，需要 Xorg、窗口管理器、桌面环境组件三者之间进行复杂的交互、协作，导致演进困难。同时 X11 对于新特性的支持较差，Hi-DPI 体验糟糕，并且完全不支持 HDR。&lt;/p&gt;
&lt;p&gt;事实上，在 2023 年5 月 17 日，deepin（深度）社区在推出 deepin V23 Beta 版的同时也已经支持了 Wayland 桌面环境。在 V23 beta 版本中，DDE 试验性的开启了 Wayland 的支持，允许用户在 Wayland 协议下的桌面工作环境启动 。在 deepin V23 中支持 Wayland，是 DDE 的一个非常重要的特性，也是今后 deepin 团队的工作重点，以实现 X11 版本的完全替代，提升 DDE 的优质体验，在 Wayland 桌面环境领域达到领先水平。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DDE（deepin desktop environment）是deepin（深度）社区自主开发的美观易用、极简操作的桌面环境，主要由桌面、启动器、任务栏、控制中心、窗口管理器等组成，系统中预装了深度特色应用，它既能让您体验到丰富多彩的娱乐生活，也可以满足您的日常工作需要。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在此背景下，deepin 团队在今年 DDUC 大会了宣布将推出 Treeland 作为今后 DDE 所有功能开发的核心。Treeland 的底层基于 wlroots，并与 Qt Quick 进行了绑定，可同时兼顾两者的优点，wlroots 是 Wayland 生态中发展最迅速的开发库之一，具有功能丰富、演进速度快等优势，将其与 Qt Quick 结合则可以弥补 wlroots 在 GUI 能力方面的欠缺，极大的降低 Wayland 合成器的开发难度，实现 Vulkan、OpenGL ES2、软件渲染等多种渲染方式的无缝切换。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./4.png&#34; alt=&#34;pic4&#34;&gt;&lt;/p&gt;
&lt;p&gt;DDE 的新架构，将桌面环境各技术领域的组件进行了统一设计，允许桌面环境开发人员对其进行完全掌控，可轻松实现设备共享、多端无缝协同等高级功能。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;./5.png&#34; alt=&#34;pic5&#34;&gt;&lt;/p&gt;
&lt;p&gt;未来，deepin（深度）社区将继续与最新技术保持同步，持续推进 DTK 的改进优化，也期待更多感兴趣的朋友加入到 deepin 开源社区中来，讨论更多内容，为推动生态发展贡献力量。&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/5g-support/">
      <title>deepin 5g wwan 支持</title>
      <link>https://blog.deepin.org/posts/5g-support/</link>
      <pubDate>Wed, 08 Nov 2023 00:00:00 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/5g-support/</guid>
      <description>&lt;p&gt;本文简述了deepin v23系统使用5G WWAN网卡连接互联网的方式，仅仅作为一个临时解决方案和技术验证使用，后期会在系统中内置此功能。&lt;/p&gt;
&lt;h2 id=&#34;第一步安装modemmanager&#34; &gt;第一步：安装modemmanager
&lt;span&gt;
    &lt;a href=&#34;#%e7%ac%ac%e4%b8%80%e6%ad%a5%e5%ae%89%e8%a3%85modemmanager&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;modemmanager是一个由freedesktop托管的项目，旨在在linux设备上运行调制解调器以让linux设备获得蜂窝无线网络连接的能力，所以我们第一步骤就是安装此软件。目前deepin已经支持此软件最新版本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;sudo apt install modemmanager
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同时，你需要确保你的内核模块已经正确加载了你的WWAN驱动，你可以通过使用&lt;code&gt;lspci -vvv&lt;/code&gt;查看详细信息&lt;/p&gt;
&lt;p&gt;在安装了modemmanager后，你可以通过&lt;code&gt;mmcli&lt;/code&gt; 命令使用命令行与其交互，使用&lt;code&gt;mmcli —help-all&lt;/code&gt;来获取全部的帮助选项。&lt;/p&gt;
&lt;h2 id=&#34;第二步使用mmcli连接5g网络&#34; &gt;第二步：使用mmcli连接5G网络
&lt;span&gt;
    &lt;a href=&#34;#%e7%ac%ac%e4%ba%8c%e6%ad%a5%e4%bd%bf%e7%94%a8mmcli%e8%bf%9e%e6%8e%a55g%e7%bd%91%e7%bb%9c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;使用&lt;code&gt;mmcli -L&lt;/code&gt; 获取你的wwan卡信息，返回的信息有三个内容，分别为：DBus地址、设备类型 、设备ID，其中DBus地址的最后一位为设备编号，你可以使用&lt;code&gt;mmcli --modem=&amp;lt;设备编号&amp;gt;&lt;/code&gt;的方式查看设备详细支持信息。&lt;/p&gt;
&lt;p&gt;如果你的SIM卡设置了PIN锁，则需要在连接网络之前使用&lt;code&gt;mmcli --modem=0 --sim=0 --pin=****&lt;/code&gt; 的方式连接，而后我们就可以启动相关设备了：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mmcli --modem&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&amp;lt;设备编号&amp;gt; --enable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后你需要使用simple connect连接网络&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mmcli -m &amp;lt;设备编号&amp;gt; --simple-connect&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;apn=&amp;lt;apn名&amp;gt;,ip-type=ipv4v6&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;比如我的连接方式为&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mmcli -m &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt; --simple-connect&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;apn=ctnet,ip-type=ipv4v6&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此时你再使用&lt;code&gt;mmcli -m &amp;lt;设备编号&amp;gt;&lt;/code&gt; 查看信息的时候 可以查看Bearer的信息，Bearer的DBus的最后一位为其编号。使用命令查看Bearer的信息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mmcli -m &amp;lt;设备编号&amp;gt; -b &amp;lt;Bearer编号&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你看到Bearer相关信息，就几乎接近成功了：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;  ------------------------------------
  General            |           path: /org/freedesktop/ModemManager1/Bearer/0
                     |           type: default
  ------------------------------------
  Status             |      connected: yes
                     |      suspended: no
                     |    multiplexed: no
                     |      interface: wwan0
                     |     ip timeout: 20
  ------------------------------------
  Properties         |            apn: ctnet
                     |        roaming: allowed
                     |        ip type: ipv4
                     |   allowed-auth: none, pap, chap, mschap, mschapv2, eap
                     |           user: ctnet@mycdma.cn
                     |       password: vnet.mobi
  ------------------------------------
  IPv4 configuration |         method: static
                     |        address: 10.122.58.19
                     |         prefix: 8
                     |        gateway: 10.122.58.17
                     |            dns: 202.103.24.68, 202.103.44.150
                     |            mtu: 1420
  ------------------------------------
  Statistics         |     start date: 2023-11-07T05:40:08Z
                     |       duration: 1260
                     |   uplink-speed: 1250000000
                     | downlink-speed: 4670000000
                     |       attempts: 1
                     | total-duration: 1260

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;第三步使用nmcli打开连接&#34; &gt;第三步：使用nmcli打开连接
&lt;span&gt;
    &lt;a href=&#34;#%e7%ac%ac%e4%b8%89%e6%ad%a5%e4%bd%bf%e7%94%a8nmcli%e6%89%93%e5%bc%80%e8%bf%9e%e6%8e%a5&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;networkmanager对mm是有做支持，在你完成上述步骤之后，可以通过&lt;code&gt;ip a&lt;/code&gt;命令查看，可以看到一个被down掉的接口，我们使用nmcli查看其详细信息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;nmcli device show
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你就可以 看到一个以wwan开头的设备：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;GENERAL.DEVICE:                         wwan0mbim0
GENERAL.TYPE:                           gsm
GENERAL.HWADDR:                         （未知）
GENERAL.MTU:                            1420
GENERAL.STATE:                          100（已连接）
GENERAL.CONNECTION:                     wwan0mbim0
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/ActiveConnection/3
IP4.ADDRESS[1]:                         10.122.58.19/8
IP4.GATEWAY:                            10.122.58.17
IP4.ROUTE[1]:                           dst = 10.0.0.0/8, nh = 0.0.0.0, mt = 700
IP4.ROUTE[2]:                           dst = 0.0.0.0/0, nh = 10.122.58.17, mt = 700
IP4.DNS[1]:                             202.103.24.68
IP4.DNS[2]:                             202.103.44.150
IP6.GATEWAY:                            --

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用下述命令打开此设备&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;nmcli d connect &amp;lt;设备名&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上述设备名就是以wwan开头的设备&lt;/p&gt;
&lt;p&gt;然后你就可以使用5G网络连接了&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/xtrace/">
      <title>xtrace</title>
      <link>https://blog.deepin.org/posts/xtrace/</link>
      <pubDate>Sat, 04 Nov 2023 10:19:31 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/xtrace/</guid>
      <description>&lt;h2 id=&#34;介绍&#34; &gt;介绍
&lt;span&gt;
    &lt;a href=&#34;#%e4%bb%8b%e7%bb%8d&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;xtrace是一个用于跟踪分析X11图形协议通信的工具,它可以监控和记录X11服务器上的各种场景,以帮助开发人员诊断和调试与图形界面相关的问题。作为一款强大的工具，xtrace可用于逆向工程、调试分析、性能分析等领域。在Linux X11系统中，xtrace能够记录一个程序在运行时所发起的X11协议请求和XServer发送给程序的事件，以及这些调用的参数。这对于在不阅读源码情况排查程序中的问题、理解程序行为、分析性能瓶颈以及进行协议审计都非常有用。笔者在工作过程中使用该工具深度剖析过腾讯会议、simplescreenrecorder等应用程序的实现，在没有阅读代码的前提下可以获得软件录屏的工作流程，配合阅读常规的X11录屏代码，可分析出其部分工功能异常原因，这些经验在xwayland适配X11应用程序截图录屏项目中通过实战解决了一系列问题。&lt;/p&gt;
&lt;p&gt;本文主要介绍的是X11协议的监视工具，希望您在读完本篇文章后可以对如何监控X11协议有比较深刻的认知，在工作中经常会碰到一些X11应用程序运行时功能异常，在没有应用程序源码的情况下通过xtrace进行调试是一个不错的选择，希望在阅读完这篇文章后，能丰富您的调试技巧。祝您阅读愉快！&lt;/p&gt;
&lt;p&gt;xtrace的安装和使用非常简单，打开终端像如下输入命令即可启动工具：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;// UOS上安装xtrace，deepin上没有可以在http://snapshot.debian.org/上下载
sudo apt install xtrace

// 运行之后协议电报内容会存储在/tmp/dde-calendar.xtrace中
xtrace -n -o /tmp/dde-calendar.xtrace dde-calendar	
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令行选项&lt;/th&gt;
&lt;th&gt;含义或用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;ndash;display, -d&lt;/td&gt;
&lt;td&gt;用于ssh远程调试，例如:xtrace -d :0 dde-calendar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;ndash;outfile, -o&lt;/td&gt;
&lt;td&gt;用于将通信内容转存到磁盘，例如:xtrace -o /tmp/x.log dde-calendar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;ndash;stopwhendone, -s&lt;/td&gt;
&lt;td&gt;进程退出后停止xtrace，例如:xtrace -s dde-calendar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;工作原理&#34; &gt;工作原理
&lt;span&gt;
    &lt;a href=&#34;#%e5%b7%a5%e4%bd%9c%e5%8e%9f%e7%90%86&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;如图1所示，X11其主要有两种通信方式，在同一个计算机内主要使用unix domain socket进行通信；在不同计算机之间使用TCP/IP通信。只需要“截获”通信消息，将其转为易于读取的格式输出即可达到监控目的。这是xtrace工作的基本原理。
&lt;figure&gt;&lt;img src=&#34;x11-frame.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图1. 不同客户端使用同一个XServer显示器框架图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;如下图2是X11窗口创建的基本流程，因为篇幅限制，图中只绘制了基础的请求和事件，但X11协议远比图中绘制的复杂，笔者在此只介绍一下基础的协议交互模型，方便在读者在查看xtrace追踪日志时有基础的认知。
&lt;figure&gt;&lt;img src=&#34;client-server.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图2. X11协议交互流程&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;总之xtrace是Xorg X Server自带的一个工具，通过分析xtrace的输出，你可以了解X Server是如何处理客户端应用程序的请求，以及可能的问题所在。xtrace 工具的作用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;调试X问题：如果你遇到了与图形界面相关的问题，例如窗口无法正常显示、图形卡驱动问题等，你可以使用xtrace来捕获X Server的活动，帮助你找出问题所在。&lt;/li&gt;
&lt;li&gt;性能分析：xtrace可以记录X Server内部的操作，帮助你分析系统的图形性能，找出潜在的瓶颈。&lt;/li&gt;
&lt;li&gt;理解X协议交互：X Server与客户端应用程序之间的通信是通过X协议进行的。xtrace可以捕获这些通信，帮助你理解应用程序与X Server之间的交互方式。&lt;/li&gt;
&lt;li&gt;功能逆向：对于依赖X11协议的软件无法查看源码，可以通过观察通信协议，逆向其部分核心功能的实现
请注意，xtrace的输出可能会非常详细，因此在使用时需要注意过滤和分析输出，以便关注于你感兴趣的信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;X11在文件系统中暴露了通信用的socket套接字文件，这使得第三方应用程序可以监控套接字文件从而“窥视&amp;quot;X11客户端和服务端之间的通信，这一技术点是xtrace工作的主要基础。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;// X11 socket套接字文件在文件系统中的位置
ls /tmp/.X11-unix
X0  X9
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如下日志所示xtrace工作的本质就是不断地获取wrote和received的数据然后将其解析为易于阅读的描述语言打印出来。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;// 全量日志
001:&amp;gt;:received &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt; bytes
001:&amp;gt;:09dc:32: Reply to InternAtom: atom&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x250&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_NET_KDE_COMPOSITE_TOGGLING&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
001:&amp;gt;:wrote &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt; bytes
001:&amp;gt;:received &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt; bytes
001:&amp;gt;:09dc: Event XKEYBOARD-XkbEvent&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;85&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x018d8071 device&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x03 not-yet-supported&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x10,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x01,0x90,0x10,0x10,0x10,0x90,0x00,0x01,0x90,0x11,0x00,0x00,0x87,0x05;
001:&amp;gt;:wrote &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt; bytes
001:&amp;gt;:received &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt; bytes
001:&amp;gt;:09dc: Event XKEYBOARD-XkbEvent&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;85&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x018d8072 device&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x03 not-yet-supported&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x10,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x01,0x90,0x11,0x00,0x00,0x87,0x05;
001:&amp;gt;:wrote &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt; bytes
001:&amp;gt;:received &lt;span style=&#34;color:#ae81ff&#34;&gt;32&lt;/span&gt; bytes
001:&amp;gt;:09dc: Event XKEYBOARD-XkbEvent&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;85&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; time&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x018d8073 device&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x03 not-yet-supported&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0x10,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x01,0x90,0x10,0x10,0x10,0x90,0x00,0x01,0x90,0x11,0x00,0x00,0x87,0x05;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其工作流程大致如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过套接字地址族AF_INET判断是tcp通信还是本地domain socket通信，然后通过generateSocketName或者calculateTCPport拿到addr相关的信息&lt;/li&gt;
&lt;li&gt;进入mainqueue死循环读取socket通信数据&lt;/li&gt;
&lt;li&gt;通过parse_server(c)翻译Event事件通信，然后打印输出&lt;/li&gt;
&lt;li&gt;parse_client(c)翻译Requst请求通信，然后打印输出&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;相关的代码调用堆栈如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;Breakpoint &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, startline (c&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x4f8270&lt;/span&gt;, d&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;TO_SERVER, format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x417a71&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%04x:%3u: Request(%hhu): %s &amp;#34;&lt;/span&gt;) at parse.c:&lt;span style=&#34;color:#ae81ff&#34;&gt;52&lt;/span&gt;
&lt;span style=&#34;color:#ae81ff&#34;&gt;52&lt;/span&gt;              &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;( (print_timestamps &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; print_reltimestamps)
(gdb) bt
&lt;span style=&#34;color:#75715e&#34;&gt;#0  startline (c=0x4f8270, d=TO_SERVER, format=0x417a71 &amp;#34;%04x:%3u: Request(%hhu): %s &amp;#34;) at parse.c:52
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#1  0x000000000040b5f5 in print_client_request (c=0x4f8270, bigrequest=false) at parse.c:1692
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#2  0x000000000040cc2d in parse_client (c=0x4f8270) at parse.c:1996
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#3  0x0000000000403b4a in mainqueue (listener=4) at main.c:406
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#4  0x00000000004045d4 in main (argc=4, argv=0x7fffffffdef8) at main.c:706
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;(gdb) c
Continuing.

Breakpoint &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, startline (c&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x4f8270&lt;/span&gt;, d&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;TO_CLIENT, format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x417b32&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%04x:%u: Reply to %s: &amp;#34;&lt;/span&gt;) at parse.c:&lt;span style=&#34;color:#ae81ff&#34;&gt;52&lt;/span&gt;
&lt;span style=&#34;color:#ae81ff&#34;&gt;52&lt;/span&gt;              &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;( (print_timestamps &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; print_reltimestamps)
(gdb) bt
&lt;span style=&#34;color:#75715e&#34;&gt;#0  startline (c=0x4f8270, d=TO_CLIENT, format=0x417b32 &amp;#34;%04x:%u: Reply to %s: &amp;#34;) at parse.c:52
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#1  0x000000000040c1f3 in print_server_reply (c=0x4f8270) at parse.c:1859
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#2  0x000000000040d0e5 in parse_server (c=0x4f8270) at parse.c:2064
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#3  0x00000000004035ed in mainqueue (listener=4) at main.c:336
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#4  0x00000000004045d4 in main (argc=4, argv=0x7fffffffdef8) at main.c:706
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;// connect socket
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;Breakpoint &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, generateSocketName (addr&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x7fffffffdd30&lt;/span&gt;, display&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;) at x11common.c:&lt;span style=&#34;color:#ae81ff&#34;&gt;114&lt;/span&gt;
&lt;span style=&#34;color:#ae81ff&#34;&gt;114&lt;/span&gt;             snprintf(addr&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;sun_path,&lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(addr&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;sun_path),&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tmp/.X11-unix/X%d&amp;#34;&lt;/span&gt;,display);
(gdb) p display 
&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;
(gdb) c
Continuing.
[Detaching after fork from child process &lt;span style=&#34;color:#ae81ff&#34;&gt;7682&lt;/span&gt;]
Got connection from unknown(local)

Breakpoint &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, generateSocketName (addr&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0x7fffffffd9d0&lt;/span&gt;, display&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) at x11common.c:&lt;span style=&#34;color:#ae81ff&#34;&gt;114&lt;/span&gt;
&lt;span style=&#34;color:#ae81ff&#34;&gt;114&lt;/span&gt;             snprintf(addr&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;sun_path,&lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(addr&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;sun_path),&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tmp/.X11-unix/X%d&amp;#34;&lt;/span&gt;,display);
(gdb) p display 
&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
(gdb) bt
&lt;span style=&#34;color:#75715e&#34;&gt;#0  generateSocketName (addr=0x7fffffffd9d0, display=0) at x11common.c:114
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#1  0x0000000000404c92 in connectToServer (displayname=0x7fffffffe363 &amp;#34;:0&amp;#34;, family=1, hostname=0x0, display=0) at x11client.c:77
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#2  0x0000000000402766 in acceptConnection (listener=3) at main.c:95
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#3  0x0000000000403edd in mainqueue (listener=3) at main.c:452
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#4  0x00000000004045d4 in main (argc=2, argv=0x7fffffffdf18) at main.c:706
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;(gdb) 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;协议分析&#34; &gt;协议分析
&lt;span&gt;
    &lt;a href=&#34;#%e5%8d%8f%e8%ae%ae%e5%88%86%e6%9e%90&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;xtrace通过拦截X11协议通信来进行分析。它捕获传输到X服务器的请求以及服务器对这些请求的响应，将他们解析化以日志输出的形式打印出来。所以本质来说协议分析指的是X11协议交互分析，需要对
X11相关的协议做到非常了解，即每个协议有什么功能，在xcb中是如何处理，在xserver中又是如何处理。受限于篇幅，笔者在此不会阐述所有的协议分析，而是拿我们平时常用的一些软件做一些分析和介绍。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;日志流关键字&lt;/th&gt;
&lt;th&gt;含义或用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Present-Request(148,1)&lt;/td&gt;
&lt;td&gt;客户端用于GLX等送显&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request(1): CreateWindow&lt;/td&gt;
&lt;td&gt;请求创建X窗口，shm、glx都有&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLX-Request(152,3): glXCreateContext&lt;/td&gt;
&lt;td&gt;请求创建GLX上下文，可以用来判断客户端是否GLX应用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MIT-SHM-Request(130,3): PutImage&lt;/td&gt;
&lt;td&gt;X11 shm客户端请求更新图像&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event XKEYBOARD-XkbEvent(85)&lt;/td&gt;
&lt;td&gt;xserver键盘事件传递给X客户端&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event Generic(35) XInputExtension&lt;/td&gt;
&lt;td&gt;鼠标事件，后面带着ButtonPress、ButtonRelease、Motion等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request(36): GrabServer&lt;/td&gt;
&lt;td&gt;grab请求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request(37): UngrabServer&lt;/td&gt;
&lt;td&gt;解除grab请求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeleteProperty&lt;/td&gt;
&lt;td&gt;请求删除X11窗口的一些属性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChangeProperty&lt;/td&gt;
&lt;td&gt;改变X11窗口的一些属性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PropertyNotify&lt;/td&gt;
&lt;td&gt;窗口属性改变发送事件通知客户端&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MIT-SHM-Request(130,4): GetImage&lt;/td&gt;
&lt;td&gt;shm方式获取屏幕图像&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request(62): CopyArea&lt;/td&gt;
&lt;td&gt;复制屏幕一部分区域图像（离屏）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request(53): CreatePixmap&lt;/td&gt;
&lt;td&gt;创建图像（离屏）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;上述表格中只是介绍了常见部分的协议，X协议非常的丰富，完整的模块如下所示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XCB BigRequests API	发送和接受超过请求长度(65535字节)限制的数据&lt;/li&gt;
&lt;li&gt;XCB Composite API	支持窗口合成，将多个窗口的内容合成最终的显示图像&lt;/li&gt;
&lt;li&gt;XCB Damage API	用于跟踪窗口或者绘图上下文的可视区域的改变&lt;/li&gt;
&lt;li&gt;XCB DPMS API	用于管理显示器的电源管理功能，控制显示器电源模式&lt;/li&gt;
&lt;li&gt;XCB DRI2 API	支持直接渲染和硬件加速&lt;/li&gt;
&lt;li&gt;XCB DRI3 API	支持直接渲染和硬件加速，对DRI2的扩展和改进&lt;/li&gt;
&lt;li&gt;XCB Glx API	提供GLX接口创建OpenGL上下文，进行图形渲染和交互&lt;/li&gt;
&lt;li&gt;XCB Present API	实现高性能的图像呈现在屏幕上&lt;/li&gt;
&lt;li&gt;XCB RandR API	管理显示器的分辨率、屏幕方向和显示器布局等&lt;/li&gt;
&lt;li&gt;XCB Record API	记录X服务器的事件流，以便进行调试、分析等&lt;/li&gt;
&lt;li&gt;XCB Render API	xrender绘图&lt;/li&gt;
&lt;li&gt;XCB ScreenSaver API	管理屏幕保护程序的行为和状态&lt;/li&gt;
&lt;li&gt;XCB Shape API	用于创建和操作不规则窗口的形状&lt;/li&gt;
&lt;li&gt;XCB Shm API	应用程序能够通过共享内存的方式高效的传输图像&lt;/li&gt;
&lt;li&gt;XCB Sync API	实现同步操作和时间戳的管理，确保预期的时序&lt;/li&gt;
&lt;li&gt;XCB XCMisc API	提供额外的杂项函数和功能，获取一些服务器信息&lt;/li&gt;
&lt;li&gt;XCB Core API	核心部分，提供了基本通信功能和操作&lt;/li&gt;
&lt;li&gt;XCB Xevie API	拦截和处理X服务器上的事件流&lt;/li&gt;
&lt;li&gt;XCB XF86Dri API	直接渲染，直接访问图形硬件，提高图形性能和效率&lt;/li&gt;
&lt;li&gt;XCB XFixes API	增强功能：光标、窗口形状、窗口属性、窗口位置&lt;/li&gt;
&lt;li&gt;XCB Xinerama API	用于管理多个显示器的配置和操作&lt;/li&gt;
&lt;li&gt;XCB Input API	处理输入事件（如键盘、鼠标、触摸屏等）交互&lt;/li&gt;
&lt;li&gt;XCB xkb API	配置与操作键盘相关的设置，键盘布局和状态&lt;/li&gt;
&lt;li&gt;XCB XPrint API	直接从应用程序打印文档、图像和其他内容&lt;/li&gt;
&lt;li&gt;XCB API	xcb基础功能&lt;/li&gt;
&lt;li&gt;XCB SELinux API	在应用程序中管理selinux安全策略和执行安全操作&lt;/li&gt;
&lt;li&gt;XCB Test API	测试协议，常用于远程控制&lt;/li&gt;
&lt;li&gt;XCB Xv API	xvideo相关，用于视频渲染、视频加速、获取视频信息&lt;/li&gt;
&lt;li&gt;XCB XvMC API	在GPU上执行视频解码和运动补偿功能
笔者在工作中也有时对xtrace日志无法分析到有用信息,此时会查看&lt;a href=&#34;https://xcb.freedesktop.org/manual/modules.html&#34;&gt;XCB&lt;/a&gt;帮助文档，通过分析对比查找到相关的xcb函数，从而逐渐熟悉X11协议。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;总结&#34; &gt;总结
&lt;span&gt;
    &lt;a href=&#34;#%e6%80%bb%e7%bb%93&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;总体来说xtrace是一个有用的工具，对于笔者来说经常会接触生态软件的图形显示问题，对于少部分软件开发商不愿意提供代码和问题复现最小demo，此时其软件对于笔者来说是一个黑盒，当问题边界靠近X相关的技术时，笔者会使用xtrace去详细分析该软件的详细功能，往往这可以在底层剖析软件的显示工作方式，配合系统上相关的组建库代码，可以方便地处理客户的紧急问题。
笔者编写这篇文档是希望可以鼓励更多的同事使用xtrace，这个工具可以帮助你熟悉X11的工具原理，同时也可以理解像qt、gtk等UI库的底层实现，在定位系统复制粘贴、图形显示、拖拽、窗口相关的问题时可以提供更加底层的日志，方便更加精准地定位问题根因！&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34; &gt;参考资料
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/X_Window_System&#34;&gt;X11&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/X_server&#34;&gt;XServer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/drm/">
      <title>drm介绍</title>
      <link>https://blog.deepin.org/posts/drm/</link>
      <pubDate>Wed, 01 Nov 2023 13:19:31 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/drm/</guid>
      <description>&lt;h2 id=&#34;介绍&#34; &gt;介绍
&lt;span&gt;
    &lt;a href=&#34;#%e4%bb%8b%e7%bb%8d&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;随着Linux图形发展的需求增长，需要更多的图形功能和性能，这促成了Direct Rending Infastructure（DRI）和Direct Rendering Manager（DRM）的出现，DRM是一个内核级别的子系统，提供了对图形设备的管理和访问控制，它允许用户空间的图形库和应用程序直接访问GPU进行渲染，而无需操作系统介入，它的主要作用如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;图形硬件管理：DRM负责管理图形硬件资源，如显存、显卡以及显示设备。它允许内核与这些硬件设备进行通信和控制。&lt;/li&gt;
&lt;li&gt;图形加速：提供对硬件加速功能的支持，例如3D渲染和视频加速。这使得图形渲染更加高效和流畅。&lt;/li&gt;
&lt;li&gt;多显示器支持：允许多个显示器的管理和配置，包括扩展桌面、镜像模式等。&lt;/li&gt;
&lt;li&gt;用户空间接口：提供了用户空间图形库（如Mesa 3D等）和应用程序与内核中DRM子系统进行通信的接口。&lt;/li&gt;
&lt;li&gt;支持不同的图形协议：DRM在支持X Window System（X11）的同时，也能与新一代图形协议，如Wayland，进行整合。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种更好的性能、3D加速和对硬件的更直接访问使得它可以支持X Window System和Wayland的图形输出和管理。其现状如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;持续发展：随着硬件技术和图形需求的不断演进，DRM在Linux内核中也在不断发展。新的功能和改进不断加入，以满足新一代图形硬件和应用程序的需求。&lt;/li&gt;
&lt;li&gt;多厂商支持：DRM的发展得到了多家硬件厂商的支持，这包括AMD、Intel、NVIDIA等，它们为Linux内核开发并贡献了各自硬件的DRM驱动程序。&lt;/li&gt;
&lt;li&gt;支持新技术：DRM也在逐步支持新的图形技术，例如，对于低功耗图形处理的优化、支持更高分辨率、HDR显示以及机器学习和AI加速等方面的改进。&lt;/li&gt;
&lt;li&gt;Wayland和DRM集成：Wayland作为新一代图形显示协议，与DRM更为紧密地整合，提供更为直接和高效的图形渲染和显示方式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总体而言，现如今很多Linux发行版上图形服务器后端都是对接的drm实现图形输出和相关控制，DRM在Linux图形子系统中扮演着关键角色，随着技术和需求的不断发展，它持续进化以适应新的硬件和应用场景，为Linux系统提供了强大的图形渲染支持。
&lt;figure&gt;&lt;img src=&#34;drm-timeline.svg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图1. DRM时间线&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;drm剖析&#34; &gt;DRM剖析
&lt;span&gt;
    &lt;a href=&#34;#drm%e5%89%96%e6%9e%90&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;framebuffer--drm&#34; &gt;Framebuffer &amp;amp;&amp;amp; DRM
&lt;span&gt;
    &lt;a href=&#34;#framebuffer--drm&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;在谈drm之前必须先了解一下它的前辈Framebuffer，Framebuffer是指一块内存区域，用户存储显示设备上每个像素的颜色信息。在早期的计算机系统中，操作系统直接将图形数据写入这块显存区域，这被称为直接显存访问（Direct Framebuffer Access），这种方式简单、直接，但随着图形复杂程度增加和硬件发展，它变得难以满足高分辨率和复杂图形的需求，随着事件的推移，由DRM提供了更多先进的功能，它追加能替代了Framebuffer，并称为linux图形系统的主要组成部分（图形框架演变如下图2）。然而Frambuffer仍然在某些特定场景下有其用户之地，尤其是在一些嵌入式系统和特定的硬件上。
总的来说，Framebuffer到DRM的发展代表了图形处理在Linux系统中的进步和演进，从最初的直接访问到高级的硬件加速和更加强大的图形功能。drm为显示硬件的适配和图形的发展贡献了很大的力量。
&lt;figure&gt;&lt;img src=&#34;kms-vs-fbdev-display.svg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图2. Framebuffer到DRM&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&#34;drm-plane&#34; &gt;drm-plane
&lt;span&gt;
    &lt;a href=&#34;#drm-plane&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;drm_plane本质是对显示控制器中scanout硬件的抽象。简单来说，给定一个plane，可以让其与一个framebuffer关联表示进行scanout的数据，同时控制scanout时进行的额外操作，比如colorspace的改变，旋转、拉伸、偏移等操作。drm_plane是与硬件强相关的，显示控制器支持的plane是固定的，其支持的功能也是由硬件决定的。所有的drm_plane必为三种类型之一：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Primary - 主plane，一般控制整个显示器的输出。CRTC必须要有一个这样的plane。&lt;/li&gt;
&lt;li&gt;Curosr - 表示鼠标光标图层（可选），一般启用的话称其为开启硬件光标，代码如下，流程如下图3。&lt;/li&gt;
&lt;li&gt;Overlay - 叠加plane，可以在主plane上叠加一层输出，可选。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;....
&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cursor &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; NULL &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; drm_connector_is_cursor_visible(conn)) {
	&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; wlr_drm_fb &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;cursor_fb &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; get_next_cursor_fb(conn);
	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (cursor_fb &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; NULL) {
		wlr_drm_conn_log(conn, WLR_DEBUG, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to acquire cursor FB&amp;#34;&lt;/span&gt;);
		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; false;
	}

	drmModeFB &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;drm_fb &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; drmModeGetFB(drm&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;fd, cursor_fb&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;id);
	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (drm_fb &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; NULL) {
		wlr_drm_conn_log_errno(conn, WLR_DEBUG, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to get cursor &amp;#34;&lt;/span&gt;
			&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;BO handle: drmModeGetFB failed&amp;#34;&lt;/span&gt;);
		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; false;
	}
	&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; cursor_handle &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; drm_fb&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;handle;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; cursor_width &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; drm_fb&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;width;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; cursor_height &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; drm_fb&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;height;
	drmModeFreeFB(drm_fb);
    &lt;span style=&#34;color:#75715e&#34;&gt;// 设置硬件光标，更新光标图像buffer
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; ret &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; drmModeSetCursor(drm&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;fd, crtc&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;id, cursor_handle,
			cursor_width, cursor_height);
	&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; set_cursor_errno &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; errno;
	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (drmCloseBufferHandle(drm&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;fd, cursor_handle) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
		wlr_log_errno(WLR_ERROR, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;drmCloseBufferHandle failed&amp;#34;&lt;/span&gt;);
	}
	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (ret &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
		wlr_drm_conn_log(conn, WLR_DEBUG, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;drmModeSetCursor failed: %s&amp;#34;&lt;/span&gt;,
			strerror(set_cursor_errno));
		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; false;
	}

    &lt;span style=&#34;color:#75715e&#34;&gt;// 移动硬件光标位置
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (drmModeMoveCursor(drm&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;fd,
			crtc&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;id, conn&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;cursor_x, conn&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;cursor_y) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
		wlr_drm_conn_log_errno(conn, WLR_ERROR, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;drmModeMoveCursor failed&amp;#34;&lt;/span&gt;);
		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; false;
	}
} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (drmModeSetCursor(drm&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;fd, crtc&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;id, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;)) {
		wlr_drm_conn_log_errno(conn, WLR_DEBUG, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;drmModeSetCursor failed&amp;#34;&lt;/span&gt;);
		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; false;
	}
}
....
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure&gt;&lt;img src=&#34;kms-display-plane.svg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图3. drm-plane框架图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;kms-plane.svg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图4. 多drm-plane合成流程图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;在笔者看来，drm-plane与layer的概念类似，只是更加偏向硬件底层概念，开发者可以通过控制显示相关的参数将plane投显到显示器上任何区域（参考图5），这种映射机制提供了非常方便地偏移、缩放方法，如果在这一层上实现移动和缩放等动画，效率将会比使用OpenGL等API制作高，将频繁变化的图像抽象工作在一个单独的drm-plane也可以达到提高性能和省电的目的。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SRC_X&lt;/td&gt;
&lt;td&gt;当前framebuffer crop区域的起始偏移x坐标&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SRC_Y&lt;/td&gt;
&lt;td&gt;当前framebuffer crop区域的起始偏移y坐标&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SRC_W&lt;/td&gt;
&lt;td&gt;当前framebuffer crop区域的宽度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SRC_H&lt;/td&gt;
&lt;td&gt;当前framebuffer crop区域的高度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRTC_X&lt;/td&gt;
&lt;td&gt;屏幕显示区域的起始偏移x坐标&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRTC_Y&lt;/td&gt;
&lt;td&gt;屏幕显示区域的起始偏移y坐标&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRTC_W&lt;/td&gt;
&lt;td&gt;屏幕显示区域的宽度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRTC_H&lt;/td&gt;
&lt;td&gt;屏幕显示区域的高度&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p align=&#34;left&#34;&gt;表1. drm-plane部分属性表&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;plane-update-coordinates.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图5. drm-plane送显参数控制&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&#34;crtccathode-ray-tube-controller&#34; &gt;crtc（Cathode Ray Tube Controller）
&lt;span&gt;
    &lt;a href=&#34;#crtccathode-ray-tube-controller&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;CRTC 是显示控制器中的一个重要部分，主要用于管理显示设备的扫描和刷新。CRTC 负责生成视频信号的定时和同步，控制屏幕上像素的扫描和刷新，以确保正确的图像显示。它管理像素的输出到屏幕上的确切位置，以及刷新率、分辨率等显示参数。在现代的图形处理中，CRTC通常由图形处理单元（GPU）或显示控制器中的专用部分来控制，以确保正确的图像输出。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;kms-display-crtc.svg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图6. crtc框架图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;drm legacy链路通过调用drmModePageFlip请求crtc更新显示器图像，其代码如下，流程如图6所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;legacy_crtc_commit&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; wlr_drm_connector &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;conn,
		&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; wlr_drm_connector_state &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;state,
		&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; flags, &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; test_only) {
	....

	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (flags &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; DRM_MODE_PAGE_FLIP_EVENT) {
		&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; page_flags &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; DRM_MODE_PAGE_FLIP_EVENT;
		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (flags &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; DRM_MODE_PAGE_FLIP_ASYNC) {
			page_flags &lt;span style=&#34;color:#f92672&#34;&gt;|=&lt;/span&gt; DRM_MODE_PAGE_FLIP_ASYNC;
		}

		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (drmModePageFlip(drm&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;fd, crtc&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;id, fb_id,
				page_flags, drm)) {
			wlr_drm_conn_log_errno(conn, WLR_ERROR, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;drmModePageFlip failed&amp;#34;&lt;/span&gt;);
			&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; false;
		}
	}

	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; true;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure&gt;&lt;img src=&#34;page-flipping.svg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图7. buffer更新流程图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&#34;encoder&#34; &gt;encoder
&lt;span&gt;
    &lt;a href=&#34;#encoder&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;编码器从CRTC获取像素数据，并将其转换为适合任何连接器的格式。在某些设备上，CRTC可以将数据发送到多个编码器。在这种情况下，两个编码器将从同一扫描输出缓冲区接收数据，从而在连接到每个编码器的连接器上产生“克隆”显示配置。
在Linux系统中，DRM（Direct Rendering Manager）是用于处理图形显示的子系统，负责在用户空间和图形硬件之间提供接口。DRM编码器在Linux中扮演了关键角色，主要用于对图形和视频内容进行编码、解码、处理和显示。在DRM框架中，编码器的作用包括但不限于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;视频压缩和解压缩： 编码器负责对视频内容进行压缩，以减小数据量并更有效地传输和存储视频流。解码器用于解压缩视频内容，以便图形硬件能够正确地渲染和显示内容。&lt;/li&gt;
&lt;li&gt;图形处理和渲染： DRM编码器能够处理图形和视频流，进行渲染和合成，然后将图像数据发送到显示设备以在屏幕上显示。&lt;/li&gt;
&lt;li&gt;硬件加速： DRM编码器还可以利用硬件加速功能，以便更快地处理图形和视频内容。这有助于提高性能和效率，特别是对于高分辨率视频或图形内容的处理。&lt;/li&gt;
&lt;li&gt;支持多种编解码标准： DRM编码器能够支持多种视频编解码标准，如H.264、H.265等，确保对不同格式的视频流进行正确的处理。
在Linux系统中，DRM编码器与图形驱动程序、图形处理单元（GPU）和显示设备等硬件密切相关。它允许Linux系统管理和控制图形硬件，处理视频流并在屏幕上显示图像，同时也与DRM系统中的访问控制和安全性机制集成，确保受保护内容的安全传输和显示。&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img src=&#34;kms-display-encoder.svg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图8. encoder框架图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&#34;connector&#34; &gt;connector
&lt;span&gt;
    &lt;a href=&#34;#connector&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;DRM connectors是用于管理和描述图形设备的连接器或端口。它们提供了对显示设备的连接和属性描述。在图形系统中，这些连接器可以代表诸如HDMI、DisplayPort、DVI等物理连接接口，而它们也可以描述内部连接，例如LCD panels。DRM connectors 在 Linux 中的作用包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;显示设备连接描述： DRM connectors 提供了关于显示设备的物理连接信息，比如类型（HDMI、VGA、DisplayPort等）、连接状态以及支持的分辨率和刷新率等信息。&lt;/li&gt;
&lt;li&gt;动态连接管理： 它们可以监测连接和断开事件。当显示设备插入或拔出时，DRM connectors 可以检测到这些变化，并通知系统相应的变化。这使系统能够动态调整图形配置以适应新的连接或断开状态。&lt;/li&gt;
&lt;li&gt;多显示器支持： DRM connectors 允许系统管理多个显示器的连接和配置。这使得可以同时使用多个显示器或监视器，或者在不同的显示设备上显示不同的内容。&lt;/li&gt;
&lt;li&gt;显示属性查询和配置： 通过 DRM connectors，可以查询连接器支持的分辨率、刷新率以及其他显示属性。这使系统可以调整和配置图形设备，以最佳方式显示图形内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总体来说，DRM connectors 在 Linux 中的作用是管理显示设备的物理连接、状态和属性，使系统能够动态适应不同的显示设备、支持多个显示器，并提供相应的配置信息，以便正确地显示图形内容。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;kms-display-connector.svg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图9. connector框架图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&#34;framebuffer&#34; &gt;framebuffer
&lt;span&gt;
    &lt;a href=&#34;#framebuffer&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;framebuffer是一个重要概念。Framebuffers是用于存储屏幕上每个像素的颜色和其他相关信息的内存区域。在DRM中，framebuffer提供了一个抽象接口，允许用户空间程序访问和操作显存，以便渲染图形数据。其作用包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;图形数据存储： Framebuffers提供一个区域，用于存储图形数据，包括每个像素的颜色、透明度等信息。这些数据构成了屏幕上显示的图像。&lt;/li&gt;
&lt;li&gt;直接访问屏幕数据： 通过framebuffers，用户空间程序或操作系统内核能够直接访问和操作图形数据，而无需经过额外的复杂处理。&lt;/li&gt;
&lt;li&gt;硬件抽象层： Framebuffers提供了一个硬件无关的抽象接口，这意味着不同的图形设备和硬件都可以通过相同的接口进行访问和操作。&lt;/li&gt;
&lt;li&gt;显示图像： Framebuffers存储了最终用于在屏幕上显示的图像数据。图形数据在 framebuffer中进行组织和处理，然后通过显示控制器输出到屏幕。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在DRM 中，framebuffers通常与CRTC和显示控制器等组件一起工作。framebuffers提供的抽象层允许操作系统或应用程序以统一的方式对图形数据进行处理和管理，无论具体的硬件设备是什么样的。总之，framebuffers 在 Linux 的 DRM 中提供了一个接口和数据结构，用于存储、操作和管理图形数据，使其能够在屏幕上正确地显示图像。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;framebuffer-fields.svg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图10. framebuffer内存结构图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&#34;drm调试工具&#34; &gt;drm调试工具
&lt;span&gt;
    &lt;a href=&#34;#drm%e8%b0%83%e8%af%95%e5%b7%a5%e5%85%b7&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;drm_info&#34; &gt;drm_info
&lt;span&gt;
    &lt;a href=&#34;#drm_info&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;用于转储有关 DRM 设备信息的小实用程序&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;drm_info

Node: /dev/dri/card1
├───Driver: i915 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;Intel Graphics&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; version 1.6.0 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;20201103&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
│   ├───DRM_CLIENT_CAP_STEREO_3D supported
│   ├───DRM_CLIENT_CAP_UNIVERSAL_PLANES supported
│   ├───DRM_CLIENT_CAP_ATOMIC supported
│   ├───DRM_CLIENT_CAP_ASPECT_RATIO supported
│   ├───DRM_CLIENT_CAP_WRITEBACK_CONNECTORS supported
│   ├───DRM_CAP_DUMB_BUFFER &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   ├───DRM_CAP_VBLANK_HIGH_CRTC &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   ├───DRM_CAP_DUMB_PREFERRED_DEPTH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;
│   ├───DRM_CAP_DUMB_PREFER_SHADOW &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   ├───DRM_CAP_PRIME &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
│   ├───DRM_CAP_TIMESTAMP_MONOTONIC &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   ├───DRM_CAP_ASYNC_PAGE_FLIP &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   ├───DRM_CAP_CURSOR_WIDTH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;256&lt;/span&gt;
│   ├───DRM_CAP_CURSOR_HEIGHT &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;256&lt;/span&gt;
│   ├───DRM_CAP_ADDFB2_MODIFIERS &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   ├───DRM_CAP_PAGE_FLIP_TARGET &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   ├───DRM_CAP_CRTC_IN_VBLANK_EVENT &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   ├───DRM_CAP_SYNCOBJ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   └───DRM_CAP_SYNCOBJ_TIMELINE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
├───Device: PCI 8086:46aa Intel Corporation Alder Lake-UP4 GT2 &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Iris Xe Graphics&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
│   └───Available nodes: primary, render
├───Framebuffer size
│   ├───Width: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 16384&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
│   └───Height: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 16384&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
├───Connectors
│   ├───Connector &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │   ├───Object ID: &lt;span style=&#34;color:#ae81ff&#34;&gt;236&lt;/span&gt;
│   │   ├───Type: eDP
│   │   ├───Status: connected
│   │   ├───Physical size: 290x180 mm
│   │   ├───Subpixel: unknown
│   │   ├───Encoders: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
│   │   ├───Modes
│   │   │   └───2880x1800@60.00 preferred driver phsync nvsync 
│   │   └───Properties
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDID&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;266&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DPMS&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;On, Standby, Suspend, Off&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; On
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;link-status&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Good, Bad&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Good
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;non-desktop&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TILE&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CRTC_ID&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;atomic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: object CRTC &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scaling mode&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Full, Center, Full aspect&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Full aspect
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;panel orientation&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Normal, Upside Down, Left Side Up, Right Side Up&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Normal
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Broadcast RGB&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Automatic, Full, Limited 16:235&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Automatic
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;max bpc&amp;#34;&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;6, 12&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Colorspace&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Default, BT709_YCC, XVYCC_601, XVYCC_709, SYCC_601, opYCC_601, opRGB, BT2020_CYCC, BT2020_RGB, BT2020_YCC, DCI-P3_RGB_D65, RGB_WIDE_FIXED, RGB_WIDE_FLOAT, BT601_YCC&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Default
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HDR_OUTPUT_METADATA&amp;#34;&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       └───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vrr_capable&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   ├───Connector &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   │   ├───Object ID: &lt;span style=&#34;color:#ae81ff&#34;&gt;245&lt;/span&gt;
│   │   ├───Type: DisplayPort
│   │   ├───Status: disconnected
│   │   ├───Encoders: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;1&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
│   │   └───Properties
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDID&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DPMS&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;On, Standby, Suspend, Off&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Off
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;link-status&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Good, Bad&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Good
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;non-desktop&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TILE&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CRTC_ID&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;atomic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: object CRTC &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;subconnector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Unknown, VGA, DVI-D, HDMI, DP, Wireless, Native&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Unknown
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;audio&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;force-dvi, off, auto, on&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; auto
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Broadcast RGB&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Automatic, Full, Limited 16:235&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Automatic
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;max bpc&amp;#34;&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;6, 12&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Colorspace&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Default, BT709_YCC, XVYCC_601, XVYCC_709, SYCC_601, opYCC_601, opRGB, BT2020_CYCC, BT2020_RGB, BT2020_YCC, DCI-P3_RGB_D65, RGB_WIDE_FIXED, RGB_WIDE_FLOAT, BT601_YCC&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Default
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HDR_OUTPUT_METADATA&amp;#34;&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vrr_capable&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content Protection&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Undesired, Desired, Enabled&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Undesired
│   │       └───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HDCP Content Type&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;HDCP Type0, HDCP Type1&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; HDCP Type0
│   └───Connector &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
│       ├───Object ID: &lt;span style=&#34;color:#ae81ff&#34;&gt;258&lt;/span&gt;
│       ├───Type: DisplayPort
│       ├───Status: disconnected
│       ├───Encoders: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;6&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
│       └───Properties
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EDID&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DPMS&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;On, Standby, Suspend, Off&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Off
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;link-status&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Good, Bad&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Good
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;non-desktop&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TILE&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CRTC_ID&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;atomic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: object CRTC &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;subconnector&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Unknown, VGA, DVI-D, HDMI, DP, Wireless, Native&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Unknown
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;audio&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;force-dvi, off, auto, on&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; auto
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Broadcast RGB&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Automatic, Full, Limited 16:235&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Automatic
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;max bpc&amp;#34;&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;6, 12&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Colorspace&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Default, BT709_YCC, XVYCC_601, XVYCC_709, SYCC_601, opYCC_601, opRGB, BT2020_CYCC, BT2020_RGB, BT2020_YCC, DCI-P3_RGB_D65, RGB_WIDE_FIXED, RGB_WIDE_FLOAT, BT601_YCC&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Default
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HDR_OUTPUT_METADATA&amp;#34;&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vrr_capable&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│           ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content Protection&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Undesired, Desired, Enabled&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Undesired
│           └───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HDCP Content Type&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;HDCP Type0, HDCP Type1&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; HDCP Type0
├───Encoders
│   ├───Encoder &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │   ├───Object ID: &lt;span style=&#34;color:#ae81ff&#34;&gt;235&lt;/span&gt;
│   │   ├───Type: TMDS
│   │   ├───CRTCS: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;0, 1, 2, 3&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
│   │   └───Clones: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
│   ├───Encoder &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   │   ├───Object ID: &lt;span style=&#34;color:#ae81ff&#34;&gt;244&lt;/span&gt;
│   │   ├───Type: TMDS
│   │   ├───CRTCS: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;0, 1, 2, 3&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
│   │   └───Clones: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;1&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;

.........

├───CRTCs
│   ├───CRTC &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │   ├───Object ID: &lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt;
│   │   ├───Legacy info
│   │   │   ├───Mode: 2880x1800@60.00 preferred driver phsync nvsync 
│   │   │   └───Gamma size: &lt;span style=&#34;color:#ae81ff&#34;&gt;256&lt;/span&gt;
│   │   └───Properties
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ACTIVE&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;atomic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MODE_ID&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;atomic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;271&lt;/span&gt;
│   │       │   └───2880x1800@60.00 preferred driver phsync nvsync 
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;OUT_FENCE_PTR&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;atomic&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, UINT64_MAX&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;VRR_ENABLED&amp;#34;&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, 1&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SCALING_FILTER&amp;#34;&lt;/span&gt;: enum &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;Default, Nearest Neighbor&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Default
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DEGAMMA_LUT&amp;#34;&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DEGAMMA_LUT_SIZE&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, UINT32_MAX&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;129&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CTM&amp;#34;&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
│   │       ├───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GAMMA_LUT&amp;#34;&lt;/span&gt;: blob &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;270&lt;/span&gt;
│   │       └───&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GAMMA_LUT_SIZE&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;immutable&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: range &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;0, UINT32_MAX&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1024&lt;/span&gt;

........


└───Planes
    ├───Plane &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
    │   ├───Object ID: &lt;span style=&#34;color:#ae81ff&#34;&gt;31&lt;/span&gt;
    │   ├───CRTCs: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
    │   ├───Legacy info
    │   │   ├───FB ID: &lt;span style=&#34;color:#ae81ff&#34;&gt;237&lt;/span&gt;
    │   │   │   ├───Object ID: &lt;span style=&#34;color:#ae81ff&#34;&gt;237&lt;/span&gt;
    │   │   │   ├───Size: 2880x1800
    │   │   │   ├───Format: ARGB2101010 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;0x30335241&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
    │   │   │   ├───Modifier: I915_FORMAT_MOD_Y_TILED &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;0x100000000000002&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
    │   │   │   └───Planes:
    │   │   │       └───Plane 0: offset &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0, pitch &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;11520&lt;/span&gt; bytes
    │   │   └───Formats:
    │   │       ├───C8 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;0x20203843&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
    │   │       ├───RGB565 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;0x36314752&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
    │   │       ├───XRGB8888 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;0x34325258&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
    │   │       ├───XBGR8888 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;0x34324258&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
    │   │       ├───ARGB8888 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;0x34325241&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
    │   │       ├───ABGR8888 &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;0x34324241&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
    
    .....
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;drm_monitor&#34; &gt;drm_monitor
&lt;span&gt;
    &lt;a href=&#34;#drm_monitor&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;用于监控 KMS 状态的 CLI 工具&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;drm_monitor -d /dev/dri/card1
CRTC 80: seq&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30095325875&lt;/span&gt; ns&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3076787603594&lt;/span&gt; delta_ns&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;16668012&lt;/span&gt; Hz&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;59.995157
CRTC 131: seq&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; ns&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; delta_ns&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; Hz&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0.000000
CRTC 182: seq&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; ns&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; delta_ns&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; Hz&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0.000000
CRTC 233: seq&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; ns&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; delta_ns&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; Hz&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0.000000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;参考资料&#34; &gt;参考资料
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.kernel.org/gpu/drm-kms.html&#34;&gt;Kernel Mode Setting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://bootlin.com/pub/conferences/2014/elce/brezillon-drm-kms/&#34;&gt;brezillon-drm-kms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gitlab.freedesktop.org/emersion/drm_info&#34;&gt;drm_info&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/emersion/drm_monitor&#34;&gt;drm_monitor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/">
      <title>浅析 linux 电源配置</title>
      <link>https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/</link>
      <pubDate>Tue, 31 Oct 2023 13:00:00 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/analyzing-the-linux-power-configuration/</guid>
      <description>&lt;h2 id=&#34;一背景&#34; &gt;一、背景
&lt;span&gt;
    &lt;a href=&#34;#%e4%b8%80%e8%83%8c%e6%99%af&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;近期，我们收到用户反馈，在使用 deepin 系统过程中遇到了 CPU 功耗过高导致的设备发热、续航较差情况，而用户在这些负载场景下，CPU 的占用往往不高。为了解决这个痛点，统信软件开源社区中心特别成立专项计划，对于 deepin 的电源进行专项优化，本文旨在针对此问题根因进行分析于说明。&lt;/p&gt;
&lt;p&gt;在对电源进行专项优化之前，我们首先对 deepin 系统进行了深入的调查和分析，以了解其在负载场景下的实际运行情况。经过对 CPU 使用率和功耗的监测，我们发现了一个令人惊讶的事实：尽管在高负载场景下 CPU 的占用率不高，但其功耗却持续升高，最终导致设备发热并影响续航。也就是说，我们前期做的省电优化工作，不仅无效，还起了反作用（具体情况作者将在下文作出仔细说明）。&lt;/p&gt;
&lt;h2 id=&#34;二问题&#34; &gt;二、问题
&lt;span&gt;
    &lt;a href=&#34;#%e4%ba%8c%e9%97%ae%e9%a2%98&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;1内核&#34; &gt;1. 内核
&lt;span&gt;
    &lt;a href=&#34;#1%e5%86%85%e6%a0%b8&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;最开始发现问题的地方在内核。有用户将我们的内核和 ubuntu 的内核进行对比后发现，虽然我们的系统和 ubuntu 系统的性能差不多，但是在发热和续航上，我们较 ubuntu 落后较多。有用户在 deepin 系统上使用 ubuntu 和其他开源 linux 发行版的配置文件分别编译内核，发现 deepin 的主要问题存在于发热控制之上。我们的测试同事高度重视这一社区反馈，遂对社区用户反馈的问题进行复现，佐证了这一现象。&lt;/p&gt;
&lt;p&gt;对于这种问题，我们立即联系了内核研发部的同事，并邀请部分对内核配置有一定研究的社区用户共同参与。在大家的合力排查下，我们发现，deepin v23 中提供的 HWE 内核存在部分 debug 和无用的内核选项被开启的情况，并且部分节电功能实际未能获得启用，这些都在一定程度上导致了 deepin v23 的续航表现不佳。&lt;/p&gt;
&lt;h3 id=&#34;2系统&#34; &gt;2. 系统
&lt;span&gt;
    &lt;a href=&#34;#2%e7%b3%bb%e7%bb%9f&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;在系统层面，我重新审视了 dde-daemon 提供的电源调度模块，并且对比内核文档提供的文件接口，分析我们用户使用的电源模式，发现其中存在可以优化的空间。这将是本文着重讲解的内容之一。&lt;/p&gt;
&lt;h2 id=&#34;三前置知识&#34; &gt;三、前置知识
&lt;span&gt;
    &lt;a href=&#34;#%e4%b8%89%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%af%86&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;1acpi&#34; &gt;1.ACPI
&lt;span&gt;
    &lt;a href=&#34;#1acpi&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;ACPI 是 Advanced Configuration and Power Interface 的缩写，是一种计算机硬件和操作系统之间交换能源相关信息的接口规范。它定义了计算机硬件的能源相关信息，如电源供应器状态、设备功耗、设备功率因数等。ACPI 是操作系统控制计算机硬件能源管理的标准，同时也是硬件厂商和操作系统之间通信的标准。&lt;/p&gt;
&lt;p&gt;在 deepin 系统中，ACPI 负责处理计算机硬件的能源管理，它与 deepin 系统的电源管理模块进行交互，以实现对计算机硬件的能源管理。&lt;/p&gt;
&lt;p&gt;在分析系统层面的问题时，我们需要了解 ACPI 和电源管理模块的作用和功能，以及它们是如何协同工作的。在本文中，我们将会详细讲解 ACPI 的工作原理以及 deepin 系统中的电源调度模块工作模式，并提出可行优化建议。&lt;/p&gt;
&lt;p&gt;首先，让我们了解一下 ACPI 的工作原理。当计算机硬件发生电源变化时，ACPI 会收集硬件信息，并向操作系统发送电源请求。操作系统收到电源请求后，会根据用户配置自动调整各个硬件的电源策略。而 deepin 系统的电源模块则是帮助用户生成配置来调整 ACPI 的行为。所以在这一方面，我们能做的就是向 ACPI 提供合理的电源策略，在保证性能的同时，降低设备温度并提升续航表现。&lt;/p&gt;
&lt;h3 id=&#34;2平台电源配置&#34; &gt;2.平台电源配置
&lt;span&gt;
    &lt;a href=&#34;#2%e5%b9%b3%e5%8f%b0%e7%94%b5%e6%ba%90%e9%85%8d%e7%bd%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;相关内核文档&amp;lt;&lt;/strong&gt;&lt;a href=&#34;https://docs.kernel.org/userspace-api/sysfs-platform_profile.html&#34;&gt;https://docs.kernel.org/userspace-api/sysfs-platform_profile.html&lt;/a&gt;&lt;strong&gt;&amp;gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UEFI ACPI 文档&amp;lt;&lt;/strong&gt;&lt;a href=&#34;https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/index.html&#34;&gt;https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/index.html&lt;/a&gt;&lt;strong&gt;&amp;gt;&lt;/strong&gt;
平台电源配置是电源管理模块的一个重要组成部分，它用于管理计算机平台硬件（指所有支持 ACPI 的设备）的电源状态，而 CPU 也是属于 ACPI 设备的一部分。不过 CPU 是计算机的核心发热大户，对于其使用平台电源管理可能粒度较粗，所以 APCI 引入了一些 CPU 独有的性能管理方式，在下一小节将详细说明。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;平台电源配置提供了三种可选模式：performance（性能模式），balance（平衡模式），low-power（节能模式）。一般情况下，用户使用平衡模式就可以。在台式机和 mini 主机类（对于功耗和发热没有任何要求）设备上默认提供性能模式，在笔记本等移动设备上默认提供平衡模式。默认不提供节能模式，因为某些 ACPI 设备在节能模式工作过程中可能出现“睡死现象”，所以为了避免此问题，默认不提供 low-power 节电模式。&lt;/p&gt;
&lt;h3 id=&#34;3cpu-电源配置&#34; &gt;3.CPU 电源配置
&lt;span&gt;
    &lt;a href=&#34;#3cpu-%e7%94%b5%e6%ba%90%e9%85%8d%e7%bd%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;传统电源模式（SCalling）内核文档&amp;lt;&lt;/strong&gt;&lt;a href=&#34;https://www.kernel.org/doc/html/v6.1/admin-guide/pm/cpufreq.html&#34;&gt;https://www.kernel.org/doc/html/v6.1/admin-guide/pm/cpufreq.html&lt;/a&gt;&lt;strong&gt;&amp;gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Intel PState 内核文档&amp;lt;&lt;/strong&gt;&lt;a href=&#34;https://www.kernel.org/doc/html/v6.1/admin-guide/pm/intel_pstate.html&#34;&gt;https://www.kernel.org/doc/html/v6.1/admin-guide/pm/intel_pstate.html&lt;/a&gt;&lt;strong&gt;&amp;gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AMD PState 内核文档&amp;lt;&lt;/strong&gt;&lt;a href=&#34;https://docs.kernel.org/admin-guide/pm/amd-pstate.html&#34;&gt;https://docs.kernel.org/admin-guide/pm/amd-pstate.html&lt;/a&gt;&lt;strong&gt;&amp;gt;&lt;/strong&gt;
如果有时间，可以自行研读上述电源文档，就很容易理解 CPU 电源配置相关内容。作者在这里的讲述将忽略大量技术细节和实现方式，仅表述如何调整 CPU 的电源模式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在&lt;code&gt;/sys/devices/system/cpu/cpufreq&lt;/code&gt;目录下有许多文件名为&lt;code&gt;policy&amp;lt;x&amp;gt;&lt;/code&gt;（x 代表核心编号），这些文件对应着你电脑上的 CPU 核心，而 CPU 的电源调度细节就在这些文件夹里面。在&lt;code&gt;policy&amp;lt;x&amp;gt;&lt;/code&gt;目录下有一个文件`scaling_driver``,使用 cat 或其他方式访问它，得到的结果就是我们当前使用的调度器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;intel_cpufreq / acpi_cpufreq : 使用 scaling freq 调度&lt;/li&gt;
&lt;li&gt;intel_pastate : 使用 Intel Pstate 调度&lt;/li&gt;
&lt;li&gt;amd-pstate : 使用 AMD Pstate 调度&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;scalling-freq-调度&#34; &gt; scalling freq 调度
&lt;span&gt;
    &lt;a href=&#34;#scalling-freq-%e8%b0%83%e5%ba%a6&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;这是最传统的 CPU 调度方式，你可以在 policy&lt;x&gt;文件夹下的 scaling_available_governors 获取可选电源模式：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;英文&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;中文&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;performance&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;性能模式&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;最极致的性能表现，最火热的 CPU 温度，最短的续航。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;powersave&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;节能模式&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;为绿色地球出一份力。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;balance&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;平衡模式&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;性能和续航兼顾。小孩子才做选择，我全都要。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;schedutil&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;平衡模式&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;平衡模式的一种，使用不同算法进行调度。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;ondemand&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;平衡模式&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;平衡模式的一种，根据当前 CPU 负载动态调整频率。当负载大于阈值时调整到最高频率，其他情况按负载比例计算频率。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;conservative&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;平衡模式&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;平衡模式的一种，根据当前 CPU 负载动态调整频率。当负载大于最大阈值时步进递增频率，当负载小于最低阈值时步进递减。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;userspace&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;用户模式&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;以用户指定的频率运行 CPU，可通过/sys/devices/system/cpu/cpuX/cpufreq/scaling_setspeed 进行配置&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;你可能好奇，为啥这里有这么多平衡模式，其实这些平衡模式的作用都是是一样的：平衡性能和续航，不过使用的算法可能不同，这里我不做详细说明，我在网络上找到一些详细资料可以参考，有兴趣的朋友可自行查阅：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;oppo 内核团队：schedutil governor 情景分析：&lt;a href=&#34;http://www.wowotech.net/process_management/schedutil_governor.html&#34;&gt;http://www.wowotech.net/process_management/schedutil_governor.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;linux 内核文档：schedutil governor：&lt;a href=&#34;https://www.kernel.org/doc/html/next/translations/zh_CN/scheduler/schedutil.html&#34;&gt;https://www.kernel.org/doc/html/next/translations/zh_CN/scheduler/schedutil.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Arch wiki：&lt;a href=&#34;https://wiki.archlinux.org/title/CPU_frequency_scaling&#34;&gt;https://wiki.archlinux.org/title/CPU_frequency_scaling&lt;/a&gt;
我们可以更改 scaling_governors 来更改此核心的电源调度（注意：只能更改为 scaling_available_governors 文件中列出的电源调度，不同电脑可能存在差异）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;intel-pstate&#34; &gt; Intel Pstate
&lt;span&gt;
    &lt;a href=&#34;#intel-pstate&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;这是 Intel 近几代 CPU 独享的 moment，内核开启 intel pstate 后（V23 内核默认开启）你会发现在 policy&lt;x&gt;文件夹下多了几个文件：&lt;/p&gt;
&lt;p&gt;我们只需要关注：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;energy_performance_available_perference : 可用的 pstate 电源调度&lt;/li&gt;
&lt;li&gt;energy_performance_perference：当前选定的 pstate 电源调度，可以更改此文件内容来更改电源调度&lt;/li&gt;
&lt;li&gt;在 Intel Pstate 中出现了两个新的调度方案：&lt;/li&gt;
&lt;li&gt;balance_performance : 平衡偏性能，平时工作频率不高，在负载增大时能快速响应&lt;/li&gt;
&lt;li&gt;balance_power : 平衡偏节能，电源策略较为保守
在部分电脑上还有 default 方案，此方案就是经过 pstate 优化过的 balance 策略。具体 PState 使用的黑魔法以及主动模式和被动模式的调度策略，可以参照内核文档进行分析。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;amd-pstate&#34; &gt;AMD PState
&lt;span&gt;
    &lt;a href=&#34;#amd-pstate&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;这是 AMD ZEN2 以上用户，以及支持 kernel 6.4.x 用户独享的 moment。其实 AMD 在 6.1 内核已经做了 PState 的支持，不过是被动模式。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;（Actvie Mode）主动模式&lt;/p&gt;
&lt;p&gt;Active Mode 仅在内核版本大于 6.4 以上，且内核选项打开 AMD PState 时可用。可能需要在 grub 内加入启动参数以打开此功能：amd_pstate=active，也可以修改文件实现 Active Mode 的电源策略和 Intel PStatewi 类似。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（Passive Mode）被动模式&lt;/p&gt;
&lt;p&gt;Passtive Mode 仅在内核大于 6.1 以上，且内核选项打开 AMD PState 时可用。可能需要在 grub 加入启动参数开启此功能：amd_pstate=passive，也可修改文件实现。&lt;/p&gt;
&lt;p&gt;Passive Mode 提供两种电源模式，在&lt;code&gt;/sys/device/system/cpu/cpufreq/scaling_governor&lt;/code&gt;文件进行调整：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;performance 使用 platform_profile 进行配置，调度积极性较高&lt;/li&gt;
&lt;li&gt;scheutils 在&lt;code&gt;/sys/device/system/cpu/cpufreq/schedutil/rate_limit_us&lt;/code&gt;文件中调整调度粒度（两次调度的间隔时间）和 ACPI 的 scheutils 类&lt;code&gt;/sys/device/system/cpu/cpufreq/scaling_governor&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（Guided Mode）引导模式&lt;/p&gt;
&lt;p&gt;Guided Mode 仅在内核大于 6.1 以上，且内核选项打开 AMD PState 时可用。可能需要在 grub 加入启动参数开启此功能：amd_pstate=guided，也可修改文件实现。这就类似汽车的自动挡，驱动程序请求最低和最大性能级别，平台自动选择此范围内适合当前工作负荷的性能级别。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;4gpu-电源管理部分&#34; &gt;4.GPU 电源管理部分
&lt;span&gt;
    &lt;a href=&#34;#4gpu-%e7%94%b5%e6%ba%90%e7%ae%a1%e7%90%86%e9%83%a8%e5%88%86&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;h4 id=&#34;amd-gpu&#34; &gt; AMD GPU
&lt;span&gt;
    &lt;a href=&#34;#amd-gpu&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;如果是 AMD GPU 则需要更改两个文件（使用 tee 命令进行写入）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/sys/class/drm/card0/device/power_dpm_state&lt;/code&gt;（这是一个遗留接口，目的是向后兼容）&lt;/li&gt;
&lt;li&gt;performance 高性能模式&lt;/li&gt;
&lt;li&gt;balance 平衡模式&lt;/li&gt;
&lt;li&gt;battery 节能模式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/sys/class/drm/card0/device/power_dpm_force_performance_level&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下设置来自 AMD 官方驱动文档：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://dri.freedesktop.org/docs/drm/gpu/amdgpu.html#power-dpm-force-performance-level&#34;&gt;https://dri.freedesktop.org/docs/drm/gpu/amdgpu.html#power-dpm-force-performance-level&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://dri.freedesktop.org/docs/drm/gpu/amdgpu.html#power-dpm-force-performance-level&#34;&gt;drm/amdgpu AMDgpu driver — The Linux Kernel  documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;power_dpm_force_performance_level:&lt;/p&gt;
&lt;p&gt;AMD GPU 驱动程序提供了一个 sysfs API，用于调整某些与功率相关的参数。文件 power-dpm-force-performance-level 将用于执行此操作。它接受以下参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;auto：当选择 auto 时，设备将尝试针对驱动中的当前条件动态选择最佳功率曲线&lt;/li&gt;
&lt;li&gt;low：当选择低时，GPU 被强制到最低功率状态&lt;/li&gt;
&lt;li&gt;high：当选择高时，GPU 被强制到最高功率状态&lt;/li&gt;
&lt;li&gt;manual：当选择手动时，用户可以通过 sysfs pp_dpm_mclk、pp_dpm_sclk 和 pp_dpm_pcie 文件手动调整每个时钟域启用的电源状态，并通过 pp_power_profile_mode sysfs 文件调整电源状态转换方式。&lt;/li&gt;
&lt;li&gt;profile_standard 固定时钟级别分析模式。此模式将时钟设置为固定级别，该级别因 ASIC 而异。这对于分析特定工作负载很有用（不常用）。&lt;/li&gt;
&lt;li&gt;profile_min_sclk 最小 sclk 分析模式。此模式将 sclk 强制设置为最低级别。这对于分析最小功耗的场景很有用（不常用）。&lt;/li&gt;
&lt;li&gt;profile_min_mclk 最小 mclk 分析模式。此模式将 mclk 强制设置为最低级别。这对于分析最小功耗的场景很有用（不常用）。&lt;/li&gt;
&lt;li&gt;profile_peak 峰值分析模式。此模式将所有时钟（mclk、sclk、pcie）设置为最高级别。这对于分析最大性能的场景很有用（不常用）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;测试：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LOW 模式的跑分&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;./low.jpg&#34; alt=&#34;LOW 模式的跑分&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;auto 模式的跑分&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;./auto.jpg&#34; alt=&#34;auto 模式的跑分&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;high 模式的跑分&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;./high.jpg&#34; alt=&#34;high 模式的跑分&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;intel-gpu&#34; &gt; Intel GPU
&lt;span&gt;
    &lt;a href=&#34;#intel-gpu&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;intel GPU 使用的 i915 驱动，并不希望你对其做出调整，因为其驱动自带的电源策略已经足够聪明。不过你也可以通过 intel 提供的 intel-gpu-tools 进行调整和获取信息。&lt;/p&gt;
&lt;p&gt;sudo apt install intel-gpu-tools&lt;/p&gt;
&lt;p&gt;然后使用&lt;/p&gt;
&lt;p&gt;sudo intel_gpu_frequency&lt;/p&gt;
&lt;p&gt;来获取当前频率（当前使用的是 Intel A750）&lt;/p&gt;
&lt;p&gt;可以看到 intel 的显卡驱动是在 600 MHz 到 2400 MHz 之间动态调整（如上图）&lt;/p&gt;
&lt;p&gt;测试笔记本下 intel 核显跑分如下
&lt;img src=&#34;./gxn.png&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;nvidia&#34; &gt;Nvidia
&lt;span&gt;
    &lt;a href=&#34;#nvidia&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;由于 nvidia 驱动不开源，所以在系统层面无法对其做控制。&lt;/p&gt;
&lt;h2 id=&#34;四应用&#34; &gt;四、应用
&lt;span&gt;
    &lt;a href=&#34;#%e5%9b%9b%e5%ba%94%e7%94%a8&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;应用级别的省电，应该就是在保证用户使用流畅度的情前提下下节省性能。之前也有用户提出过，是否能参照 vivo 的 origin3 os 的不公平调度算法来实现优化。毕竟安卓系统的底层也是 linux，理论上实现难度不大。&lt;/p&gt;
&lt;p&gt;Cgroups，全称 Control Groups，是 Linux 内核提供的一种资源管理机制，用于对进程分组并对其资源进行限制和隔离。Cgroups 可以用于限制进程的 CPU、内存、磁盘、网络等资源，也可以用于限制进程的优先级和 IO 权限。利用其提供的能力，我们很容易实现类似不公平调度算法（我们新的 AM 天然支持 Cgroups 的操作），但是我还有一些顾虑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不同于手机操作系统，计算机操作系统是多任务并行的，在多数窗口管理器下，我们并没有一个明显的前台应用，此时使用不公平调度可能存在隐患；&lt;/li&gt;
&lt;li&gt;容易引发人机对抗。在我的观念里面，计算机是为人服务的，那么用户的意志必定是第一优先级，所以我们不应改变用户的行为，如果使用不平衡调度和用户预期不一致，会极大降低用户体验；&lt;/li&gt;
&lt;li&gt;使用前后台区分应用，可能导致开销和收益比下降，性价比不高。linux 桌面不像安卓设备有明显前后台，那么用户频繁切换应用的操作将导致我们的调度器频繁切换调度，使得开销过大。
我认为最佳的解决方案是：提供能力，但不提供方案。我们可以提供基于 Cgroups 方式修改应用组的优先级，然后让用户自己选择什么应用优先级更高，什么应用优先级低，以实现调度（比如在 dock 上右键选择优先级）或提供一套配置以供用户自由选择。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果一个电脑需要使用不平衡调度来保证使用流畅性，可能这并不是一个操作系统能解决的问题，而更应该考虑硬件是否需要更换，以保证多任务使用的流畅性。&lt;/p&gt;
&lt;h2 id=&#34;附录常用的调试测试工具&#34; &gt;附录——常用的调试测试工具
&lt;span&gt;
    &lt;a href=&#34;#%e9%99%84%e5%bd%95%e5%b8%b8%e7%94%a8%e7%9a%84%e8%b0%83%e8%af%95%e6%b5%8b%e8%af%95%e5%b7%a5%e5%85%b7&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;1s-tui&#34; &gt;1. S-tui
&lt;span&gt;
    &lt;a href=&#34;#1s-tui&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;可以看到 CPU 频率变化，配合 stress 可以对 cpu 进行压力测试。
&lt;img src=&#34;./s-tui.jpg&#34; alt=&#34;图片&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;2intel-gpu-tools&#34; &gt;2. intel-gpu-tools
&lt;span&gt;
    &lt;a href=&#34;#2intel-gpu-tools&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;可以使用 intel_gpu_frequency 来获取和调整 i965 的驱动频率。&lt;/p&gt;
&lt;h3 id=&#34;3glmark2&#34; &gt;3. glmark2
&lt;span&gt;
    &lt;a href=&#34;#3glmark2&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;GPU 跑分软件。&lt;/p&gt;
&lt;h3 id=&#34;4stress-ng&#34; &gt;4. stress-ng
&lt;span&gt;
    &lt;a href=&#34;#4stress-ng&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;CPU 压力测试软件。&lt;/p&gt;
&lt;h3 id=&#34;5powertop&#34; &gt;5. powertop
&lt;span&gt;
    &lt;a href=&#34;#5powertop&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;电源测试软件，可以看到电源的功耗和使用情况。&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/impressions-after-compiling-wireshark/">
      <title>Wireshark构建编译随笔</title>
      <link>https://blog.deepin.org/posts/impressions-after-compiling-wireshark/</link>
      <pubDate>Mon, 25 Sep 2023 01:14:57 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/impressions-after-compiling-wireshark/</guid>
      <description>&lt;h2 id=&#34;随笔&#34; &gt;随笔
&lt;span&gt;
    &lt;a href=&#34;#%e9%9a%8f%e7%ac%94&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;在给Wireshark做完编译、调整、上架之后，我打算到论坛里看看大家对开源应用有没有什么疑惑的地方，没想到大家现在对开源的关注度这么高了。在看到最近一些闹得比较沸沸扬扬的话题之后，我也对我现在开源应用适配工作的很多地方产生了疑惑，我这样跟开源还有关系吗？&lt;/p&gt;
&lt;h2 id=&#34;重新适配开源应用的意义&#34; &gt;重新适配开源应用的意义
&lt;span&gt;
    &lt;a href=&#34;#%e9%87%8d%e6%96%b0%e9%80%82%e9%85%8d%e5%bc%80%e6%ba%90%e5%ba%94%e7%94%a8%e7%9a%84%e6%84%8f%e4%b9%89&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;在一些实际的场景中，其实有很多&amp;quot;重复造轮子&amp;quot;的情况，即明确存在其他开源项目可用来替代的情况下仍然自己重头造一个项目来实现相同/类似的功能。比如各大桌面环境都有自己的一套文管:DDE的dde-file-manager、KDE的Dolphin，但严格意义上他们不是重复造轮子，而是更好地融入桌面环境中。
另外一种情况就是我现在做的适配开源应用，我的作品很大一部分可以在系统的应用商店中找到。但细心的小伙伴可能会发现:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&amp;ldquo;诶，这个应用不是系统仓库里有吗？为什么还要消耗资源去重复适配?&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;ldquo;Debian 12的这个应用已经去到3.6.1了，为什么商店里的还是3.6.0?&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;ldquo;为什么软件官方已经提供了deepin可以直接安装的包，应用商店还要另起一个新包名互不兼容?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;想到这几个问题，大家是不是血压都上来了?这不就是妥妥重复劳动吗?别急，听我慢慢道出里面的小细节。我愿总结为两个核要义:平易、近人。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;易用性&#34; &gt;易用性
&lt;span&gt;
    &lt;a href=&#34;#%e6%98%93%e7%94%a8%e6%80%a7&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;首先问大家一个问题，抛开命令真爱粉不谈，你认为用apt安装包方便还是在应用商店看着截图、应用描述、评论等等来一站式点击&amp;quot;安装&amp;quot;方便?
虽然命令也可以直接安装各种系统仓内的各种包，但对于新手用户个人认为还是有一定难度的。而且不使用应用商店来下载，很有可能就与应用商店各种特性擦肩而过了，比如定期的应用推荐、应用版本迭代的changelog。&lt;/p&gt;
&lt;h3 id=&#34;用户侧体验优化&#34; &gt;用户侧体验优化
&lt;span&gt;
    &lt;a href=&#34;#%e7%94%a8%e6%88%b7%e4%be%a7%e4%bd%93%e9%aa%8c%e4%bc%98%e5%8c%96&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;不知道大家有没有体验最近Wireshark-4.0.8，有使用过的朋友应该会发现我们很贴心的准备了两个desktop启动方式，一个是普通模式一个是root模式，中间的取舍故事今天就不多赘述了，大家有兴趣可以后面单独拎出来。
至于为什么会设置这么两个模式?其实我在适配开源项目前一般都会预览大家在应用商店里的反馈，这次的举动主要是由于Wireshark用普通user权限可以打开程序，但只是能看个壳子，只有root权限打开才是完整的功能。而且大家也反馈了比较多这个情况，我在评估一段之间、测试可行性之后给大家带来了&amp;quot;两个desktop+提示窗+双语支持&amp;quot;的独家体验。
&amp;ldquo;两个desktop+提示窗&amp;quot;是为了让大家方便以不同的模式打开，&amp;ldquo;提示窗&amp;quot;则是根据不同模式给大家展示相关提示信息，&amp;ldquo;双语支持&amp;quot;则是根据系统语言来设置中文和其他语言下分别显示中文提示和英文提示，一定程度上兼顾海外友人的体验。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://storage.deepin.org/thread/202309071547445194_MTY4ODg1NTA3OTQ2MTg2MQ_195246_7TDtqIUatOie8hMn_1693545829.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;我观察了下，Wireshark编译之后的原版desktop文件是不提供这种级别的优化的；而现在deepin应用商店里的版本实实在在可以做到了这个层次的优化，亲近人心。
大家可以把细节打在评论上。&lt;/p&gt;
&lt;h3 id=&#34;生态多样性&#34; &gt;生态多样性
&lt;span&gt;
    &lt;a href=&#34;#%e7%94%9f%e6%80%81%e5%a4%9a%e6%a0%b7%e6%80%a7&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;严格意义上，每个包只有包名是相对唯一的，这也是因为大部分包管理器都会通过包名来检查该包的情况。但对于同一个应用特别是开源项目而言，它允许被多次修改分发，除了用版本号用以区分，也并没有强制要求每个人维护的包名都遵循同一个。
但为什么Debian等主流发行版一般包名都会随着版本迭代而保留使用，一般并不轻易修改包名?个人认为有以下几个可能:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;一般主流发行版仓库中某个重要项目/库为了保证长期稳定性和可维护性，一般都由同一个维护者负责维护，所以包名不会发生明显改动。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;包名变动过大，相关联的其他包/库均需要重新调整依赖关系，不太利于用户体验和仓库维护管理。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接第一个情况，即便每个人都有编译开源项目的自由，但大家都比较自觉不会向系统仓中投递已经确切存在的包。&lt;/p&gt;
&lt;p&gt;除此之外，如果你同时在应用商店和外部安装一个包名一致的应用，则只会保留版本较新的一个。或者说，如果你想要保留本地安装的版本，但你在应用商店里对其进行升级，那此时你本地的包就会被替换掉了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;稳定性测试&#34; &gt;稳定性测试
&lt;span&gt;
    &lt;a href=&#34;#%e7%a8%b3%e5%ae%9a%e6%80%a7%e6%b5%8b%e8%af%95&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;其实在开源应用上架到应用商店前都会有测试流程，大家也很难保障哪一个版本会有致命问题，或者哪一个版本会丧失预期特性。所以我们在选包之后，一般是先编译一遍，没有致命问题我们才进行其他操作，尽最大努力保驾护航。&lt;/p&gt;
&lt;h2 id=&#34;适配开源应用的难度&#34; &gt;适配开源应用的难度
&lt;span&gt;
    &lt;a href=&#34;#%e9%80%82%e9%85%8d%e5%bc%80%e6%ba%90%e5%ba%94%e7%94%a8%e7%9a%84%e9%9a%be%e5%ba%a6&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;要是你觉得我们就是单纯把一些官方提供的二进制归档文件甚至是直接可用的deb包转化来上架应用商店，比如Visual Studio Code。那我很坦白告诉你，就是单纯几个字，&amp;ldquo;解包&amp;ndash;运行&amp;ndash;重新打包&amp;rdquo;。
但如果你是看了上文内容的话，就知道事情并不简单了。不只是从源码开始编译，核心在于如何与终端用户、各大开发者站在统一战线。当然，这里指的并不只是不断去满足用户的需要，这种可能会比较适合C端方向。我更希望的，是帮助用户在专业性比较强的问题面前能够提供满意的答卷，比如这次Wireshark，其实大部分用户真的不一定知道需要使用root来运行，所以我们做了优化。
应用/软件包只是一个普通产出物，不可替代的其实是整个适配过程中与其他人产生差异化的用户思维。&lt;/p&gt;
&lt;h2 id=&#34;结语&#34; &gt;结语
&lt;span&gt;
    &lt;a href=&#34;#%e7%bb%93%e8%af%ad&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;曾经，我觉得我适配出来的开源应用只上架应用商店确实是玷污了开源，而且我并不是对程序Core本身做出了代码性贡献，这些优化充其量可能也就是普通建议而已。但后来，能看到大家对开源的支持，我也释怀了。
我希望以后有机会可以分享我在编译中发生的故事，同时也很支持使用其他发行版的小伙伴可以使用我在deepin上编译、优化调整之后的开源应用。
我也希望在开源这片蓝天里，我不是孤军奋战。&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/deepin-ide-debug/">
      <title>deepin-IDE中的调试技术</title>
      <link>https://blog.deepin.org/posts/deepin-ide-debug/</link>
      <pubDate>Fri, 22 Sep 2023 18:19:31 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/deepin-ide-debug/</guid>
      <description>&lt;p&gt;前不久深度科技旗下deepin社区发布了自己的 IDE：deepin-IDE，得到了全网用户尤其是开源社区用户的广泛关注，目前在 GitHub（&lt;a href=&#34;https://github.com/linuxdeepin/deepin-unioncode&#34;&gt;https://github.com/linuxdeepin/deepin-unioncode&lt;/a&gt;）仓库的 star 数量已经达到 600 多个，说明大家的热情还是很高涨的。&lt;/p&gt;
&lt;div align=center&gt;&lt;img src=&#34;https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922163311.png&#34;&gt;&lt;/div&gt;
&lt;p&gt;为了从技术层面给大家的热情做一个反馈，本文试着将 deepin-IDE 内部的一些实现方法进行分享，希望能够解答友友们的疑惑并得到积极的反馈。&lt;/p&gt;
&lt;p&gt;本篇挑了大家关心的“调试”部分进行分享。需要说明的是，deepin-IDE 的调试功能是选用 DAP（Debug Adapter Protocol ）调试适配协议实现的，所以整体架构是围绕该协议搭建的，至于 &lt;code&gt;DAP&lt;/code&gt; 具体是什么，让我们带着问号往下看。&lt;/p&gt;
&lt;h2 id=&#34;什么是-dap-协议&#34; &gt;什么是 &lt;code&gt;DAP&lt;/code&gt; 协议
&lt;span&gt;
    &lt;a href=&#34;#%e4%bb%80%e4%b9%88%e6%98%af-dap-%e5%8d%8f%e8%ae%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;DAP&lt;/code&gt; 即调试适配协议( Debug Adapter Protocol )，顾名思义，它是用来对多种调试器进行抽象统一的适配层，将原有 &lt;code&gt;IDE&lt;/code&gt; 和调试工具直接交互的模式更改为和 &lt;code&gt;DAP&lt;/code&gt; 进行交互。该模式可以让 &lt;code&gt;IDE&lt;/code&gt; 集成多种调试器变得更简单，且灵活性更好。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;IDE&lt;/code&gt; 中的调试功能有许多小功能组成，包括单步执行、断点、查看变量值等，常规的实现方式是在每个 &lt;code&gt;IDE&lt;/code&gt; 中去实现这些逻辑，且因为调试工具的接口不同，还需要为每个调试工具做一些适配工作，这将导致大量且重复的工作，如下图所示：&lt;/p&gt;
&lt;div align=center&gt;&lt;img src=&#34;https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922164609.png&#34;&gt;&lt;/div&gt;
&lt;p&gt;调试适配器协议背后的想法是标准化一个抽象协议，用于开发工具如何与具体调试器通信。这个思想和 &lt;code&gt;LSP&lt;/code&gt;(Language Server Protocol)和 &lt;code&gt;BSP&lt;/code&gt;(Build Server Protocol)类似，都是通过协议去统一相同功能在不同工具之间的差异性。其所处位置如下图所示，其中左边为不同的开发工具，右边为不能同的调试器，不同于开发工具和调试器直接交互的方式，&lt;code&gt;DAP&lt;/code&gt; 将这些交互统一了起来，让开发工具和调试工具都面向 &lt;code&gt;DAP&lt;/code&gt; 编程。&lt;/p&gt;
&lt;p&gt;上图中的交互是通过协议进行，所以不会像通过 &lt;code&gt;API&lt;/code&gt; 的方式存在语言限制，可以更好的适应调试器的集成。&lt;/p&gt;
&lt;h2 id=&#34;dap-如何工作&#34; &gt;DAP 如何工作
&lt;span&gt;
    &lt;a href=&#34;#dap-%e5%a6%82%e4%bd%95%e5%b7%a5%e4%bd%9c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;以下部分解释了开发工具（例如 &lt;code&gt;IDE&lt;/code&gt; 或编辑器）和调试适配器之间的交互，包括具体的协议格式说明、交互流程等。&lt;/p&gt;
&lt;h3 id=&#34;调试会话&#34; &gt;调试会话
&lt;span&gt;
    &lt;a href=&#34;#%e8%b0%83%e8%af%95%e4%bc%9a%e8%af%9d&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;开发工具有两种基础的方式和调试器进行交互，分别是:&lt;/p&gt;
&lt;p&gt;【单会话模式】&lt;/p&gt;
&lt;p&gt;在这种模式下，开发工具启动一个调试适配器作为一个单独的进程并且通过标准的std接口进行通信。在调试会话的结束时调试适配器就终止，对于当前的调试会话，开发工具往往需要实现多个调试适配。&lt;/p&gt;
&lt;p&gt;【多会话模式】&lt;/p&gt;
&lt;p&gt;在这种模式下，开发工具不会启动调试适配器，而是假定它已经在运行并且会在特定端口上侦听连接尝试，对于每个调试会话，开发工具在特定端口上启动一个新的通信会话并在会话结束时断开连接。&lt;/p&gt;
&lt;p&gt;在与调试适配器建立连接后，开发工具和调试适配器之间通过基础协议进行通信。&lt;/p&gt;
&lt;h3 id=&#34;基础协议&#34; &gt;基础协议
&lt;span&gt;
    &lt;a href=&#34;#%e5%9f%ba%e7%a1%80%e5%8d%8f%e8%ae%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;基础协议由两部分组成，包括头和内容(类似于 HTTP)，头部和内容部分通过“\r\n”进行分割：&lt;/p&gt;
&lt;p&gt;【协议头】&lt;/p&gt;
&lt;p&gt;协议头部分由字段组成， 每个头字段由一个键和一个值组成，用‘:’（一个冒号和一个空格）分隔， 每个头字段都以“\r\n“结尾。由于最后一个协议头字段和整个协议头本身都以 \r\n 终止，并且由于协议头是强制性的，所以消息的内容部分总是在（并唯一标识）两个 \r\n 序列之前。当前只支持一个协议头字段：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;头字段名&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;值类型&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Content-Length&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;数字&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;这个字段是必须的，用来记录内容字段的长度，单位是字节。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;协议头部分使用的是“ASCII”编码。&lt;/p&gt;
&lt;p&gt;【内容部分】&lt;/p&gt;
&lt;p&gt;内容部分包含了实际要传输的数据，这些数据用 &lt;code&gt;JSON&lt;/code&gt; 格式来描述请求、响应和事件。内容部分用的是 &lt;code&gt;utf-8&lt;/code&gt; 编码&lt;/p&gt;
&lt;p&gt;为了有个具体的认识，这里举个简单的例子。在调试过程中，开发人员经常会使用到下一步操作，在 &lt;code&gt;DAP&lt;/code&gt; 中其协议为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;Content-Length: 119\r\n
\r\n
{
    &amp;#34;seq&amp;#34;: 153,
    &amp;#34;type&amp;#34;: &amp;#34;request&amp;#34;,
    &amp;#34;command&amp;#34;: &amp;#34;next&amp;#34;,
    &amp;#34;arguments&amp;#34;: {
        &amp;#34;threadId&amp;#34;: 3
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;类型是“请求”，命令是下一步，参数部分可以携带多个，这里是用的线程Id。
这个协议看着挺简单的，是吧？接下来就讲讲如何使用它。&lt;/p&gt;
&lt;h3 id=&#34;使用方法&#34; &gt;使用方法
&lt;span&gt;
    &lt;a href=&#34;#%e4%bd%bf%e7%94%a8%e6%96%b9%e6%b3%95&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;详细的使用方法这里就不涉及，因为用一个时序图就可以说明：&lt;/p&gt;
&lt;div align=center&gt;&lt;img src=&#34;https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922164954.png&#34;&gt;&lt;/div&gt;
&lt;p&gt;可以看到，初始化、请求、响应等必要的步骤都在图中。其中调试适配器可以理解为调试器的抽象，调试功能的最终执行者是由对应语言的调试工具实现的。&lt;/p&gt;
&lt;h2 id=&#34;在-deepin-ide-中的实现&#34; &gt;在 deepin-IDE 中的实现
&lt;span&gt;
    &lt;a href=&#34;#%e5%9c%a8-deepin-ide-%e4%b8%ad%e7%9a%84%e5%ae%9e%e7%8e%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;在 deepin-IDE 中，调试功能的实现是结合 &lt;code&gt;cppdap&lt;/code&gt; + &lt;code&gt;debugmanager&lt;/code&gt; 实现的。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cppdap&lt;/code&gt; 是一款基于 C++ 开发的 &lt;code&gt;SDK&lt;/code&gt;，基本实现了 &lt;code&gt;DAP&lt;/code&gt; 的全量协议。 deepin-IDE 的客户端和服务端都是应用的该 &lt;code&gt;SDK&lt;/code&gt; 进行开发，据此可以实现以下功能：&lt;/p&gt;
&lt;p&gt;1.通信功能，包括服务端的 TCP 监听，客户端的 TCP 连接等；&lt;/p&gt;
&lt;p&gt;2.&lt;code&gt;DAP&lt;/code&gt; 协议的封装，并实现协议的串行化和解串行化；&lt;/p&gt;
&lt;p&gt;3.提供注册回调功能，从而可以在回调内处理各种事件、请求等；&lt;/p&gt;
&lt;p&gt;它的层级结构如下：&lt;/p&gt;
&lt;div align=center&gt;&lt;img src=&#34;https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922165226.png&#34;&gt;&lt;/div&gt;
&lt;p&gt;用 &lt;code&gt;cppdap&lt;/code&gt; 可以减少客户端和服务端不少工作量，也统一了两边的协议数据。而 debugmanager 可以理解为调试器的抽象，包含所有必要的调试要素。整体结构如下：&lt;/p&gt;
&lt;div align=center&gt;&lt;img src=&#34;https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922165251.png&#34;&gt;&lt;/div&gt;
&lt;p&gt;左边是客户端，右边是服务端，内部实现如下：&lt;/p&gt;
&lt;h3 id=&#34;客户端实现&#34; &gt;客户端实现
&lt;span&gt;
    &lt;a href=&#34;#%e5%ae%a2%e6%88%b7%e7%ab%af%e5%ae%9e%e7%8e%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;客户端包含了两个个主要功能，一个是和 &lt;code&gt;DAP&lt;/code&gt; 服务端进行交互，发送调试命令或处理返回的数据；另一个是将&lt;code&gt;DAP&lt;/code&gt; 数据转换后显示到用户界面，并响应界面发送的事件。概括起来就包含业务模块、事件模块、&lt;code&gt;DAP&lt;/code&gt; 模块和界面4个部分。&lt;/p&gt;
&lt;p&gt;业务模块&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;业务模块包含了插件类、调试参数、调试管理类等，其中插件类负责插件加载、初始化、获取上下文等，调试管理类用来组合事件、&lt;code&gt;DAP&lt;/code&gt;、界面几个模块。
事件模块&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;事件模块包含两个子模块，分别是事件发送和事件接收，比如页面跳转事件、添加\移除断点事件等。
&lt;code&gt;DAP&lt;/code&gt; 模块&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;DAP&lt;/code&gt; 模块基于 &lt;code&gt;cppdap&lt;/code&gt; 开发，采用层级结构，底层是原始 &lt;code&gt;DAP&lt;/code&gt; 协议封装，中间层是针对业务做的进一步封装，简化了向外提供的接口，最上层是对整个调试功能的整合，包括数据缓存、界面元素、命令收发。&lt;/p&gt;
&lt;div align=center&gt;&lt;img src=&#34;https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922165331.png&#34;&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;界面部分
界面模块包含堆栈界面、变量界面、断点列表、异步对话框等，用于 &lt;code&gt;DAP&lt;/code&gt; 的数据展示。&lt;/li&gt;
&lt;/ul&gt;
&lt;div align=center&gt;&lt;img src=&#34;https://wiki.deepin.org/05_HOW-TO/deepin-unioncode/img-20230922165342.png&#34;&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;如上图所示，灰色部分为 &lt;code&gt;DAP&lt;/code&gt; 客户端的界面呈现。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;服务端实现&#34; &gt;服务端实现
&lt;span&gt;
    &lt;a href=&#34;#%e6%9c%8d%e5%8a%a1%e7%ab%af%e5%ae%9e%e7%8e%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;服务端的功能分为两个部分，一个是基于 &lt;code&gt;cppdap&lt;/code&gt; 实现命令的收发，另一个是与 &lt;code&gt;gdb&lt;/code&gt; 交互，实现调试程序的启动、暂停、退出等一系列动作。&lt;/p&gt;
&lt;p&gt;DAP&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;和客户端一样，服务端也是基于&lt;code&gt;cppdap&lt;/code&gt;实现的通信和协议封装和解析。
调试工具&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;和调试工具的交互是通过进程调用的方式实现，接收进程输出得到返回信息。如果调试工具本身支持 &lt;code&gt;DAP&lt;/code&gt; 协议，则可以直接交互。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;至此，本次的分享就到这儿啦！不知道你对 &lt;code&gt;deepin-IDE&lt;/code&gt; 中的调试功能有所了解了吗？&lt;/p&gt;
&lt;p&gt;温馨提示，deepin-IDE 还包含很多有意思的功能，如果大家感兴趣可以积极反馈，后续有机会再进行分享。&lt;/p&gt;
&lt;h2 id=&#34;参考文档&#34; &gt;参考文档
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%82%e8%80%83%e6%96%87%e6%a1%a3&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;&lt;a href=&#34;https://microsoft.github.io/debug-adapter-protocol/overview&#34;&gt;debug-adapter-protocol&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://wiki.deepin.org/zh/05_HOW-TO/02_%E5%BC%80%E5%8F%91%E7%9B%B8%E5%85%B3/deepin-unioncode&#34;&gt;deepin-IDE 使用手册&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;内容来源：deepin社区&lt;/p&gt;
&lt;p&gt;内容作者：deepin-mozart、toberyan&lt;/p&gt;
&lt;p&gt;转载请注明出处&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/history-of-deep-learning/">
      <title>深度学习的历史与八卦</title>
      <link>https://blog.deepin.org/posts/history-of-deep-learning/</link>
      <pubDate>Fri, 01 Sep 2023 13:50:31 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/history-of-deep-learning/</guid>
      <description>&lt;h2 id=&#34;楔子&#34; &gt;楔子
&lt;span&gt;
    &lt;a href=&#34;#%e6%a5%94%e5%ad%90&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  有这么一个说法，每多一个数学公式，读者就减少一半。深度学习想来也无法免俗，毕竟技术文章不免艰涩，而要完全绕过公式讲好深度学习与大模型，以臣妾微薄的实力实在是做不到啊。&lt;/p&gt;
&lt;p&gt;  因此，本文先歪歪楼，讲讲深度学习与大模型的历史与八卦，一方面是让大家稍微了解下技术发展的脉络，另一方面也是尝试挽救一下读者的欢心，毕竟历史八卦，人人都爱。&lt;/p&gt;
&lt;h2 id=&#34;历史&#34; &gt;历史
&lt;span&gt;
    &lt;a href=&#34;#%e5%8e%86%e5%8f%b2&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  说到神经网络的起源，一般都会追溯到沃尔特·皮茨（Walter Pitts）与麦卡洛克（McCulloch），其中皮茨起到了更主要的作用，而且更具传奇性。皮茨（见图1）于1923年出生于美国底特律的铁匠家庭，家庭教育以老爸的拳头为主，而在这种家庭环境里，他主要靠自学学会了拉丁文、希腊文、逻辑和数学。在12岁那年，皮茨看完了罗素与怀特海的大厚本《数学原理》，并向罗素写邮件附上了自己发现的一些问题，罗素不免大吃一惊，还回信邀请皮茨到剑桥大学读他的研究生。由于家庭与年龄原因，皮茨当然不可能成行，不过当三年后，罗素到美国芝加哥讲学的消息传到皮茨耳朵里时，他就离家出走，而且终其一生再也没有回去过。其后皮茨遇到了麦卡洛克，两人在数学、逻辑和神经网络上有着共同的看法，并一起努力，于1943年合作完成了知名论文《A LOGICAL CALCULUS OF THE IDEAS IMMANENT IN NERVOUS ACTIVITY》，在这篇论文中，他们用二进制逻辑门来表示神经元，而且证明了此模型可以实现任何经典逻辑，从而表明了神经网络的通用性，奠定了深度学习的基础，同时也建立了神经科学和计算机科学之间的交叉研究。高中未毕业的皮茨受到了数学家与控制论之父维纳的欣赏，破格进入麻省理工学院攻读博士学位，被引荐给了冯·诺依曼，而上述论文也成为了冯·诺依曼关于计算机架构的著名论文《First Draft of a Report on the EDVAC》所引用的唯一一篇文章。但是在其后，首先是维纳与皮茨等断绝了关系，然后科学家们又发现神经网络并不能解释一切生物感知现象，皮茨深感失望，于是烧掉了自己历经数年写作的博士论文，从此退出了科研界，并死于酗酒相关的病症。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;img-20230831173324.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图1 沃尔特·皮茨&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  我们要介绍的下一位是弗兰克·罗森布拉特（Frank Rosenblatt），他是感知机（Perceptron）的提出者（感知机在我们之前的文章“深度学习入门”里介绍过），并且于1958年在实验了50次之后，让IBM 704自行学会了识别打孔卡上的标记是在左侧还是在右侧。他认为，通过感知机可以不依赖人类的训练与控制，就能感知、识别和辨认周边的环境，其代表作是《Principles of Neurodynamics: Perceptrons and the Theory of Brain Mechanisms》。但是，当时罗森布拉特设计的感知机实际上是只有一层的神经网络，能力非常有限，相比之下，现代成熟的神经网络则有数十层，因此同样是人工智能专家的明斯基撰文指出了感知机的局限性，并使得相应的研究进入了低潮。直到在2004年，大家重新看到了神经网络的潜力，IEEE协会为此也专门设立了罗森布拉特奖。&lt;/p&gt;
&lt;p&gt;  在神经网络漫长的寒冬期，研究者相对较少，其中就包括了于1974年在其博士论文中提出了反向传播（backpropagation）的Werbos（并由于此贡献获得了IEEE罗森布拉特奖），还有于上个世纪八十年代提出了Hopfield 神经网络的Hopfield。&lt;/p&gt;
&lt;p&gt;  下面隆重登场的是深度学习之父，大名鼎鼎的杰弗里·辛顿（Geoffrey Hinton）。辛顿1986年发表的论文《Learning Representations by Back-Propagating Errors》，给出了通过反向传播学习表征的算法，于2006年其推出了深度学习（Deep Learning）的概念，为深度学习与大模型的大潮解开了序幕。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;img-20230831173612.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图2 杰弗里·辛顿&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  另一位深度学习的大佬杨立昆（Yann LeCun）在辛顿麾下求学后就职于贝尔实验室，并在1989～1993年间发明了卷积神经网络（CNN），可以用来解决手写数字识别（如MNIST）的问题。当时可没有GPU，那时CPU的性能也相当低下。&lt;/p&gt;
&lt;p&gt;  2012年是深度学习重要的一年。在这一年，辛顿和他的两个学生Alex Krizhevsky，以及Ilya Sutskeverz共同发布了AlexNet这个多层神经网络。这个神经网络用到了诸多的新技术，包括使用了ReLU作为激活函数，使用了CUDA利用英伟达的GPU来进行神经网络的计算，使用了dropout作为神经网络的一种优化方法等。AlexNet在知名的图像识别分类比赛ImageNet中一鸣惊人，它不仅获得了比赛的冠军，而且其错误率达到了16.4%，比当年亚军的26.2%低了将近10%，比2011年冠军的25.8%低了超过9%，几乎可以认为是降维打击。自此，深度学习名声大噪，大量研究人员都转向深度学习尝试完成计算机视觉等相关的人工智能任务。&lt;/p&gt;
&lt;p&gt;  下面深度学习的发展就顺畅了很多，2014年Ian Goodfellow推出了GAN，通过生成式对抗网络能生成逼真的图像甚至视频；2016年 DeepMind 推出了AlphaGo，其水平很快就远远超过了人类围棋冠军；同样在 2016年，何恺明等研究者推出了ResNet，它成为了包括大语言模型在内的各神经网络的通用技术，对应论文引用截止2023年上半年已经超过了17万，成为了深度学习领域引用最高的文章。&lt;/p&gt;
&lt;p&gt;  2017年是大模型关键技术transformer的诞生年，它是在《Attention is All You Need》这篇论文中被提出的，其后就成为包括BERT、GPT、T5等大语言模型使用的框架，而且跨界到了计算机视觉领域，形成了ViT等新的研究方向，几乎成了一统江湖的标准模型。&lt;/p&gt;
&lt;p&gt;  2018年是深度学习三巨头辛顿、杨立昆与约书亚·本吉奥（Yoshua Bengio）的收获之年，他们因为在深度学习方面的诸多成就与影响力获得了ACM图灵奖，这项计算机科学领域的最高奖。在接下来的几年，深度学习的各项研究成果仍然难以大规模落地，诸多深度学习相关的公司持续烧钱，很多人认为这一波人工智能的热潮马上又要过去了。&lt;/p&gt;
&lt;p&gt;  让我们快进到2022年，上半年以stable diffusion为代表的图像生成模型风靡一时，而到了下半年的十一月底，以ChatGPT为代表的大语言模型横空出世，其知识广度、推理能力与多轮对话能力使得它成为了历史上最快达到一亿用户的产品，重新点燃了人工智能产业化的火炬，并使得业界的诸多大佬认定它是划时代的产品，其影响深远，延续至今。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;img-20230831173802.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图3 DNNResearch团队&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  顺便说一句，GPT系列大模型的出品者是OpenAI，而OpenAI的首席科学家就是之前提到过的Ilya Sutskeverz。在2012年AlexNet一飞冲天后，多个大厂邀请辛顿等三人加入，于是辛顿团队成立了一个名为DNNResearch的公司，公司仅有他们叁，公司唯一的目的就是被大厂整体收购。竞拍的公司包括谷歌、百度、微软与DeepMind，随着竞拍价格的逐步走高，微软与DeepMind首先被淘汰出局，谷歌与百度均出价到了4400万美元。辛顿当晚暂停了竞拍，第二天早上便决定公司被谷歌收购。Ilya Sutskeverz在其后做出了诸多贡献，包括首创seq2seq（大语言模型的前身），参与深度学习框架Tensorflow与AlphaGo的开发等，最终于2015年加入OpenAI，成为了GPT等系列产品的核心技术人物。&lt;/p&gt;
&lt;h2 id=&#34;八卦&#34; &gt;八卦
&lt;span&gt;
    &lt;a href=&#34;#%e5%85%ab%e5%8d%a6&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  关于深度学习之父辛顿，还有很多不少八卦，其家族还与中国有着不少联系。&lt;/p&gt;
&lt;p&gt;  从头说起，George Everest是十九世纪英国的大地测量工作者，其主要的工作地域都在印度，他和其前任测量了从喜马拉雅山到印度次大陆最南端科莫林角11.5度的经向弧，由于这些贡献，珠穆朗玛峰（Mount Everest）以他的名字命名。George Everest有一个侄女婿名为乔治·布尔（George Boole），也就是著名的布尔代数的发明者，现代计算机所使用的逻辑与、或、非等运算均来自于他。&lt;/p&gt;
&lt;p&gt;  乔治·布尔的小女儿艾捷尔·丽莲·伏尼契（Ethel Lilian Voynich）则是知名的《牛虻》的作者，这本书描写的是19世纪意大利爱国者反对奥地利统治者的斗争，是风靡一时的革命书籍，在苏联和中国都拥有广大读者。&lt;/p&gt;
&lt;p&gt;  乔治·布尔的大女儿玛丽（Mary）则嫁给了一个姓辛顿的数学家，他们有一个孙子威廉·辛顿（William Hinton），以及孙女琼·辛顿（Joan Hinton），不过在中国，他们一般被称为韩丁与寒春。寒春是芝加哥大学核子物理研究所的研究生，也是曼哈顿计划中少数的女科学家之一，在洛斯阿拉莫斯（Los Alamos）武器试验室做费米的助手，其后反对核战争，并于1948年来到中国，进入延安，投身中国革命，并与阳早（Erwin Engst）结婚，成为了奶牛专家。现在在北京还留有他们的工作成果，那就是北京市昌平区沙河大学城农机院的学农基地，寒春与阳早均为农机院学农基地的创办人，时至今日，每年仍然有大量的北京中学生来到学农基地参加学农实习（笔者的小孩有幸成为其中的一员）。寒春和阳早把大部分生命都奉献给了中国的事业，并最终在中国去世。2004年8月，中国开始实施“绿卡”制度，寒春成为了第一个获得中国“绿卡”的外国人。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;img-20230831173819.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图4 阳早与寒春&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  玛丽的另一支当然就是深度学习之父杰弗里·辛顿了，他是玛丽的曾孙，因此比寒春、韩丁晚一辈。当然，他们之间并没有直接联系。&lt;/p&gt;
&lt;p&gt;  深度学习的历史与八卦到此告一段落。下面，我们又将步入正轨，重新来讲讲产品与技术了，咱们下次再见。&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/approaching-llms/">
      <title>走进大模型</title>
      <link>https://blog.deepin.org/posts/approaching-llms/</link>
      <pubDate>Thu, 24 Aug 2023 10:02:31 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/approaching-llms/</guid>
      <description>&lt;h2 id=&#34;应用大模型&#34; &gt;应用大模型
&lt;span&gt;
    &lt;a href=&#34;#%e5%ba%94%e7%94%a8%e5%a4%a7%e6%a8%a1%e5%9e%8b&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  AIGC 是基于大模型的，而大模型的基础是深度学习。上一篇文章对深度学习进行了初步介绍，首先是深度学习的神经元起源，引发了基于线性函数的模拟，又因为线性函数无法习得逻辑异或，因此引入了非线性的激活函数，再通过三层神经网络给出了MNIST手写数字识别的模型，接着又介绍了神经网络是如何通过数据与反向传播来学习与调整参数的，最后给出了神经网络的分层结构。&lt;/p&gt;
&lt;p&gt;  大模型的直观应用当然首先体现在包括ChatGPT、文心一言、讯飞星火等问答型产品的使用上，另一方面也体现在编程上，在此先给出大模型的编程应用。以下使用的模型、库与样例均来自于Hugging Face。&lt;/p&gt;
&lt;p&gt;  图1给出了基于大模型的英中翻译代码与运行结果。从图1中可以看到，真实的翻译代码只有14、15两行，其逻辑是使用了Helsinki-NLP的opus-mt-en-zh模型，其中mt代表机器翻译（machine translation）、en和zh分别表示英文和中文。从图1中同样可以看到，翻译结果相对还是比较准确的。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%871.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图1 基于大模型的英翻中&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  图2给出了基于大模型的文本情感分析的代码与运行结果。从图2中可以看到，实际有用的代码也仅需14、16两行，而且这次没有指定具体模型，只给出了需要text-classification这种模型。代码运行结果是认为文本情绪是负面的（NEGATIVE），准确度大概是90.1546%，这显然是符合实际的，因为文本是对商家发错货的抱怨。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%872.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图2 基于大模型的文本情感分析&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  图3给出了基于大模型的问答。这次的代码稍多一点，但实际的代码也只有三行。第14行给出了需要一个问答（question-answering）的大模型，但是没有指定大模型的名字，第15行是提问的字符串“What does the customer want”，即用户到底想要什么。第16行则使用上述文本作为上下文，提问字符串作为问题，传给问答大模型获取答案。从运行结果看来，答案还是蛮靠谱的。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%873.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图3 基于大模型的问答&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  当然，基于大模型的程序还有很多，但是从上面三个例子已经可以看出，基于大模型可以写出简短而强大的自然语言处理的程序，下面让我们走进大模型，看看它究竟是如何做到这一点的。&lt;/p&gt;
&lt;h2 id=&#34;走进大模型&#34; &gt;走进大模型
&lt;span&gt;
    &lt;a href=&#34;#%e8%b5%b0%e8%bf%9b%e5%a4%a7%e6%a8%a1%e5%9e%8b&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  大模型在自然语言处理领域里大放异彩，因此首先需要了解自然语言的特点。&lt;/p&gt;
&lt;p&gt;  自然语言的显著特点（也是难点），那就是词与词之间有着广泛的关联。比如下面两句英文：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to the bank to get some money.&lt;/li&gt;
&lt;li&gt;Go to the bank to get some water.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  只有看到每一句的最后一个词，才能分辨出 bank 到底是银行还是堤坝。再比如下面这两句中文：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;今天太冷了，能穿多少穿多少。&lt;/li&gt;
&lt;li&gt;今天太热了，能穿多少穿多少。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  整句话唯一不同的就是冷与热这两个字，但也就是这句话的一字之差，就导致了整句话的意义完全不同了。&lt;/p&gt;
&lt;p&gt;  因此，自然语言处理的关键点就在于如何能准确地判断词与词之间的关系，如果能准确地知道所有词之间的关系，那即使缺了一个词，也能根据关系推出缺的词应该是什么词。当下处理这一问题的主流技术是transformer，这个词不好翻译，主要因为它和变形金刚的英文一模一样。transformer的核心概念是注意力（attention），即每个词到底在注意其它的哪个词，或者说哪些词之间有什么关系。&lt;/p&gt;
&lt;p&gt;  注意力具体由以下关键概念组成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个词（实际上是词元，token）均有对应的 q/query（查询）、k/key（键值）与 v/value（值）这三个矩阵变量&lt;/li&gt;
&lt;li&gt;q[i]用来查询本词（i）与其它词（j）之间的注意力&lt;/li&gt;
&lt;li&gt;k[j]是词j回应查询的键值，具体是q[i]与k[j]相乘后缩放，接着用softmax激活函数处理，再乘以v[j]，这就得到了词i针对词j的注意力att[i,j]。&lt;/li&gt;
&lt;li&gt;词i的对应输出为计算得到的注意力之和，即 y[i] = att[i,1] + att[i,2] + &amp;hellip; + att[i,n]&lt;/li&gt;
&lt;li&gt;注意力可以有多个（multi-head），每个注意力可以关注不同的方向，例如有的注意力关注的是词与词之间的意义，有的关注的是押韵，等等&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  图4给出了一个句子中各个词注意力的计算过程。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%874.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图4 注意力计算过程&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  句子是“小明、小刚、小红是小强的朋友，小明是&amp;hellip;”，当前的词是“小红”，序号是3，可以看到小红的q值与每个词的k值相乘之后再用softmax处理（缩放操作在这里省略了），接着再与每个词的q值相乘，最后相加即可得到序号为3的输出。&lt;/p&gt;
&lt;p&gt;  那么这些k、q、v 等的值如何确定呢？当然是通过上一篇文章里提到的反向传播进行学习的，那反向传播学习自然语言的正确输出是什么呢？在大语言模型中，其训练手段是使用大量的高质量语料，将词语按序逐批输入大模型，以原句子中的下一个词或者特意被空缺出来的词为正确输出来学习的。例如在上面的句子中，大模型在输入了“小明、小刚、小红是小强的”之后，应该能计算输出“朋友”这个词，如果输出错误，则通过反向传播调整各个参数。也就是说，大模型就是根据一个词之前的词或者周围的词是什么来计算出这个词的，这就是大模型的理论基础和学习方法。&lt;/p&gt;
&lt;p&gt;  注意力虽然是大模型的核心概念，但除此之外，大模型还用到了其他技术，它们包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一步需要将词转为数值向量（vector）的嵌入层（embedding），这也是现在很火的向量数据库的那个向量，向量数据库就是用来查询哪些文本向量比较相似，从而提取出对应的文字材料的&lt;/li&gt;
&lt;li&gt;给词向量加上含位置信息的位置编码，以分辨出词的先后顺序，显然“我爱你”与“你爱我”的意义截然不同，因此词序是很重要的，而注意力本身并没有用到词序&lt;/li&gt;
&lt;li&gt;在注意力层之后的残差处理与归一化处理，技术原理并不难，感兴趣的读者可以看参考资料&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  现在我们已经对大模型的主要技术原理有了粗浅的了解，下面来看看深度学习与大模型的特点。&lt;/p&gt;
&lt;h2 id=&#34;大模型的特点&#34; &gt;大模型的特点
&lt;span&gt;
    &lt;a href=&#34;#%e5%a4%a7%e6%a8%a1%e5%9e%8b%e7%9a%84%e7%89%b9%e7%82%b9&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  从对深度学习以及大模型的初步应用与原理了解，其实我们已经可以得出一些结论。&lt;/p&gt;
&lt;p&gt;  首先，深度学习的算法模型是神经网络及其节点上的参数/权重构成的，深度学习是端到端的机器学习，它不用由人类专家预先设计特征算法，例如不用苦思冥想怎么设计一个提取图像中线条的算法，只需要用大量的高质量标注数据对其进行投喂，就有可能最终训练出质量不错的神经网络（中的各个参数）来。&lt;/p&gt;
&lt;p&gt;  其次，它是一个黑盒子，因为没有特征设计，因此这么多数值构成的神经网络，它为什么能达到最终的目标，为什么某个参数就必须是最后训练出来的那个数值，为什么有的时候识别会失败或者生成会出错，是否可以手工修改某些特定的参数使得其成功率上升，同时又不会导致原来成功的事例失败，这些问题在现阶段都是难以回答的，这就是深度学习的可解释性问题。&lt;/p&gt;
&lt;p&gt;  在ChatGPT之前，大模型其实也在发展，但是ChatGPT表现出来的多领域通用性和流畅性与之前的产品相比有明显的超越，所以造成了轰动。其后的GPT-4表现更为优异，在《Sparks of Artificial General Intelligence：Early Experiments with GPT-4》这篇论文里，微软团队给出了这样的一个示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;提问：Can you write a proof that there are infinitely many primes, with every line that rhymes?
GPT4：Yes, I think I can,
Though it might take a clever plan.
I’ll start by noting Euclid’s proof,
Which shows that primes aren’t just aloof.
Assume we have a finite list,
Of primes, and that none have been missed.
Multiply them all together,
And add one, just to be clever.
The result will be a number,
That has no prime factors, oh wonder!
But every number has a prime,
That divides it, it’s just a matter of time.
So we’ve found a contradiction,
And our finite list needs eviction.
There must be infinitely many primes,
And that’s the end of my rhyming lines.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;  提问者让GPT-4写一首诗，来证明素数有无限多个，然后GPT-4很快就完成了这首诗，每两行押韵，而且用欧几里得的经典方法给出了证明。当然，在论文中还有很多让人印象深刻的例子，但是上面的例子已能说明GPT-4确实有了很强的能力。&lt;/p&gt;
&lt;p&gt;  GPT中的G表示生成（Generative），这表示它主要的工作方式是生成内容，内容在这里主要是自然语言文本。按照OpenAI首席执行官Sam Altman的说法，The most important difference between ChatGPT and GPT-4, is that the GPT-4 predicts the next word with greater accuracy，即 ChatGPT与GPT-4最大的差异就在于GPT-4在预测下一个词的准确度比ChatGPT更高。&lt;/p&gt;
&lt;p&gt;  GPT中的P表示预训练（Pretrain），即首先用大量语料训练出基础模型（foundation model），然后再用下游任务相关的语料进行精调（FT，即finetuning）。这些下游任务可能是文本分类、翻译、对话等等，这样就可以不用单独为某个特定任务从头训练了。我们可以把中学教育和通识教育看作是预训练，它为大学最终的专业选择，以及以后更细的工作分工打下了坚实的基础。反过来看，在小学年龄阶段没有上学可能会对以后的择业带来很大的限制，这也可以看成是大脑在应预训练的阶段没有进行有效的预训练导致的问题。&lt;/p&gt;
&lt;p&gt;  GPT中的T表示transformer，这个已经在上文中介绍了。&lt;/p&gt;
&lt;p&gt;  一般认为，GPT-4有更强能力的原因在于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它提供了大量的高质量数据，原始数据有45T，清洗后的语料是570G，清洗比例接近1%，这是之前几乎没有团队做到的&lt;/li&gt;
&lt;li&gt;数据中混合了大量的代码，原始数据中有830G代码，这一般被认为是推理能力提升的关键点之一，当然另一方面也大大提升了它的代码能力&lt;/li&gt;
&lt;li&gt;展开了大量不同种类的下游任务，如生成、问答、脑暴、闲聊、摘要、分类、提取等等，以上两点也属于多样化工作，它为GPT4的通用性打下了基础&lt;/li&gt;
&lt;li&gt;使用了基于人工反馈的增强学习（RLHF）方法，召集了40个众包团队，撰写了数十万的提示数据以对齐主流价值观&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  一般认为，大模型的表现之所以如此智能，但是之前的小模型神经网络却那么智障，其原因可能在于涌现（emergence）。涌现可以简单认为是单个个体微观上简单的行为，在宏观上大量复合呈现出难以预料的规律。比如每只蚂蚁其行为其实是挺简单的，但是一群蚂蚁在一起，就可以表现出复杂的规律。又如每个神经元的行为都很简单，但是这么多神经元聚集在一起，就形成了聪明的人类大脑，这也算是一种涌现。&lt;/p&gt;
&lt;p&gt;  涌现最直观的例子可能就是康威的生命游戏（Conwey’s Life Game）了，这个游戏是在一个网格平面（类似围棋棋盘）上发生的，每个个里要么有一个存活的细胞，（用黑色格表示），要么就是一个死亡的细胞（用白色格表示），其规则也很简单，只有以下四条：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当前细胞存活时，当周围存活细胞&amp;lt;2时，该细胞死亡（模拟生命数量稀少）&lt;/li&gt;
&lt;li&gt;当前细胞存活时，当周围有2个或3个存活细胞时，该细胞保持存活&lt;/li&gt;
&lt;li&gt;当前细胞存活时，当周围存活细胞&amp;gt;3时，该细胞死亡（模拟生命数量拥挤）&lt;/li&gt;
&lt;li&gt;当前细胞死亡时，当周围存活细胞=3时，该细胞复活（模拟繁殖）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;  那么图5里的四个样式就表示绝对静止的细胞群体。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%875.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图5 康威生命游戏中绝对静止的细胞群体&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  对每个样式进行分析很快就会知道为什么它们会绝对静止。以第二个样式为例，其每个存活细胞周围都刚好有两个存活细胞，按照规则2，它们都应该保持存活。而任何一个死亡细胞周围都没有四个存活细胞，因此此样式将永远不变。&lt;/p&gt;
&lt;p&gt;  图6给出了震荡循环的细胞群体。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%876.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图6 康威生命游戏中震荡循环的细胞群体&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  按照康威生命游戏的规则，可以发现图6中的两个样式会演变几步之后又变成当前的样式。以第二个样式为例，其一共有左中右三个存活细胞。左侧与右侧的存活细胞附近只有一个存活细胞，因此按照规则1，会在下一轮死亡。中间的存活细胞附近有两个存活细胞，因此按照规则2，保持存活。同时又可以发现，中间的存活细胞上侧和下侧的死亡细胞由于其附近有三个存活细胞，因此根据规则4，在下一轮它们将复活。以此类推，样式会由横三转为纵三，又转回横三，永远震荡循环。&lt;/p&gt;
&lt;p&gt;  康威生命游戏有着远超过上述样式的复杂度，在宏观上甚至可以看到游走、巡回、扩张、凋零等多种细胞社群的样式，因此四条简单的微观规则就衍生出了让人事先难以预料的宏观样式上的复杂度，是涌现的一个生动形象的例子。&lt;/p&gt;
&lt;p&gt;  以ChatGPT与GPT-4为代表的大模型由于其使用了自然语言对话而引发了轰动，让普通人都能直观感受到大模型的魅力，但同时它作为一个基础设施，也提出了一个难题，就是它的编程接口是基于自然语言的，所以需要做所谓的提示工程（prompt engineering）。所谓提示工程，指的就是想让大模型好好干活，那就需要自己好好琢磨怎么和大模型好好说话。俗话说见人下菜，或者说见人说人话，见鬼说鬼话，那见了ChatGPT，当然就得说ChatGPT话了，不然它就没法理解问题，自然也没法给出好的回答了。Linux圈子里有Linus大佬的一句名言：“talk is cheap, show me your code”，中文翻译也很传神：“废话少说，放码过来”，俗一点的话那就是“少哔哔，秀代码”，但是自打GPT横空出世，以后可能就是“code is cheap, show me your talk”了，毕竟，给GPT一个提示，它可以还你百行代码。&lt;/p&gt;
&lt;p&gt;  不过神经网络毕竟是一种信息压缩，或说是一种函数拟合，因此中间肯定会有信息损失，或说是自己瞎想的填补空白，那就避免不了GPT一本正经的胡说八道，也就是所谓的幻觉（hallucination）了。幻觉是当前大模型应用的主要障碍之一，一般认为，大模型近期的发展将沿着消减幻觉、工具集成（即能使用外部工具）、多模态（即除了文本以外，也能理解和生成图形、语音、视频等内容）、垂直领域、类脑智能、具身（embodied）智能等方向发展。&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/getting-started-with-deep-learning/">
      <title>深度学习入门</title>
      <link>https://blog.deepin.org/posts/getting-started-with-deep-learning/</link>
      <pubDate>Mon, 21 Aug 2023 16:22:31 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/getting-started-with-deep-learning/</guid>
      <description>&lt;h2 id=&#34;背景&#34; &gt;背景
&lt;span&gt;
    &lt;a href=&#34;#%e8%83%8c%e6%99%af&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  从去年底以来，AIGC 炙手可热，多个业界大佬都认为 AIGC 会给整个产业带来一场革命，甚至所有的软件都会用 AI 重写。从历史上来看，人机交互方式的变革往往会将操作系统带入下一个世代，著名的例子如从命令行界面的 DOS 到键鼠图形界面的 Windows，以及带来触控界面的 iPhone，领创者都成为了世界顶级企业，带动了整个生态的发展。&lt;/p&gt;
&lt;p&gt;  从技术上来看，AIGC 是基于大模型的，而大模型的基础是深度学习，因此，为了在产品上结合 AIGC，首先从技术上首先需要对深度学习进行有深度的学习。&lt;/p&gt;
&lt;p&gt;  对深度学习与大模型的探索将由一系列文章组成，本文是系列里的第一篇，主要关注的是深度学习的技术入门探索。&lt;/p&gt;
&lt;h2 id=&#34;从神经元开始&#34; &gt;从神经元开始
&lt;span&gt;
    &lt;a href=&#34;#%e4%bb%8e%e7%a5%9e%e7%bb%8f%e5%85%83%e5%bc%80%e5%a7%8b&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  回溯历史，深度学习起始于向人类的大脑学习如何学习。人类大脑皮质的思维活动就是通过大量中间神经元的极其复杂的反射活动，因此不妨先看看神经元的工作机制。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%871.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图1 神经元结构&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  图1给出了神经元的大体结构，左边是神经元的主体，其输入是左侧的多个树突，其输出是右侧的一个轴突。只有当输入树突的信号足够强烈的时候，输出轴突上才会有信号产生。受此启发，就可以设计一个最简单的有两个输入x&lt;sub&gt;1&lt;/sub&gt;与x&lt;sub&gt;2&lt;/sub&gt;，以及一个输出y的线性函数来模拟单个神经元，引入阈值θ，当 w&lt;sub&gt;1&lt;/sub&gt;x&lt;sub&gt;1&lt;/sub&gt; + w&lt;sub&gt;2&lt;/sub&gt;x&lt;sub&gt;2&lt;/sub&gt; ≥ θ时，y为1（表示有信号），否则y为0（表示无信号）。其中w&lt;sub&gt;1&lt;/sub&gt;与w&lt;sub&gt;2&lt;/sub&gt;分别是x&lt;sub&gt;1&lt;/sub&gt;与x&lt;sub&gt;2&lt;/sub&gt;的参数或权重（weight）。&lt;/p&gt;
&lt;p&gt;  有了这个函数，下面来看看它究竟能做什么。按照逻辑主义的设想，数学可以通过逻辑推衍出来，那么不妨看看，上面的函数是否可以表征出基本逻辑运算，如与、或、异或等，在这里x&lt;sub&gt;1&lt;/sub&gt;、x&lt;sub&gt;2&lt;/sub&gt;与y的取值都只能是0或1。&lt;/p&gt;
&lt;p&gt;  对于逻辑与来说，只有当x&lt;sub&gt;1&lt;/sub&gt;与x&lt;sub&gt;2&lt;/sub&gt;都是1的时候，y才是1，否则y是0，容易尝试得到一组可能的w&lt;sub&gt;1&lt;/sub&gt;、w&lt;sub&gt;2&lt;/sub&gt;与θ，分别是0.5、0.5与0.7，如图2所示。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%872.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图2 逻辑与的线性函数图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  图2中横轴为x&lt;sub&gt;1&lt;/sub&gt;，纵轴为x&lt;sub&gt;2&lt;/sub&gt;，从图2中可以看到，(1, 1) 点为实心圆，表示y为1，在(0, 0)、(0, 1)与(1, 0)都是空心圆，表示y为0，中间的虚线表示w&lt;sub&gt;1&lt;/sub&gt;x&lt;sub&gt;1&lt;/sub&gt; + w&lt;sub&gt;2&lt;/sub&gt;x&lt;sub&gt;2&lt;/sub&gt; = θ这条直线，只要这条直线能将(1, 1)点与其它点划分到不同区域，则显然就可以找到至少一组w&lt;sub&gt;1&lt;/sub&gt;、w&lt;sub&gt;2&lt;/sub&gt;与θ满足条件。基于同样的分析，容易知道逻辑或也可以找到对应的w&lt;sub&gt;1&lt;/sub&gt;、w&lt;sub&gt;2&lt;/sub&gt;与θ。但是对于逻辑异或来说，问题就严重了，显然无法找到满足条件的w&lt;sub&gt;1&lt;/sub&gt;、w&lt;sub&gt;2&lt;/sub&gt;与θ，如图3所示。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%873.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图3 逻辑异或的函数图&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  逻辑异或是当x&lt;sub&gt;1&lt;/sub&gt;与x&lt;sub&gt;2&lt;/sub&gt;中一个为0，另一个为1时y才为1，否则y为0，因此在图3中，点(0,1)与点(1,0)为实心圆，而(0, 0)与(1, 1)为空心圆，显然是无法找到一条直线将两个实心圆与两个空心圆划分在两个不同区域的。因此，上述最朴素的线性神经元函数无法表示逻辑异或，也就意味着有大量的运算无法通过上述线性神经元函数来进行。&lt;/p&gt;
&lt;h2 id=&#34;引入激活函数&#34; &gt;引入激活函数
&lt;span&gt;
    &lt;a href=&#34;#%e5%bc%95%e5%85%a5%e6%bf%80%e6%b4%bb%e5%87%bd%e6%95%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  是否能改造上述函数，让它能支持所有运算，从而能承担学习的任务呢？至少，人脑肯定是能学会异或的。现在看来，主要是因为原始的神经元函数太线性导致的这个问题。因此，在深度学习中，就引入了非线性的激活函数（activation function），如图4所示。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%874.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图4 引入激活函数&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  在图4中，首先原函数被修改成了支持多个输入和多个输出的线性变换函数，这样就能处理更多种类的问题了。因为有了多个输入x&lt;sub&gt;1&lt;/sub&gt;、x&lt;sub&gt;2&lt;/sub&gt;&amp;hellip;x&lt;sub&gt;m&lt;/sub&gt;与多个输出h&lt;sub&gt;1&lt;/sub&gt;、h&lt;sub&gt;2&lt;/sub&gt;&amp;hellip;h&lt;sub&gt;n&lt;/sub&gt;，因此权重的下标也带有两个数字，以表示每个权重的作用，例如 w&lt;sub&gt;12&lt;/sub&gt; 是输入x&lt;sub&gt;2&lt;/sub&gt;与输出h&lt;sub&gt;1&lt;/sub&gt;间的权重。还有一个特殊的权重b&lt;sub&gt;i&lt;/sub&gt;，它被称为偏置（bias），是一个待确定的常数项。这样，h就等于相应的x与w相乘后再加上b。例如，h&lt;sub&gt;i&lt;/sub&gt; = x&lt;sub&gt;i&lt;/sub&gt;w&lt;sub&gt;i1&lt;/sub&gt; + x&lt;sub&gt;2&lt;/sub&gt;w&lt;sub&gt;i2&lt;/sub&gt; + &amp;hellip; + x&lt;sub&gt;m&lt;/sub&gt;w&lt;sub&gt;im&lt;/sub&gt; + b&lt;sub&gt;i&lt;/sub&gt;。
  经过线性变换后得到的输出h&lt;sub&gt;1&lt;/sub&gt;、h&lt;sub&gt;2&lt;/sub&gt;&amp;hellip;h&lt;sub&gt;n&lt;/sub&gt;只是中间过程的输出，在之后，还需要加入一个非线性的激活函数的处理，以得到最终的输出y&lt;sub&gt;1&lt;/sub&gt;～y&lt;sub&gt;n&lt;/sub&gt;，如图4所示。&lt;/p&gt;
&lt;p&gt;  在具体激活函数的选择上，比较常见的有 softmax、sigmoid 与 relu 等。其中 softmax 函数是多分类问题最常用的输出激活函数（多分类问题指的是一个问题有多个确定个数的可能答案，例如是/否问题是二分类问题，而分辨一个手写阿拉伯数字是哪个数就是一个十分类问题，因为可能答案有0～9一共十个），softmax也是包括ChatGPT在内的大模型使用的输出函数。
  使用了激活函数以后，神经网络就可以学习到所有函数了。下面来看一个经典的神经网络的例子，手写数字识别问题，或MNIST问题。MNIST涉及的手写数字在网上是公开的，如图5所示。程序员们可以先想想，如果自己来写一个程序识别手写数字会怎么写。可以识别手写数字的（一个）神经网络的结构如图6所示。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%875.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图5 MNIST手写数字样例&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%876.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图6 能识别手写数字的神经网络&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  可以看到图6的神经网络一共用到了三个线性变换，并使用了两个sigmoid 激活函数，以及最后的softmax激活函数，因此可以说这个神经网络是三层的。神经网络的输入（x&lt;sub&gt;1&lt;/sub&gt;～x&lt;sub&gt;784&lt;/sub&gt;）是一个长度为784的数组，其实就是一个28x28=784的手写数字的黑白图像。神经网络的输出（y&lt;sub&gt;1&lt;/sub&gt;～y&lt;sub&gt;10&lt;/sub&gt;）分别代表了0～9的阿拉伯数字，这是一个典型的十分类问题，因此使用softmax也是非常自然的。&lt;/p&gt;
&lt;p&gt;  图6中的神经网络一共有(784x50+50) + (50x100+100) + (100x10+10) = 45360个参数，对比ChatGPT上千亿个参数，这显然是一个微模型，但是它的识别能力却可以达到92.53%，也就是说一万个手写数字，它能正确识别出9253个来。
  那问题就来了，这45360个参数是怎么来的呢？肯定不能是随便什么 45360 个数都能带来这么高的识别率的，要解决这个问题，就需要看看神经网络是怎么学习的了。&lt;/p&gt;
&lt;h2 id=&#34;神经网络的学习&#34; &gt;神经网络的学习
&lt;span&gt;
    &lt;a href=&#34;#%e7%a5%9e%e7%bb%8f%e7%bd%91%e7%bb%9c%e7%9a%84%e5%ad%a6%e4%b9%a0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;  在上面已经看到，神经网络里有大量的参数。在最开始，这些参数会被随机分配一些数字（当然如何随机分配也有讲究的，简洁起见，此处先不提），此外也需要准备大量的数据，这些数据一般是多个输入输出的对(x, t)。例如在上面的手写数字识别问题中，输入x就是一个28x28的手写数字图像，输出t就是这个图像对应的0～9中的一个数字。
  这些数据会被分成训练集与测试集。训练集中的数据用来训练神经网络，让神经网络中的参数最终达到正确的值。测试集中的数据用来测试训练后的神经网络，对比看训练后的神经网络在新的数据下得到的结果是否正确。&lt;/p&gt;
&lt;p&gt;  神经网络的训练过程可以大体分为下面几步：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对训练集中的输入输出对(x, t)进行如下处理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将x输入到神经网络中，计算得到y&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将y与正确的输出t进行运算得到损失L，损失的计算函数一般是均方差或交叉熵，前者针对的是回归问题（连续函数拟合），后者针对的是分类问题&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;根据L调整神经网络的参数，调整的方向是减少L，调整的方法是下面要讲的反向传播&lt;/p&gt;
&lt;p&gt;图7给出了神经网络训练的过程。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%877.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图7 神经网络训练过程&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  一旦训练完毕，使用的时候就不需要正确输出t，也不需要计算损失L和调整神经网络的参数了，这个过程被称为推理（inference），如图8所示。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%878.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图8 神经网络推理过程&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  顺便说一句，图中的深度神经网络与神经网络结构是一样的，但是层数较多，因此被称为深度神经网络。&lt;/p&gt;
&lt;p&gt;  下面，再来看看神经网络究竟是怎样通过损失L来调整网络参数的。最简单，也是最直观的方法就是将每个参数都稍微调大或者调小一点，看L会如何变化，如果L变小，则保持此参数的调整，如果L变大，则将此参数反过来调整。以上即正向调整法，思路清晰，操作方法简单，但是计算量极大，因为每调整一个参数就要重新计算一遍y与L。&lt;/p&gt;
&lt;p&gt;  另一种方法就是现在主流的反向传播（BP，backpropagation）法，此方法类似系统发生故障时的根因分析，首先分析最后一层的参数是怎样影响到L的，然后分析倒数第二层的参数是如何影响到最后一层的输入的，如此类推。在数学上，其实就是计算L对某个特定参数w的（偏）导数，因为导数就代表了w的变化会导致L如何变化。根据链式求导法则，L对w的导数等于L对中间变量h的导数乘以h对w的导数，前者相当于计算最后一层参数的导数，后者相当于计算倒数第二层参数的导数，两者相乘即为L对导数第二层参数的导数。&lt;/p&gt;
&lt;p&gt;  下面主要通过求导来展示反向传播，如果希望更直观一点，可以阅读计算图相关的资料。假设真实函数是y=2x+1，则待求函数为wx+b（当然w与b的真实值应该是2与1）。下面通过一组数据（训练集）来通过反向传播逐步计算更新w与b，看看它们否会逐渐逼近2与1。&lt;/p&gt;
&lt;p&gt;  由于这是一个回归问题，因此使用均方差(y-t)&amp;lt; sup&amp;gt;2&amp;lt; /sup&amp;gt;/2作为损失L的函数，显然L对y的导数是y-t，参数更新使用经典的梯度下降法（SGD），即参数新值=参数旧值 - 学习率x(L对参数的导数)，梯度下降有一个粗糙但是直观的理解，那就是学习应该向着导数（梯度）相反（下降）的方向走，在这里学习率这个参数设为0.01。&lt;/p&gt;
&lt;p&gt;  首先，将w与b随机化为0.5与0.6。&lt;/p&gt;
&lt;p&gt;  假设第一个训练对为(0, 1)，则 y = wx + b = 0.5·0 + 0.6 = 0.6，L对w的导数=L对y的导数乘以y对w的导数=(y-t)·x=(0.6-1)·0=0，L对b的导数=L对y的导数乘以y对b的导数=(y-t)·1=-0.4。则w的新值为w-0.01·0=0.5，b的新值为b-0.01·(-0.4)=0.604，显然新的w与b比原来的更接近(2, 1)。&lt;/p&gt;
&lt;p&gt;  若第二个训练对为(1, 2.9)（本来应为1与3，但是增加了一点误差干扰），可以以同样的方法得到新的w为0.51796，而新的b为0.62196，显然比上一对w与b又接近了2与1一点。&lt;/p&gt;
&lt;p&gt;  实际上，若继续增加2x+1附近的数据，可以发现到了十几对训练数对之后，w与b即可相当接近2与1了。&lt;/p&gt;
&lt;p&gt;  以上例子是为了直观感受反向传播的计算而给出的，实际上这种线性函数的回归可以通过数据集基于矩阵一次性算出来，而且训练本身也要考虑收敛的问题，因此实际的深度学习会更复杂一些，但是原理是类似的。&lt;/p&gt;
&lt;p&gt;  总地来说，深度神经网络是由多个层组成的，每一层均有前向（forward）推理的函数，用来从输入计算得到输出，这个过程即为推理。每一层也有反向（backward）传播的函数，用来从后一层传来的导数计算得到本层向前一层传递的导数，并同时更新本层的参数。如果是训练，则需要在最后一层再加上一个输入为t与y的损失层，输出为L，如图9所示。&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;%e5%9b%be%e7%89%879.png&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;图9 多层神经网络结构&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;  通过以上几乎标准化的神经网络层，深度学习的研究者就可以像搭积木一样对多个层进行排列组合，得到多种多样的深度神经网络，并首先通过反向传播训练出神经网络的参数，继而使用神经网络进行推理应用了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/">
      <title>V23 Beta DDE Wayland解读</title>
      <link>https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/</link>
      <pubDate>Thu, 01 Jun 2023 13:37:41 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/v23-beta-dde-wayland%E8%A7%A3%E8%AF%BB/</guid>
      <description>&lt;p&gt;在 V23 beta 版本中，DDE 试验性的开启了 Wayland 的支持，允许用户在 Wayland 协议下的桌面工作环境启动 。本篇文章会向大家介绍一下 Wayland 是什么，我们尝试做了什么改变，以及 DDE Wayland 未来会支持哪些新特性。（注：单独提出 Wayland， 通常和 Wayland 合成器、Wayland 服务器、显示服务器被视为同一个内容；X Window System 和 X11 也被视为同一个内容。）&lt;/p&gt;
&lt;h2 id=&#34;什么是-wayland&#34; &gt;什么是 Wayland？
&lt;span&gt;
    &lt;a href=&#34;#%e4%bb%80%e4%b9%88%e6%98%af-wayland&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;Wayland 是一个通信协议，规定了显示服务器与客户端之间的通信方式，而使用这个协议的显示服务器称为 Wayland Compositor。Wayland 只专注于图形，并希望使用其他库与输入硬件进行通信，以降低自身的复杂度。Wayland 最大的好处也是大家都推崇的原因，那就是 Wayland 在设计上会考虑安全，例如默认不允许窗口获取其他窗口的数据，合成器和窗口管理器的合并也降低了对系统资源的消耗。&lt;/p&gt;
&lt;h3 id=&#34;wayland-与-x-window-system-有什么不同&#34; &gt;Wayland 与 X Window System 有什么不同？
&lt;span&gt;
    &lt;a href=&#34;#wayland-%e4%b8%8e-x-window-system-%e6%9c%89%e4%bb%80%e4%b9%88%e4%b8%8d%e5%90%8c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;Wayland 与 X Window System 的最大不同在于，Wayland 与 X Window System 的最大不同在于，它的窗口管理器和 Wayland Server 在同一个进程，并且客户端能够通过 EGL 以及一些 Wayland 特定的 EGL 扩充组件直接在显示内存中绘制自己的缓冲区。 窗口管理器简化成显示管理服务，专门负责绘制那些屏幕上的程序。 这比 X Window System 中的窗口管理器要更简单、高效。&lt;/p&gt;
&lt;h3 id=&#34;wayland-协议有哪些组成&#34; &gt;Wayland 协议有哪些组成？
&lt;span&gt;
    &lt;a href=&#34;#wayland-%e5%8d%8f%e8%ae%ae%e6%9c%89%e5%93%aa%e4%ba%9b%e7%bb%84%e6%88%90&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;h4 id=&#34;1协议概述&#34; &gt;1.协议概述
&lt;span&gt;
    &lt;a href=&#34;#1%e5%8d%8f%e8%ae%ae%e6%a6%82%e8%bf%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;Wayland 协议被描述为异步面向对象协议。协议是异步的，这意味着不必等待回复或者响应 ACK，避免了往返时间并提高性能。协议封装为面向对象的设计，则是面向对象的设计方式，能很好的对服务器上不同窗口数据及接口进行封装。&lt;/p&gt;
&lt;p&gt;Wayland 合成器可以定义和公开自己的附加接口，被称为扩展协议，不同的 Wayland 会提供功能完全不同，甚至功能相反的协议，这带来了很大灵活性，但使用客户端时需要自行判断。&lt;/p&gt;
&lt;h4 id=&#34;2协议架构&#34; &gt;2.协议架构
&lt;span&gt;
    &lt;a href=&#34;#2%e5%8d%8f%e8%ae%ae%e6%9e%b6%e6%9e%84&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;Wayland 协议是一种“客户端 —— 服务器”模型，客户端是请求在屏幕上显示画面的图形应用程序，服务器是控制应用程序显示在屏幕上的管理程序。Wayland 参考实现被设计成两层协议，既：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下层协议：处理客户端和服务器之间的进程间通信，以及在内部的数据封装处理。&lt;/li&gt;
&lt;li&gt;上层协议：处理客户端和服务器交换的数据，以实现窗口系统的基本功能，这一层被实现为异步面向对象协议。
下层协议是使用 C 语言开发的，而上层协议是根据 XML 格式的协议描述文件自动生成，每当 XML 协议的描述发生变化时，就可以重新生成该协议的源代码，这使得协议非常灵活、可扩展性好且防止出错。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;如下 Wayland 工作原理图：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Wayland_display_server_protocol.svg/700px-Wayland_display_server_protocol.svg.png&#34; alt=&#34;wayland.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;以下对应图中所标编号作说明：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;①&lt;/strong&gt;Linux 内核中的 evdev 模块接收事件并将它们发送到 Wayland 合成器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;②&lt;/strong&gt;Wayland 合成器查看场景图并确定哪个窗口应接收事件。场景图对应于屏幕上显示的内容，Wayland 合成器显示对应用事件的场景图中元素的转换。因此，Wayland 合成器可以反向变换以找到正确的窗口，并将屏幕上的坐标转换为窗口中的坐标。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;③&lt;/strong&gt;当客户端收到事件时，Wayland 的客户端只需通过 EGL 渲染并向合成器发送请求以通知更新的范围即可。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;④&lt;/strong&gt;Wayland 合成器 从客户端收集更改请求并重新配置屏幕。 然后，合成器直接发出 ioctl 让 KMS 重绘屏幕。&lt;/p&gt;
&lt;p&gt;当了解了 Wayland 相关基本介绍之后，基于它我们在 DDE 上将会作哪些适配功过呢？将从以下几个方面说一下我们在 Wayland 技术预览版里面所做的适配工作。&lt;/p&gt;
&lt;h2 id=&#34;dde-适配-wayland-都做了哪些工作&#34; &gt;DDE 适配 Wayland 都做了哪些工作
&lt;span&gt;
    &lt;a href=&#34;#dde-%e9%80%82%e9%85%8d-wayland-%e9%83%bd%e5%81%9a%e4%ba%86%e5%93%aa%e4%ba%9b%e5%b7%a5%e4%bd%9c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;DDE 原本设计为在 X11 协议下工作，很多组件直接或间接依赖 X11 的接口，多数组件依赖的功能并没有在 XWayland 中提供，所以就需要进行一些修改。&lt;/p&gt;
&lt;p&gt;首先在 Qt 插件中实现和 Wayland 特定性相关的功能，Qt 提供了一个 Wayland Shell Integration 的插件，允许我们在这里调用 DDE Wayland 合成器提供的扩展协议。&lt;/p&gt;
&lt;p&gt;Qt 已提供核心协议的适配，所以 DDE 只需要在现有框架下实现扩展协议即可。&lt;/p&gt;
&lt;p&gt;目前 DDE 提供的扩展协议有以下几个方面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;设置圆角窗口&lt;/li&gt;
&lt;li&gt;请求获取窗口数据（截图权限）&lt;/li&gt;
&lt;li&gt;划分工作区可用区域
DTK 程序可以通过设置窗口的属性，或者使用 DTK 提供的平台接口，即可调用扩展的 Wayland 协议，非 DTK 程序则需要手动使用扩展协议的 XML 文件进行代码生成调用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;dde-wayland-未来会支持什么特性&#34; &gt;DDE Wayland 未来会支持什么特性？
&lt;span&gt;
    &lt;a href=&#34;#dde-wayland-%e6%9c%aa%e6%9d%a5%e4%bc%9a%e6%94%af%e6%8c%81%e4%bb%80%e4%b9%88%e7%89%b9%e6%80%a7&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;1hdr-支持&#34; &gt;1.HDR 支持
&lt;span&gt;
    &lt;a href=&#34;#1hdr-%e6%94%af%e6%8c%81&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;高动态范围（HDR）是比平时更高的动态范围。HDR 的内容过于深入，在这里只简单的进行说明。&lt;/p&gt;
&lt;p&gt;HDR 可以保存更多的内容信息，在支持 HDR 的屏幕上观看 HDR 的内容，可以获得更好的体验，DDE 目前正在准备支持 HDR 内容的输出，这会让 DDE 拥有更好的显示效果。&lt;/p&gt;
&lt;h3 id=&#34;2成体系的窗口动画&#34; &gt;2.成体系的窗口动画
&lt;span&gt;
    &lt;a href=&#34;#2%e6%88%90%e4%bd%93%e7%b3%bb%e7%9a%84%e7%aa%97%e5%8f%a3%e5%8a%a8%e7%94%bb&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;在 X11 下，由于窗口管理和画面合成管理是两个进程，并且在启动速度上存在差异，所以只能采用一些“巧妙”的设计来规避视觉错误。&lt;/p&gt;
&lt;p&gt;但是在 Wayland 下，窗口管理器和窗口合成器被合并成一个进程，那么启动后就可以立即使用动画效果，例如可以设计视觉效果更好的登录动画。&lt;/p&gt;
&lt;p&gt;目前 DDE 的窗口动画支持并不多，且大部分是单调的线性动画，有些情况还需要客户端自己实现虚假动画，例如任务栏发生位置变化时，桌面的图标会进行计算，并自行改变大小，这引入了非必要的依赖。如果使用窗口动画，桌面并不需要关心任何外部因素，只需要设置自己在可用工作区域最大化，当任务栏发生位置改变时，合成器会自动调整桌面的大小，并产生相应的窗口动画。&lt;/p&gt;
&lt;p&gt;未来 DDE 会使用更多窗口动画来减少组件之间的依赖，以及实现更多更好的视觉效果。&lt;/p&gt;
&lt;h2 id=&#34;最后总结&#34; &gt;最后总结
&lt;span&gt;
    &lt;a href=&#34;#%e6%9c%80%e5%90%8e%e6%80%bb%e7%bb%93&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;阅读至此，是不是对 Wayland 的概念及工作原理更加清晰啦？如果您有什么疑问也欢迎与我们互动探讨 Wayland。&lt;/p&gt;
&lt;p&gt;坦白说，DDE 的 Wayland 支持还处于初步阶段（&lt;strong&gt;技术预览版，请谨慎使用&lt;/strong&gt;），未来我们会使用更多合成器提供的功能，来为桌面环境降低开发难度，提升性能。提供更好的体验。&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/">
      <title>第一届小浣熊杯修 bug 大赛</title>
      <link>https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/</link>
      <pubDate>Thu, 09 Mar 2023 16:29:07 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/the-1st-racoon-cup-bug-hunter-game/</guid>
      <description>&lt;p&gt;deepin v23 beta 的发布在即，为了能够使相关的 bug 能够得以更快解决，并促进研发团队的协作变得更高效，我们（开源社区中心）决定在 deepin 员工内部举办小规模的 bug hunting 性质的比赛，并命名该比赛为“小浣熊杯修 bug 大赛”。而首届“小浣熊杯”也于昨天顺利落幕，那么就让我们一起了解一下这个比赛吧！&lt;/p&gt;
&lt;h2 id=&#34;比赛介绍&#34; &gt;比赛介绍
&lt;span&gt;
    &lt;a href=&#34;#%e6%af%94%e8%b5%9b%e4%bb%8b%e7%bb%8d&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;如上所述，“小浣熊杯“的比赛大致内容即为在比赛时间内对已有缺陷进行修复。我们将所有参赛的员工划分为多个组，每个组除研发外也配备一个测试人员。在比赛时间周期内，研发从指定的缺陷看板中挑选自己”中意“的 BUG 进行修复，并在修复后将修复公布在相关群内，由 &lt;strong&gt;其他组&lt;/strong&gt; 的测试人员进行测试。当修复被测试人员验证没有问题后，即可进行计分。最终，会以本组研发人员所修复的数量与测试人员所完成的测试数量相组合，并计算小组人均得分，最后以小组人均得分的高低决定最终排名。&lt;/p&gt;
&lt;p&gt;小组得分的计算公示为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;小组得分 = (本组研发修复的缺陷数量 + 本组测试所验证的缺陷数量 / 3) / 小组总人数
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;比赛会在 &lt;a href=&#34;https://github.com/linuxdeepin/.bug-game/&#34;&gt;https://github.com/linuxdeepin/.bug-game/&lt;/a&gt; 中进行，每次比赛会创建一个看板来跟进整个比赛的实时情况，并创建一个对应的 issue 记录相关进展与结果。&lt;/p&gt;
&lt;p&gt;另外，考虑到比赛过程中对缺陷的修复不需要经过其他研发人员的 code review ，因而可能存在实际的代码质量问题，故相关的 PR 均不要求在比赛结束前合入，相关提交仍需按照正常流程，经过有效 review 获得 approval 后合入。&lt;/p&gt;
&lt;h2 id=&#34;首届状况介绍&#34; &gt;首届状况介绍
&lt;span&gt;
    &lt;a href=&#34;#%e9%a6%96%e5%b1%8a%e7%8a%b6%e5%86%b5%e4%bb%8b%e7%bb%8d&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://user-images.githubusercontent.com/13449038/223312494-6039b4a2-309a-44be-a8ac-61edffa9f963.png&#34; alt=&#34;比赛过程看板截图&#34;&gt;&lt;/p&gt;
&lt;p&gt;首届比赛共划分了四个小队参赛，比赛时间从 3 月 7 日开始，为期两天。比赛过程与结果在 &lt;a href=&#34;https://github.com/linuxdeepin/.bug-game/issues/1&#34;&gt;这个 Issue&lt;/a&gt; 中汇总，最终的缺陷修复情况也可以参见 &lt;a href=&#34;https://github.com/orgs/linuxdeepin/projects/26&#34;&gt;这个看板&lt;/a&gt;。比赛过程中“修 bug”队一度领先，随后被”进击的小浣熊“队反超，最终经过了两天的”激烈比拼“后，本次比赛总计处理了 42 个 Issue，由”进击的小浣熊“队以 18.333 分的总积分获得人均积分第一夺冠。“修 bug”队紧随其后，“呆呆鹅” 与 “bug 收割小分队” 获得随后的名次。&lt;/p&gt;
&lt;p&gt;比赛过程中，各个小组对已有 bug 的挑选与“占坑”以及测试人员对新提交修复的“抢单”是过程中最有趣的事情之一。快速挑选便于修复的 BUG 并进行有效的修复成为了获胜的关键之一，根据表象快速分析推测问题的能力，以及在陌生项目&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;中快速尝试定位和修复问题的能力也变得至关重要。作为花絮，有的小组也在选择 BUG 的过程中连续发现自己所选择的缺陷实际早已在版本迭代中被修复，耗费了较多时间而造成了相对的失利，但这个过程也对现存 BUG 的有效性验证有很大的帮助&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;赛后，我们进行了比赛的颁奖，获胜队伍获得了比赛限定奖杯与荣誉证书，以及一个 deepin 主题背包。获奖队伍也进行了合影：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://user-images.githubusercontent.com/10095765/224207416-e1fa516d-8993-4d1c-8d1f-81114c0766ce.png&#34; alt=&#34;荣誉证书&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://user-images.githubusercontent.com/13449038/223911631-376c0d58-17a5-4401-843d-414dc287b7be.png&#34; alt=&#34;颁奖截图&#34;&gt;&lt;/p&gt;
&lt;p&gt;无论是否获奖，我们都感谢各个参赛队伍的积极参与，也希望各位能在后续的比赛中能够获得优异的成绩。&lt;/p&gt;
&lt;section class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;注：缺陷的修复不限于自己所维护的项目，研发人员也可以尝试修复由其他项目组所维护的缺陷。&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;或许在后续的比赛中应当为此类也算作计分项。&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/about/">
      <title>关于 deepin 社区博客</title>
      <link>https://blog.deepin.org/about/</link>
      <pubDate>Wed, 15 Feb 2023 02:23:58 +0800</pubDate>
      
      <guid>https://blog.deepin.org/about/</guid>
      <description>&lt;h2 id=&#34;deepin-社区博客&#34; &gt;deepin 社区博客
&lt;span&gt;
    &lt;a href=&#34;#deepin-%e7%a4%be%e5%8c%ba%e5%8d%9a%e5%ae%a2&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;deepin 社区公共博客平台。&lt;/p&gt;
&lt;h2 id=&#34;用法说明&#34; &gt;用法说明
&lt;span&gt;
    &lt;a href=&#34;#%e7%94%a8%e6%b3%95%e8%af%b4%e6%98%8e&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;此博客平台使用了 &lt;a href=&#34;https://gohugo.io/&#34;&gt;HUGO&lt;/a&gt; 作为文章平台工具，进行博客文章的静态生成，并最终利用 GitHub Pages 呈现网页。故整个过程你只需按照 HUGO 的正常使用方式进行使用即可。&lt;/p&gt;
&lt;p&gt;若未安装 HUGO，您可以从 &lt;a href=&#34;https://github.com/gohugoio/hugo/releases&#34;&gt;HUGO 官方 GitHub 仓库的 Release 页面&lt;/a&gt;获取适用于您设备的 HUGO 版本，并放置其于 PATH 内以便调用。&lt;/p&gt;
&lt;p&gt;安装就绪后，大致常用命令如下（在此源码仓库的根目录下执行）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hugo server # 启动一个本地服务器，预览目前状态下的网站内容
$ hugo new posts/my-post.md # 创建一篇新文章，以便进行编辑
$ hugo server -D # 启用本地服务器，并且能够预览状态为草稿（`draft: true`）的文章
$ hugo -D # 生成静态页面（如果需要），生成的文件将位于 public 目录下
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;创建文章时，创建格式为 &lt;code&gt;&amp;lt;分类&amp;gt;/&amp;lt;文件名&amp;gt;.&amp;lt;格式&amp;gt;&lt;/code&gt;，上面给出的例子中，分类为 posts，文件名为 my-post，格式为 md （markdown）。创建文章后，默认会使用 &lt;a href=&#34;https://gohugo.io/content-management/front-matter/&#34;&gt;YAML front-matter&lt;/a&gt; 标记文章的一些元信息，请留意 draft 草稿状态的文章最终不会显示。&lt;/p&gt;
&lt;p&gt;若要获取 HUGO，请参考&lt;a href=&#34;https://gohugo.io/getting-started/installing&#34;&gt;官方文档给出的安装方式&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;文章内容指导建议&#34; &gt;文章内容指导建议
&lt;span&gt;
    &lt;a href=&#34;#%e6%96%87%e7%ab%a0%e5%86%85%e5%ae%b9%e6%8c%87%e5%af%bc%e5%bb%ba%e8%ae%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;向此博客平台投递文章需要经过相关人员的 review，合入后即投递完成。需要注意的是，向此处投递的文章必须是与 deepin 社区发展所相关的文章。对于与 deepin 开源社区贡献相关的个人感想或随笔等博客，可以考虑发布到 planet.deepin.org 聚合平台，此博客平台的文章也会出现 planet.deepin.org 之中。&lt;/p&gt;
&lt;p&gt;在发布您的文章时，建议使用标签来标记您文章所相关的主题，以便读者更方便的查阅您的文章。例如 &lt;code&gt;tags: [&amp;quot;持续集成&amp;quot;]&lt;/code&gt; 或 &lt;code&gt;tags: [&amp;quot;指南文档&amp;quot;, &amp;quot;CMake&amp;quot;]&lt;/code&gt;。另外也建议在文章的元信息中附带作者信息，例如 &lt;code&gt;authors: [&amp;quot;张三&amp;quot;]&lt;/code&gt;（可为多人）。&lt;/p&gt;
&lt;h2 id=&#34;改善此项目的样式风格&#34; &gt;改善此项目的样式风格
&lt;span&gt;
    &lt;a href=&#34;#%e6%94%b9%e5%96%84%e6%ad%a4%e9%a1%b9%e7%9b%ae%e7%9a%84%e6%a0%b7%e5%bc%8f%e9%a3%8e%e6%a0%bc&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;此项目使用了 &lt;a href=&#34;https://themes.gohugo.io/hugo-geekblog/&#34;&gt;geekblog&lt;/a&gt; 主题（基于 &lt;code&gt;v0.5.3&lt;/code&gt;，有改动）。若你有任何建议，可以进行讨论并修改或提供更适合的主题。&lt;/p&gt;
</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/">
      <title>REUSE使用指南</title>
      <link>https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/</link>
      <pubDate>Wed, 10 Aug 2022 13:00:00 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/reuse%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/</guid>
      <description>&lt;h1 id=&#34;什么是reuse&#34; &gt;什么是REUSE
&lt;span&gt;
    &lt;a href=&#34;#%e4%bb%80%e4%b9%88%e6%98%afreuse&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;REUSE是一个工具，准确的来说是一个帮助我们下载开源许可证书和检查开源声明是否合规的脚本：&lt;/p&gt;
&lt;h1 id=&#34;使用reuse配置项目&#34; &gt;使用REUSE配置项目
&lt;span&gt;
    &lt;a href=&#34;#%e4%bd%bf%e7%94%a8reuse%e9%85%8d%e7%bd%ae%e9%a1%b9%e7%9b%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;h2 id=&#34;安装&#34; &gt;安装
&lt;span&gt;
    &lt;a href=&#34;#%e5%ae%89%e8%a3%85&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;REUSE依赖于本地的python环境，所以需要首先安装python和pip&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;sudo apt install python3
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
sudo python3 get-pip.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后安装REUSE&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;pip3 install --user reuse

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在此之后，确保它&lt;code&gt;~/.local/bin&lt;/code&gt;在您的&lt;code&gt;$PATH&lt;/code&gt;中。在 Windows 上，您的环境所需的路径可能类似于 &lt;code&gt;%USERPROFILE%\AppData\Roaming\Python\Python39\Scripts&lt;/code&gt;，具体取决于您安装的 Python 版本。&lt;/p&gt;
&lt;h2 id=&#34;使用&#34; &gt;使用
&lt;span&gt;
    &lt;a href=&#34;#%e4%bd%bf%e7%94%a8&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;初始化项目&#34; &gt;初始化项目
&lt;span&gt;
    &lt;a href=&#34;#%e5%88%9d%e5%a7%8b%e5%8c%96%e9%a1%b9%e7%9b%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;进入一个项目，使用&lt;code&gt;reuse init&lt;/code&gt;命令可以初始化项目，此时在你的命令行菜单上会以交互的方式询问你需要的许可证和版权所有人以及邮箱。注意：许可证的名称需要规范：&lt;a href=&#34;https://spdx.org/licenses/&#34; title=&#34;许可证名称&#34;&gt;许可证名称&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;如果你在这一步遗漏了也没事，可以使用&lt;code&gt;reuse download &amp;lt;许可证名称&amp;gt;&lt;/code&gt;的方式下载所需的许可证，这些许可证会一并放入项目下的License文件夹中&lt;/p&gt;
&lt;h3 id=&#34;为代码文件添加版权声明&#34; &gt;为代码文件添加版权声明
&lt;span&gt;
    &lt;a href=&#34;#%e4%b8%ba%e4%bb%a3%e7%a0%81%e6%96%87%e4%bb%b6%e6%b7%bb%e5%8a%a0%e7%89%88%e6%9d%83%e5%a3%b0%e6%98%8e&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;h4 id=&#34;使用自动化脚本添加&#34; &gt;使用自动化脚本添加
&lt;span&gt;
    &lt;a href=&#34;#%e4%bd%bf%e7%94%a8%e8%87%aa%e5%8a%a8%e5%8c%96%e8%84%9a%e6%9c%ac%e6%b7%bb%e5%8a%a0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;含有代码逻辑文件添加版权声明方法为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;reuse addheader --copyright&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;UnionTech Software Technology Co., Ltd.&amp;#34;&lt;/span&gt; --license&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;GPL-3.0-or-later -r &amp;lt;目录&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这只是一个简单的例子，本质上resuse还有更多的用法：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--copyright-style&lt;/code&gt;可以将默认值更改 SPDX-FileCopyrightText为以下样式之一：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;spdx:           SPDX-FileCopyrightText: &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
spdx-symbol:    SPDX-FileCopyrightText: © &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
string:         Copyright &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
string-c:       Copyright (C) &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
string-symbol:  Copyright © &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
symbol:         © &amp;lt;year&amp;gt; &amp;lt;statement&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;reuse不会限制你使用多少个copyright 也不会限制你使用的license的个数。&lt;/p&gt;
&lt;p&gt;reuse 会猜测你使用的语言，并且添加对应的注释格式，如果不能正确添加或者需要更多的需求可以考虑下面的方法。&lt;/p&gt;
&lt;h4 id=&#34;手动进行添加&#34; &gt;手动进行添加
&lt;span&gt;
    &lt;a href=&#34;#%e6%89%8b%e5%8a%a8%e8%bf%9b%e8%a1%8c%e6%b7%bb%e5%8a%a0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;reuse允许你手动进行添加版权声明，只要你的版权声明符合它的规范即可。你所需要的是：充分的耐心，键盘上未曾损坏的&lt;code&gt;Ctrl&lt;/code&gt; &lt;code&gt;C&lt;/code&gt; &lt;code&gt;V&lt;/code&gt; 三个按键即可完成这一切。我个人建议是用上面提供的自动化脚本添加一个版权声明作为模板，然后自己修改下，就可以展现你作为CV工程师的实力了。&lt;/p&gt;
&lt;h4 id=&#34;自动进行添加&#34; &gt;自动进行添加
&lt;span&gt;
    &lt;a href=&#34;#%e8%87%aa%e5%8a%a8%e8%bf%9b%e8%a1%8c%e6%b7%bb%e5%8a%a0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;理论上reuse不限制你添加版权声明的方式，你可以选择你喜欢的脚本语言来完成这一切。&lt;/p&gt;
&lt;h3 id=&#34;为非代码文件添加版权声明&#34; &gt;为非代码文件添加版权声明
&lt;span&gt;
    &lt;a href=&#34;#%e4%b8%ba%e9%9d%9e%e4%bb%a3%e7%a0%81%e6%96%87%e4%bb%b6%e6%b7%bb%e5%8a%a0%e7%89%88%e6%9d%83%e5%a3%b0%e6%98%8e&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;在项目中是存在非代码文件，比如图片，音频等等。部分也是受到版权保护的，但是我们也没法直接在上面操作（别和我说水印和音频水印，这俩玩意不能无损添加）所以我们要使用一个外置的dep5文件去声明这些非代码文件的版权，dep5文件在执行项目&lt;code&gt;init&lt;/code&gt;之后就已经自动帮你生成好了，所以直接写就是的了。比如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;# css
Files: *.css
Copyright: None
License: CC0-1.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一行就是注释 ，第二行是适合的文件，第三行是版权信息（如果使用cc0,则填写None） ，第四行是使用的licence。第一行的注释你可以选择不写，但是为了以后的可读性，强烈建议你写上，甚至可以作为分类的标签。&lt;/p&gt;
&lt;p&gt;第二行文件，支持使用通配符进行模糊匹配，但是不支持正则表达式：仅支持，&lt;code&gt;*&lt;/code&gt;匹配任意多个字符 &lt;code&gt;？&lt;/code&gt;匹配一个字符。所以可以利用通配符来制定匹配某一个文件夹下面的所有文件&lt;/p&gt;
&lt;p&gt;例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;# assets
Files: styleplugins/dstyleplugin/assets/*
Copyright: UnionTech Software Technology Co., Ltd.
License: GPL-3.0-or-later
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个例子展示了有版权信息的情况&lt;/p&gt;
&lt;p&gt;例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;# png svg
Files: platformthemeplugin/icons/* styleplugins/chameleon/menu_shadow.svg styles/images/woodbackground.png
       styles/images/woodbutton.png tests/iconengines/builtinengine/icons/actions/icon_Layout_16px.svg
       tests/iconengines/svgiconengine/icon_window_16px.svg
Copyright: None
License: CC0-1.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个例子展示了为多个文件路径及文件配置版权信息&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;强烈推荐你使用路径和具体文件名的方式来制定版权信息，而要避免大范围使用通配符的情况&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: qt5integration
Upstream-Contact: UnionTech Software Technology Co., Ltd.  &amp;lt;&amp;gt;
Source: https://github.com/linuxdeepin/qt5integration

# README
Files: README.md CHANGELOG.md
Copyright: None
License: CC0-1.0

# assets
Files: styleplugins/dstyleplugin/assets/*
Copyright: UnionTech Software Technology Co., Ltd.
License: GPL-3.0-or-later

# Project file
Files: *.pro *.prf *.pri
Copyright: None
License: CC0-1.0

# css
Files: *.css
Copyright: None
License: CC0-1.0

# qrc
Files: *.qrc
Copyright: None
License: CC0-1.0

# png svg
Files: platformthemeplugin/icons/* styleplugins/chameleon/menu_shadow.svg styles/images/woodbackground.png
       styles/images/woodbutton.png tests/iconengines/builtinengine/icons/actions/icon_Layout_16px.svg
       tests/iconengines/svgiconengine/icon_window_16px.svg
Copyright: None
License: CC0-1.0

# sh
Files: tests/test-recoverage-qmake.sh
Copyright: None
License: CC0-1.0

# ignore git
Files: .git*
Copyright: None
License: CC0-1.0

# xml toml json conf yaml
Files: *.xml *.toml *.json *conf *.yaml
Copyright: None
License: CC0-1.0

# rpm
Files: rpm/*
Copyright: None
License: CC0-1.0

# debian
Files: debian/*
Copyright: None
License: CC0-1.0

# Arch
Files: archlinux/*
Copyright: None
License: CC0-1.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;无需版权声明文件的处理方案&#34; &gt;无需版权声明文件的处理方案
&lt;span&gt;
    &lt;a href=&#34;#%e6%97%a0%e9%9c%80%e7%89%88%e6%9d%83%e5%a3%b0%e6%98%8e%e6%96%87%e4%bb%b6%e7%9a%84%e5%a4%84%e7%90%86%e6%96%b9%e6%a1%88&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;有部分文件是无需版权声明的，比如说某些资源文件脚本文件或者序列化的文件，这种文件有两种处理方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.gitignore&lt;/code&gt; 是用来忽略某些与项目无关的文件和编译过程中产生的文件，同样的如果reuse也会忽略在&lt;code&gt;.gitignore&lt;/code&gt;中标记的文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dep5&lt;/code&gt; 在执行项目init之后，会在项目目录下产生&lt;code&gt;.reuse/dep5&lt;/code&gt; 文件，打开后能看到官方给的范例，如果一个文件不需要特殊版权声明则使用&lt;code&gt;CC0-1.0&lt;/code&gt;（会放弃对此文件的所有版权）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;检查项目合规&#34; &gt;检查项目合规
&lt;span&gt;
    &lt;a href=&#34;#%e6%a3%80%e6%9f%a5%e9%a1%b9%e7%9b%ae%e5%90%88%e8%a7%84&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;reuse lint
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个命令会列出项目文件数，如果项目已经合规，将会以&lt;code&gt;：-）&lt;/code&gt;提示，如果项目有些文件不符合开源许可证规范将会以&lt;code&gt;：-（&lt;/code&gt;提示，此时你就需要按照上面的提示进行相应的修改即可。&lt;/p&gt;
&lt;p&gt;一般情况下，每个文件都需要有与之相关的版权和许可信息。REUSE规范详细说明了几种方法。总的来说，有这些方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将标签放在文件的标题中。&lt;/li&gt;
&lt;li&gt;将标签放置在与&lt;code&gt;.license&lt;/code&gt;文件相邻的文件中。&lt;/li&gt;
&lt;li&gt;将信息放入 &lt;code&gt;DEP5&lt;/code&gt; 文件中。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果发现一个文件没有与之关联的版权和/或许可信息，则该项目不合规。&lt;/p&gt;
&lt;h2 id=&#34;个人建议&#34; &gt;个人建议
&lt;span&gt;
    &lt;a href=&#34;#%e4%b8%aa%e4%ba%ba%e5%bb%ba%e8%ae%ae&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;个人建议如果对现有项目使用reuse配置且需要保留版权的时间信息，不要使用一键配置版权头的方式，而是进行手动替换，这里推荐使用vscode的文件筛选功能，筛选出你需要添加的文件，然后cv下去。在配置dep5文件的时候，一定小心通配符的范围，不宜过大。并且对于第三方版权文件一定得小心规避，同样需要完整保留第三方版权信息。&lt;/p&gt;
&lt;h2 id=&#34;统信软件常见dep5书写参考&#34; &gt;统信软件常见Dep5书写参考
&lt;span&gt;
    &lt;a href=&#34;#%e7%bb%9f%e4%bf%a1%e8%bd%af%e4%bb%b6%e5%b8%b8%e8%a7%81dep5%e4%b9%a6%e5%86%99%e5%8f%82%e8%80%83&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;参考详细见：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: dtkgui
Upstream-Contact: UnionTech Software Technology Co., Ltd. &amp;lt;&amp;gt;
Source: https://github.com/linuxdeepin/dtkgui

# ci
Files: .github/* .gitlab-ci.yml
Copyright: None
License: CC0-1.0

# gitignore #
Files: .gitignore
Copyright: None
License: CC0-1.0

# json conf yaml
Files: *.json *conf *.yaml
Copyright: None
License: CC0-1.0

#interface
Files: src/util/D* src/kernel/D* src/filedrag/D*
Copyright: None
License: CC0-1.0

# rpm
Files: rpm/*
Copyright: None
License: CC0-1.0

# debian
Files: debian/*
Copyright: None
License: LGPL-3.0-or-later

# Arch
Files: archlinux/*
Copyright: None
License: CC0-1.0

# README&amp;amp;doc
Files: README.md doc/src/*.qdoc
Copyright: None
License:  CC-BY-4.0

# DBus
Files: src/dbus/*.xml
Copyright: None
License: CC0-1.0

# Project file
Files: *.pro *.prf *.pri *.qrc *CMakeLists.txt
Copyright: None
License: CC0-1.0

# svg
Files: src/util/icons/actions/*  src/util/icons/icons/* src/util/icons/texts/*
  tests/images/logo_icon.svg
Copyright: UnionTech Software Technology Co., Ltd.
License: LGPL-3.0-or-later
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;常见开源协议选择&#34; &gt;常见开源协议选择
&lt;span&gt;
    &lt;a href=&#34;#%e5%b8%b8%e8%a7%81%e5%bc%80%e6%ba%90%e5%8d%8f%e8%ae%ae%e9%80%89%e6%8b%a9&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;一般情况下对于文档信息使用&lt;code&gt;CC-BY-4.0&lt;/code&gt; 对于我们的一般开源项目使用&lt;code&gt;LGPL-3.0-or-later&lt;/code&gt;许可证，如果遇到第三方文件，则是需要保留原有许可证，保留原版版权声明。&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/lambda/">
      <title>lambda</title>
      <link>https://blog.deepin.org/posts/lambda/</link>
      <pubDate>Sun, 10 Jul 2022 02:23:58 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/lambda/</guid>
      <description>&lt;p&gt;这里整理一些关于&lt;code&gt;c++&lt;/code&gt;中的匿名函数的知识.&lt;/p&gt;
&lt;p&gt;《C++ primer》上有的内容就不在重述了.这里重点讲一些primer上&lt;strong&gt;可能&lt;/strong&gt;没有的东西.&lt;/p&gt;
&lt;h2 id=&#34;捕获的时机&#34; &gt;捕获的时机
&lt;span&gt;
    &lt;a href=&#34;#%e6%8d%95%e8%8e%b7%e7%9a%84%e6%97%b6%e6%9c%ba&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;primer 向我们介绍到: 有两种捕获变量的方式, 值捕获和引用捕获. 其中:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;如果我们采用引用方式捕获一个变量,就必须确保被引用的对象在lambda执行的时候是存
在的.lambda捕获的都是局部变量,这些变量在函数结束后就不复存在了.&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们看一段代码:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod1&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; variableA &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [variableA](&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA; };
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}
&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test1&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test1---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod1(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里&lt;code&gt;funtionTimesMod1(int mod)&lt;/code&gt;返回一个签名为&lt;code&gt;int(int, int)&lt;/code&gt;的函数,这个函数计算
两个参数相乘对&lt;code&gt;mod&lt;/code&gt;取模的结果.&lt;/p&gt;
&lt;p&gt;当然可以正常运行,结果为&lt;code&gt;4&lt;/code&gt;,如果我们将它换成引用捕获:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod2&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; variableA &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;variableA](&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA; };
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}
&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test2&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test2---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod2(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这段代码就已经不能正常工作了,它的输出是 &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;我们来看看为啥:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Process 15820 stopped
* thread #1, name = &#39;tmp&#39;, stop reason = step in
    frame #0: 0x000000000040096b tmp`funtionTimesMod2(mod=7) at tmp.cpp:17
   14   }
   15
   16   auto funtionTimesMod2(int mod) {
-&amp;gt; 17     int variableA = mod;
   18     auto f = [&amp;amp;variableA](int a, int b) { return a % variableA * b % variableA; };
   19     return f;
   20   }
(lldb) p &amp;amp;variableA
(int *) $1 = 0x00007fffffffe120

Process 15820 stopped
* thread #1, name = &#39;tmp&#39;, stop reason = step over
    frame #0: 0x00000000004009dc tmp`test2() at tmp.cpp:25
   22     cout &amp;lt;&amp;lt; &amp;quot;---test2---&amp;quot; &amp;lt;&amp;lt; endl;
   23     int a = 10, b = 20, c = 7;
   24     auto times = funtionTimesMod2(c);
-&amp;gt; 25     cout &amp;lt;&amp;lt; times(a, b) &amp;lt;&amp;lt; endl;
   26   }
   27   auto funtionTimesMod3(int mod) {
   28     int variableA = mod;
(lldb) p times
((anonymous class)) $2 = {
  variableA = 0x00007fffffffe120
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到&lt;code&gt;times&lt;/code&gt;这个对象中保存下来的&lt;code&gt;variableA&lt;/code&gt;只是一个指针,它指向我们之前创建的
局部变量&lt;code&gt;variableA&lt;/code&gt;,这个地址在栈上,这意味着当我们真的调用&lt;code&gt;times&lt;/code&gt;的时候,局部变
量&lt;code&gt;variableA&lt;/code&gt;所在的那片内存已经被使用过了.所以会返回错误的结果.接下来,我们可以
看到实际上调用的时候&lt;code&gt;variableA&lt;/code&gt;里面是&lt;code&gt;20&lt;/code&gt;,这是因为刚好参数&lt;code&gt;b&lt;/code&gt;被放置在
了&lt;code&gt;variableA&lt;/code&gt;之前的位置上.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Process 21271 stopped
* thread #1, name = &#39;tmp&#39;, stop reason = step in
    frame #0: 0x0000000000400a32 tmp`funtionTimesMod2(this=0x00007fffffffe158, a=10, b=20)::$_1::operator()(int, int) const at tmp.cpp:18
   15
   16   auto funtionTimesMod2(int mod) {
   17     int variableA = mod;
-&amp;gt; 18     auto f = [&amp;amp;variableA](int a, int b) { return a % variableA * b % variableA; };
   19     return f;
   20   }
   21   void test2() {
(lldb) p variableA
(int) $3 = 20
(lldb) p &amp;amp;b
(int *) $4 = 0x00007fffffffe120
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;lambda-的实现&#34; &gt;lambda 的实现
&lt;span&gt;
    &lt;a href=&#34;#lambda-%e7%9a%84%e5%ae%9e%e7%8e%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;根据《C++ Primer》在10.3以及14.8.1的介绍,我们知道一个lambda表达式是由编译器负责
翻译成一个&lt;strong&gt;没有类名,重载过调用运算符&lt;/strong&gt;的对象的.&lt;/p&gt;
&lt;p&gt;这解释了为什么我们只能用&lt;code&gt;auto&lt;/code&gt;来定义一个lambda类型的变量,因为这个类是没有名字的,
或者说它的名字是编译器自己生成的,我们并不能知道它叫什么.&lt;/p&gt;
&lt;p&gt;这听起来很美好,我们也确实可以写出代码,用一个自己创建的,重载过调用运算符的对象,来
模拟一个lambda:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;INT&lt;/span&gt; {
&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; num;
  INT(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;i) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; num(i.num) { cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Copy constructor&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; }
  INT() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
};

&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod3&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  INT variableA;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----A&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----B&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [variableA](&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) {
    cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----C&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num;
  };
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----D&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test3&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test3---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----E&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod3(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----F&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----G&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod4&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  INT variableA;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----A&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----B&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;function&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT variableA;

  &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
    function(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;variableA) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; variableA(variableA) {}
    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;operator&lt;/span&gt;()(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) {
      cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----C&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num;
    }
  } f(variableA);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----D&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test4&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test4---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----E&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod4(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----F&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----G&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为了观察发生的值拷贝的时机,以确定我们自己写的这个类的行为和lambda真的完全一致,我
们可以自定义一个class,并且让他发生拷贝构造的时候打印一些信息.&lt;/p&gt;
&lt;p&gt;运行结果如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---test3---
----E
----A
----B
Copy constructor
----D
----F
----C
4
----G
---test4---
----E
----A
----B
Copy constructor
----D
----F
----C
4
----G
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这又一次说明了说明捕获引起的值拷贝发生在lambda被初始化的时候.&lt;/p&gt;
&lt;p&gt;看起来这两个东西的行为几乎一致, 但是真的是这样吗?&lt;/p&gt;
&lt;h2 id=&#34;编译器的优化&#34; &gt;编译器的优化
&lt;span&gt;
    &lt;a href=&#34;#%e7%bc%96%e8%af%91%e5%99%a8%e7%9a%84%e4%bc%98%e5%8c%96&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;编译器会对我们写的代码做出一些优化,以减少复制对象的次数.如果不了解这点可以看看&lt;a href=&#34;https://www.cnblogs.com/kekec/p/11303391.html&#34;&gt;
这篇&lt;/a&gt;博客.&lt;/p&gt;
&lt;p&gt;如果我们关闭返回值优化,那么运行的结果是这样的:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---test3---
----E
----A
----B
Copy constructor
Copy constructor
----D
Copy constructor
Copy constructor
----F
----C
4
----G
---test4---
----E
----A
----B
Copy constructor
----D
Copy constructor
Copy constructor
----F
----C
4
----G
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到我们自己写的类,少了一次拷贝构造.&lt;/p&gt;
&lt;p&gt;我们先来解释一下lambda为什么会发生这么多次拷贝构造.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod3&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  INT variableA; &lt;span style=&#34;color:#75715e&#34;&gt;// 5
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----A&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; &lt;span style=&#34;color:#75715e&#34;&gt;// 6 
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod; &lt;span style=&#34;color:#75715e&#34;&gt;// 7
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----B&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; &lt;span style=&#34;color:#75715e&#34;&gt;// 8
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [variableA](&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) { 
    cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----C&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; &lt;span style=&#34;color:#75715e&#34;&gt;// 14
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num; &lt;span style=&#34;color:#75715e&#34;&gt;//15
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  }; &lt;span style=&#34;color:#75715e&#34;&gt;// 9
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----D&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; &lt;span style=&#34;color:#75715e&#34;&gt;// 10
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f; &lt;span style=&#34;color:#75715e&#34;&gt;// 11
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}

&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test3&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test3---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; &lt;span style=&#34;color:#75715e&#34;&gt;// 1
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// 2
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----E&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; &lt;span style=&#34;color:#75715e&#34;&gt;// 3
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod3(c); &lt;span style=&#34;color:#75715e&#34;&gt;// 4
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----F&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; &lt;span style=&#34;color:#75715e&#34;&gt;// 12
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; &lt;span style=&#34;color:#75715e&#34;&gt;// 13
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----G&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; &lt;span style=&#34;color:#75715e&#34;&gt;//16
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;首先我们的程序是按照如上的顺序运行的.&lt;/p&gt;
&lt;p&gt;可以看到前两个拷贝构造发生在9,而第3、4次发生在11.&lt;/p&gt;
&lt;p&gt;如果完全按照语义来看的话,9这句话可以有两种理解方式:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建一个lambda对象,对象名字叫f,这个对象的内容就是后面那个lambda表达式.&lt;/li&gt;
&lt;li&gt;创建一个lambda表达式,然后将其作为参数,调用同类型对象f的拷贝构造函数.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果按照第一种方法来理解,那么第9行发生两次拷贝构造就不是很能理解了.&lt;/p&gt;
&lt;p&gt;所以应该是第二种.&lt;/p&gt;
&lt;p&gt;在10之后也发生了两次拷贝调用.应该是先建立了一个变量用来做返回值,比如说叫r,然后
将f赋值给返回值变量r,然后返回值变量r在被赋值给test3()中的times,这样发生的两次拷
贝构造.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod4&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  INT variableA;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----A&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----B&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;function&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT variableA;

  &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
    function(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;variableA) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; variableA(variableA) {}
    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;operator&lt;/span&gt;()(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) {
      cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----C&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num;
    }
  } f(variableA); &lt;span style=&#34;color:#75715e&#34;&gt;// &amp;lt;- 这里
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----D&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test4&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test4---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----E&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod4(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----F&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----G&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;之前自己模拟lambda的这个代码,我们在注释标注的那个位置的实现和lambda中,&amp;ldquo;先建一个
右值,然后拷贝构造出f&amp;quot;的行为不太一样.导致这里少了一次拷贝构造.所以实际上应该是这
样:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod5&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  INT variableA;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----A&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----B&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;function&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT variableA;

  &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
    function(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;variableA) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; variableA(variableA) {}
    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;operator&lt;/span&gt;()(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) {
      cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----C&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num;
    }
  };
  function f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; function(variableA);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----D&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test5&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test5---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----E&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod5(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----F&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----G&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实际上没必要纠结那么多,正常编译的话,其实是不会去先建一个右值的对象的.&lt;/p&gt;
&lt;h2 id=&#34;代码&#34; &gt;代码
&lt;span&gt;
    &lt;a href=&#34;#%e4%bb%a3%e7%a0%81&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;bits/stdc++.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;namespace&lt;/span&gt; std;

&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod1&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; variableA &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [variableA](&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA; };
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}
&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test1&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test1---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod1(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod2&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; variableA &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;variableA](&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) { &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA; };
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}
&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test2&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test2---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod2(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;INT&lt;/span&gt; {
&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; num;
  INT(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;i) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; num(i.num) { cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Copy constructor&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl; }
  INT() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;;
};

&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod3&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  INT variableA;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----A&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----B&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [variableA](&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) {
    cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----C&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num;
  };
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----D&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test3&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test3---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----E&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod3(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----F&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----G&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod4&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  INT variableA;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----A&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----B&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;function&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT variableA;

  &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
    function(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;variableA) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; variableA(variableA) {}
    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;operator&lt;/span&gt;()(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) {
      cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----C&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num;
    }
  } f(variableA);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----D&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test4&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test4---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----E&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod4(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----F&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----G&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;funtionTimesMod5&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; mod) {
  INT variableA;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----A&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mod;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----B&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;function&lt;/span&gt; {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT variableA;

  &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt;
    function(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; INT &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;variableA) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; variableA(variableA) {}
    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;operator&lt;/span&gt;()(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a, &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; b) {
      cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----C&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; b &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; variableA.num;
    }
  };
  function f &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; function(variableA);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----D&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; f;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test5&lt;/span&gt;() {
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;---test5---&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;, b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;, c &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----E&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; times &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; funtionTimesMod5(c);
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----F&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; times(a, b) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
  cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;----G&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
  test1();
  test2();
  test3();
  test4();
  test5();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/if-constexpr/">
      <title>使用 if constexpr 实现条件编译</title>
      <link>https://blog.deepin.org/posts/if-constexpr/</link>
      <pubDate>Thu, 10 Mar 2022 13:03:27 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/if-constexpr/</guid>
      <description>&lt;p&gt;在项目开发中，我们通常会使用条件编译对代码进行裁剪，选择性地排除不需要的代码，比如在某个平台下完全不支持某个功能，那么这个功能就不应该被编译。&lt;/p&gt;
&lt;p&gt;一般我们使用宏来判断代码，选择性的挑选需要编译的部分，并在构建系统中开启这样的条件。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#ifdef XXXXXXXXXX
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello world!&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;endl;
&lt;span style=&#34;color:#75715e&#34;&gt;#else
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;good bye!&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;endl;
&lt;span style=&#34;color:#75715e&#34;&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 C 语言的项目中，这样的行为是很正常的，甚至在 C++ 项目中，我们也会选择使用宏进行条件判断。&lt;/p&gt;
&lt;p&gt;但是如果定义的宏多了，则很容易导致代码碎片化，并且无法直观地看到工作流程。&lt;/p&gt;
&lt;p&gt;例如这样的代码:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define KWIN_VERSION_CHECK(major, minor, patch, build) ((major&amp;lt;&amp;lt;24)|(minor&amp;lt;&amp;lt;16)|(patch&amp;lt;&amp;lt;8)|build)
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#ifdef KWIN_VERSION_STR
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define KWIN_VERSION KWIN_VERSION_CHECK(KWIN_VERSION_MAJ, KWIN_VERSION_MIN, KWIN_VERSION_PAT, KWIN_VERSION_BUI)
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#endif
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;#if defined(KWIN_VERSION) &amp;amp;&amp;amp; KWIN_VERSION &amp;lt; KWIN_VERSION_CHECK(5, 21, 3, 0)
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; TimeArgType;
&lt;span style=&#34;color:#75715e&#34;&gt;#else
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typedef&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;chrono&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;milliseconds TimeArgType;
&lt;span style=&#34;color:#75715e&#34;&gt;#endif
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;#if defined(Q_OS_LINUX) &amp;amp;&amp;amp; !defined(QT_NO_DYNAMIC_LIBRARY) &amp;amp;&amp;amp; !defined(QT_NO_LIBRARY)
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;QT_BEGIN_NAMESPACE
QFunctionPointer &lt;span style=&#34;color:#a6e22e&#34;&gt;qt_linux_find_symbol_sys&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;symbol);
QT_END_NAMESPACE
QFunctionPointer KWinUtils&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;resolve(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;symbol)
{
    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;QT_PREPEND_NAMESPACE&lt;/span&gt;(qt_linux_find_symbol_sys)(symbol);
&lt;span style=&#34;color:#75715e&#34;&gt;#else
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;QFunctionPointer KWinUtils&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;resolve(&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;symbol)
{
    &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; QString lib_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kwin.so.&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; qApp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;applicationVersion();

    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; QLibrary&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;resolve(lib_name, symbol);
&lt;span style=&#34;color:#75715e&#34;&gt;#endif
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从上面的例子中可以看到，代码中一旦出现大量重复的判断条件，代码非常不直观，而且被宏分割成了很多部分。&lt;/p&gt;
&lt;p&gt;在一次偶然的机会，我看到了一篇介绍 C++ 17 中的 if constexpr 的用法，可以在编译期进行一些计算，虽然我很早就知道了 constexpr 的用法，但是大家举的例子基本上都是数值计算，让编译器在编译期间将数值进行计算，从而减轻运行时的消耗，我也从来想到其他用法，所以一直没有在项目中使用到。&lt;/p&gt;
&lt;p&gt;constexpr 的作用并不是编译期计算数值，而是编译期进行的代码分析，如果代码较小且非常直观，比如大家经常举的例子，在编译期间计算斐波那契数列，这种例子即使不使用 constexpr 显式要求，编译器也会帮助我们开启优化，直接给出结果。&lt;/p&gt;
&lt;p&gt;但是如果代码非常复杂，编译器就不一定会为我们做这样的优化，就需要我们手动标记可以计算的位置，要求编译器在编译期间进行求值和优化。&lt;/p&gt;
&lt;p&gt;我设想的是，使用 cmake 在构建时，先生成一份文件，将开关的值记录下来，在需要进行判断的地方，就可以直接使用 if constexpr 进行条件判断，在编译期间，编译器会发现有一个分支确定不会被执行（相当于  &lt;code&gt;if(false) {}&lt;/code&gt;），那么这个分支就不会进行编译，直接剔除。&lt;/p&gt;
&lt;p&gt;CMakeLists.txt 中需要做一些工作，将编译参数加入构建系统。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;option (&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Enable Module&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;ON&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;if(&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;    set(&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;else()&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;    set(&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;endif(&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;configure_file (
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${CMAKE_CURRENT_SOURCE_DIR}/options/options.h.in&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${CMAKE_CURRENT_BINARY_DIR}/options/options.h&amp;#34;&lt;/span&gt;
)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 options/options.h.in 文件里，按照 cmake 的要求将变量导入进文件中，进行内容替换。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#pragma once
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;#cmakedefine01 ENABLE_MODULE
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里我仍然使用的是宏定义，也可以直接写成如下形式:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;option (&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Enable Module&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;ON&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;if(&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;    set(&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;else()&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;    set(&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;false&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;endif(&lt;span style=&#34;color:#e6db74&#34;&gt;ENABLE_MODULE&lt;/span&gt;)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&lt;/span&gt;configure_file (
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${CMAKE_CURRENT_SOURCE_DIR}/options/options.h.in&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${CMAKE_CURRENT_BINARY_DIR}/options/options.h&amp;#34;&lt;/span&gt;
)&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#pragma once
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; ENABLE_MODULE{&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;@&lt;/span&gt;ENABLE_MODULE&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;@&lt;/span&gt;};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 main.cpp 中写一段测试代码:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;#34;options/options.h&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;constexpr&lt;/span&gt; (ENABLE_MODULE) {
        std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Now Enable Module&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;endl;
    }

    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;constexpr&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;ENABLE_MODULE) {
        std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Now Disable Module&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;endl;
    }

    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行结果是符合预期的。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:39]
$ cmake ../ -G Ninja -DENABLE_MODULE=ON
-- The CXX compiler identification is AppleClang 13.0.0.13000029
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/lxz/Develop/constexpr-demo/build

# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:45]
$ ninja
[2/2] Linking CXX executable src/constexpr

# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:48]
$ ./src/constexpr
Now Enable Module

# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:52]
$ cmake ../ -G Ninja -DENABLE_MODULE=OFF
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/lxz/Develop/constexpr-demo/build

# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:28:58]
$ ninja
[2/2] Linking CXX executable src/constexpr

# lxz @ lxz-MacBook-Pro in ~/Develop/constexpr-demo/build on git:master o [13:29:00]
$ ./src/constexpr
Now Disable Module
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;虽然结果是符合的，但是我们其实不确定是否真的在编译期间就完成了代码剔除，所以使用命令进行汇编，查看汇编中是否包含了判断指令和两段输出的字符串。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;clang -S main.cpp -o main.s -I./
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;main.s&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-txt&#34; data-lang=&#34;txt&#34;&gt;	.text
	.file	&amp;#34;main.cpp&amp;#34;
	.globl	main                            // -- Begin function main
	.p2align	2
	.type	main,@function
main:                                   // @main
	.cfi_startproc
// %bb.0:
	stp	x29, x30, [sp, #-32]!           // 16-byte Folded Spill
	str	x19, [sp, #16]                  // 8-byte Folded Spill
	mov	x29, sp
	.cfi_def_cfa w29, 32
	.cfi_offset w19, -16
	.cfi_offset w30, -24
	.cfi_offset w29, -32
	adrp	x19, :got:_ZSt4cout
	ldr	x19, [x19, :got_lo12:_ZSt4cout]
	adrp	x1, .L.str
	add	x1, x1, :lo12:.L.str
	mov	w2, #18
	mov	x0, x19
	bl	_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
	ldr	x8, [x19]
	ldur	x8, [x8, #-24]
	add	x8, x8, x19
	ldr	x19, [x8, #240]
	cbz	x19, .LBB0_5
// %bb.1:
	ldrb	w8, [x19, #56]
	cbz	w8, .LBB0_3
// %bb.2:
	ldrb	w1, [x19, #67]
	b	.LBB0_4
.LBB0_3:
	mov	x0, x19
	bl	_ZNKSt5ctypeIcE13_M_widen_initEv
	ldr	x8, [x19]
	mov	w1, #10
	mov	x0, x19
	ldr	x8, [x8, #48]
	blr	x8
	mov	w1, w0
.LBB0_4:
	adrp	x0, :got:_ZSt4cout
	ldr	x0, [x0, :got_lo12:_ZSt4cout]
	bl	_ZNSo3putEc
	bl	_ZNSo5flushEv
	ldr	x19, [sp, #16]                  // 8-byte Folded Reload
	mov	w0, wzr
	ldp	x29, x30, [sp], #32             // 16-byte Folded Reload
	ret
.LBB0_5:
	bl	_ZSt16__throw_bad_castv
.Lfunc_end0:
	.size	main, .Lfunc_end0-main
	.cfi_endproc
                                        // -- End function
	.section	.text.startup,&amp;#34;ax&amp;#34;,@progbits
	.p2align	2                               // -- Begin function _GLOBAL__sub_I_main.cpp
	.type	_GLOBAL__sub_I_main.cpp,@function
_GLOBAL__sub_I_main.cpp:                // @_GLOBAL__sub_I_main.cpp
	.cfi_startproc
// %bb.0:
	stp	x29, x30, [sp, #-32]!           // 16-byte Folded Spill
	str	x19, [sp, #16]                  // 8-byte Folded Spill
	mov	x29, sp
	.cfi_def_cfa w29, 32
	.cfi_offset w19, -16
	.cfi_offset w30, -24
	.cfi_offset w29, -32
	adrp	x19, _ZStL8__ioinit
	add	x19, x19, :lo12:_ZStL8__ioinit
	mov	x0, x19
	bl	_ZNSt8ios_base4InitC1Ev
	adrp	x0, :got:_ZNSt8ios_base4InitD1Ev
	ldr	x0, [x0, :got_lo12:_ZNSt8ios_base4InitD1Ev]
	mov	x1, x19
	ldr	x19, [sp, #16]                  // 8-byte Folded Reload
	adrp	x2, __dso_handle
	add	x2, x2, :lo12:__dso_handle
	ldp	x29, x30, [sp], #32             // 16-byte Folded Reload
	b	__cxa_atexit
.Lfunc_end1:
	.size	_GLOBAL__sub_I_main.cpp, .Lfunc_end1-_GLOBAL__sub_I_main.cpp
	.cfi_endproc
                                        // -- End function
	.type	_ZStL8__ioinit,@object          // @_ZStL8__ioinit
	.local	_ZStL8__ioinit
	.comm	_ZStL8__ioinit,1,1
	.hidden	__dso_handle
	.type	.L.str,@object                  // @.str
	.section	.rodata.str1.1,&amp;#34;aMS&amp;#34;,@progbits,1
.L.str:
	.asciz	&amp;#34;Now Disable Module&amp;#34; // 关键在这里
	.size	.L.str, 19

	.section	.init_array,&amp;#34;aw&amp;#34;,@init_array
	.p2align	3
	.xword	_GLOBAL__sub_I_main.cpp
	.ident	&amp;#34;clang version 13.0.1&amp;#34;
	.section	&amp;#34;.note.GNU-stack&amp;#34;,&amp;#34;&amp;#34;,@progbits
	.addrsig
	.addrsig_sym _GLOBAL__sub_I_main.cpp
	.addrsig_sym _ZStL8__ioinit
	.addrsig_sym __dso_handle
	.addrsig_sym _ZSt4cout
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看整个 main.s 汇编，发现只在 .L.str 段中有预期的文本字符串，可以得出结论，代码是在编译期间完成了剔除，符合我们的要求。&lt;/p&gt;
&lt;p&gt;原文链接：&lt;a href=&#34;https://blog.justforlxz.com/2022/03/10/if-constexpr/&#34;&gt;https://blog.justforlxz.com/2022/03/10/if-constexpr/&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/qdir-stdfilesystem/">
      <title>QDir 和 std::filesystem 的简单对比</title>
      <link>https://blog.deepin.org/posts/qdir-stdfilesystem/</link>
      <pubDate>Fri, 04 Mar 2022 10:11:45 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/qdir-stdfilesystem/</guid>
      <description>&lt;p&gt;作为一名使用 Qt 的开发人员，Qt 为我提供了大量好用的基础设施，例如广受好评的 QString、QNetwork之类的，这是 Qt 平台为我提供的帮助，我只需要在这个平台上开发就足够了。&lt;/p&gt;
&lt;p&gt;同样作为一名 C++ 开发人员，C++ 标准库也是我需要用的基础设施，但是标准库提供的功能就不如 Qt 了，最令人诟病的就是 C++ 的 std::string，业内充斥着对 std::string 的不屑与谩骂。&lt;/p&gt;
&lt;p&gt;但是这一情况将会在 C++ 20 标准后改善，这里不具体展开，将来我会准备写一份 C++ 20 的功能介绍。&lt;/p&gt;
&lt;p&gt;今天在开发项目的时候，为了节省资源，我仅使用标准库完成开发，缺少了 Qt 平台为我提供的有利帮助，项目开发的难度瞬间增加，在此期间，我发现了 C++ 的目录操作似乎能比的上 Qt 提供的 QDir封装，我打算在本篇文章中介绍一下这两者的不同。&lt;/p&gt;
&lt;h2 id=&#34;qdir&#34; &gt;QDir
&lt;span&gt;
    &lt;a href=&#34;#qdir&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;Qt 的基本思路是继承大于组合，所以 Qt 为我们提供的都是各种继承的类，在 Qt 中，我们使用 QDir 类进行目录操作。&lt;/p&gt;
&lt;p&gt;QDir 类使用相对或绝对文件路径来指向一个文件或目录。如果总是使用 “/” 作为目录分隔符，Qt 将会把你的路径转化为符合底层的操作系统的。&lt;/p&gt;
&lt;p&gt;QDir 类由于不涉及 IO 的具体操作，所以没有继承自 QObject 或者其他 QIO 的类。&lt;/p&gt;
&lt;p&gt;QDir 提供了非常多的方法，可以方便的获取目录的名称、绝对路径、相对路径、设置目录过滤器等。&lt;/p&gt;
&lt;p&gt;一个基本的用法如下:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-Qt&#34; data-lang=&#34;Qt&#34;&gt;QDir dir(&amp;quot;/tmp&amp;quot;);
if (!dir.exists()) {
    return
}

for (const QFileInfo&amp;amp; item : dir.entryInfoList()) {
    if (item.isDir()) {
        // ...
      }
}

dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
dir.setSorting(QDir::Size | QDir::Reversed);

const QFileInfoList&amp;amp; list = dir.entryInfoList();
for (const QFileInfo&amp;amp; item : list) {
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在上面的示例代码中可以看到，QDir 整体是非常符合面向对象的，我们使用 QDir 对象，对目录执行各种操作，涉及到具体的文件（目录是特殊的文件），我们可以使用 QFileInfo 类获取具体文件的信息。&lt;/p&gt;
&lt;p&gt;除了以上演示涉及到的方法，QDir 还有很多其他方法，使用 &lt;code&gt;count()&lt;/code&gt; 方法统计目录内文件和目录的数量，使用 &lt;code&gt;remove()&lt;/code&gt; 方法删除指定的目录，这里就不一一列举了。&lt;/p&gt;
&lt;p&gt;QDir 支持设置多种过滤，过滤(Filter) 和 排序(Sorting) 都会影响到 entryInfoList 方法返回的内容。&lt;/p&gt;
&lt;p&gt;QDir 接受的过滤器枚举:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;枚举&lt;/th&gt;
&lt;th&gt;枚举值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Dirs&lt;/td&gt;
&lt;td&gt;0x001&lt;/td&gt;
&lt;td&gt;列出与过滤器匹配的目录。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::AllDirs&lt;/td&gt;
&lt;td&gt;0x400&lt;/td&gt;
&lt;td&gt;列出所有目录；即不要将过滤器应用于目录名称。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Files&lt;/td&gt;
&lt;td&gt;0x002&lt;/td&gt;
&lt;td&gt;列出文件。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Drives&lt;/td&gt;
&lt;td&gt;0x004&lt;/td&gt;
&lt;td&gt;列出磁盘驱动器（在Unix下忽略）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::NoSymLinks&lt;/td&gt;
&lt;td&gt;0x008&lt;/td&gt;
&lt;td&gt;不要列出符号链接（不支持符号链接的操作系统忽略）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::NoDotAndDotDot&lt;/td&gt;
&lt;td&gt;NoDot&lt;/td&gt;
&lt;td&gt;NoDotDot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::NoDot&lt;/td&gt;
&lt;td&gt;0x2000&lt;/td&gt;
&lt;td&gt;不要列出特殊条目“.”。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::NoDotDot&lt;/td&gt;
&lt;td&gt;0x4000&lt;/td&gt;
&lt;td&gt;不要列出特殊条目“..”。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::AllEntries&lt;/td&gt;
&lt;td&gt;Dirs&lt;/td&gt;
&lt;td&gt;Files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Readable&lt;/td&gt;
&lt;td&gt;0x010&lt;/td&gt;
&lt;td&gt;列出应用程序具有读取访问权限的文件。可读值需要与Dirs或Files合并。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Writable&lt;/td&gt;
&lt;td&gt;0x020&lt;/td&gt;
&lt;td&gt;列出应用程序具有写入访问权限的文件。可写值需要与Dirs或Files合并。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Executable&lt;/td&gt;
&lt;td&gt;0x040&lt;/td&gt;
&lt;td&gt;列出应用程序具有执行访问权限的文件。可执行值需要与Dirs或Files合并。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Modified&lt;/td&gt;
&lt;td&gt;0x080&lt;/td&gt;
&lt;td&gt;仅列出已修改的文件（在Unix上忽略）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Hidden&lt;/td&gt;
&lt;td&gt;0x100&lt;/td&gt;
&lt;td&gt;列出隐藏的文件（在Unix上，以“.”开头的文件）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::System&lt;/td&gt;
&lt;td&gt;0x200&lt;/td&gt;
&lt;td&gt;列出系统文件（包括Unix、FIFO、套接字和设备文件；在Windows上，包括.lnk文件）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::CaseSensitive&lt;/td&gt;
&lt;td&gt;0x800&lt;/td&gt;
&lt;td&gt;过滤器应该区分大小写。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;QDir 接受的排序枚举:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;枚举&lt;/th&gt;
&lt;th&gt;枚举值&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Name&lt;/td&gt;
&lt;td&gt;0x00&lt;/td&gt;
&lt;td&gt;按名称排序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Time&lt;/td&gt;
&lt;td&gt;0x01&lt;/td&gt;
&lt;td&gt;按时间（修改时间）排序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Size&lt;/td&gt;
&lt;td&gt;0x02&lt;/td&gt;
&lt;td&gt;按文件大小排序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Type&lt;/td&gt;
&lt;td&gt;0x80&lt;/td&gt;
&lt;td&gt;按文件类型（扩展名）排序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Unsorted&lt;/td&gt;
&lt;td&gt;0x03&lt;/td&gt;
&lt;td&gt;不要排序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::NoSort&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;默认情况下未排序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::DirsFirst&lt;/td&gt;
&lt;td&gt;0x04&lt;/td&gt;
&lt;td&gt;先放目录，然后放文件。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::DirsLast&lt;/td&gt;
&lt;td&gt;0x20&lt;/td&gt;
&lt;td&gt;先放文件，然后放目录。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::Reversed&lt;/td&gt;
&lt;td&gt;0x08&lt;/td&gt;
&lt;td&gt;颠倒排序顺序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::IgnoreCase&lt;/td&gt;
&lt;td&gt;0x10&lt;/td&gt;
&lt;td&gt;不区分大小写的排序。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QDir::LocaleAware&lt;/td&gt;
&lt;td&gt;0x40&lt;/td&gt;
&lt;td&gt;使用当前区域设置对项目进行适当排序。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;stdfilesystem&#34; &gt;std::filesystem
&lt;span&gt;
    &lt;a href=&#34;#stdfilesystem&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;上面介绍了 Qt 的设计风格，而标准库的设计风格是组合大于继承，标准库提供各种非常具体的类或者函数，将一个系列的操作拆分为各个子项，最终完成任务。&lt;/p&gt;
&lt;p&gt;这里使用一个小例子来说明一下:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;filesystem&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;path tmp{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tmp&amp;#34;&lt;/span&gt;};
&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;filesystem&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;exists(tmp)) {
    std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;目录不存在&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;endl;
    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;filesystem&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;create_directories(tmp);
&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;result) {
    std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;目录创建失败，也许是已经存在。&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;endl;
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; dir_entry : std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;filesystem&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;directory_iterator(tmp)) {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (dir_entry.is_directory()) {
        &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    }
}

&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;filesystem&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;is_directory(tmp)) {
    &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到，C++ 标准库使用多个类共同完成了对目录的检查和遍历，这种基于组合的方式可以带来更多的灵活性，如果需要对某个部分进行修改，只需要继承特定类就可以完成，如果是 Qt 的 QDir，则不是很轻松。&lt;/p&gt;
&lt;p&gt;在使用标准库的时候需要注意的是，标准库通常不会约束使用者，使用当前的例子举例，QDir 提供了过滤器枚举，可以帮助开发者简单的实现文件过滤功能，但是 std::filesystem 则不提供这种接口，标准库提供了机制，但是不提供策略，开发者需要使用标准库提供的各种接口，&lt;strong&gt;组合&lt;/strong&gt; 出自己的业务，所以如果想要使用标准库的时候也能实现过滤和排序，就只能自己提供相应的操作。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;filesystem&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;path tmp{ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/tmp&amp;#34;&lt;/span&gt; };
std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;filesystem&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;directory_entry&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; list;
std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;string&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; filter{ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.jpg&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.txt&amp;#34;&lt;/span&gt; };
std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;filesystem&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;directory_iterator iter{ tmp };
std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;copy_if(iter.begin(),
             iter.end(),
             std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;back_inserter(list),
             [filter](std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;filesystem&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;directory_entry entry) {
                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;entry.is_directory() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
                       std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;find_if(filter.begin(),
                                    filter.end(),
                                    [entry](std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;string s) {
                                      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; entry.path().extension() &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; s;
                                    }
                       );
              }
);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;del&gt;可以看到，代码变得异常丑陋（逃&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;标准库提供了一些比较方便的函数，例如 std::copy_if、 std::back_inserter 和 std::find_if 等，还有一个较为常用的 std::transform。标准库提供了迭代器抽象，这样我们可以使用迭代器对象和迭代器算法，方便的进行各种遍历、复制和转换。&lt;/p&gt;
&lt;p&gt;从上面的例子可以看出，Qt 确实为开发者提供了很好的帮助，这是 Qt 作为一个平台力所能及的工作，当然，即使我们使用 Qt，也还是可以写出上面一样的代码。&lt;/p&gt;
&lt;h2 id=&#34;总结&#34; &gt;总结
&lt;span&gt;
    &lt;a href=&#34;#%e6%80%bb%e7%bb%93&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;以上就是简单的 QDir 和 std::filesystem 的不同，综合来看，Qt 库和标准库其实各有优缺，他们的目标和面向的开发者是不同的，Qt 最近一直在尝试使用标准库的内容来代替自己的一部分组件，最新的Qt6 就已经升级到了 C++ 17 标准，将 qSort 宏改为了使用 std::sort，也许在不久的将来，我们再也不用为使用 Qt 还是标准库而争论或者站队。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;引用资料&lt;/strong&gt;
&lt;a href=&#34;https://en.cppreference.com/w/cpp/filesystem&#34;&gt;std::filesystem https://en.cppreference.com/w/cpp/filesystem&lt;/a&gt;
&lt;a href=&#34;https://doc.qt.io/qt-5/qdir.html&#34;&gt;QDir https://doc.qt.io/qt-5/qdir.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;原文链接：&lt;a href=&#34;https://blog.justforlxz.com/2022/03/04/qdir-stdfilesystem/&#34;&gt;https://blog.justforlxz.com/2022/03/04/qdir-stdfilesystem/&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/git-from-the-bottom-up/">
      <title>翻译: Git from the bottom up | 自底向上聊 Git</title>
      <link>https://blog.deepin.org/posts/git-from-the-bottom-up/</link>
      <pubDate>Fri, 21 Jan 2022 17:40:08 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/git-from-the-bottom-up/</guid>
      <description>&lt;p&gt;这是一个 &lt;a href=&#34;https://jwiegley.github.io/git-from-the-bottom-up/&#34;&gt;git from the bottom
up&lt;/a&gt; 的中文翻译.&lt;/p&gt;
&lt;p&gt;由于译者水平有限, 保留了英文, 防止误解.&lt;/p&gt;
&lt;p&gt;如果真有人看, 还发现了什么错误, 欢迎提 issue, 欢迎 pr.&lt;/p&gt;
&lt;p&gt;为了保持整个文章在一个文件里面所以我并没有单独保存图片文件, 引用了原文的图片链
接, 只要原文不挂应该问题就不大.&lt;/p&gt;
&lt;p&gt;This text is licensed under Creative Commons BY 4.0. Please see the text of that
license here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://creativecommons.org/licenses/by/4.0/legalcode&#34;&gt;https://creativecommons.org/licenses/by/4.0/legalcode&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;0-introduction--介绍&#34; &gt;0 Introduction | 介绍
&lt;span&gt;
    &lt;a href=&#34;#0-introduction--%e4%bb%8b%e7%bb%8d&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Welcome to the world of Git. I hope this document will help to advance your
understanding of this powerful content tracking system, and reveal a bit of
the simplicity underlying it — however dizzying its array of options may seem
from the outside.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;欢迎来到 Git 的世界. 我希望这个文档可以加深读者对这一个内容跟踪系统的认识. 虽然
Git 的操作选项从使用者的角度看起来令人头晕眼花, 但是我希望借这篇文档展示它底层的
简洁性.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Before we dive in, there are a few terms which should be mentioned first,
since they’ll appear repeatedly throughout this text:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在我们进一步讨论之前, 有几个名词我们需要先做出一些解释, 之后我们将在这篇文章中不
断地见到他们.&lt;/p&gt;
&lt;h3 id=&#34;01-repository&#34; &gt;0.1 repository
&lt;span&gt;
    &lt;a href=&#34;#01-repository&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;repository&lt;/strong&gt; — A &lt;strong&gt;repository&lt;/strong&gt; is a collection of &lt;em&gt;commits&lt;/em&gt;, each of
which is an archive of what the project&amp;rsquo;s &lt;em&gt;working tree&lt;/em&gt; looked like at a
past date, whether on your machine or someone else&amp;rsquo;s. It also defines HEAD
(see below), which identifies the branch or commit the current working tree
stemmed from. Lastly, it contains a set of &lt;em&gt;branches&lt;/em&gt; and &lt;em&gt;tags&lt;/em&gt;, to
identify certain commits by name.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;repository&lt;/strong&gt; — &lt;strong&gt;repository&lt;/strong&gt; 是由若干 &lt;em&gt;commit&lt;/em&gt; 组成的集合, 这些 &lt;em&gt;commit&lt;/em&gt;
可以存在你的机器上, 也可以在其他的地方, 它们是 &lt;em&gt;working tree&lt;/em&gt; 曾经状态的存档.
&lt;strong&gt;repository&lt;/strong&gt; 同时定义了 HEAD (下面有), 它标志着当前的 working tree 的来源.
&lt;strong&gt;repository&lt;/strong&gt; 还含有一个由 &lt;em&gt;branch&lt;/em&gt; 和 &lt;em&gt;tag&lt;/em&gt; 组成的集合, &lt;em&gt;branch&lt;/em&gt; 和 &lt;em&gt;tag&lt;/em&gt; 可
以看作是某个 commit 的别名, 用户可以通过这个别名来找到某个 commit.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;02-the-index&#34; &gt;0.2 the index
&lt;span&gt;
    &lt;a href=&#34;#02-the-index&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;the index&lt;/strong&gt; — Unlike other, similar tools you may have used, Git does not
commit changes directly from the &lt;em&gt;working tree&lt;/em&gt; into the &lt;em&gt;repository&lt;/em&gt;.
Instead, changes are first registered in something called &lt;strong&gt;the index&lt;/strong&gt;.
Think of it as a way of “confirming” your changes, one by one, before
doing a commit (which records all your approved changes at once). Some find
it helpful to call it instead as the “staging area”, instead of the index.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;the index&lt;/strong&gt; — Git 并不像其他你使用过的类似工具那样直接把 &lt;em&gt;working tree&lt;/em&gt; 中
的更改直接 commit 到 &lt;em&gt;repository&lt;/em&gt; 中, 而是先将 &lt;em&gt;working tree&lt;/em&gt; 中的更改注册到
一个叫 &lt;strong&gt;the index&lt;/strong&gt; 的地方. 你可以把这看作是 Git 要求你在做一个 commit 之前,
对你的每个更改的再次确认, 这个 commit 将会一次性记录所有你再次确认过的更改. 有
人认为与其叫它 the index 不如将其称作 staging area (暂存区).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;03-working-tree&#34; &gt;0.3 working tree
&lt;span&gt;
    &lt;a href=&#34;#03-working-tree&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;working tree&lt;/strong&gt; — A &lt;strong&gt;working tree&lt;/strong&gt; is any directory on your filesystem
which has a &lt;em&gt;repository&lt;/em&gt; associated with it (typically indicated by the
presence of a sub-directory within it named &lt;code&gt;.git&lt;/code&gt;.). It includes all the
files and sub-directories in that directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;working tree&lt;/strong&gt; — &lt;strong&gt;working tree&lt;/strong&gt; 是在你的文件系统中任何一个与某个
&lt;em&gt;repository&lt;/em&gt; 相关联的文件夹, 通常来说这样的文件夹都会有一个叫 &lt;code&gt;.git&lt;/code&gt; 的子目录.
一个 &lt;strong&gt;working tree&lt;/strong&gt; 包含了这样一个文件夹下所有的文件和子目录.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;04-commit&#34; &gt;0.4 commit
&lt;span&gt;
    &lt;a href=&#34;#04-commit&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;commit&lt;/strong&gt; — A &lt;strong&gt;commit&lt;/strong&gt; is a snapshot of your working tree at some point
in time. The state of HEAD (see below) at the time your commit is made
becomes that commit’s parent. This is what creates the notion of a
“revision history”.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;commit&lt;/strong&gt; — &lt;strong&gt;commit&lt;/strong&gt; 是某个时间点上你的 working tree 的一个快照&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. 当
你提交一个 commit 的时候, HEAD 指向的 commit 会成为你提交的 commit 的父
commit. 这个机制让我们得以追溯修改的历史.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;05-branch&#34; &gt;0.5 branch
&lt;span&gt;
    &lt;a href=&#34;#05-branch&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;branch&lt;/strong&gt; — A &lt;strong&gt;branch&lt;/strong&gt; is just a name for a commit (and much more will
be said about commits in a moment), also called a reference. It’s the
parentage of a commit which defines its history, and thus the typical notion
of a “branch of development”.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;branch&lt;/strong&gt; — &lt;strong&gt;branch&lt;/strong&gt; 只是某个的 commit 的别名&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;, 也可以认为是一个引用.
它是一个 commit 的来历, 记录着我们是如何到达这个 commit 的. 这很好地体现了分支
开发的理念.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;06-tag&#34; &gt;0.6 tag
&lt;span&gt;
    &lt;a href=&#34;#06-tag&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tag&lt;/strong&gt; — A &lt;strong&gt;tag&lt;/strong&gt; is also a name for a commit, similar to a &lt;em&gt;branch&lt;/em&gt;,
except that it always names the same commit, and can have its own
description text.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tag&lt;/strong&gt; — &lt;strong&gt;tag&lt;/strong&gt; 是另一种 commit 的别名, 和 &lt;em&gt;branch&lt;/em&gt; 很类似, 除了它永远都是
某个特定 commit 的别名, 以及一个 tag 可以有它自己的说明文字.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;07-master&#34; &gt;0.7 master
&lt;span&gt;
    &lt;a href=&#34;#07-master&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;master&lt;/strong&gt; — The mainline of development in most repositories is done on a
branch called “&lt;strong&gt;master&lt;/strong&gt;”. Although this is a typical default, it is in
no way special.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;master&lt;/strong&gt; — 大部分主要的开发都在一个叫 &amp;ldquo;&lt;strong&gt;master&lt;/strong&gt;&amp;rdquo; 的分支上完成, 这只是一个
默认的名称, 并没有什么特殊的.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;08-head&#34; &gt;0.8 HEAD
&lt;span&gt;
    &lt;a href=&#34;#08-head&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HEAD&lt;/strong&gt; — &lt;strong&gt;HEAD&lt;/strong&gt; is used by your repository to define what is currently
checked out:
&lt;ul&gt;
&lt;li&gt;If you checkout a branch, HEAD symbolically refers to that branch,
indicating that the branch name should be updated after the next commit
operation.&lt;/li&gt;
&lt;li&gt;If you checkout a specific commit, HEAD refers to that commit only. This
is referred to as a detached &lt;em&gt;HEAD&lt;/em&gt;, and occurs, for example, if you check
out a tag name.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HEAD&lt;/strong&gt; — &lt;strong&gt;HEAD&lt;/strong&gt; 是被你的 repository 用来确定什么东西是当前 checkout 的:
&lt;ul&gt;
&lt;li&gt;如果你 checkout 一个 branch, HEAD 就会指向那个 branch, 这意味着这个 branch
在下一次 commit 之后会更新.&lt;/li&gt;
&lt;li&gt;如果你 checkout 某个特定的 commit, 那么 HEAD 只会指向那个 commit. 发生这种事
情的时候, 我们称一个 HEAD 处在脱离的状态. 当你 checkout 一个 tag 的时候也会
发生这样的事情.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The usual flow of events is this: After creating a repository, your work is
done in the working tree. Once your work reaches a significant point — the
completion of a bug, the end of the working day, a moment when everything
compiles — you add your changes successively to the index. Once the index
contains everything you intend to commit, you record its content in the
repository. Here’s a simple diagram that shows a typical project’s
life-cycle:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通常来说, 你用 Git 工作的流程是这样的: 在创建一个 repository 之后, 你在 working
tree 中完成工作. 每当你完成了一个阶段的工作, 比方说修正了一个 bug, 或者下班了,
还有比如&amp;quot;终于能过编译了!&amp;quot;,你就把所有的更改都一个一个添加到 the index 中. 当
the index 中包含了所有你想 commit 的内容, 你就将这个 commit 的内容加入到
repository 中.这里有一个简单的示意图,展示了一个项目通常的生命周期:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/lifecycle.png&#34; alt=&#34;ProjectLifecycle&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With this basic picture in mind, the following sections shall attempt to
describe how each of these different entities is important to the operation of
Git.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;请将上述的内容记在心里, 接下来的几节我们将说明这些东西对 Git 的操作的重要性.&lt;/p&gt;
&lt;h2 id=&#34;1-repository&#34; &gt;1 Repository
&lt;span&gt;
    &lt;a href=&#34;#1-repository&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;11-repository-directory-content-tracking--repository-目录内容跟踪&#34; &gt;1.1 Repository: Directory content tracking | Repository: 目录内容跟踪
&lt;span&gt;
    &lt;a href=&#34;#11-repository-directory-content-tracking--repository-%e7%9b%ae%e5%bd%95%e5%86%85%e5%ae%b9%e8%b7%9f%e8%b8%aa&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;As mentioned above, what Git does is quite rudimentary: it maintains snapshots
of a directory’s contents. Much of its internal design can be understood in
terms of this basic task.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;就像上面提到的一样, Git 实际上做的事情非常的基础: 只是在维护一个目录里的内容的快
照. Git 的许多内部设计都可以从这个角度来理解.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The design of a Git repository in many ways mirrors the structure of a UNIX
filesystem:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Git 的 repository 的设计很多时候都和 UNIX 的文件系统很相似:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A &lt;em&gt;filesystem&lt;/em&gt; begins with a root directory, which typically consists of other
directories, most of which have leaf nodes, or &lt;em&gt;files&lt;/em&gt;, that contain data.
Meta-data about these files’ contents is stored both in the directory (the
names), and in the i-nodes that reference the contents of those files (their
size, type, permissions, etc). Each &lt;em&gt;i-node&lt;/em&gt; has a unique number that
identifies the contents of its related file. And while you may have many
directory entries pointing to a particular i-node (i.e., hard-links), it’s
the i-node which “owns” the contents stored on your filesystem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;文件系统&lt;/em&gt; 是从根目录开始的, 通常它会包括其他的所有目录. 大部分的目录会含有叶子
节点或者说是 &lt;em&gt;文件&lt;/em&gt; , 文件中才真正含有数据. 关于文件内容的元数据同时被存放在目
录里, 以及引用这个文件的内容的那个 i-node 里. 在目录里只存了个文件名, 而在
i-node 中存放着文件的大小, 类型, 权限等等. 每个 i-node 有一个唯一的编码, 用于区
分它关联的文件内容, 但是要注意: 是可能会出现多个文件目录指向了同一个 i-node 的
情况的(比如说硬链接). 总的来说,是 i-node 最终管理着你存在文件系统里的数据文件.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Internally, Git shares a strikingly similar structure, albeit with one or two
key differences.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从内部逻辑的角度来看, Git 除了一两处关键的不同之处以外, 与这个结构有着惊人的相
似.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;First, it represents your file’s contents in &lt;em&gt;blobs&lt;/em&gt;, which are also leaf
nodes in something awfully close to a directory, called a &lt;em&gt;tree&lt;/em&gt;. Just as an
i-node is uniquely identified by a system-assigned number, a blob is named by
computing the SHA1 hash id of its size and contents.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;首先, Git 将你的文件内容以 &lt;em&gt;blob&lt;/em&gt; 的方式呈现. 而 &lt;em&gt;blob&lt;/em&gt; 也是一个和目录十分类似的
树形结构中的叶子节点, 这里我们直接将这种树形结构称为 &lt;em&gt;tree&lt;/em&gt;. 和 i-node 有一个系
统赋予的, 独一无二的值一样, 一个 blob 也有一个独一无二的标识符, 而这个标识符也
就是这个 blob 的名字是从它的大小和内容运用 SHA1 哈希算法计算得出的.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For all intents and purposes this is just an arbitrary number, like an i-node,
except that it has two additional properties:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个值具体是什么并不重要, 在 Git 中我们基本上就是需要一个值来唯一地确定一个
blob, 这一点上和 i-node 很类似. 但是这个通过 SHA1 计算出来的哈希值却有两个额外的
性质:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;first, it verifies the blob’s contents will never change; and second, the
same contents shall always be represented by the same blob, no matter where it
appears: across commits, across repositories — even across the whole
Internet. If multiple trees reference the same blob, this is just like
hard-linking: the blob will not disappear from your repository as long as
there is at least one link remaining to it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;首先, 这个值能够保证一个 blob 的内容不会被随意更改; 其次, 只要 blob 的内容一样,
他们总会有同一个标识符, 即使他们在不同的地方也一样: 在不同的 commit 之间相同内
容的 blob 会有同一个名字, 不同仓库间的 blob 也一样, 即使是网络上的其他地方的
blob 也一样. 如果有多个 tree 引用了同一个 blob, 这情况几乎就和硬链接没什么两样:
直到没有 tree 引用这个 blob 了, 它才会从你的 repository 中消失.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The difference between a Git blob and a filesystem’s file is that a blob
stores no metadata about its content. All such information is kept in the tree
that holds the blob. One tree may know those contents as a file named “foo”
that was created in August 2004, while another tree may know the same contents
as a file named “bar” that was created five years later.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Git 中的 blob 和文件系统中的文件的一大区别就是 blob 中并没有存储与其内容相关的元
数据. 这些信息是存放在引用这个 blob 的 tree 中的. 某个 tree 也许会觉得这个 blob
中的内容是一个叫 &amp;ldquo;foo&amp;rdquo; 的, 于 2004 年八月创建的文件, 同时另一个 tree 却会认为同
样的一个 blob 对应着名叫 &amp;ldquo;bar&amp;rdquo; 的, 五年后才被创建的文件.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In a normal filesystem, two files with the same contents but with such
different metadata would always be represented as two independent files. Why
this difference? Mainly, it’s because a filesystem is designed to support
files that change, whereas Git is not. The fact that data is immutable in the
Git repository is what makes all of this work and so a different design was
needed. And as it turns out, this design allows for much more compact storage,
since all objects having identical content can be shared, no matter where they
are.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通常来说, 在文件系统中, 两个内容一样却有不一样的元数据的文件总是会被当作是两个独
立的文件. 为什么会有这种不同呢? 基本上我们可以认为这是因为文件系统主要是为了会
变化的文件设计的, 而 Git 并不是, 我们反而是希望在 Git 中被存下来的快照不会发生
变化的. 实际上这种设计甚至可以用来压缩存储空间, 因为它允许两个有相同内容的文件
共享空间&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3 id=&#34;12-introducing-the-blob--关于-blob-的二三事&#34; &gt;1.2 Introducing the blob | 关于 blob 的二三事
&lt;span&gt;
    &lt;a href=&#34;#12-introducing-the-blob--%e5%85%b3%e4%ba%8e-blob-%e7%9a%84%e4%ba%8c%e4%b8%89%e4%ba%8b&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Now that the basic picture has been painted, let’s get into some practical
examples. I’m going to start by creating a sample Git repository, and showing
how Git works from the bottom up in that repository. Feel free to follow along
as you read:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们已经大致了解了 Git 的工作原理, 现在让我们看一些实践中的案例. 我将创建一个
Git repository 作为例子, 用它来自底向上地展示 Git 是如何工作的. 你完全可以在阅读
的同时也自己动手试试:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ mkdir sample; cd sample
$ echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello, world!&amp;#39;&lt;/span&gt; &amp;gt; greeting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Here I’ve created a new filesystem directory named “sample” which contains
a file whose contents are prosaically predictable. I haven’t even created a
repository yet, but already I can start using some of Git’s commands to
understand what it’s going to do. First of all, I’d like to know which hash
id Git is going to store my greeting text under:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里我创建了一个叫叫 &amp;ldquo;sample&amp;rdquo; 的文件夹, 它的内容是显而易见的. 目前我还没有创建一
个 repository, 但是我实际上已经可以使用一些 Git 命令来简单解释一下接下来我们要
干些啥. 首先, 我想知道一下这个叫 greeting 的文件被使用 Git 进行版本管理之后, 它
对应的那个 blob 的名字是啥:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git hash-object greeting
af5626b4a114abcb82d63db7c8082c3c4756e51b
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;If you run this command on your system, you’ll get the same hash id. Even
though we’re creating two different repositories (possibly a world apart,
even) our greeting blob in those two repositories will have the same hash id.
I could even pull commits from your repository into mine, and Git would
realize that we’re tracking the same content — and so would only store one
copy of it! Pretty cool.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果你也在你的机子上跑了这条命令,你一定会和我得到一个同样的哈希值. 虽然我们甚至
可能不在同一个位面&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;, 在两个不同的 repository 中, 同样的内容还是有同样的哈
希值. 我甚至可以从你的 repository 中 pull 一些 commit 到我这里来, 如果我真的那
么做了, Git 也会认识到我们两个人是在跟踪同样的一个内容 — 所以它也只会存储一份
数据. 这真的很棒!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The next step is to initialize a new repository and commit the file into it. I
’m going to do this all in one step right now, but then come back and do it
again in stages so you can see what’s going on underneath:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;接下来我们创建一个新的 repository 然后把文件 commit 进去. 我接下来将一次性完成
它, 之后我会再一步步的做一次, 这样有利于你理解到底发生了什么:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git init
$ git add greeting
$ git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Added my greeting&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;At this point our blob should be in the system exactly as we expected, using
the hash id determined above. As a convenience, Git requires only as many
digits of the hash id as are necessary to uniquely identify it within the
repository. Usually just six or seven digits is enough:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;此时, 我们的那个 blob 应该正如我们预想的那样出现在这个系统中了, 它的哈希值正如上
面所说的那样, 应该是&lt;code&gt;af5626b4a114abcb82d63db7c8082c3c4756e51b&lt;/code&gt;. 方便起见, 只要
你给出足以在这个 repository 中唯一确定这个 blob 的哈希值前缀, Git 就可以找到这
样一个 blob. 通常来说六七个字符就够了:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git cat-file -t af5626b
blob
$ git cat-file blob af5626b
Hello, world!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;There it is! I haven’t even looked at which commit holds it, or what tree it
’s in, but based solely on the contents I was able to assume it’s there,
and there it is. It will always have this same identifier, no matter how long
the repository lives or where the file within it is stored. These particular
contents are now verifiably preserved, forever.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;看, 我们已经找到他了, 我甚至都不需要知道是哪个 commit 引用了这个 blob, 也不用知
道这个 blob 在哪个 tree 上, 我们只需要通过文件的内容就足以确定这个 blob 的存在.
这个文件一直都可以以这种方式来唯一确定, 不管这个repository存在了多久, 也不用管这
个 blob 到底被存储在 repository 中的哪里. 这个内容已经被很好的保护了起来.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In this way, a Git blob represents the fundamental data unit in Git. Really,
the whole system is about blob management.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Git 以这种方式在 repository 中呈现数据. 讲真, 整个 Git 都只是在管理 blob 而已.&lt;/p&gt;
&lt;h3 id=&#34;13-blobs-are-stored-in-trees--blob-是存在-tree-里的&#34; &gt;1.3 Blobs are stored in trees | Blob 是存在 tree 里的
&lt;span&gt;
    &lt;a href=&#34;#13-blobs-are-stored-in-trees--blob-%e6%98%af%e5%ad%98%e5%9c%a8-tree-%e9%87%8c%e7%9a%84&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;The contents of your files are stored in blobs, but those blobs are pretty
featureless. They have no name, no structure — they’re just “blobs”, after
all.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Git 在追踪文件的过程中, 将你文件里的内容存储在 blob 中, 但是 blob 的功能相当的有
限, 它们没有文件名, 没有结构, 它们只是 &amp;ldquo;blob&amp;rdquo;, 仅此而已.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In order for Git to represent the structure and naming of your files, it
attaches blobs as leaf nodes within a tree. Now, I can’t discover which
tree(s) a blob lives in just by looking at it, since it may have many, many
owners. But I know it must live somewhere within the tree held by the commit I
just made:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为了能够记录下你文件的名字和结构, Git 将 blob 看作是 tree 的叶子节点来管理. 我们
显然不能通过查看一个 blob 来看出它到底属于哪个 tree, 因为它很可能同时出现在相当
多个 tree 中. 但是我知道它一定存在于哪个我们刚刚创建的 commit 所管理的 tree 中:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git ls-tree HEAD
&lt;span style=&#34;color:#ae81ff&#34;&gt;100644&lt;/span&gt; blob af5626b4a114abcb82d63db7c8082c3c4756e51b greeting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;There it is! This first commit added my greeting file to the repository. This
commit contains one Git tree, which has a single leaf: the greeting content’s
blob.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是那个 blob 了! 这是我刚提交的那个, 将我的 greeting 文件添加到 repository
中的 commit. 这个 commit 里面有一个 Git tree, 而这个 tree 中只有一个叶子节点 —
那就是存放着 greeting 文件内容的那个 blob.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Although I can look at the tree containing my blob by passing HEAD to
&lt;code&gt;ls-tree&lt;/code&gt;, I haven’t yet seen the underlying tree object referenced by that
commit. Here are a few other commands to highlight that difference and thus
discover my tree:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;尽管我可以通过向 &lt;code&gt;ls-tree&lt;/code&gt; 这个命令传入 HEAD 来查看那个含有我的 blob 的 tree, 但
是我还没从底层看到过那个被 commit 引用的 tree 对象. 这里有几个命令能说明这两者
有什么不同&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;, 我们来看看:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git rev-parse HEAD
588483b99a46342501d99e3f10630cfc1219ea32 &lt;span style=&#34;color:#75715e&#34;&gt;# different on your system&lt;/span&gt;

$ git cat-file -t HEAD
commit

$ git cat-file commit HEAD
tree 0563f77d884e4f79ce95117e2d686d7d6e282887
author John Wiegley &amp;lt;johnw@newartisans.com&amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1209512110&lt;/span&gt; -0400
committer John Wiegley &amp;lt;johnw@newartisans.com&amp;gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1209512110&lt;/span&gt; -0400
Added my greeting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;The first command decodes the HEAD alias into the commit it references, the
second verifies its type, while the third command shows the hash id of the
tree held by that commit, as well as the other information stored in the
commit object. The hash id for the commit is unique to my repository —
because it includes my name and the date when I made the commit — but the
hash id for the tree should be common between your example and mine,
containing as it does the same blob under the same name.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第一个命令通过 HEAD 找到它所指向的那个 commit, 第二个可以指明 HEAD 指针的类型,
而第三个命令则展示了那个 commit 所管理的 tree 的哈希值, 以及这个 commit 的一些
相关信息. 这个 commit 的哈希值和你的系统中得到的是不同的, 因为如你所见, 一个
commit 中式包含作者和提交时间,还有说明文字等信息的, 这些东西都会加入哈希值的计
算, 所以一定是不同的. 然而 tree 的哈希值却和你的系统中是一样的. 因为我们两的系
统中, 这个 tree 的内容是完全一致的.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let’s verify that this is indeed the same tree object:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们来确认一下这个 tree 是真的和你那边相同:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git ls-tree 0563f77
&lt;span style=&#34;color:#ae81ff&#34;&gt;100644&lt;/span&gt; blob af5626b4a114abcb82d63db7c8082c3c4756e51b greeting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;There you have it: my repository contains a single commit, which references a
tree that holds a blob — the blob containing the contents I want to record.
There’s one more command I can run to verify that this is indeed the case:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里你可以看到, 我的 repository 中只包含一个 commit, 这个 commit 它关联到一个
tree, 而这个 tree 管理了一个 blob, 这个 blob 里面是我希望记录下来的内容. 还有一
个命令也能展示这个情况:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ find .git/objects -type f | sort
.git/objects/05/63f77d884e4f79ce95117e2d686d7d6e282887
.git/objects/58/8483b99a46342501d99e3f10630cfc1219ea32
.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51b
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;From this output I see that the whole of my repo contains three objects, each
of whose hash id has appeared in the preceding examples. Let’s take one last
look at the types of these objects, just to satisfy curiosity:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在这个命令的输出中可以看到我的整个 repository 中只有三个对象, 每一个的哈希值你都
已经见到过了. 为了满足好奇心, 我们甚至可以看一下他们对应的类型:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git cat-file -t 588483b99a46342501d99e3f10630cfc1219ea32
commit
$ git cat-file -t 0563f77d884e4f79ce95117e2d686d7d6e282887
tree
$ git cat-file -t af5626b4a114abcb82d63db7c8082c3c4756e51b
blob
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;I could have used the show command at this point to view the concise contents
of each of these objects, but I’ll leave that as an exercise to the reader.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我本可以使用 show 命令来查看这些对象的内容, 但是我选择将这个留作读者的练习.&lt;/p&gt;
&lt;h3 id=&#34;14-how-trees-are-made--tree-是怎样炼成的&#34; &gt;1.4 How trees are made | Tree 是怎样炼成的
&lt;span&gt;
    &lt;a href=&#34;#14-how-trees-are-made--tree-%e6%98%af%e6%80%8e%e6%a0%b7%e7%82%bc%e6%88%90%e7%9a%84&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Every commit holds a single tree, but how are trees made? We know that blobs
are created by stuffing the contents of your files into blobs — and that
trees own blobs — but we haven’t yet seen how the tree that holds the blob
is made, or how that tree gets linked to its parent commit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;每个 commit 对应一个 tree, 但是 tree 是怎样炼成的呢? 我们已经知道了 blob 是基于
你的文件内容创建的数据块, 以及 Git 是用 tree 来管理 blob 的. 但是我们还并不理解
这些管理 blob 用的 tree 是如何被创建的, 或者说我们并不懂 tree 是如何连接到它的
父 commit 的.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let’s start with a new sample repository again, but this time by doing things
manually, so you can get a feeling for exactly what’s happening under the
hood:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们来看上一节中提到的那个例子, 不过这次我们一步一步手动地做这些事情, 这样你可以
明白底层到底发生了什么:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ rm -fr greeting .git
$ echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Hello, world!&amp;#39;&lt;/span&gt; &amp;gt; greeting
$ git init
$ git add greeting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;It all starts when you first add a file to the index. For now, let’s just say
that the index is what you use to initially create blobs out of files. When I
added the file &lt;code&gt;greeting&lt;/code&gt;, a change occurred in my repository. I can’t see
this change as a commit yet, but here is one way I can tell what happened:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这一切是从你首次将一个文件加入 the index 开始的. 现在让我们先这么认为: the index
是你初次用来将文件内容装填进 blob 时所需要使用的工具.当我添加名为 &lt;code&gt;greeting&lt;/code&gt; 的
文件时, 我的 repository 发生了一次修改, 而我目前还不能够将这个更改看作是
commit, 但是我还是有一个方法能够搞清到底发生了什么:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git log &lt;span style=&#34;color:#75715e&#34;&gt;# this will fail, there are no commits!&lt;/span&gt;
fatal: bad default revision &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;HEAD&amp;#39;&lt;/span&gt;
$ git ls-files --stage &lt;span style=&#34;color:#75715e&#34;&gt;# list blob referenced by the index&lt;/span&gt;
&lt;span style=&#34;color:#ae81ff&#34;&gt;100644&lt;/span&gt; af5626b4a114abcb82d63db7c8082c3c4756e51b &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; greeting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;What’s this? I haven’t committed anything to the repository yet, but already
an object has come into being. It has the same hash id I started this whole
business with, so I know it represents the contents of my &lt;code&gt;greeting&lt;/code&gt; file. I
could use &lt;code&gt;cat-file -t&lt;/code&gt; at this point on the hash id, and I’d see that it was
a blob. It is, in fact, the same blob I got the first time I created this
sample repository. The same file will always result in the same blob (just in
case I haven’t stressed that enough).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么这是啥呢? 我还没有 commit 过任何东西啊? 但是实际上已经有一个东西在上述的操作
中被生成了出来. 它的哈希值和我们一开始对 &lt;code&gt;greeting&lt;/code&gt; 算出来的那个是一样的, 所以
我们可以知道这个东西它里面是文件 &lt;code&gt;greeting&lt;/code&gt; 的数据. 此时, 我可以使用命令
&lt;code&gt;cat-file-t&lt;/code&gt; 来查看那个哈希值对应的文件, 实际上我们也看过了, 这个文件本身是一个
blob. 实际上它和我们一开始创建的那个 repository 中的那个 blob 是完全一样的东西.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This blob isn’t referenced by a tree yet, nor are there any commits. At the
moment it is only referenced from a file named &lt;code&gt;.git/index&lt;/code&gt;, which references
the blobs and trees that make up the current index. So now let’s make a tree
in the repo for our blob to hang off of:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个 blob 目前还没有被某个 tree 引用, 实际上也没有被任何一个 commit 引用. 在这个
时候, 实际上只有一个叫 &lt;code&gt;.git/index&lt;/code&gt;的文件引用了这个 blob, 这文件里面引用了所有
构成了当前的 index 的 blob 和 tree. 那么我们现在用这个 blob 来创建一个 tree:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git write-tree &lt;span style=&#34;color:#75715e&#34;&gt;# record the contents of the index in a tree&lt;/span&gt;
0563f77d884e4f79ce95117e2d686d7d6e282887
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This number should look familiar as well: a tree containing the same blobs
(and sub-trees) will always have the same hash id. I don’t have a commit
object yet, but now there is a tree object in that repository which holds the
blob. The purpose of the low-level &lt;code&gt;write-tree&lt;/code&gt; command is to take whatever
the contents of the index are and tuck them into a new tree for the purpose of
creating a commit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个值也令人十分熟悉: 两个包含同样的 blob, 或者说 sub-tree 们的 tree 的哈希值总
时一样的. 我的 repository 中到现在还没有一个 commit, 但是已经有一个 tree 了, 这
个 tree 还引用了一个 blob. &lt;code&gt;wirte-tree&lt;/code&gt; 这个底层命令的存在, 使得我们可以将任何
在 the index 中的内容装载到一个新的 tree 中, 然后用来创建一个 commit.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I can manually make a new commit object by using this tree directly, which is
just what the &lt;code&gt;commit-tree&lt;/code&gt; command does:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;只需要用 &lt;code&gt;commit-tree&lt;/code&gt; 命令, 我现在就可以用上面创建的 tree 来手动地创建一个新的
commit 对象, 就像这样:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Initial commit&amp;#34;&lt;/span&gt; | git commit-tree 0563f77
5f1bc85745dcccce6121494fdd37658cb4ad441f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;The raw &lt;code&gt;commit-tree&lt;/code&gt; command takes a tree’s hash id and makes a commit
object to hold it. If I had wanted the commit to have a parent, I would have
had to specify the parent commit’s hash id explicitly using the &lt;code&gt;-p&lt;/code&gt; option.
Also, note here that the hash id differs from what will appear on your system:
This is because my commit object refers to both my name, and the date at which
I created the commit, and these two details will always be different from
yours.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个 &lt;code&gt;commit-tree&lt;/code&gt; 命令需要知道那个 tree 的哈希值, 然后创建一个 commit 来管理它.
如果我想要这个 commit 有一个父 commit, 那我需要使用 &lt;code&gt;-p&lt;/code&gt; 参数指定父 commit 的哈
希值. 以及我们可以注意到, 这个 tree 的哈希值和你的系统上的是不同的, 这是因为我
的 commit 中实际上包含了我的名字信息, 以及我创建这个 commit 的日期, 这两个值是
和你的仓库中的情况不同的.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our work is not done yet, though, since I haven’t registered the commit as
the new head of a branch:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;活还没完, 我们还没更新这个 commit 对应的分支:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ echo 5f1bc85745dcccce6121494fdd37658cb4ad441f &amp;gt; .git/refs/heads/master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This command tells Git that the branch name “master” should now refer to our
recent commit. Another, much safer way to do this is by using the command
&lt;code&gt;update-ref&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个命令告诉 Git 该用 &amp;ldquo;master&amp;rdquo; 这个名字指代我们新提交的那个 commit 了. 还有一点
要说的就是, 完成上面这件事情还有一个更安全的方法, 那就是使用 &lt;code&gt;update-ref&lt;/code&gt; 命令:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git update-ref refs/heads/master 5f1bc857
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;After creating &lt;code&gt;master&lt;/code&gt;, we must associate our working tree with it. Normally
this happens for you whenever you check out a branch:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在创建&lt;sup id=&#34;fnref:6&#34;&gt;&lt;a href=&#34;#fn:6&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;6&lt;/a&gt;&lt;/sup&gt;完 master 之后,我们必须将我们的 working tree 调整成和 master 相匹配
的样子. 一般来说, 这个操作是用于 check out 一个分支的:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git symbolic-ref HEAD refs/heads/master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This command associates HEAD symbolically with the master branch. This is
significant because any future commits from the working tree will now
automatically update the value of &lt;code&gt;refs/heads/master&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个命令会将 HEAD 绑定到 master 分支上. 这一点是相当自然的, 因为我们接下来再从
working tree 中做出更改之后提交的 commit 应该要更新这个名叫 master 的分支, 也就
是文件 &lt;code&gt;refs/heads/master&lt;/code&gt; 的值.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s hard to believe it’s this simple, but yes, I can now use log to view my
newly minted commit:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;虽然很难相信, 但是 Git 的工作就是如此简单, 我还可以使用 log 命令来查看我刚一步步
手动添加的这个 commit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git log
commit 5f1bc85745dcccce6121494fdd37658cb4ad441f
Author: John Wiegley &amp;lt;johnw@newartisans.com&amp;gt;
Date:   Mon Apr &lt;span style=&#34;color:#ae81ff&#34;&gt;14&lt;/span&gt; 11:14:58 &lt;span style=&#34;color:#ae81ff&#34;&gt;2008&lt;/span&gt; -0400
        Initial commit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;A side note: if I hadn’t set &lt;code&gt;refs/heads/master&lt;/code&gt; to point to the new commit,
it would have been considered “unreachable”, since nothing currently refers
to it nor is it the parent of a reachable commit. When this is the case, the
commit object will at some point be removed from the repository, along with
its tree and all its blobs. (This happens automatically by a command called
&lt;code&gt;gc&lt;/code&gt;, which you rarely need to use manually). By linking the commit to a name
within &lt;code&gt;refs/heads&lt;/code&gt;, as we did above, it becomes a reachable commit, which
ensures that it’s kept around from now on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里有个值得注意的地方: 如果我不曾将文件 &lt;code&gt;refs/heads/master&lt;/code&gt; 指向那个新的
commit, 这个 commit 会被看作是 &amp;ldquo;不可达的&amp;rdquo;, 因为没有任何其他的东西和他有关联, 它
也不是任何一个可达的 commit 的祖先. 当发生这种情况的时候, 这个 commit 将会被
Git 在某个时候从 repository 中移除, 它对应的 tree 和 blob 也一样, (这个事情会在
运行一个叫 &lt;code&gt;gc&lt;/code&gt; 的命令的时候自动地发生, 当然这个命令基本不会被用户手动运行). 如
果我们像上面做的那样, 在目录 &lt;code&gt;.git/heads&lt;/code&gt; 下用某个名字关联到这个 commit, 这个
commit 就是可达的了, 这可以保证它不会被删掉.&lt;/p&gt;
&lt;h3 id=&#34;15-the-beauty-of-commits--commit-之美&#34; &gt;1.5 The beauty of commits | Commit 之美
&lt;span&gt;
    &lt;a href=&#34;#15-the-beauty-of-commits--commit-%e4%b9%8b%e7%be%8e&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Some version control systems make “branchCes” into magical things, often
distinguishing them from the “main line” or “trunk”, while others discuss
the concept as though it were very different from commits. But in Git there
are no branches as separate entities: there are only blobs, trees and commits.
Since a commit can have one or more parents, and those commits can have
parents, this is what allows a single commit to be treated like a branch:
because it knows the whole history that led up to it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一些版本控制系统将 branch 弄得很晦涩难懂, 经常将 &amp;ldquo;主线&amp;rdquo; (或者说 &amp;ldquo;主干&amp;rdquo; ), 和 &amp;ldquo;分
支&amp;rdquo; 区分开来, 而另一些版本控制系统则认为分支这个概念和 commit 有很大区别. 但是
在 Git 中并没有 branch 这个实体: Git 里只有 blob, tree 以及 commit 这三个概念.
因为一个 commit 可以有多个父 commit 而这些父 commit 也有父亲, 所以我们实际上可
以把单个 commit 当作 branch 来看待: 因为我们实际上可以从这个 commit 开始, 回溯
出文件内容是如何一步步地被修改, 更迭到当前这个 commit 的这整个历史.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can examine all the top-level, referenced commits at any time using the
&lt;code&gt;branch&lt;/code&gt; command:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;你随时都可以通过 &lt;code&gt;branch&lt;/code&gt; 命令查看所有顶层的, 被引用的 commit :&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git branch -v
* master 5f1bc85 Initial commit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Say it with me: A branch is nothing more than a named reference to a commit.
In this way, branches and tags are identical, with the sole exception that
tags can have their own descriptions, just like the commits they reference.
Branches are just names, but tags are descriptive, well, “tags”.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;来跟着我一起念: &amp;ldquo;一个 branch 仅仅只是一个 commit 的引用, 只是这个引用本身还有一
个名字罢了.&amp;rdquo; 从这种角度来看, branch 和 tags 几乎没有什么区别, 只有一个例外: tag
就像它指向的那个 commit 一样还可以拥有自己的说明性文字. branch 只是 commit 的名
字, 但是 tag 是描述性的, 毕竟人家是 &amp;ldquo;标签&amp;rdquo; 嘛.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But the fact is, we don’t really need to use aliases at all. For example, if
I wanted to, I could reference everything in the repository using only the
hash ids of its commits. Here’s me being straight up loco and resetting the
head of my working tree to a particular commit:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是实际上我们也可以完全就不用这些别名. 比如说, 如果我想的话, 我实际上可以用
repository 中任何一个 commit 的哈希值来唯一地确定它. 下面这个是如果我想直接把我
的 working tree 对应的 HEAD 指向某个特定的 commit 需要用的命令:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git reset --hard 5f1bc85
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;The &amp;ndash;hard option says to erase all changes currently in my working tree,
whether they’ve been registered for a checkin or not (more will be said about
this command later). A safer way to do the same thing is by using &lt;code&gt;checkout&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个 &lt;code&gt;--hard&lt;/code&gt; 选项的意思就是说, 让 Git 不管我当前的 working tree 中相对于给定的
commit 发生的所有更改是否有被记录下来过, 都把它们清除掉. 我们以后还会聊聊
&lt;code&gt;reset&lt;/code&gt; 这个命令. 完成这件事情还有一个更安全的方式, 那就是使用 checkout 命令:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git checkout 5f1bc85
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;The difference here is that changed files in my working tree are preserved. If
I pass the &lt;code&gt;-f&lt;/code&gt; option to &lt;code&gt;checkout&lt;/code&gt;, it acts the same in this case to &lt;code&gt;reset --hard&lt;/code&gt;, except that checkout only ever changes the working tree, whereas
&lt;code&gt;reset --hard&lt;/code&gt; changes the current branch&amp;rsquo;s HEAD to reference the specified
version of the tree.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这两者的区别在于, 后者对于我的 working tree 中的文件修改操作是有一定保护的, 对于
没记录过的数据会做一次是否清除的询问. 如果我们将参数 &lt;code&gt;-f&lt;/code&gt; 传递给 &lt;code&gt;checkout&lt;/code&gt;命
令, Git 的行为就会几乎和执行 &lt;code&gt;reset --hard&lt;/code&gt; 时一模一样. 它们两之间的区别在于
&lt;code&gt;checkout&lt;/code&gt; 命令只是清除 working tree 中的文件变化, 而 &lt;code&gt;reset --hard&lt;/code&gt; 会将当前的
HEAD 指向的那个 branch 一起移动到某个版本的 tree 上.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Another joy of the commit-based system is that you can rephrase even the most
complicated version control terminology using a single vocabulary. For
example, if a commit has multiple parents, it’s a “merge commit” — since
it merged multiple commits into one. Or, if a commit has multiple children, it
represents the ancestor of a “branch”, etc. But really there is no
difference between these things to Git: to it, the world is simply a
collection of commit objects, each of which holds a tree that references other
trees and blobs, which store your data. Anything more complicated than this is
simply a device of nomenclature.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;另一个使用基于 commit 的版本控制系统带来的乐趣是你可以用很简单的话语来描述版本控
制系统中那些晦涩难懂的术语. 比方说, 如果一个 commit 拥有两个父 commit, 那么我们
称这个 commit 是一个 &amp;ldquo;merge commit&amp;rdquo; — 因为它确实将多个 commit 合并了起来嘛.还
有,如果一个 commit 拥有多个子 commit 这代表着这里是分支的开始, 诸如此类. 但是实
际上这些术语所描述的事情对于 Git 来说并没有什么区别: 对于 Git 来说, 整个世界就
是由 commit 对象组成的, 它们管理着一个 tree 而这个 tree 中又引用了 其他的 tree
以及 blob, 在 blob 中实际存放着你的数据. 其他比这个复杂的东西都只是其他人对某个
行为取的名字罢了.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Here is a picture of how all these pieces fit together:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里有一张关于上面提到的种种对象之间的关系的图,也许可以帮助你的理解:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/commits.png&#34; alt=&#34;Commits&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;16-a-commit-by-any-other-name--commit-的名字&#34; &gt;1.6 A commit by any other name&amp;hellip; | Commit 的名字
&lt;span&gt;
    &lt;a href=&#34;#16-a-commit-by-any-other-name--commit-%e7%9a%84%e5%90%8d%e5%ad%97&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Understanding commits is the key to grokking Git. You’ll know you have
reached the Zen plateau of branching wisdom when your mind contains only
commit topologies, leaving behind the confusion of branches, tags, local and
remote repositories, etc. Hopefully such understanding will not require
lopping off your arm — although I can appreciate if you’ve considered it by
now.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对 commit 的理解对于弄懂 Git 来说非常关键. 当你抛弃了那些令人困惑的什么 branch,
tag, 本地的以及远程 repository 之后, 当你的头脑中只剩下了 commit 相关的拓扑结构
的时候, 你将会对 &amp;ldquo;分支&amp;rdquo; 的智慧有崭新的理解. 想过去这种对 commit 的理解对于你来
不会是什么很困难的事情, 如果你现在就能开始试图理解 commit 我会很开心.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If commits are the key, how you name commits is the doorway to mastery. There
are many, many ways to name commits, ranges of commits, and even some of the
objects held by commits, which are accepted by most of the Git commands. Here
’s a summary of some of the more basic usages:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果说 commit 是关键, 那么如何给 commit 命名就是理解 Git 的门道所在. 实际上有很
多很多给 commit 命名的方式, 甚至连一次性给某个范围内的 commit 命名, 乃至于给一
些由 commit 管理的对象命名, 都是可以办到的. 大部分的 Git 命令都支持这种操作. 这
里有一些你需要知道的用法总结:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;branchname&lt;/strong&gt; — As has been said before, the name of any branch is simply
an alias for the most recent commit on that “branch”. This is the same as
using the word HEAD whenever that branch is checked out.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;branchname&lt;/strong&gt; — 就像之前说的那样, branch 的名字只是那个 &amp;ldquo;分支&amp;rdquo; 上最新的一个
commit 的别名. 这和你使用单词 HEAD 来表示当前 check out 的那个 branch 是一样
的.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tagname&lt;/strong&gt; — A tag-name alias is identical to a branch alias in terms of
naming a commit. The major difference between the two is that tag aliases
never change, whereas branch aliases change each time a new commit is
checked in to that branch.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tagname&lt;/strong&gt; — 从给 commit 命名的角度来看, 一个 tag 几乎和一个 branch 是一模一
样的. 主要的区别在于 branch 指代的 commit 是可能会变化的, 它会在新的 commit
提交到这个 branch 的时候被更新成新的那个, 而 tag 不会.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HEAD&lt;/strong&gt; — The currently checked out commit is always called HEAD. If you
check out a specific commit — instead of a branch name — then HEAD refers
to that commit only and not to any branch. Note that this case is somewhat
special, and is called “using a detached HEAD” (I’m sure there’s a joke
to be told here&amp;hellip;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HEAD&lt;/strong&gt; — 当前 check out 的那个 commit 被称作 HEAD. 如果你 check out 了一个
特定的 commit — 而不是一个 branch 的话 — 那么 HEAD 就只是指向了那个 commit
而已, 并没有指向任何一个 branch. 需要注意的是这是一种特殊的情况, 我们称这种情
况为 HEAD 指针的脱离.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;c82a22c39cbc32&amp;hellip;&lt;/strong&gt; — A commit may always be referenced using its
full, 40-character SHA1 hash id. Usually this happens during
cut-and-pasting, since there are typically other, more convenient ways to
refer to the same commit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;c82a22c&lt;/strong&gt; — You only need use as many digits of a hash id as are needed
for a unique reference within the repository. Most of the time, six or seven
digits is enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;c82a22c39cbc32&amp;hellip;&lt;/strong&gt; — 我们总是可以通过一个 commit 的那个由四十个字符组成的
SHA1 哈希值来找到它. 通常这种事情发生在你需要复制粘贴的时候, 因为在其他情况下,
几乎都有更方便的方式来找到它.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;c82a22c&lt;/strong&gt; — 其实你只需要给出足够在当前的 repository 中唯一确定一个 commit
的哈希值的前缀就足以找到那个 commit 了, 一般来说是 6 到 7 位左右.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name^&lt;/strong&gt; — The parent of any commit is referenced using the caret symbol.
If a commit has more than one parent, the first is used.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;name^^&lt;/strong&gt; — Carets may be applied successively. This alias refers to “the
parent of the parent” of the given commit name.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;name^2&lt;/strong&gt; — If a commit has multiple parents (such as a merge commit), you
can refer to the &lt;em&gt;nth&lt;/em&gt; parent using &lt;code&gt;name^n&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;name~10&lt;/strong&gt; — A commit’s &lt;em&gt;nth&lt;/em&gt; generation ancestor may be referenced
using a tilde (~) followed by the ordinal number. This type of usage is
common with &lt;code&gt;rebase -i&lt;/code&gt;, for example, to mean “show me a bunch of recent
commits”. This is the same as name^^^^^^^^^^.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;name:path&lt;/strong&gt; — To reference a certain file within a commit’s content
tree, specify that file’s name after a colon. This is helpful with show, or
to show the difference between two versions of a committed file:&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;接下来的几条中的 &amp;ldquo;name&amp;rdquo; 可以使用上文提到的所有可以找到 commit 的字符串替换, 以下
是一些相对某个 commit 查询另一个 commit 的方法:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name^&lt;/strong&gt; — &lt;code&gt;^&lt;/code&gt; 这个符号可以找到一个 commit 的父 commit, 如果这个 commit 有多
个父 commit 那么查询的结果是第一个&lt;sup id=&#34;fnref:7&#34;&gt;&lt;a href=&#34;#fn:7&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;7&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- end list --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name^^&lt;/strong&gt; — &lt;code&gt;^&lt;/code&gt; 这个符号是可以被连续的调用的&lt;sup id=&#34;fnref:8&#34;&gt;&lt;a href=&#34;#fn:8&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;8&lt;/a&gt;&lt;/sup&gt;, 这意味着你寻找的是当前
commit 的爷爷.&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- end list --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name^2&lt;/strong&gt; — 如果一个 commit 有多个父 commit — 比方说一个 merge commit — 那
么如果你想找其中的第 n 个父 commit, 可以使用 &lt;code&gt;name^n&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;name~10&lt;/strong&gt; — 这意味着一个 commit 十代之前的那个祖宗, 第 n 代祖先,可以通过一
个 &lt;code&gt;~&lt;/code&gt; 符号后面跟着一个数字来找到. 这一般是用于执行 &lt;code&gt;rebase -i&lt;/code&gt; 命令的时候用
的. 效果和 name^^^^^^^^^^ 是完全一致的.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;name:path&lt;/strong&gt; — 你可以用这种方式来从一个 commit 中根据路径来找到 tree 中的某
个特定文件. 一般来说这个功能在你想比较两个 commit 之间某个文件的差异的时候比
较有用, 比如像下面这样:&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- end list --&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;  $ git diff HEAD^1:Makefile HEAD^2:Makefile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name^{tree}&lt;/strong&gt; — You can reference just the tree held by a commit, rather
than the commit itself.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name^{tree}&lt;/strong&gt; — 上面说的都是找到某个 commit 的方法, 而你可以通过这个方式来
指定一个 commit 管理的那个 tree.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name1..name2&lt;/strong&gt; — This and the following aliases indicate &lt;em&gt;commit ranges&lt;/em&gt;,
which are supremely useful with commands like log for seeing what’s
happened during a particular span of time. The syntax to the left refers to
all the commits reachable from &lt;strong&gt;name2&lt;/strong&gt; back to, but not including,
&lt;strong&gt;name1&lt;/strong&gt;. If either &lt;strong&gt;name1&lt;/strong&gt; or &lt;strong&gt;name2&lt;/strong&gt; is omitted, HEAD is used in its
place.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;name1&amp;hellip;name2&lt;/strong&gt; — A “triple-dot” range is quite different from the
two-dot version above. For commands like log, it refers to all the commits
referenced by &lt;strong&gt;name1&lt;/strong&gt; or &lt;strong&gt;name2&lt;/strong&gt;, but not by both. The result is then a
list of all the unique commits in both branches. For commands like &lt;code&gt;diff&lt;/code&gt;,
the range expressed is between &lt;strong&gt;name2&lt;/strong&gt; and the common ancestor of
&lt;strong&gt;name1&lt;/strong&gt; and &lt;strong&gt;name2&lt;/strong&gt;. This differs from the &lt;code&gt;log&lt;/code&gt; case in that changes
introduced by &lt;strong&gt;name1&lt;/strong&gt; are not shown.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;master..&lt;/strong&gt; — This usage is equivalent to “&lt;code&gt;master..HEAD&lt;/code&gt;”. I’m adding
it here, even though it’s been implied above, because I use this kind of
alias constantly when reviewing changes made to the current branch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;..master&lt;/strong&gt; — This, too, is especially useful after you’ve done a &lt;code&gt;fetch&lt;/code&gt;
and you want to see what changes have occurred since your last &lt;code&gt;rebase&lt;/code&gt; or
&lt;code&gt;merge&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ndash;since=&amp;ldquo;2 weeks ago&amp;rdquo;&lt;/strong&gt; — Refers to all commits since a certain date.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ndash;until=”1 week ago”&lt;/strong&gt; — Refers to all commits before a certain date.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ndash;grep=pattern&lt;/strong&gt; — Refers to all commits whose commit message matches the
regular expression pattern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ndash;committer=pattern&lt;/strong&gt; — Refers to all commits whose committer matches the
pattern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ndash;author=pattern&lt;/strong&gt; — Refers to all commits whose author matches the
pattern. The author of a commit is the one who created the changes it
represents. For local development this is always the same as the committer,
but when patches are being sent by e-mail, the author and the committer
usually differ.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ndash;no-merges&lt;/strong&gt; — Refers to all commits in the range that have only one
parent — that is, it ignores all merge commits.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;以下都是一个范围内的 commit 的别名, 通常来说, 在调用 log 命令来查看过去的某段时
间内, 代码到底发生了什么变化的时候非常的有用:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;name1..name2&lt;/strong&gt; — 这意味着你选中了在树上遍历时, 从 name2 出发, 在遍历到
name1 之前所有可以遍历到的 commit, 当然是不包括 name1 的.如果你在 name1 或者
name2 的位置什么都没写, 那么意味着你希望在相应位置使用 HEAD 来代替
name1/name2.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;master..&lt;/strong&gt; — 这样可以选中 master 到 HEAD 的所有 commit, 一般来说, 在查看
dev 比 master 快了多少的时候比较有用.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;..master&lt;/strong&gt; — 这个通常用于: 在你完成了一次 fetch 之后, 想查看自己本地的
master 在上一次 rebase 或者是 merge 之后在远程仓库中被做了什么样的修改.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;name1&amp;hellip;name2&lt;/strong&gt; — 中间有三个点的范围和上面说的只有两个点的有很大的差异. 对
于 &lt;code&gt;log&lt;/code&gt; 这类的命令, 三个点的范围选中了所有被 name1 或者 name2 其中之一直接或
者间接引用的所有 commit, 注意: 并不包括两者同时引用的 commit. 结果上来说, 这
意味着你找到了一系这两个分支并不共同拥有的 commit. 而对于像 &lt;code&gt;diff&lt;/code&gt; 这样的指令
而言, 这样会选中从 name2 到 name1 和 name2 的最近公共祖先之间的所有 commit.
&lt;code&gt;differ&lt;/code&gt; 和 &lt;code&gt;log&lt;/code&gt; 的区别实际上在于, 从 LCA 到 name1 这条路径上的 commit 并不会
被选中.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ndash;since=&amp;ldquo;2 weeks ago&amp;rdquo;&lt;/strong&gt; — 这样可以选中某个特定日期之后的所有 commit.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ndash;until=”1 week ago”&lt;/strong&gt; — 与上一条类似, 选中某个特定日期之前的所有 commit.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ndash;grep=pattern&lt;/strong&gt; — 选中所有在 commit message 中有和 pattern 匹配的内容的
commit.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ndash;committer=pattern&lt;/strong&gt; — 选中所有提交者名字和 pattern 相匹配的 commit.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ndash;author=pattern&lt;/strong&gt; — 选中所有作者名字和 pattern 相匹配的 commit. 作者和提交
者是有区别的, 作者是指实际上对目录中的内容做出更改的那个人, 而提交者是将这个
更改加入到 repository 中的人. 对于本地仓库来说, 作者和提交者总是一样的, 但是
比如说某些更改是通过邮件发送的, 那么作者和提交者往往是不同的.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ndash;no-merges&lt;/strong&gt; — 顾名思义, 是指选中所有只有一个父 commit 的 commit, 即将所有
merge commit 排除在外.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Most of these options can be mixed-and-matched. Here is an example which shows
the following log entries: changes made to the current branch (branched from
master), by myself, within the last month, which contain the text “foo”:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这些选项通常都可以混合使用. 下面这个例子是我选中在当前分支 (这是一个比 master 更
快的分支, 比如 dev 分支) 中由我在过去一个月内写的, 并且还在 commit message 中含
有 &amp;ldquo;foo&amp;rdquo; 的所有 commit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git log --grep&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt; --author&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;johnw&amp;#39;&lt;/span&gt; --since&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 month ago&amp;#34;&lt;/span&gt; master..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;17-branching-and-the-power-of-rebase--分支-以及-rebase-的力量&#34; &gt;1.7 Branching and the power of rebase | 分支, 以及 rebase 的力量
&lt;span&gt;
    &lt;a href=&#34;#17-branching-and-the-power-of-rebase--%e5%88%86%e6%94%af-%e4%bb%a5%e5%8f%8a-rebase-%e7%9a%84%e5%8a%9b%e9%87%8f&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;One of Git’s most capable commands for manipulating commits is the
innocently-named rebase command. Basically, every branch you work from has one
or more “base commits”: the commits that branch was born from. Take the
following typical scenario, for example. Note that the arrows point back in
time because each commit references its parent(s), but not its children.
Therefore, the D and Z commits represent the heads of their respective
branches:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Git 最好用的命令之一就是操作 commit 的 rebase 命令, 顾名思义, rebase 是用来更改
commit 的 base 的. 通常来说, 你的每个分支都会有一个或者是更多个的 &amp;ldquo;base
commits&amp;rdquo;: 指你的分支是从哪个 commit 开始创建的. 以下面这张图描述的这种典型情况为
例, 我们可以注意到箭头是指向父 commit 的, 而不是指向子 commit, 因为实际上是子
commit 中含有对父 commit 的引用. 我们通常将 D 和 Z 视作它们所在分支的 &lt;code&gt;HEAD&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/branching.png&#34; alt=&#34;Branching&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In this case, running branch would show two “heads”: &lt;code&gt;D&lt;/code&gt; and &lt;code&gt;Z&lt;/code&gt;, with the
common parent of both branches being A. The output of show-branch shows us
just this information:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在如上图所示的这个情况中, 我们可以看到这些分支一共有两个 &amp;ldquo;&lt;code&gt;HEAD&lt;/code&gt;&amp;quot;: &lt;code&gt;D&lt;/code&gt; 和 &lt;code&gt;Z&lt;/code&gt;,
它们的公共祖先是 &lt;code&gt;A&lt;/code&gt;. &lt;code&gt;show-branch&lt;/code&gt; 指令的输出结果向我们展示了这一点:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git branch
  Z
* D

$ git show-branch
! &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Z&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Z
 * &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;D&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; D
--
 * &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;D&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; D
 * &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;D^&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; C
 * &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;D~2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; B
+  &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Z&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;Z
+  &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Z^&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;Y
+  &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Z~2&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; X
+  &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;Z~3&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; W
+* &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;D~3&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; A
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Reading this output takes a little getting used to, but essentially it’s no
different from the diagram above. Here’s what it tells us:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;阅读这个输出需要我们花一点时间来习惯它, 但是它实际上给出的信息和上面那张示意图没
有什么区别. 以下是这个输出的阅读方法&lt;sup id=&#34;fnref:9&#34;&gt;&lt;a href=&#34;#fn:9&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;9&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The branch we’re on experienced its first divergence at commit &lt;code&gt;A&lt;/code&gt; (also
known as commit &lt;code&gt;D~3&lt;/code&gt;, and even &lt;code&gt;Z~4&lt;/code&gt; if you feel so inclined). The syntax
&lt;code&gt;commit^&lt;/code&gt; is used to refer to the parent of a commit, while &lt;code&gt;commit~3&lt;/code&gt;
refers to its third parent, or great-grandparent.&lt;/li&gt;
&lt;li&gt;Reading from bottom to top, the first column (the plus signs) shows a
divergent branch named &lt;code&gt;Z&lt;/code&gt; with four commits: &lt;code&gt;W&lt;/code&gt;, &lt;code&gt;X&lt;/code&gt;, &lt;code&gt;Y&lt;/code&gt; and &lt;code&gt;Z&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The second column (the asterisks) show the commits which happened on the
current branch, namely three commits: &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;C&lt;/code&gt; and &lt;code&gt;D&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The top of the output, separated from the bottom by a dividing line,
identifies the branches displayed, which column their commits are labelled
by, and the character used for the labeling.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;我们目前在 repository 中拥有的两个分支是从 &lt;code&gt;A&lt;/code&gt; 开始分支的.&lt;/li&gt;
&lt;li&gt;从下往上读,第一列字符 (一列 &lt;code&gt;+&lt;/code&gt;) 告诉我们, 分支 &lt;code&gt;Z&lt;/code&gt; 在分叉后拥有的 commit 依次
是: &lt;code&gt;W&lt;/code&gt;, &lt;code&gt;X&lt;/code&gt;, &lt;code&gt;Y&lt;/code&gt; 还有 &lt;code&gt;Z&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;同样, 第二列字符 (一列 &lt;code&gt;*&lt;/code&gt;) 告诉我们, 分支 &lt;code&gt;D&lt;/code&gt; 在分叉后拥有的 commit 依次是:
&lt;code&gt;B&lt;/code&gt;, &lt;code&gt;C&lt;/code&gt; 还有 &lt;code&gt;D&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;在整个输出的最上面有一些被 &lt;code&gt;--&lt;/code&gt; 分开的部分, 这里是在告诉我们分支的 &lt;code&gt;HEAD&lt;/code&gt; 是
谁, 以及下面的一系列输出中, 开头的第几列是和这个分支对应的, 使用 &lt;code&gt;*&lt;/code&gt; 标注的分
支是当前 checkout 的, 其他的分支 &lt;code&gt;HEAD&lt;/code&gt; 使用 &lt;code&gt;!&lt;/code&gt;, 而在接下来的部分中使用 &lt;code&gt;+&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The action we’d like to perform is to bring the working branch &lt;code&gt;Z&lt;/code&gt; back up to
speed with the main branch, &lt;code&gt;D&lt;/code&gt;. In other words, we want to incorporate the
work from &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;C&lt;/code&gt;, and &lt;code&gt;D&lt;/code&gt; into &lt;code&gt;Z&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们接下来想做的事情是把分支 &lt;code&gt;Z&lt;/code&gt; 并入主分支 &lt;code&gt;D&lt;/code&gt;. 换句话说, 我们希望把&lt;code&gt;B&lt;/code&gt;, &lt;code&gt;C&lt;/code&gt; 以
及 &lt;code&gt;D&lt;/code&gt; 做出的更改也写进 &lt;code&gt;Z&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In other version control systems this sort of thing can only be done using a
“branch merge”. In fact, a branch merge can still be done in Git, using
&lt;code&gt;merge&lt;/code&gt;, and remains needful in the case where &lt;code&gt;Z&lt;/code&gt; is a published branch and
we don’t want to alter its commit history. Here are the commands to run:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在其他的版本控制系统中, 这是一件只能通过 &amp;ldquo;分支合并&amp;rdquo; 操作完成的事情. 而实际上所谓
的分支合并在 Git 中也是可以被完成的, 只要使用 &lt;code&gt;merge&lt;/code&gt; 命令就可以了, 如果我们不
想更改 &lt;code&gt;Z&lt;/code&gt; 的 commit 历史, 那么我们可以运行这些命令:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git checkout Z &lt;span style=&#34;color:#75715e&#34;&gt;# switch to the Z branch&lt;/span&gt;
$ git merge D &lt;span style=&#34;color:#75715e&#34;&gt;# merge commits B, C and D into Z&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This is what the repository looks like afterward:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;运行完了以后你的 repository 看起来会像是下面图中的这样:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/branch-merge.png&#34; alt=&#34;BranchMerge&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If we checked out the &lt;code&gt;Z&lt;/code&gt; branch now, it would contain the contents of the
previous &lt;code&gt;Z&lt;/code&gt; (now referenceable as &lt;code&gt;Z^&lt;/code&gt;), merged with the contents of &lt;code&gt;D&lt;/code&gt;.
(Though note: a real merge operation would have required resolving any
conflicts between the states of &lt;code&gt;D&lt;/code&gt; and &lt;code&gt;Z&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果我现在检出分支 &lt;code&gt;Z&lt;/code&gt;, 注意 &amp;ldquo;分支 &lt;code&gt;Z&lt;/code&gt;&amp;rdquo; 只是一个 commit 的别名, 它曾经指 commit
Z, 而现在它是图中的 &lt;code&gt;Z&#39;&lt;/code&gt;, 分支 &lt;code&gt;Z&lt;/code&gt; (现在指代 commit &lt;code&gt;Z&lt;/code&gt;) 现在会包含 commit &lt;code&gt;Z&lt;/code&gt;
和 commit &lt;code&gt;D&lt;/code&gt; 合并过后的内容. 当然, 要进行一个 &lt;code&gt;merge&lt;/code&gt; 操作需要先处理所有 &lt;code&gt;D&lt;/code&gt;
和 &lt;code&gt;Z&lt;/code&gt; 之间的冲突.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Although the new &lt;code&gt;Z&lt;/code&gt; now contains the changes from &lt;code&gt;D&lt;/code&gt;, it also includes a new
commit to represent the merging of &lt;code&gt;Z&lt;/code&gt; with &lt;code&gt;D&lt;/code&gt;: the commit now shown as &lt;code&gt;Z ’&lt;/code&gt;. This commit doesn’t add anything new, but represents the work done to
bring &lt;code&gt;D&lt;/code&gt; and &lt;code&gt;Z&lt;/code&gt; together. In a sense it’s a “meta-commit”, because its
contents are related to work done solely in the repository, and not to new
work done in the working tree.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在新的 &lt;code&gt;Z&lt;/code&gt; 现在包含了 &lt;code&gt;D&lt;/code&gt; 中做出的更改的同时, 在新 &lt;code&gt;Z&lt;/code&gt; 中同样存在着一个新的
commit, 这个 commit 是用来合并 &lt;code&gt;Z&lt;/code&gt; 和 &lt;code&gt;D &lt;/code&gt;的: 就是上图中的 &lt;code&gt;Z&#39;&lt;/code&gt;. 这个 commit
很可能没有添加任何新的更改, 只是意味着做了一些工作将 &lt;code&gt;D&lt;/code&gt; 和 &lt;code&gt;Z&lt;/code&gt; 合并到了一起.
某种意义上来说, 这算是一种 &amp;ldquo;meta-commit&amp;rdquo;, 因为它的内容只是对 repository 的更改,
而不是对 working tree 的更改.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is a way, however, to transplant the &lt;code&gt;Z&lt;/code&gt; branch straight onto &lt;code&gt;D&lt;/code&gt;,
effectively moving it forward in time: by using the powerful rebase command.
Here’s the graph we’re aiming for:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实际上还有一种办法能直接将分支 &lt;code&gt;Z&lt;/code&gt; 移植到 &lt;code&gt;D&lt;/code&gt; 上, 通过 &lt;code&gt;rebase&lt;/code&gt; 命令, 可以直接把
&lt;code&gt;D&lt;/code&gt; 快进. 如下图所示:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/rebase.png&#34; alt=&#34;Rebase&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This state of affairs most directly represents what we’d like done: for our
local, development branch &lt;code&gt;Z&lt;/code&gt; to be based on the latest work in the main
branch &lt;code&gt;D&lt;/code&gt;. That’s why the command is called “rebase”, because it changes
the base commit of the branch it’s run from. If you run it repeatedly, you
can carry forward a set of patches indefinitely, always staying up-to-date
with the main branch, but without adding unnecessary merge commits to your
development branch. Here are the commands to run, compared to the merge
operation performed above:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们可以这样来描述我们实际上想干啥: 我们直接把我们在本地进行开发的分支 &lt;code&gt;Z&lt;/code&gt; 的
&amp;ldquo;base commit&amp;rdquo; 改成了 &lt;code&gt;D&lt;/code&gt;. 这正是上问提到的 &lt;code&gt;rebase&lt;/code&gt; 命令为什么会被称作 &amp;ldquo;rebase&amp;rdquo;.
如果你不停地运行这个命令, 就可以在没有额外的 merge commit 的情况下更新主分支的数
据. 以下是运行 &lt;code&gt;rebase&lt;/code&gt; 需要的指令:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git checkout Z &lt;span style=&#34;color:#75715e&#34;&gt;# switch to the Z branch&lt;/span&gt;
$ git rebase D &lt;span style=&#34;color:#75715e&#34;&gt;# change Z’s base commit to point to D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Why is this only for local branches? Because every time you rebase, you’re
potentially changing every commit in the branch. Earlier, when &lt;code&gt;W&lt;/code&gt; was based
on &lt;code&gt; A&lt;/code&gt;, it contained only the changes needed to transform &lt;code&gt;A&lt;/code&gt; into &lt;code&gt;W&lt;/code&gt;.
After running rebase, however, &lt;code&gt;W&lt;/code&gt; will be rewritten to contain the changes
necessary to transform &lt;code&gt;D&lt;/code&gt; into &lt;code&gt;W’&lt;/code&gt;. Even the transformation from &lt;code&gt;W&lt;/code&gt; to &lt;code&gt;X&lt;/code&gt;
is changed, because &lt;code&gt;A+W+X&lt;/code&gt; is now &lt;code&gt;D+W’+X’&lt;/code&gt; — and so on. If this were a
branch whose changes are seen by other people, and any of your downstream
consumers had created their own local branches off of &lt;code&gt;Z&lt;/code&gt;, their branches
would now point to the old &lt;code&gt;Z&lt;/code&gt;, not the new &lt;code&gt;Z’&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为什么我们说这种操作只能对本地分支进行呢? 这是因为每当运行 &lt;code&gt;rebase&lt;/code&gt; 命令的时候,
我们实际上将分支中的每一个 commit 都进行了更改. 之前的 &lt;code&gt;W&lt;/code&gt; 是基于 &lt;code&gt;A&lt;/code&gt; 做出的修
改, 如果我们认为一个 commit 中只包含更改的信息的话&lt;sup id=&#34;fnref:10&#34;&gt;&lt;a href=&#34;#fn:10&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;10&lt;/a&gt;&lt;/sup&gt;, 那么 &lt;code&gt;W&lt;/code&gt; 实际上只含
有 &amp;ldquo;从 &lt;code&gt;A&lt;/code&gt; 变成 &lt;code&gt;W&lt;/code&gt;&amp;rdquo; 做出的变化. 但是运行完 &lt;code&gt;rebase&lt;/code&gt; 之后, &lt;code&gt;W&lt;/code&gt; 实际上就包含了从
&lt;code&gt;D&lt;/code&gt; 变成 &lt;code&gt;W&#39;&lt;/code&gt; 产生的变化. 以及 &lt;code&gt;W&lt;/code&gt; 和 &lt;code&gt;X&lt;/code&gt; 之间的更改都产生了改变, 因为原来的
&lt;code&gt;A+W+X&lt;/code&gt; 现在是 &lt;code&gt;D+W&#39;+X&#39;&lt;/code&gt; — 其他的 commit 也有类似的变化. 如果这样的一个分支中的
更改对于其他人来说是可见的, 以及从你的 repository 中获得源代码的下游人员中有人
用原来的 &lt;code&gt;Z&lt;/code&gt; 创建了新的分支, 那么他们的 &lt;code&gt;Z&lt;/code&gt; 并不会指向 &lt;code&gt;rebase&lt;/code&gt; 后的那个新 &lt;code&gt;Z&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Generally, the following rule of thumb can be used: Use rebase if you have a
local branch with no other branches that have branched off from it, and use
merge for all other cases. merge is also useful when you’re ready to pull
your local branch’s changes back into the main branch.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通常来说, 以下的经验法则是很有用的: 只要一个本地分支上没有再分出其他的分支, 那么
我们就使用 &lt;code&gt;rebase&lt;/code&gt; 命令来合并分支, 其他的情况一律使用 &lt;code&gt;merge&lt;/code&gt;. &lt;code&gt;merge&lt;/code&gt; 这个命
令在你想把你的本地分支合并进主分支的时候很有用.&lt;/p&gt;
&lt;h3 id=&#34;18-interactive-rebasing--交互式的-rebase-命令&#34; &gt;1.8 Interactive rebasing | 交互式的 rebase 命令
&lt;span&gt;
    &lt;a href=&#34;#18-interactive-rebasing--%e4%ba%a4%e4%ba%92%e5%bc%8f%e7%9a%84-rebase-%e5%91%bd%e4%bb%a4&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;When rebase was run above, it automatically rewrote all the commits from &lt;code&gt;W&lt;/code&gt;
to &lt;code&gt;Z&lt;/code&gt; in order to rebase the &lt;code&gt;Z&lt;/code&gt; branch onto the &lt;code&gt;D&lt;/code&gt; commit (i.e., the head
commit of the &lt;code&gt;D&lt;/code&gt; branch). You can, however, take complete control over how
this rewriting is done. If you supply the &lt;code&gt;-i&lt;/code&gt; option to &lt;code&gt;rebase&lt;/code&gt;, it will pop
you into an editing buffer where you can choose what should be done for every
commit in the local &lt;code&gt;Z&lt;/code&gt; branch:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;就像上面说的那样, 当你运行一个 rebase 命令的时候, 它将为了将 &lt;code&gt;Z&lt;/code&gt; 分支的 base 更
改到分支 &lt;code&gt;D&lt;/code&gt; 的 &lt;code&gt;HEAD&lt;/code&gt; 上, 而自动的重写从 &lt;code&gt;W&lt;/code&gt; 到 &lt;code&gt;Z&lt;/code&gt; 的所有 commit. 其实你是完
全可以控制这个重写的过程的. 给 &lt;code&gt;rebase&lt;/code&gt; 传递一个 &lt;code&gt;-i&lt;/code&gt; 参数, 那么你将进入一个可
编辑的缓冲区, 在这个界面里, 你可以选择到底应该对 &lt;code&gt;Z&lt;/code&gt; 分支上的每个 commit 做些什
么:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pick&lt;/strong&gt; — This is the default behavior chosen for every commit in the
branch if you don’t use interactive mode. It means that the commit in
question should be applied to its (now rewritten) parent commit. For every
commit that involves conflicts, the &lt;code&gt;rebase&lt;/code&gt; command gives you an
opportunity to resolve them.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pick&lt;/strong&gt; — 这是如果你不使用交互模式的时候会执行的默认操作, 它意味着你选中了这
个分支中的每一个 commit, 也就是说: 每一个分支上的 commit 都将更改自己的父
commit 到它被重写过的父 commit 上. 对于每个可能产生冲突的 commit, 你会有机会解
决它们.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;squash&lt;/strong&gt; — A squashed commit will have its contents “folded” into the
contents of the commit preceding it. This can be done any number of times.
If you took the example branch above and squashed all of its commits (except
the first, which must be a &lt;strong&gt;pick&lt;/strong&gt; in order to &lt;strong&gt;squash&lt;/strong&gt;), you would end
up with a new &lt;code&gt;Z&lt;/code&gt; branch containing only one commit on top of &lt;code&gt;D&lt;/code&gt;. Useful if
you have changes spread over multiple commits, but you’d like the history
rewritten to show them all as a single commit.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;squash&lt;/strong&gt; — 一个被 &amp;ldquo;压扁&amp;rdquo; 的 commit 将会将它之中的更改 &amp;ldquo;折叠&amp;rdquo; 进它的新父
commit 里. 这个操作可以不限次数地进行. 如果你对上面说的那个例子, 将除了第一个
commit 以外 (为了进行 &lt;strong&gt;squash&lt;/strong&gt; 需要先进行一个 &lt;strong&gt;pick&lt;/strong&gt;) 的所有 commit &amp;ldquo;压
扁&amp;rdquo;, 那么你将会将分支 &lt;code&gt;D&lt;/code&gt; 中的所有更改折叠成一个 commit, 然后将这个 commit 应
用在分支 &lt;code&gt;Z&lt;/code&gt; 上. 如果你不想保留更改历史, 只想留下一个 commit 的话, 这个模式还
挺好用的.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;edit&lt;/strong&gt; — If you mark a commit as &lt;strong&gt;edit&lt;/strong&gt;, the rebasing process will stop
at that commit and leave you at the shell with the current working tree set to
reflect that commit. The index will have all the commit’s changes registered
for inclusion when you run &lt;code&gt;commit&lt;/code&gt;. You can thus make whatever changes you
like: amend a change, undo a change, etc.; and after committing, and running
&lt;code&gt;rebase --continue&lt;/code&gt;, the commit will be rewritten as if those changes had been
made originally.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;edit&lt;/strong&gt; — 如果你将一个 commit 标记为了 &lt;strong&gt;edit&lt;/strong&gt; 模式, 那么这个 rebase 命令将
在这个 commit 这里停下来, 然后让你在你的 working tree 中编辑文件, 你可以重做
一次这个 commit 做过的修改. 随后你运行 &lt;code&gt;commit&lt;/code&gt; 命令的时候, the index 中将会
含有所有已经提交的更改. 你可能会在类似以下的情况下使用这个模式: 添加一个更改
到这个 commit 中, 或者说撤销一个更改. 在你提交 commit 之后, 运行 &lt;code&gt;rebase --continue&lt;/code&gt;, 那个被标记为 edit 的 commit 将会被你新编辑的这个 commit 代替.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;(drop)&lt;/strong&gt; — If you remove a commit from the interactive rebase file, or if
you comment it out, the commit will simply disappear as if it had never been
checked in. Note that this can cause merge conflicts if any of the later
commits in the branch depended on those changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;(drop)&lt;/strong&gt; — 如果你打算删除一个 commit. 这个 commit 将会像是从来没有被记录过
一样不复存在. 注意:如果后续的其他 commit 是基于这个 commit 的更改的, 那么这个
操作很可能引起合并冲突.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;The power of this command is hard to appreciate at first, but it grants you
virtually unlimited control over the shape of any branch. You can use it to:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;交互式的 rebase 命令的用处很难一下子说清, 但是这个命令让你有了几乎不受限制的能
力, 来对任何分支做出任何形式的调整. 你实际上可以用这个命令来:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Collapse multiple commits into single ones.&lt;/li&gt;
&lt;li&gt;Re-order commits.&lt;/li&gt;
&lt;li&gt;Remove incorrect changes you now regret.&lt;/li&gt;
&lt;li&gt;Move the base of your branch &lt;em&gt;onto any other commit in the repository&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Modify a single commit, to amend a change long after the fact.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;将多个 commit 折叠成一个.&lt;/li&gt;
&lt;li&gt;重新调整 commit 的顺序.&lt;/li&gt;
&lt;li&gt;将不正确的更改删除.&lt;/li&gt;
&lt;li&gt;将你的分支的 base 移动到你的 repository 中的任何一个 commit 上.&lt;/li&gt;
&lt;li&gt;更改一个单独的 commit, 比如说在实际上这个 commit 已经被提交很久之后, 往里面添
加更改.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;I recommend reading the man page for &lt;code&gt;rebase&lt;/code&gt; at this point, as it contains
several good examples how the true power of this beast may be unleashed. To
give you one last taste of how potent a tool this is, consider the following
scenario and what you’d do if one day you wanted to migrate the secondary
branch &lt;code&gt;L&lt;/code&gt; to become the new head of &lt;code&gt;Z&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我在这里十分建议读者阅读一下 &lt;code&gt;rebase&lt;/code&gt; 的帮助页面. 其中有几个可以帮助你理解这个强
大命令的能力到底应该被如果使用的优秀例子. 为了让读者至少对这个工具到底有多屌有
一点点的认识, 我们来看一下下面这个例子:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/rebasing-branches-1.png&#34; alt=&#34;Rebasing Multiple BranchesPart 1&#34;&gt;&lt;/p&gt;
&lt;p&gt;如果你打算将分支 &lt;code&gt;L&lt;/code&gt; 迁移到分支 &lt;code&gt;Z&lt;/code&gt; 上, 并且使得 commit &lt;code&gt;L&lt;/code&gt; 是分支 &lt;code&gt;Z&lt;/code&gt; 的新
&lt;code&gt;HEAD&lt;/code&gt;, 你会怎么做呢?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The picture reads: we have our main-line of development, &lt;code&gt;D&lt;/code&gt;, which three
commits ago was branched to begin speculative development on &lt;code&gt;Z&lt;/code&gt;. At some
point in the middle of all this, back when &lt;code&gt;C&lt;/code&gt; and &lt;code&gt;X&lt;/code&gt; were the heads of their
respective branches, we decided to begin another speculation which finally
produced &lt;code&gt;L&lt;/code&gt;. Now we’ve found that &lt;code&gt;L&lt;/code&gt;’s code is good, but not quite good
enough to merge back over to the main-line, so we decide to move those changes
over to the development branch &lt;code&gt;Z&lt;/code&gt;, making it look as though we’d done them
all on one branch after all. Oh, and while we’re at it, we want to edit &lt;code&gt;J&lt;/code&gt;
real quick to change the copyright date, since we forgot it was 2008 when we
made the change! Here are the commands needed to untangle this knot:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个图实际上在说: 我们主要的开发发生在分支 &lt;code&gt;D&lt;/code&gt; 上, 而这个分支 &lt;code&gt;D&lt;/code&gt; 在三个 commit
之前, 分支出了一个 &lt;code&gt;Z&lt;/code&gt; 来进行试探性质的开发. 在 &lt;code&gt;C&lt;/code&gt; 和 &lt;code&gt;X&lt;/code&gt; 还是分支的 &lt;code&gt;HEAD&lt;/code&gt; 的
时候, 有人尝试合并了两个分支, 进行了新一轮的试探性开发, 最终产出了分支 &lt;code&gt;L&lt;/code&gt;. 那
么现在我们知道了 commit &lt;code&gt;L&lt;/code&gt; 的代码是很优秀的, 但是还没有优秀到足以合并回主分支
的程度, 我们为了进一步改进这个代码, 希望把 &lt;code&gt;L&lt;/code&gt; 上的更改移动到分支 &lt;code&gt;Z&lt;/code&gt; 上, 然后
调整得足够好了以后, 再合并回主分支. 顺便, 我们在做这一切的同时, 还希望编辑一下
commit &lt;code&gt;J&lt;/code&gt; 来简单的变更一下版权信息的日期, 因为当初提交 commit &lt;code&gt;J&lt;/code&gt; 的时候忘了改
它. 那么以下是解决以上这些麻烦的问题所需要的命令:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git checkout L
$ git rebase -i Z
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;After resolving whatever conflicts emerge, I now have this repository:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;处理完冲突以后, 我的 repository 看起来像下图这样:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/rebasing-branches-2.png&#34; alt=&#34;Rebasing Multiple BranchesPart 2&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As you can see, when it comes to local development, rebasing gives you
unlimited control over how your commits appear in the repository.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;就像你看到的那样, 如果只是本地开发, rebase 让你几乎有了对 commit 完全的控制能力,
你想让他们是什么样子就可以是什么样子.&lt;/p&gt;
&lt;h2 id=&#34;2-the-index&#34; &gt;2 The Index
&lt;span&gt;
    &lt;a href=&#34;#2-the-index&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;21-the-index-meet-the-middle-man--the-index--中间人&#34; &gt;2.1 The Index: Meet the middle man | The Index : 中间人
&lt;span&gt;
    &lt;a href=&#34;#21-the-index-meet-the-middle-man--the-index--%e4%b8%ad%e9%97%b4%e4%ba%ba&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Between your data files, which are stored on the filesystem, and your Git
blobs, which are stored in the repository, there stands a somewhat strange
entity: the Git index. Part of what makes this beast hard to understand is
that it’s got a rather unfortunate name. It’s an index in the sense that it
refers to the set of newly created trees and blobs which you created by
running add. These new objects will soon get bound into a new tree for the
purpose of committing to your repository — but until then, they are only
referenced by the index. That means that if you unregister a change from the
index with reset, you’ll end up with an orphaned blob that will get deleted
at some point at the future.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在你存储在文件系统中的数据文件, 与 Git 中存储的 blob 之间还存在着一个奇怪的媒介,
我们称之为 the Git index. 这个令人困惑的名字使得它很难被人理解. 从某种意义上来
讲, 它确实是一种索引: 它引用一个由新添加的 tree&lt;sup id=&#34;fnref:11&#34;&gt;&lt;a href=&#34;#fn:11&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;11&lt;/a&gt;&lt;/sup&gt; 和 blob 组成的集合, 这个
集合是用户通过运行 add 命令创建的. 在这些对象真正被添加到一个 tree, 并且最终成
为一个 commit 加入到你的 repository 中之前, 这些新的对象, 它们仅仅只被 the
index 引用而已. 这意味着如果你通过 reset 命令将一个被记录到 the index 中的更改撤
销掉的话, 那么你原本新创建的 blob 会成为没有人引用的孤儿, 这种 blob 在未来的某
个时间点会被删除.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The index is really just a staging area for your next commit, and there’s a
good reason why it exists: it supports a model of development that may be
foreign to users of CVS or Subversion, but which is all too familiar to Darcs
users: the ability to build up your next commit in stages.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The index 实际上起到的作用只是一个为你的下一个 commit 而设立的暂存区罢了. 它使得
你有了在一个暂存区里搭建你的下一个 commit 的能力, 这种开发方式对于 Darcs 的用户
来说相对熟悉, 而对于 CVS 或者是 Subversion 的用户来说这可能会很陌生.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/the-index.png&#34; alt=&#34;TheIndex&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;First, let me say that there is a way to ignore the index almost entirely: by
passing the &lt;code&gt;-a&lt;/code&gt; flag to commit. Look at the way Subversion works, for
example. When you type &lt;code&gt;svn status&lt;/code&gt;, what you’ll see is a list of actions to
be applied to your repository on the next call to &lt;code&gt;svn commit&lt;/code&gt;. In a way, this
“list of next actions” is a kind of informal index, determined by comparing
the state of your working tree with the state of HEAD. If the file &lt;code&gt;foo.c&lt;/code&gt; has
been changed, on your next commit those changes will be saved. If an unknown
file has a question mark next to it, it will be ignored; but a new file which
has been added with &lt;code&gt;svn add&lt;/code&gt; will get added to the repository.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;首先, 实际上是有一种办法能几乎忽略掉 the index 的方法的: 只要在使用 commit 命令
的时候传递 &lt;code&gt;-a&lt;/code&gt; 这个参数, 就可以了. 我们以 Subversion 的工作方式为例. 我们输入
&lt;code&gt;svn status&lt;/code&gt;, 这个命令的输出是一个关于你的下一个 &lt;code&gt;svn commit&lt;/code&gt; 命令将对于你的
repository 做出怎样的更改的列表. 在这个例子中, 那个列表可以看作是 Git 中的 the
index, 毕竟这个通过比较你当前 working tree 和 HEAD 之间的状态差异而得到的列表,
从某种程度上来说是一种信息的索引. 比方说如果文件 &lt;code&gt;foo.c&lt;/code&gt; 被更改了, 那么就会出现
在这个列表中, 这意味着你的下一个 commit 会将这个更改写入你的 repository 中. 如
果一个文件的后面有一个 &lt;code&gt;?&lt;/code&gt;, 那么意味着这个文件将被版本管理系统忽略; 但是如果通
过 &lt;code&gt;snv add&lt;/code&gt; 将文件添加进管理, 那么这个文件将被加入到 repository 中.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is no different from what happens if you use &lt;code&gt;commit -a&lt;/code&gt;: new, unknown
files are ignored, but new files which have been added with add are added to
the repository, as are any changes to existing files. This interaction is
nearly identical with the Subversion way of doing things.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实际上当你在 Git 中使用 &lt;code&gt;commit -a&lt;/code&gt; 命令的时候, 发生的事情几乎和上面描述的一致:
新建的文件需要使用 add 手动添加, 否则就像被忽略的文件一样, 并不会被添加进
repository. 而以前添加过的文件中的更改则会被加入 repository. 这个交互方式几乎和
Subversion 没有什么区别.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The real difference is that in the Subversion case, your “list of next
actions” is always determined by looking at the current working tree. In Git,
the “list of next actions” &lt;em&gt;is&lt;/em&gt; the contents of the index, which represents
what will become the next state of HEAD, and that you can manipulate directly
before executing &lt;code&gt;commit&lt;/code&gt;. This gives you an extra layer of control over what
’s going to happen, by allowing you to stage those changes in advance.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这两者之间的区别实际上在于, 在 Subversion 中, 那个列表总是通过检查你的当前工作区
来列出的. 而在 Git 中, 这个列表的功能, 由 the index 中记录的内容来承担. 这个
the index 中指示着 HEAD 这个 commit 接下来会是什么样的状态, 而你可以在 commit 之
前直接对 the index 做出调整. 这实际上让你对更改的控制能力变得更强了.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If this isn’t clear yet, consider the following example: you have a trusty
source file, &lt;code&gt;foo.c&lt;/code&gt;, and you’ve made two sets of unrelated changes to it.
What you’d like to do is to tease apart these changes into two different
commits, each with its own description. Here’s how you’d do this in
Subversion:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果上面这种说法还不够明确. 那么我们可以考虑一下下面这个例子: 你有一个可以信任的
源文件, 它是 &lt;code&gt;foo.c&lt;/code&gt;, 你还有对他做出了两组没什么关联的更改. 你现在想做的事情是
将这两个更改分成两个不同的 commit 进行提交, 每个 commit 都有它自己的说明. 那么
你在 Subversion 中应该这么做:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ svn diff foo.c &amp;gt; foo.patch
$ vi foo.patch
&amp;lt;edit foo.patch, keeping the changes I want to commit later&amp;gt;
$ patch -p1 -R &amp;lt; foo.patch  &lt;span style=&#34;color:#75715e&#34;&gt;# remove the second set of changes&lt;/span&gt;
$ svn commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;First commit message&amp;#34;&lt;/span&gt;
$ patch -p1 &amp;lt; foo.patch  &lt;span style=&#34;color:#75715e&#34;&gt;# re-apply the remaining changes&lt;/span&gt;
$ svn commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Second commit message&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Sounds like fun? Now repeat that many times over for a complex, dynamic set of
changes. Here’s the Git version, making use of the index:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;听起来不错? 但是如果你如果用对付的是更复杂的情况呢? 下面是 Git 中的做法, 让我们
利用 the index:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git add --patch foo.c
&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; the hunks I want to commit first&amp;gt;
$ git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;First commit message&amp;#34;&lt;/span&gt;
$ git add foo.c  &lt;span style=&#34;color:#75715e&#34;&gt;# add the remaining changes&lt;/span&gt;
$ git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Second commit message&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;What’s more, it gets even easier! If you like Emacs, the superlative tool
&lt;code&gt;gitsum.el&lt;/code&gt;, by Christian Neukirchan, puts a beautiful face on this
potentially tedious process. I recently used it to tease apart 11 separate
commits from a set of conflated changes. Thank&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这是不是简单多了? 如果你喜欢 Emacs, 那么还有个工具叫 &lt;code&gt;gitsum.el&lt;/code&gt;, 这个工具是由
Christian Neukirchan 开发的, 给这个可能看起来单调乏味的过程添加了一个漂亮的界面.
我最近实际上用它将一个更改拆分成 11 个独立的 commit 过. 真是谢谢 TA 了.&lt;/p&gt;
&lt;h3 id=&#34;22-taking-the-index-further--进一步了解-the-index&#34; &gt;2.2 Taking the index further | 进一步了解 the index
&lt;span&gt;
    &lt;a href=&#34;#22-taking-the-index-further--%e8%bf%9b%e4%b8%80%e6%ad%a5%e4%ba%86%e8%a7%a3-the-index&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Let’s see, the index&amp;hellip; With it you can pre-stage a set of changes, thus
iteratively building up a patch before committing it to the repository. Now,
where have I heard that concept before&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根据上面所说的,我们可以如此来描述 the index 的功能: 通过它, 我们可以将一系列更改
暂存下来, 还可以在更改提交到 repository 之前, 将一次对分拣的更改分成若干的
patch. 我好像曾经在什么地方听到过这个&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you’re thinking “Quilt!”, you’re exactly right. In fact, the index is
little different from Quilt, it just adds the restriction of allowing only one
patch to be constructed at a time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;你很可能会想到一个叫 &amp;ldquo;&lt;a href=&#34;https://en.wikipedia.org/wiki/Quilt_(software)&#34;&gt;Quilt&lt;/a&gt;&amp;rdquo;
的软件. 事实上, the index 和 Quilt 有一点点不同, Quilt 实际上只是添加了一个 &amp;ldquo;一
次只允许对代码做出一个 patch 的更改&amp;rdquo; 的限定而已&lt;sup id=&#34;fnref:12&#34;&gt;&lt;a href=&#34;#fn:12&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;12&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But what if, instead of two sets of changes within &lt;code&gt;foo.c&lt;/code&gt;, I had four? With
plain Git, I’d have to tease each one out, commit it, and then tease out the
next. This is made much easier using the index, but what if I wanted to test
those changes in various combinations with each other before checking them in?
That is, if I labelled the patches A, B, C and D, what if I wanted to test A +
B, then A + C, then A + D, etc., before deciding if any of the changes were
truly complete?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是如果我在文件 &lt;code&gt;foo.c&lt;/code&gt; 中实际上一共有四组的更改呢? 在原生的 Git 中, 我实际上需
要一个一个的将属于其中一个 patch 的更改挑选出来, commit, 然后再回去选下一组的.
The index 实际上已经大大简化了这个过程, 但是如果我想在提交这些更改前, 对这些更改
的多种组合分别做测试呢? 比方说我有四个 patch, 它们分别是 A, B, C, D. 如果我想以
这种顺序进行测试: A + B, 然后 A + C, 随后 A + D, 以此类推. 那么我应该怎么做呢?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is no mechanism in Git itself that allows you to mix and match parallel
sets of changes on the fly. Sure, multiple branches can let you do parallel
development, and the index lets you stage multiple changes into a series of
commits, but you can’t do both at once: staging a series of patches while at
the same time selectively enabling and disabling some of them, to verify the
integrity of the patches in concert before finally committing them. What you
’d need in order to do something like this would be an index which allows
for greater depth than one commit at a time. This is exactly what Stacked Git
provides.&lt;/p&gt;
&lt;p&gt;Here’s how I’d commit two different patches into my working tree using plain
Git:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 Git 中并没有这样的功能. 多分支确实可以让你完成平行开发, the index 也确实可以
让你将更改拆成若干个 commit, 但是实际上你并不能同时做这两个事情: 你不能暂存下一
系列 commit 中的更改, 然后去选择其中一些出于启用/关闭的状态, 以此来在提交前验证
更改的正确性.&lt;/p&gt;
&lt;p&gt;你实际上想做的事情是希望你的 the index 中, 可以存下多个 commit&lt;sup id=&#34;fnref:13&#34;&gt;&lt;a href=&#34;#fn:13&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;13&lt;/a&gt;&lt;/sup&gt;, 这个功能
实际上可以由 Stacked Git 来提供的.&lt;/p&gt;
&lt;p&gt;以下是我 commit 两个不同的更改组到我的 working tree 的过程:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git add -i &lt;span style=&#34;color:#75715e&#34;&gt;# select first set of changes&lt;/span&gt;
$ git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;First commit message&amp;#34;&lt;/span&gt;
$ git add -i &lt;span style=&#34;color:#75715e&#34;&gt;# select second set of changes&lt;/span&gt;
$ git commit -m &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Second commit message&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This works great, but I can’t selectively disable the first commit in order
to test the second one alone. To do that, I’d have to do the following:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这样实际上还不错, 但是我无法在做完这一切之后, 再将第一个 commit 暂时禁用, 来单独
测试第二个 commit. 如果我想做这个事情, 那么我接下来还得做这么一个事情:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git log &lt;span style=&#34;color:#75715e&#34;&gt;# find the hash id of the first commit&lt;/span&gt;
$ git checkout -b work &amp;lt;first commit’s hash id&amp;gt;
$ git cherry-pick &amp;lt;second commit’s hash id&amp;gt;
&amp;lt;... run tests ...&amp;gt;
$ git checkout master &lt;span style=&#34;color:#75715e&#34;&gt;# go back to the master &amp;#34;branch&amp;#34;&lt;/span&gt;
$ git branch -D work &lt;span style=&#34;color:#75715e&#34;&gt;# remove my temporary branch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Surely there has to be a better way! With &lt;code&gt;stg&lt;/code&gt; I can queue up both patches
and then re-apply them in whatever order I like, for independent or combined
testing, etc. Here’s how I’d queue the same two patches from the previous
example, using &lt;code&gt;stg&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是应该会有更好的方法. 使用 &lt;code&gt;stg&lt;/code&gt; 命令, 我可以对两个 patch 进行任意顺序的排序,
然后按这个顺序来应用它. 这样就可以进行独立测试/组合测试了. 以下是一个例子:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ stg new patch1
$ git add -i  &lt;span style=&#34;color:#75715e&#34;&gt;# select first set of changes&lt;/span&gt;
$ stg refresh --index
$ stg new patch2
$ git add -i  &lt;span style=&#34;color:#75715e&#34;&gt;# select second set of changes&lt;/span&gt;
$ stg refresh --index
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Now if I want to selectively disable the first patch to test only the second,
it’s very straightforward:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么完成上面说的事情就非常简单了:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ stg applied
patch1
patch2
&amp;lt;...  &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; tests using both patches ...&amp;gt;
$ stg pop patch1
&amp;lt;...  &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; tests using only patch2 ...&amp;gt;
$ stg pop patch2
$ stg push patch1
&amp;lt;...  &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt; tests using only patch1 ...&amp;gt;
$ stg push -a
$ stg commit -a  &lt;span style=&#34;color:#75715e&#34;&gt;# commit all the patches&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This is definitely easier than creating temporary branches and using
&lt;code&gt;cherry-pick&lt;/code&gt; to apply specific commit ids, followed by deleting the temporary
branch.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这比上面提到的建立临时分支并使用 &lt;code&gt;cherry-pick&lt;/code&gt; 来应用特别的 commit 哈希值, 后面
再把他删掉的方式舒服多了.&lt;/p&gt;
&lt;h2 id=&#34;3-reset&#34; &gt;3 Reset
&lt;span&gt;
    &lt;a href=&#34;#3-reset&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;31-to-reset-or-not-to-reset--要不要-reset-这是一个问题&#34; &gt;3.1 To reset, or not to reset | 要不要 reset, 这是一个问题
&lt;span&gt;
    &lt;a href=&#34;#31-to-reset-or-not-to-reset--%e8%a6%81%e4%b8%8d%e8%a6%81-reset-%e8%bf%99%e6%98%af%e4%b8%80%e4%b8%aa%e9%97%ae%e9%a2%98&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;One of the more difficult commands to master in Git is &lt;code&gt;reset&lt;/code&gt;, which seems to
bite people more often than other commands. Which is understandable, giving
that it has the potential to change both your working tree and your current
HEAD reference. So I thought a quick review of this command would be useful.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 Git 中比较麻烦的命令之一就是 &lt;code&gt;reset&lt;/code&gt;, 大家似乎都更容易在这个命令上遇到问题.
看起来这个命令由于同时更改了你的 working tree 和你当前的 HEAD 指针, 所以会显得
难以理解. 所以我这里需要快速提一下这个命令的作用.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Basically, &lt;code&gt;reset&lt;/code&gt; is a reference editor, an index editor, and a working tree
editor. This is partly what makes it so confusing, because it’s capable of
doing so many jobs. Let’s examine the difference between these three modes,
and how they fit into the Git commit model.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;简单来说, &lt;code&gt;reset&lt;/code&gt; 是一个能编辑 HEAD / index / working tree 的工具. 这可以部分解
释它为什么看起来这么难懂, 因为它实际上确实一下子完成了很多很多工作. 我们会在下
面慢慢解释上面提到的三种模式之间的区别, 以及它们在我们前文建立的, 从 commit 的
角度理解 Git 的模型中, 到底是如何工作的.&lt;/p&gt;
&lt;h3 id=&#34;32-doing-a-mixed-reset--应用一个-mixed-reset&#34; &gt;3.2 Doing a mixed reset | 应用一个 mixed reset
&lt;span&gt;
    &lt;a href=&#34;#32-doing-a-mixed-reset--%e5%ba%94%e7%94%a8%e4%b8%80%e4%b8%aa-mixed-reset&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;If you use the &lt;code&gt;--mixed&lt;/code&gt; option (or no option at all, as this is the default),
reset will revert parts of your index along with your HEAD reference to match
the given commit. The main difference from &lt;code&gt;--soft&lt;/code&gt; is that &lt;code&gt;--soft&lt;/code&gt; only
changes the meaning of HEAD and doesn’t touch the index.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果你使用了 &lt;code&gt;--mixed&lt;/code&gt; 这个选项 (如果你不带参数调用, 那么默认就是它), &lt;code&gt;reset&lt;/code&gt; 命
令会在将你的 HEAD 指针指向给定的那个 commit, 同时调整 index, 使其与 HEAD 匹配.
&lt;code&gt;--soft&lt;/code&gt; 和这个选项的区别在于, &lt;code&gt;--soft&lt;/code&gt; 并没有更改 index.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git add foo.c  &lt;span style=&#34;color:#75715e&#34;&gt;# add changes to the index as a new blob&lt;/span&gt;
$ git reset HEAD  &lt;span style=&#34;color:#75715e&#34;&gt;# delete any changes staged in the index&lt;/span&gt;
$ git add foo.c  &lt;span style=&#34;color:#75715e&#34;&gt;# made a mistake, add it back&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;33-doing-a-soft-reset--应用一个-soft-reset&#34; &gt;3.3 Doing a soft reset | 应用一个 soft reset
&lt;span&gt;
    &lt;a href=&#34;#33-doing-a-soft-reset--%e5%ba%94%e7%94%a8%e4%b8%80%e4%b8%aa-soft-reset&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;If you use the &lt;code&gt;--soft&lt;/code&gt; option to &lt;code&gt;reset&lt;/code&gt;, this is the same as simply changing
your HEAD reference to a different commit. Your working tree changes are left
untouched. This means the following two commands are equivalent:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果你加上了 &lt;code&gt;--soft&lt;/code&gt; 选项, 那么这个 &lt;code&gt;reset&lt;/code&gt; 命令就非常的简单易懂了: 实际上和你
直接把你的 &lt;code&gt;HEAD&lt;/code&gt; 指针指向了另一个不同的 commit 没什么两样. 你的 working tree
中的所有更改都会在执行完 reset 之后原样保留. 这意味着下面写的两条命令是完全等价
的:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git reset --soft HEAD^     &lt;span style=&#34;color:#75715e&#34;&gt;# backup HEAD to its parent,&lt;/span&gt;
                             &lt;span style=&#34;color:#75715e&#34;&gt;# effectively ignoring the last commit&lt;/span&gt;
$ git update-ref HEAD HEAD^  &lt;span style=&#34;color:#75715e&#34;&gt;# does the same thing, albeit manually&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;In both cases, your working tree now sits on top of an older HEAD, so you
should see more changes if you run &lt;code&gt;status&lt;/code&gt;. It’s not that your files have
been changed, simply that they are now being compared against an older
version. It can give you a chance to create a new commit in place of the old
one. In fact, if the commit you want to change is the most recent one checked
in, you can use &lt;code&gt;commit --amend&lt;/code&gt; to add your latest changes to the last commit
as if you’d done them together.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在上述的两种情况里, 运行完 &lt;code&gt;reset&lt;/code&gt; 之后, 你的 working tree 中的文件内容是领先于
你的 HEAD 的, 所以如果你运行 &lt;code&gt;status&lt;/code&gt; 命令来查看变更的情况, 你会看到的实际上是
你的 working tree 和一个更老的 HEAD 的差别. 这使得你可以在老的 HEAD 上重新建立
一个新的 commit, 事实上 &lt;code&gt;commit --amend&lt;/code&gt; 这个命令做的事情就是类似于这样的.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But please note: if you have downstream consumers, and they’ve done work on
top of your previous head — the one you threw away — changing HEAD like this
will force a merge to happen automatically after their next pull. Below is
what your tree would look like after a soft reset and a new commit:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是请注意一下: 如果你有下游用户, 而他们使用了被你 &lt;code&gt;reset&lt;/code&gt; + &lt;code&gt;commit&lt;/code&gt; 之后丢弃掉
的那个 &lt;code&gt;commit&lt;/code&gt; 的话, 那么像这样去更改你的 HEAD, 将会在他们的下一个 pull 时导致
一个自动出现的强制 merge. 下图是你的 repository 在执行完 soft reset 之后又提交
了一个新的 commit 之后的样子:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/soft-reset-commit.png&#34; alt=&#34;Soft ResetCommit&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;And here’s what your consumer’s HEAD would look like after they pulled
again, with colors to show how the various commits match up:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;而下图是你的下游用户下一次 pull 之后的情况:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://jwiegley.github.io/git-from-the-bottom-up/images/soft-reset-pull.png&#34; alt=&#34;Soft ResetPull&#34;&gt;&lt;/p&gt;
&lt;p&gt;可以从其中给 commit 上的颜色看出来这些 commit 是如何对应的.&lt;/p&gt;
&lt;h3 id=&#34;34-doing-a-hard-reset--应用一个-hard-reset&#34; &gt;3.4 Doing a hard reset | 应用一个 hard reset
&lt;span&gt;
    &lt;a href=&#34;#34-doing-a-hard-reset--%e5%ba%94%e7%94%a8%e4%b8%80%e4%b8%aa-hard-reset&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;A hard reset (the &lt;code&gt;--hard&lt;/code&gt; option) has the potential of being very dangerous,
as it’s able to do two different things at once:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;hard reset 操作起来有一定的风险, 它能同时完成两件不同的事情:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;First, if you do a hard reset against your current HEAD, it will erase all
changes in your working tree, so that your current files match the contents of
HEAD. There is also another command, &lt;code&gt;checkout&lt;/code&gt;, which operates just like
&lt;code&gt;reset --hard&lt;/code&gt; if the index is empty. Otherwise, it forces your working tree
to match the index.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;首先, 如果你对你当前的 HEAD 做 hard reset, 这将从你的 working tree 中抹去所有的
更改, 这样一来你的 working tree 就和你的 HEAD 指向的那个 commit 完全一致了. 如
果你的 index 是空的的话, 其实还有一个命令叫 &lt;code&gt;checkout&lt;/code&gt; 同样能做到这个事情. 如果
你的 index 不空, 那么对某个文件进行 checkout 命令只会使得 working tree 中的文件
和 index 中一致.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Now, if you do a hard reset against an earlier commit, it’s the same as first
doing a soft reset and then using reset &lt;code&gt;--hard&lt;/code&gt; to reset your working tree.
Thus, the following commands are equivalent:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果你对一个之前的 commit 调用 reset 命令, 那么这个事情和先使用 soft reset 之后
再使用 hard reset 来重置你的工作区是完全一致的. 所以说下面的两组命令是等价的:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git reset --hard HEAD~3  &lt;span style=&#34;color:#75715e&#34;&gt;# Go back in time, throwing away changes&lt;/span&gt;

$ git reset --soft HEAD~3  &lt;span style=&#34;color:#75715e&#34;&gt;# Set HEAD to point to an earlier commit&lt;/span&gt;
$ git reset --hard  &lt;span style=&#34;color:#75715e&#34;&gt;# Wipe out differences in the working tree&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;As you can see, doing a hard reset can be very destructive. Fortunately, there
is a safer way to achieve the same effect, using the Git stash (see the next
section):&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;读过以上的内容, 我们可以发现: 一个 hard reset 很可能是具有破环性的. 幸运的是, 我
们实际上有一种更安全的方法来完成同样的事情, 只要使用下一节中提到的 Git stash 命
令就可以了:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git stash
$ git checkout -b new-branch HEAD~3   &lt;span style=&#34;color:#75715e&#34;&gt;# head back in time!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This approach has two distinct advantages if you’re not sure whether you
really want to modify the current branch just now:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当你并不确定你是否要对当前的分支进行修改的时候, 这种方法有两个明显的优点:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;It saves your work in the stash, which you can come back to at any time.
Note that the stash is not branch specific, so you could potentially stash
the state of your tree while on one branch, and later apply the differences
to another.&lt;/li&gt;
&lt;li&gt;It reverts your working tree back to a past state, but on a new branch, so
if you decide to commit your changes against the past state, you won’t
have altered your original branch.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;这样可以将你的工作保存在 stash 中, 你随时可以再回到你原来的工作状态. 需要注意
的是这个 stash 和分支无关, 你完全可以在某个分支上将你当前的 working tree 存
入, 而随后在另一个分支上取出它.&lt;/li&gt;
&lt;li&gt;这样操作的确将代码回退到了过去的状态, 但是是在一个新的分支上, 如果你决定在过
去的状态上修改, 这并不会影响到你原先那个更快的分支.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;If you do make changes to &lt;code&gt;new-branch&lt;/code&gt; and then decide you want it to become
your new master branch, run the following commands:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果你对 new-branch 做出了更改, 然后想让它成为你新的主分支, 可以运行以下的命令来
完成这件事情:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git branch -D master  &lt;span style=&#34;color:#75715e&#34;&gt;# goodbye old master (still in reflog)&lt;/span&gt;
$ git branch -m new-branch master  &lt;span style=&#34;color:#75715e&#34;&gt;# the new-branch is now my master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;The moral of this story is: although you can do major surgery on your current
branch using &lt;code&gt;reset --soft&lt;/code&gt; and &lt;code&gt;reset --hard&lt;/code&gt; (which changes the working tree
too), why would you want to? Git makes working with branches so easy and
cheap, it’s almost always worth it to do your destructive modifications on a
branch, and then move that branch over to take the place of your old master.
It has an almost Sith-like appeal to it&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;以上的事情是在告诉你: 尽管你是可以用 reset &amp;ndash;soft 和 reset &amp;ndash;hard 命令来操作你当
前的分支,但是你几乎没有理由必须在你当前的分支上这么做. Git 让分支相关的操作变得
简洁而容易, 所以我们往往都将可能有破坏性的操作放在另一个分支上操作, 完成以后再
移动回来, 这种操作方式几乎在任何情况下都比直接在原分支上修改更为优秀.&lt;sup id=&#34;fnref:14&#34;&gt;&lt;a href=&#34;#fn:14&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;14&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;And what if you do accidentally run &lt;code&gt;reset --hard&lt;/code&gt;, losing not only your
current changes but also removing commits from your master branch? Well,
unless you’ve gotten into the habit of using stash to take snapshots (see
next section), there’s nothing you can do to recover your lost working tree.
But you can restore your branch to its previous state by again using &lt;code&gt;reset --hard&lt;/code&gt; with the reflog (this will also be explained in the next section):&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么如果你不小心运行了 hard reset, 然后同时丢失了你当前的更改和之前的一部分
commit 有办法弥补么? 除非你已经养成了使用 stash 来建立快照, 否则几乎没有办法来挽
回这种情况对于 working tree 带来的损失, 但是你可以将你的分支回溯到它之前的那个
状态. 只要像下面这样使用 &lt;code&gt;reset --hard&lt;/code&gt; 这个命令就可以了:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git reset --hard HEAD@&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;1&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;# restore from reflog before the change&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下一节我们来解释上面这条命令的意思.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To be on the safe side, never use &lt;code&gt;reset --hard&lt;/code&gt; without first running
&lt;code&gt;stash&lt;/code&gt;. It will save you many white hairs later on. If you did run stash, you
can now use it to recover your working tree changes as well:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为了安全起见, 我们不应该在运行 &lt;code&gt;stash&lt;/code&gt; 之前使用 &lt;code&gt;reset --hard&lt;/code&gt;. 这个习惯将让你少
掉几根头发. 如果你跑过 &lt;code&gt;stash&lt;/code&gt;, 你实际上就可以使用它来还原你的 working tree:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git stash  &lt;span style=&#34;color:#75715e&#34;&gt;# because it&amp;#39;s always a good thing to do&lt;/span&gt;
$ git reset --hard HEAD~3  &lt;span style=&#34;color:#75715e&#34;&gt;# go back in time&lt;/span&gt;
$ git reset --hard HEAD@&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;1&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# oops, that was a mistake, undo it!&lt;/span&gt;
$ git stash apply  &lt;span style=&#34;color:#75715e&#34;&gt;# and bring back my working tree changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;4-stashing-and-the-reflog--stash-和-reflog&#34; &gt;4 Stashing and the reflog | Stash 和 reflog
&lt;span&gt;
    &lt;a href=&#34;#4-stashing-and-the-reflog--stash-%e5%92%8c-reflog&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Until now we’ve described two ways in which blobs find their way into Git:
first they’re created in your index, both without a parent tree and without
an owning commit; and then they’re committed into the repository, where they
live as leaves hanging off of the tree held by that commit. But there are two
other ways a blob can dwell in your repository.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;直到现在我们描述过两种在 Git 中找到某个 blob 的方法:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当 blob 在你的 index 中被创建时, 此时还没有一个 tree 在引用它们, 它们也没有被
某个 commit 所拥有, 这时我们可以通过 index 来找到它;&lt;/li&gt;
&lt;li&gt;随后它们被提交给你的 repository, 之后 blob 就成为了 被某个 commit 管理的 tree
上的叶节点.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但是实际上在 Git 中还有两种 blob 存在的形式.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The first of these is the Git &lt;code&gt;reflog&lt;/code&gt;, a kind of meta-repository that records
— in the form of commits — every change you make to your repository. This
means that when you create a tree from your index and store it under a commit
(all of which is done by &lt;code&gt;commit&lt;/code&gt;), you are also inadvertently adding that
commit to the reflog, which can be viewed using the following command:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第一种就是 Git &lt;code&gt;reflog&lt;/code&gt;, 这是一种以 commit 的方式记录了所有你对你的 repository
做出的更改的 meta-repository. 这意味着当你从一个 index 建立一个 tree, 并且将它
存在一个 commit 中的时候, 你实际上无意之中也将这个 commit 添加到了 the reflog
中. 你可以通过以下的命令查看 the reflog:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git reflog
5f1bc85...  HEAD@&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;: commit &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;initial&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;: Initial commit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;The beauty of the reflog is that it persists independently of other changes in
your repository. This means I could unlink the above commit from my repository
(using &lt;code&gt;reset&lt;/code&gt;), yet it would still be referenced by the reflog for another 30
days, protecting it from garbage collection. This gives me a month’s chance
to recover the commit should I discover I really need it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the reflog 的妙处就在于它与其他你对你的 repository 做出的更改是独立的. 这意味着
当我将上面的命令展示的那个 commit 从我的 repository 中解除链接的时候 (比方说我
跑了一个 &lt;code&gt;reset&lt;/code&gt;), 这个 commit 仍然被这个 meta-repository, 也就是 the reflog 所
引用, 直到 30 天以后, 这个 commit 才会进入垃圾回收系统的考虑范围. 这使得你有一
个月的时间来找回你需要的 commit.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The other place blobs can exist, albeit indirectly, is in your working tree
itself. What I mean is, say you’ve changed a file &lt;code&gt;foo.c&lt;/code&gt; but you haven’t
added those changes to the index yet. Git may not have created a blob for you,
but those changes do exist, meaning the content exists — it just lives in
your filesystem instead of Git’s repository. The file even has its own SHA1
hash id, despite the fact no real blob exists. You can view it with this
command:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;尽管很显然, 但是实际上还有一个地方是 blob 可以呆的, 那就是你的 working tree. 我
的意思是说, 如果你更改了一个文件, 比如说 &lt;code&gt;foo.c&lt;/code&gt; 但是还没有将这个更改添加到 the
index 中, 那么由于我们说 blob 的本质是数据本身, 所以即使 Git 并没有建立对应的
blob, 数据本身也仍然是存在的, 只不过是存在于你的文件系统中罢了. 这个文件本身实际
上是有 SHA1 值的, 尽管其实并没有实际上的 blob 存在. 我们可以通过下面这条命令来
查看它:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git hash-object foo.c
&amp;lt;some hash id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;What does this do for you? Well, if you find yourself hacking away on your
working tree and you reach the end of a long day, a good habit to get into is
to stash away your changes:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这意味着啥呢? 如果你在你的 working tree 中到处乱改, 然后经过了漫长的一整天工作,
将所有的更改都用 stash 存起来会是一个好习惯:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git stash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This takes all your directory’s contents — including both your working tree,
and the state of the index — and creates blobs for them in the git
repository, a tree to hold those blobs, and a pair of stash commits to hold
the working tree and index and record the time when you did the stash.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个命令将你的那个目录中的所有内容 — 包括你的 working tree, 甚至是 the index 的
状态 — 都保存了下来, 并且为它们在 Git repository 中创建了 blob, 以及一个 tree
来管理这些 blob, 还有一对 stash commit 来管理你的 working tree 和 index 还得记
录下你执行 stash 命令的时间.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a good practice because, although the next day you’ll just pull your
changes back out of the stash with &lt;code&gt;stash apply&lt;/code&gt;, you’ll have a reflog of all
your stashed changes at the end of every day. Here’s what you’d do after
coming back to work the next morning (WIP here stands for “Work in progress
”):&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;尽管你第二天会把所有的更改都用 &lt;code&gt;stash apply&lt;/code&gt; 命令从 stash 中重新拉出来, 但是做完
这件事情之后你相当于在 reflog 中存了一个你每天工作的结果的快照. 第二天你回来工
作的时候需要做的事情如下:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git stash list
stash@&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;: WIP on master: 5f1bc85...  Initial commit

$ git reflog show stash &lt;span style=&#34;color:#75715e&#34;&gt;# same output, plus the stash commit&amp;#39;s hash id 2add13e... stash@{0}: WIP on master: 5f1bc85... Initial commit&lt;/span&gt;

$ git stash apply
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中WIP意味着 &amp;ldquo;Work in progress&amp;rdquo;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Because your stashed working tree is stored under a commit, you can work with
it like any other branch — at any time! This means you can view the log, see
when you stashed it, and checkout any of your past working trees from the
moment when you stashed them:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因为你已经把 working tree 存在了一个 commit 里面, 所以你任何时候都实际上可以把他
当作一个分支一样来看待. 这意味着你甚至可以查看日志, 可以看你 stash 的时间, 以及
将你过去存进 stash 的 working tree 重新取出:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git stash list
stash@&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;0&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;: WIP on master: 73ab4c1...  Initial commit
...
stash@&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;32&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;: WIP on master: 5f1bc85...  Initial commit
$ git log stash@&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;32&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# when did I do it?&lt;/span&gt;
$ git show stash@&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;32&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# show me what I was working on&lt;/span&gt;
$ git checkout -b temp stash@&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;32&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# let’s see that old working tree!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This last command is particularly powerful: behold, I’m now playing around in
an uncommitted working tree from over a month ago. I never even added those
files to the index; I just used the simple expedient of calling &lt;code&gt;stash&lt;/code&gt; before
logging out each day (provided you actually had changes in your working tree
to stash), and used &lt;code&gt;stash apply&lt;/code&gt; when I logged back in.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最后那个命令其实非常的有用: 你看, 我可以把一个月前没有 commit 的 working tree 重
新拿出来玩. 我甚至从来没有把这些文件添加到 the index 中; 只不过是在我每天下班关
机之前运行了一次 &lt;code&gt;stash&lt;/code&gt; 命令而已. 然后第二天上班的时候, 只要 &lt;code&gt;stash apply&lt;/code&gt; 就
行了.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you ever want to clean up your stash list — say to keep only the last 30
days of activity — don’t use &lt;code&gt;stash clear&lt;/code&gt;; use the &lt;code&gt;reflog expire&lt;/code&gt; command
instead:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是如果你什么时候想清理你的 stash 列表了, 比方说想把 30 天之前的 stash 的历史删
掉&lt;sup id=&#34;fnref:15&#34;&gt;&lt;a href=&#34;#fn:15&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;15&lt;/a&gt;&lt;/sup&gt;, 那么麻烦不要使用 &lt;code&gt;stash clear&lt;/code&gt; — 那样你将会丢失所有 stash 中的历史;
而应该使用 &lt;code&gt;reflog expire&lt;/code&gt; 命令:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ git stash clear  &lt;span style=&#34;color:#75715e&#34;&gt;# DON&amp;#39;T! You&amp;#39;ll lose all that history&lt;/span&gt;
$ git reflog expire --expire&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;30.days refs/stash
&amp;lt;outputs the stash bundles that&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;#39;&lt;/span&gt;ve been kept&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;The beauty of &lt;code&gt;stash&lt;/code&gt; is that it lets you apply unobtrusive version control to
your working process itself: namely, the various stages of your working tree
from day to day. You can even use &lt;code&gt;stash&lt;/code&gt; on a regular basis if you like, with
something like the following &lt;code&gt;snapshot&lt;/code&gt; script:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;stash&lt;/code&gt; 的妙处就在于让你可以做一些不那么刻意的版本控制, 比如说像上面那样每天保存
一下工作进度. 你愿意的话甚至可以定期的运行 &lt;code&gt;stash&lt;/code&gt;, 比如通过下面这样的脚本:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ cat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;&amp;lt;EOF &amp;gt; /usr/local/bin/git-snapshot
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#!/bin/sh
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;git stash &amp;amp;&amp;amp; git stash apply
&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;EOF&lt;/span&gt;
$ chmod +x $_
$ git-snapshot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;There’s no reason you couldn’t run this from a &lt;code&gt;cron&lt;/code&gt; job every hour, along
with running the &lt;code&gt;reflog expire&lt;/code&gt; command every week or month.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;没有什么理由能阻止你使用 &lt;code&gt;cron&lt;/code&gt; 来每个小时跑一次这个命令, 如果担心仓库太大, 完全
可以同时每个月或者每周跑 &lt;code&gt;reflog expire&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;5-conclusion--结论&#34; &gt;5 Conclusion | 结论
&lt;span&gt;
    &lt;a href=&#34;#5-conclusion--%e7%bb%93%e8%ae%ba&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Over the years I’ve used many version control systems, and many backup
schemes. They all have facilities for retrieving the past contents of a file.
Most of them have ways to show how a file has differed over time. Many permit
you to go back in time, begin a divergent line of reasoning, and then later
bring these new thoughts back to the present. Still fewer offer fine-grained
control over that process, allowing you to collect your thoughts however you
feel best to present your ideas to the public. Git lets you do all these
things, and with relative ease — once you understand its fundamentals.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在过去的几年中我使用过非常多的版本控制系统, 以及很多种的备份模式. 它们都有一系列
工具来追回过去文件的内容. 大部分工具都有展示一个文件随着时间发展如何一步步被更
改的的方式. 很多工具都可以让你穿越时间回到过去, 让你在过去做一些尝试, 然后把产
生的想法重新带回现在. 还是有很小一部分提供了对这个过程的良好的控制机制, 让你可
以良好的操作过去的历史, 可以以任意你喜欢的方式整理好你的想法, 然后将它展示给公
众. Git 可以让你做到上述的一切. 甚至相对来说还是比较容易地做到这一切 — 只要你
真的搞懂了 Git 的原理.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s not the only system with this kind of power, nor does it always employ
the best interface to its concepts. What it does have, however, is a solid
base to work from. In the future, I imagine many new methods will be devised
to take advantage of the flexibilities Git allows. Most other systems have led
me to believe they’ve reached their conceptual plateau — that all else from
now will be only a slow refinement of what I’ve seen before. Git gives me the
opposite impression, however. I feel we’ve only begun to see the potential
its deceptively simple design promises.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这不是唯一一个有这些能力的系统, 它也没有对于它的理念提供最好的交互. 但是 Git 拥
有的这些东西, 却的的确确是一个进一步开发的好基础. 在未来, 我可以预想存在着很多
更好的工具被发明出来, 它们能更好地利用 Git 类似的构造提供的这种种便利. 其他的大
部分系统都让我相信它们到达了他们的瓶颈期 — 它们要不然就像我之前见到的那样在慢
慢慢慢地进行改进, 要不然将来也会进入这样的状态. Git 却给了我一个更为乐观的印象.
我感觉我们即将见识到它出乎人意料的简单的涉及理念中爆发出来的潜力.&lt;/p&gt;
&lt;h2 id=&#34;6-further-reading--阅读更多&#34; &gt;6 Further reading | 阅读更多
&lt;span&gt;
    &lt;a href=&#34;#6-further-reading--%e9%98%85%e8%af%bb%e6%9b%b4%e5%a4%9a&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;If your interest to learn Git more has been piqued, please check out the
following articles:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果你还想学一些 Git 可以看看下面这些文章:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;A tour of Git: the basics - &lt;a href=&#34;http://cworth.org/hgbook-git/tour/&#34;&gt;http://cworth.org/hgbook-git/tour/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Manage source code using Git -
&lt;a href=&#34;http://www.ibm.com/developerworks/linux/library/l-git/&#34;&gt;http://www.ibm.com/developerworks/linux/library/l-git/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A tutorial introduction to git - &lt;a href=&#34;http://git-scm.com/docs/gittutorial&#34;&gt;http://git-scm.com/docs/gittutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitFaq — GitWiki - &lt;a href=&#34;https://git.wiki.kernel.org/index.php/GitFaq&#34;&gt;https://git.wiki.kernel.org/index.php/GitFaq&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A git core tutorial for developers -
&lt;a href=&#34;https://www.kernel.org/pub/software/scm/git/docs/gitcore-tutorial.html&#34;&gt;http://www.kernel.org/pub/software/scm/git/docs/gitcore-tutorial.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;git for the confused -
&lt;a href=&#34;http://www.gelato.unsw.edu.au/archives/git/0512/13748.html&#34;&gt;http://www.gelato.unsw.edu.au/archives/git/0512/13748.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The thing About Git - &lt;a href=&#34;http://tomayko.com/writings/the-thing-about-git&#34;&gt;http://tomayko.com/writings/the-thing-about-git&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;section class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 这个描述貌似和上面对 working tree 的描述有冲突, 上面对 working tree
的说法是 working tree 包含了 &lt;code&gt;.git&lt;/code&gt; 子目录,但是出于译者对 commit 的认识而
言, commit 中应该没有 &lt;code&gt;.git&lt;/code&gt; 中的相关信息. 这里可以尝试理解为文中说的
working tree 是类似工作区的概念, 并不包括 &lt;code&gt;.git&lt;/code&gt; 子目录.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 这个地方括号中的内容不知道怎么翻译, 可以理解为, &amp;ldquo;我们还有很多关于
commit 的东西马上就要谈到&amp;rdquo;, 但是感觉和上下文不搭.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 有理由相信 github 可以用这个方式来省钱, 这样 fork 出来的 repository
就不会那么占空间了.&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 这个地方我皮了一下, 但是实际上我并不确定原文是不是这个意思.&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:5&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 最后这句话的翻译不确定是否正确.&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:6&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 这个时候真的还没有 &amp;ldquo;master&amp;rdquo; 这个分支么? 为什么可以说是 create 呢?&amp;#160;&lt;a href=&#34;#fnref:6&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:7&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 这里的 &amp;ldquo;first&amp;rdquo; 是按提交 commit 的时间排序的么?&amp;#160;&lt;a href=&#34;#fnref:7&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:8&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 毕竟你可以将 &amp;ldquo;name^&amp;rdquo; 看作是一个 &amp;ldquo;name&amp;rdquo; 嘛.&amp;#160;&lt;a href=&#34;#fnref:8&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:9&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 实际上这些内容可以在命令 &lt;code&gt;git help show-branch&lt;/code&gt; 中看到.&amp;#160;&lt;a href=&#34;#fnref:9&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:10&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 虽然实际上并不是这样, 因为每个 commit 中的 tree 都只是一个快照而已.&lt;/p&gt;
&lt;p&gt;另外上面这段话我个人觉得可以这样理解:&lt;/p&gt;
&lt;p&gt;commit 是一个 object, 在 Git 中一个 object 的名字是由它的 SHA1 值来决定的,
而 SHA1 值是由内容决定的. 那么当我们说, 一个 commit 被改变的时候, 我们实际
上想表达的意思是, 这个 commit 的 SHA1 值发生了变化, 也就是 commit 的内容发
生了变化.&lt;/p&gt;
&lt;p&gt;根据前文的说法我们知道, 一个 commit 中, 是包含有它的父 commit 的引用的, 如果
一个 commit 的父 commit 被用 &lt;code&gt;rebase&lt;/code&gt; 指令改掉了, 那么它本身的 SHA1 值是会
发生变化的, 而它本身发生了变化之后, 所有直接或者间接引用了它的其他 commit
的值也都会因为这样的理由而发生变化.&amp;#160;&lt;a href=&#34;#fnref:10&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:11&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 这里的 tree 指的是被新 commit 直接管理的那个 tree 的 sub-tree.&amp;#160;&lt;a href=&#34;#fnref:11&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:12&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 这里我并不确定是不是这个意思, 因为译者并没有使用过 Quilt, 这句是根据
维基猜的, 主要是前面那个 it 不知道指的是谁.&amp;#160;&lt;a href=&#34;#fnref:12&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:13&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 这里的意思应该是, 如果你的暂存区里有多个 commit, 那么你就可以把他们
以任意的组合加到 repository 中, 然后就可以实现上面说的那个事情了.&amp;#160;&lt;a href=&#34;#fnref:13&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:14&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 最后一句貌似是个星战梗, 不知道啥意思. Sith 是西斯, 星战的反派.&amp;#160;&lt;a href=&#34;#fnref:14&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:15&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;译者注: 这里按前文的说法, stash 的历史也一样是由 reflog 保存的, 那么 reflog
本身只会保存 30 天历史, 所以这里手动清理应该没什么意义吧, 除非只是希望手动
完成一下垃圾回收器完成的事情?&amp;#160;&lt;a href=&#34;#fnref:15&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-2/">
      <title>Starting sessions with systemd part 2</title>
      <link>https://blog.deepin.org/posts/starting-sessions-with-systemd-part-2/</link>
      <pubDate>Wed, 29 Dec 2021 21:09:07 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/starting-sessions-with-systemd-part-2/</guid>
      <description>&lt;p&gt;接着上一篇文章，这篇文章主要讲解一下具体的实现。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/linuxdeepin/dde-session&#34;&gt;https://github.com/linuxdeepin/dde-session&lt;/a&gt; 已经包含了所有的文件和提交。&lt;/p&gt;
&lt;p&gt;上面文章说过，为了让 dde 使用 systemd &amp;ndash;user 来关系服务，我提供了一组服务：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dde-session-initialized.target
dde-session-manager.service
dde-session-manager.target
dde-session-pre.target
dde-session-restart-dbus.service
dde-session-shutdown.service
dde-session-shutdown.target
dde-session.target
dde-session-x11-services-ready.target
dde-session-x11-services.target
dde-session-x11.target
org.deepin.Session.service
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;lightdm 在认证通过以后，会启动 dde-session 进程，dde-session 会通过 systemd 的 dbus 启动 &lt;code&gt;org.deepin.Session.service&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;org.deepin.Session.service&lt;/code&gt; 中会执行 &lt;code&gt;dde-session-ctl --systemd-service&lt;/code&gt;，启动 &lt;code&gt;dde-session-x11.target&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dde-session-x11.target&lt;/code&gt; 关联了所有初始化的服务，systemd 会帮助自动运行这些服务，并且最终会运行到 &lt;code&gt;dde-session-manager.service&lt;/code&gt; 上。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;dde-session-manager.service&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[Service]&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;simple&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;ExecStart&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/usr/bin/startdde&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;ExecStopPost&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/usr/lib/libexec/dde-session-ctl --logout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 &lt;code&gt;org.deepin.Session.service&lt;/code&gt; 中会启动一个特殊的服务，这个服务会监听 dde-session 的退出，因为总要有一个服务去监听 session 的状态，要确保 session 在服务在，session 退服务退，服务退 session 退，共存亡，这也是所有操作中最麻烦的一步。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;org.deepin.Session.service&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[Service]&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;dbus&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;BusName&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;org.deepin.Session&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;ExecStart&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/usr/bin/dde-session --systemd-service&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;ExecStop&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/usr/lib/libexec/dde-session-ctl --shutdown&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 dde-session-ctl 中是这么实现 shutdown 的:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-c++&#34; data-lang=&#34;c++&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (isShutdown) {
    &lt;span style=&#34;color:#75715e&#34;&gt;// kill startdde-session or call login1
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    QDBusInterface &lt;span style=&#34;color:#a6e22e&#34;&gt;systemd&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;org.freedesktop.systemd1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/org/freedesktop/systemd1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;org.freedesktop.systemd1.Manager&amp;#34;&lt;/span&gt;);
    qInfo() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; systemd.call(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;StartUnit&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dde-session-shutdown.target&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;replace-irreversibly&amp;#34;&lt;/span&gt;);
    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 dde-session-ctl 中是这么实现 logout 的:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (parser.isSet(logout)) {
    org&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;deepin&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Session session(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;org.deepin.Session&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/org/deepin/Session&amp;#34;&lt;/span&gt;, QDBusConnection&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;sessionBus());
    session.Logout();

    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;dde-session main.cpp&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-c++&#34; data-lang=&#34;c++&#34;&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 添加启动参数，表示这是通过 systemd 启动的。
&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;QCommandLineOption &lt;span style=&#34;color:#a6e22e&#34;&gt;systemd&lt;/span&gt;(QStringList{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;d&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;systemd-service&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;wait for systemd services&amp;#34;&lt;/span&gt;});
parser.addOption(systemd);
parser.process(app);

&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (parser.isSet(systemd)) {
    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
}

QDBusServiceWatcher &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;watcher &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; QDBusServiceWatcher(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;org.deepin.Session&amp;#34;&lt;/span&gt;, QDBusConnection&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;sessionBus(), QDBusServiceWatcher&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;WatchForUnregistration);
    watcher&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;connect(watcher, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;QDBusServiceWatcher&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;serviceUnregistered, [&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;] {
        QDBusInterface &lt;span style=&#34;color:#a6e22e&#34;&gt;systemdDBus&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;org.freedesktop.systemd1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/org/freedesktop/systemd1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;org.freedesktop.systemd1.Manager&amp;#34;&lt;/span&gt;);
        qInfo() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; systemdDBus.call(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;StartUnit&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dde-session-shutdown.service&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;replace&amp;#34;&lt;/span&gt;);
        qApp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;quit();
    });
    qInfo() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; systemdDBus.call(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;StartUnit&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dde-session-x11.target&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;replace&amp;#34;&lt;/span&gt;);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;dde-session-shutdown.service&lt;/code&gt; 只负责执行 &lt;code&gt;dde-session-ctl --shutdown&lt;/code&gt;，用于启动 &lt;code&gt;dde-session-shutdown.target&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;dde-session-shutdown.service&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[Service]&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;oneshot&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;ExecStart&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/usr/lib/libexec/dde-session-ctl --logout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过 systemd 的接口启动了 &lt;code&gt;dde-session-shutdown.target&lt;/code&gt;，从而启动了清理流程。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dde-session-shutdown.target&lt;/code&gt; 没有执行任何内容，仅仅是关联了一组服务，但是是冲突这些服务，因为这是启动的时候&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;dde-session-shutdown.target&lt;/code&gt; 中还关联了最重要的一个服务: &lt;code&gt;dde-session-restart-dbus.service&lt;/code&gt;，这个服务是用来关闭 &lt;code&gt;dbus.service&lt;/code&gt; 服务的，这样就可以保证 session 结束的时候，不会有 dbus 服务逃逸出去。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;dde-session-restart-dbus.service&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[Service]&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;notify&lt;/span&gt;
&lt;span style=&#34;color:#a6e22e&#34;&gt;ExecStart&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/usr/lib/libexec/dde-session-ctl --restart-dbus&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 dde-session-ctl 中是这么实现 restart-dbus 的:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (isRestartDBus) {
    QDBusInterface &lt;span style=&#34;color:#a6e22e&#34;&gt;systemd&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;org.freedesktop.systemd1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/org/freedesktop/systemd1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;org.freedesktop.systemd1.Manager&amp;#34;&lt;/span&gt;);
    qInfo() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; systemd.call(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;StopUnit&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dbus.service&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;replace-irreversibly&amp;#34;&lt;/span&gt;);
    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;复盘一下整个流程，在登录界面输入密码登录以后，lightdm 会启动 &lt;code&gt;dde-session&lt;/code&gt; 作为 session 的入口，&lt;code&gt;dde-session&lt;/code&gt; 会注册一个 &lt;code&gt;org.deepin.Session&lt;/code&gt; 的 dbus 服务，并且会启动关联的 systemd 服务，启动一个 &lt;code&gt;dde-session --systemd-service&lt;/code&gt;，在这个进程里，会启动 &lt;code&gt;dde-session-x11.target&lt;/code&gt;，从而最终启动了 &lt;code&gt;dde-session-manager.service&lt;/code&gt;，将原本的会话入口 startdde 启动了。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;dde-session --systemd-service&lt;/code&gt; 会监听 &lt;code&gt;org.deepin.Session&lt;/code&gt; 服务的存活，当服务不存在时，就会开始执行退出流程，从而启动了 &lt;code&gt;dde-session-ctl --shutdown&lt;/code&gt;，在 dde-session-ctl 的帮助下，启动了 &lt;code&gt;dde-session-shutdown.target&lt;/code&gt;，将所有启动的 dde 核心服务都冲突掉，从而完成了服务关闭，在最后阶段再将 &lt;code&gt;dbus.service&lt;/code&gt; 服务停止，完成最终的清理。&lt;/p&gt;
&lt;p&gt;还有一种退出模式，手动启动了 &lt;code&gt;dde-session-shutdown.service&lt;/code&gt; 服务，也会触发完成的退出流程，&lt;code&gt;dde-session-shutdown.service&lt;/code&gt; 会利用 &lt;code&gt;dde-session-ctl --logout&lt;/code&gt; 将会话注销，从而触发上面的执行流程，完成会话与服务的关闭。&lt;/p&gt;
&lt;p&gt;目前还有一些问题没有解决，没有创建独立的 systemd.slice 和 systemd.scope，这可以帮助 dde 细致的划分和管理自己的子 services，退出 dbus.service 的手段非常黑，而且 dbus 服务的程序由于图形服务也已经结束了，导致成片的 X11 Error。这些都是要解决的。&lt;/p&gt;
&lt;p&gt;下一步准备写几个 service，用来代替 startdde 的组件启动，比如 kwin、dde-desktop、dde-dock 和 xdg-autostart 等。&lt;/p&gt;
&lt;p&gt;就目前的情况来看，任重而道远。&lt;/p&gt;
&lt;p&gt;原文链接：&lt;a href=&#34;https://blog.justforlxz.com/2021/12/29/Starting-sessions-with-systemd-part-2/&#34;&gt;https://blog.justforlxz.com/2021/12/29/Starting-sessions-with-systemd-part-2/&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/">
      <title>Starting sessions with systemd part 1</title>
      <link>https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/</link>
      <pubDate>Sat, 25 Dec 2021 22:00:53 +0000</pubDate>
      
      <guid>https://blog.deepin.org/posts/starting-sessions-with-systemd-part-1/</guid>
      <description>&lt;p&gt;DDE 现在正在做 Wayland 的支持，所以我们需要对目前的桌面环境结构进行调整，
考虑到 GNOME 和 KDE 都已经使用 &lt;code&gt;systemd&lt;/code&gt; 来管理 session，我认为 deepin 团队也可以考虑这一步了。&lt;/p&gt;
&lt;h2 id=&#34;为什么需要-systemd&#34; &gt;为什么需要 systemd？
&lt;span&gt;
    &lt;a href=&#34;#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81-systemd&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;目前 &lt;code&gt;systemd&lt;/code&gt; 作为事实上胜利的 init 进程，它现在负责的功能已经越来越多了，支持但不限于：&lt;code&gt;udev&lt;/code&gt;、&lt;code&gt;sleep/hibernate/suspend&lt;/code&gt;、&lt;code&gt;dbus&lt;/code&gt;、&lt;code&gt;services&lt;/code&gt; 等。现在越来越多的程序也开始使用 &lt;code&gt;systemd&lt;/code&gt; 管理后台服务。&lt;/p&gt;
&lt;h2 id=&#34;不使用-systemd-来管理-session-会有什么问题吗&#34; &gt;不使用 systemd 来管理 session 会有什么问题吗？
&lt;span&gt;
    &lt;a href=&#34;#%e4%b8%8d%e4%bd%bf%e7%94%a8-systemd-%e6%9d%a5%e7%ae%a1%e7%90%86-session-%e4%bc%9a%e6%9c%89%e4%bb%80%e4%b9%88%e9%97%ae%e9%a2%98%e5%90%97&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;存在一个进程逃逸问题，不过这个问题与是否采用 &lt;code&gt;systemd&lt;/code&gt; 管理 &lt;code&gt;session&lt;/code&gt; 没有太大关系。&lt;/p&gt;
&lt;p&gt;我先来简单介绍一下目前 DDE 的工作模型：&lt;/p&gt;
&lt;p&gt;首先，DDE 使用 &lt;code&gt;LightDM&lt;/code&gt; 作为显示服务器（Display Manager），DDE 提供了 &lt;code&gt;lightdm-deepin-greeter&lt;/code&gt; 作为登录界面，greeter 可以通过调用 LightDM 的 api 进行 linux-pam 的认证。&lt;/p&gt;
&lt;p&gt;当认证通过后，LightDM 会启动一个会话（Session），之后运行的程序都将是以登录的用户身份启动，然后 &lt;code&gt;systemd --user&lt;/code&gt; 进程会被启动，同时 &lt;code&gt;dbus-daemon&lt;/code&gt; 也会启动，之后 LightDM 会启动 &lt;code&gt;startdde。&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;此时就开始执行到 DDE &lt;code&gt;的初始化阶段，startdde&lt;/code&gt; 会启动 &lt;code&gt;dde-session-daemon&lt;/code&gt; 和 DDE 的核心组件，之后就并行启动 autostart 中的 desktop 文件。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;startdde&lt;/code&gt; 目前的流程本身没有问题，问题出在 &lt;code&gt;systemd&lt;/code&gt; 接管了 dbus，并且从 &lt;code&gt;systemd 226&lt;/code&gt; 版本开始，&lt;code&gt;/etc/pam.d/system-login&lt;/code&gt; 默认配置中的 &lt;code&gt;pam_systemd&lt;/code&gt; 模块会在用户首次登录的时候, 自动运行一个 &lt;code&gt;systemd --user&lt;/code&gt; 实例。 只要用户还有会话存在，这个进程就不会退出；用户所有会话退出时，进程将会被销毁。当#随系统自动启动 &lt;code&gt;systemd&lt;/code&gt; 用户实例启用时, 这个用户实例将在系统启动时加载，并且不会被销毁。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;systemd --user&lt;/code&gt; 实例是针对每个用户处理的，而不是针对会话。这样做的原理是用户服务处理的大部分资源，像 socket 或状态文件是针对每个用户的（存活于用户的主目录下）而不是会话。这意味着所有的用户服务是独立于会话之外运行的。最终，我们得出结论：基于会话运行的程序可能会导致用户服务中断。&lt;code&gt;systemd&lt;/code&gt; 处理用户会话的方式是非常生硬的（pretty much in flux）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么问题就来了，上面说了，systemd 接管了 dbus 服务，通过 dbus 启动的程序会在 &lt;code&gt;dbus.service&lt;/code&gt; 下面，成为它的子进程，而 &lt;code&gt;systemd --user&lt;/code&gt; 在有用户其它登录的会话存在时，并不会停止 &lt;code&gt;systemd --user&lt;/code&gt;，所以 &lt;code&gt;dbus.service&lt;/code&gt; 也不会停止。但是 dde 的会话
已经完成注销了，但是通过 dde 启动的程序却没有被退出，从而导致了程序 &lt;strong&gt;逃逸&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;解决方案&#34; &gt;解决方案？
&lt;span&gt;
    &lt;a href=&#34;#%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;如果只是想解决逃逸问题，那么想一个办法，在 &lt;code&gt;session&lt;/code&gt; 停止的时候，主动把 &lt;code&gt;dbus.service&lt;/code&gt; 服务停掉其实就可以解决了，但是我们想要利用 &lt;code&gt;systemd&lt;/code&gt; 的自动依赖解决，这样就不用重复造一点点小轮子了。&lt;/p&gt;
&lt;p&gt;那么需要怎么修改才能符合桌面环境的要求？&lt;/p&gt;
&lt;p&gt;GNOME 和 KDE 现在已经完成了 &lt;code&gt;systemd session&lt;/code&gt; 的工作，我们可以在这两个老大哥身上学习。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;提供了一个新的会话入口点 &lt;code&gt;gnome-session-systemd&lt;/code&gt;。 这需要一个参数，即开始会话的单元（通常是 .target 单元）。 它将执行一些清理功能，初始化 systemd 环境，然后启动单元并等待它停止。会话通过修改它们的 Exec 行并删除 &lt;code&gt;RequiredComponents&lt;/code&gt; 来选择以这种方式启动。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;XDG autostart 现在 &lt;code&gt;systemd&lt;/code&gt; 提供了一个 wrapper，可以自动生成出 service 单元，所以功能可以复用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;在 systemd 管理的系统上，每个用户都被分配了一个 &lt;code&gt;user-x.slice (systemd.slice(5))&lt;/code&gt;,并且用户的会话将在 &lt;code&gt;session-Y.scope (systemd.scope(5))&lt;/code&gt; 中运行。您还可以在主机上看到一些其它用户特定的单元，包括 &lt;code&gt;user@x.service&lt;/code&gt;,它是用户的 &lt;code&gt;systemd&lt;/code&gt; 实例。这是用户的一个单独的 &lt;code&gt;systemd&lt;/code&gt; 进程，如果用户不再登录，它将再次关闭。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;随着 &lt;code&gt;systemd&lt;/code&gt; 的移动，不仅 DBus 激活的应用程序和服务，您的整个会话现在都使用用户的 &lt;code&gt;systemd&lt;/code&gt; 实例启动。 这有一些副作用，起初可能看起来很奇怪。 例如，之前提到的 session-Y.scope 曾经包含 200 多个进程，现在减少到仅 4 个进程。 另一个副作用是更难理解进程属于哪个会话（这与许多服务相关）或者 ps 将不再显示 tty。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;但是根据 GNOME blog 的文章说明，这些副作用已经被处理了。GNOME会话仍然始终绑定到&lt;code&gt;session-Y.scope&lt;/code&gt;（例如，使用 &lt;code&gt;loginctl kill-session&lt;/code&gt; 继续可靠地工作）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;查看了 GNOME 提供的 dbus 服务单元文件，最终明白了 GNOME 是怎么做到防止 dbus 程序逃逸的，其实操作非常简单，就是提供了一个 &lt;code&gt;gnome-session-shutdown.target&lt;/code&gt;，在这个 target 里关联了 &lt;code&gt;gnome-sessino-restart-dbus.service&lt;/code&gt;，里面执行的是 &lt;code&gt;gnome-session-ctl --restart-dbus&lt;/code&gt;，
其实就是通过 DBus 调用了 &lt;code&gt;systemd --user&lt;/code&gt; 的 dbus 方法，将 &lt;code&gt;dbus.service&lt;/code&gt; 给停掉了。（- - |）&lt;/p&gt;
&lt;h2 id=&#34;改造方案&#34; &gt;改造方案
&lt;span&gt;
    &lt;a href=&#34;#%e6%94%b9%e9%80%a0%e6%96%b9%e6%a1%88&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;既然了解了前因后果，那么我们可以着手改造 DDE 了。&lt;/p&gt;
&lt;p&gt;还需要补充一个知识，会话启动以后，会有一个进程作为会话的入口，会话所有的程序都是从这个入口开始启动的，这个入口就是 &lt;code&gt;startdde&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们既然希望利用 &lt;code&gt;systemd&lt;/code&gt; 的服务来启动，并且帮助我们自动解决依赖，但是拆分项目目前压力比较大，我们还计划重写一部分后端功能，可以在重写时进行调整。&lt;/p&gt;
&lt;h3 id=&#34;启动入口&#34; &gt;启动入口
&lt;span&gt;
    &lt;a href=&#34;#%e5%90%af%e5%8a%a8%e5%85%a5%e5%8f%a3&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;首先我们需要创建一系列的 &lt;code&gt;service&lt;/code&gt; 和 &lt;code&gt;target&lt;/code&gt; 文件。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dde-session-initialized.target
dde-session-manager.service
dde-session-manager.target
dde-session-pre.target
dde-session-restart-dbus.service
dde-session-shutdown.service
dde-session-shutdown.target
dde-session.target
dde-session-x11-services-ready.target
dde-session-x11-services.target
dde-session-x11.target
org.deepin.Session.service
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;看起来非常的多，不用担心，他们都有各自的作用，你可以认为这是将生命周期在 &lt;code&gt;systemd&lt;/code&gt; 实现了。&lt;/p&gt;
&lt;p&gt;由于 &lt;code&gt;systemd&lt;/code&gt; 会帮助我们自动完成依赖解析，那么我们只需要保留一个入口服务，其它服务都禁止手动启动即可。&lt;/p&gt;
&lt;p&gt;我选择使用 &lt;code&gt;dde-session-x11.target&lt;/code&gt; 作为入口，因为未来 deepin 还要支持 wayland，那么在这里就区分开会比较方便一些，因为桌面环境的后续启动与使用什么图形服务没有太大关系。&lt;/p&gt;
&lt;p&gt;根据文件名称就可以方便的了解到这些文件是在什么阶段被执行的。&lt;/p&gt;
&lt;h3 id=&#34;退出时清理&#34; &gt;退出时清理
&lt;span&gt;
    &lt;a href=&#34;#%e9%80%80%e5%87%ba%e6%97%b6%e6%b8%85%e7%90%86&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;创建的 &lt;code&gt;dde-session-shutdown.target&lt;/code&gt; 用来关联所有退出时需要执行的 &lt;code&gt;services&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;清理-dbusservice&#34; &gt;清理 dbus.service
&lt;span&gt;
    &lt;a href=&#34;#%e6%b8%85%e7%90%86-dbusservice&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;提供了一个 &lt;code&gt;dde-session-restart-dbus.service&lt;/code&gt; 用来注销以后关闭 &lt;code&gt;dbus.service&lt;/code&gt;，不要问，问就是没办法～。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;dde-session-shutdown.target&lt;/code&gt; 中会关联这个服务，当用户的会话注销或者桌面环境服务出现问题时，就可以退出所有的 &lt;code&gt;dbus.service&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;当发现会话注销时，&lt;code&gt;dde-session-manager.service&lt;/code&gt; 会执行退出，在服务关闭时启动 &lt;code&gt;dde-session-shutdown.target&lt;/code&gt;，并且使用 &lt;code&gt;replace-irreversibly&lt;/code&gt; 标记为不可撤销。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dde-session-shutdown.target&lt;/code&gt; 中又会清理 &lt;code&gt;dbus.service&lt;/code&gt; 下的所有程序，这样就避免了服务可以通过 dbus 逃逸出会话。&lt;/p&gt;
&lt;h3 id=&#34;架构模型&#34; &gt;架构模型
&lt;span&gt;
    &lt;a href=&#34;#%e6%9e%b6%e6%9e%84%e6%a8%a1%e5%9e%8b&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;center&gt;
&lt;p&gt;&lt;img src=&#34;model.svg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;/center&gt;
&lt;h3 id=&#34;最终效果&#34; &gt;最终效果
&lt;span&gt;
    &lt;a href=&#34;#%e6%9c%80%e7%bb%88%e6%95%88%e6%9e%9c&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;可以看到 startdde 和它的进程树挂在 systemd 下面。&lt;/p&gt;
&lt;center&gt;
&lt;p&gt;&lt;img src=&#34;110276978.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;/center&gt;
&lt;p&gt;原本的位置现在只有一个占位的程序。&lt;/p&gt;
&lt;center&gt;
&lt;p&gt;&lt;img src=&#34;3503592248.jpeg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;/center&gt;
&lt;h2 id=&#34;引用资料&#34; &gt;引用资料
&lt;span&gt;
    &lt;a href=&#34;#%e5%bc%95%e7%94%a8%e8%b5%84%e6%96%99&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://blogs.gnome.org/benzea/2019/10/01/gnome-3-34-is-now-managed-using-systemd/&#34;&gt;https://blogs.gnome.org/benzea/2019/10/01/gnome-3-34-is-now-managed-using-systemd/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;原文链接：&lt;a href=&#34;https://blog.justforlxz.com/2021/12/25/Starting-sessions-with-systemd/&#34;&gt;https://blog.justforlxz.com/2021/12/25/Starting-sessions-with-systemd/&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/xdc2019-wrap-up/">
      <title>翻译：记 2019 年的 X.org 开发者研讨会 - emersion</title>
      <link>https://blog.deepin.org/posts/xdc2019-wrap-up/</link>
      <pubDate>Thu, 11 Mar 2021 11:03:00 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/xdc2019-wrap-up/</guid>
      <description>&lt;p&gt;原文：https://emersion.fr/blog/2019/xdc2019-wrap-up&lt;/p&gt;
&lt;p&gt;&amp;gt; XDC 2019 wrap-up 2019-10-10&lt;/p&gt;
&lt;p&gt;这篇文章是我从加拿大蒙特利尔飞往赫尔辛基的路上写的，我刚参加完 X.Org 的开发者研讨会。从去年西班牙那次会议以来，这是我第二次参加 XDC（译者注：X.Org Developer’s Conference）。&lt;/p&gt;
&lt;p&gt;今年的研讨会非常的给力。我遇到了很多一起工作过的同志，他们来自四面八方的各个组织和项目。会议内容也很牛，我会尝试总结一下这次讨论的一些事情。&lt;/p&gt;
&lt;h1 id=&#34;libliftoff&#34; &gt;libliftoff
&lt;span&gt;
    &lt;a href=&#34;#libliftoff&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;前一阵子，我搞了一个新项目，名字叫 &lt;a href=&#34;https://github.com/emersion/libliftoff&#34;&gt;libliftoff&lt;/a&gt;。它是我在这次会议上的亮点，所以接下来我会详细的介绍它。带着问题往下看：“libliftoff 是用来解决什么问题的呢？”&lt;/p&gt;
&lt;h1 id=&#34;libliftoff-是什么&#34; &gt;libliftoff 是什么
&lt;span&gt;
    &lt;a href=&#34;#libliftoff-%e6%98%af%e4%bb%80%e4%b9%88&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;它是一个合成器的中间层，负责从 clients 中获取 buffers，把它们绘制在一个独立的 buffer 上，并把这个 buffer 显示到屏幕上。假设我现在打开了一个“文本编辑器”和一个“终端”，合成器会把它们的窗口 buffer 复制到屏幕的 buffer 上，一般是用 OpenGL 做这个事。&lt;/p&gt;
&lt;p&gt;不过，复制 buffers 的操作很浪费资源，因为它会做很多事情，比如：它总是进行 alpha 混合、在不同格式之间转换、让渲染引擎一直干活（注：GPU 在执行 OpenGL/Vulkan 的命令）等等，这些操作不仅占用时间，而且也会增加耗电量。&lt;/p&gt;
&lt;p&gt;为了改善性能，许多 GPU 都提供了硬件实现的“叠加平面（planes）”支持（下文直接称为“叠加平面”）。“叠加平面”可以作为显示引擎负责合成工作，这种行为称为&lt;code&gt;直接显示&lt;/code&gt;，可以避免全让合成器做复制 buffers 的活。&lt;/p&gt;
&lt;p&gt;在 Android 上，硬件“叠加平面”在“Hardware Composer”组件中用的很广。许多 Wayland 合成器也会为光标使用“叠加平面”，但是用在其它场景的情况还是很少见。Weston（译者注：一个 Wayland 合成器） 是仅有的使用“叠加平面”的合成器之一。&lt;/p&gt;
&lt;p&gt;使用“叠加平面”并不是一件容易的事情，它有一些限制，比如：不支持 Alpha 通道、不是任意的 buffer 都可以用。虽然这些限制往往会让人觉得很扯淡，但它是由硬件特性决定的。举个例子：对于某些格式的 buffers，Intel 的硬件只能将其放在“叠加平面”的偶数坐标位置、某些 buffers 是在内存上分配的，“叠加平面”干脆就不支持这一类的 buffer、另外，显示设备一般都有带宽限制，在“叠加平面”上使用过大的 buffer 时会失败、某些 ARM 设备上，多个“叠加平面”之间不支持重叠。&lt;/p&gt;
&lt;p&gt;目前为止，关于硬件的这些限制信息，合成器程序还查询不了，因为硬件之间的差异性很大，所以很难设计一个能查询硬件限制信息的 API，甚至在某些新出 GPU 上还会有更奇怪的限制。唯一的方法是搞一些前置工作，用它做一些尝试性的使用。&lt;/p&gt;
&lt;p&gt;因此，设计和实现一个能有效的使用“叠加平面”的代码是一个相当复杂的事情。我一直在想，共享这些代码，是不是会对合成器使用“叠加平面”有所帮助，于是我就奔着这个目标设计了 libliftoff。&lt;/p&gt;
&lt;h1 id=&#34;libliftoff-的研讨会&#34; &gt;libliftoff 的研讨会
&lt;span&gt;
    &lt;a href=&#34;#libliftoff-%e7%9a%84%e7%a0%94%e8%ae%a8%e4%bc%9a&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sr.ht/Fd_h.jpg&#34; alt=&#34;图一&#34;&gt;&lt;/p&gt;
&lt;p&gt;我在 XDC 搞了一个&lt;a href=&#34;https://xdc2019.x.org/event/5/contributions/583/&#34;&gt;研讨会&lt;/a&gt;，讨论关于 libliftoff 的事情，目的是把合成器和驱动专家凑一块，一起商量如何才让合成器有效的用上“叠加平面”。&lt;/p&gt;
&lt;p&gt;libliftoff 的受欢迎程度让我很惊讶，连桌子附近都坐满了人：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在合成器这边，有 wlroots 团队的人（Drew DeVault, Scott Anderson 和我自己），KDE 的 Roman Gilg，Weston 的 Daniel Stone，还有 X server 的 Keith Packard。&lt;/li&gt;
&lt;li&gt;在驱动这边，有很多来自于不同厂商的开发者：有 AMD、Arm、Google、Nvidia、Qualcomm 等等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大家貌似都很兴奋，为 libliftoff 提了很多宝贵的意见。我在此感谢所有参与研讨会的人！&lt;/p&gt;
&lt;p&gt;短期的计划是，让 libliftoff 为合成器提供能实际使用的功能（实验性的），不过有个事我还需要弄清楚，那就是应该怎么样支持多个 output：因为每个 output 都有自己的时序，所以，如何将“叠加平面”从一个 output 迁移到另一个 output 是一件很难搞的问题。最终，如果能实现这样的功能就完美了：为每一个图层分配一个优先级，并把那些频繁在别的图层之前更新的图层放在同一个“叠加平面”上。&lt;/p&gt;
&lt;p&gt;另外也讨论了一些长期计划。&lt;/p&gt;
&lt;p&gt;首先，我们想搞定一个跟内存相关的问题：clients 往往会在内存上分配 buffer，这些 buffer 没法直接用在“叠加平面”上。通常，合成器都会给一些提示信息：“你现在正在使用 &lt;code&gt;Y_TILED&lt;/code&gt;，所以我不能将你用在“叠加平面”上，但是如果你能使用 &lt;code&gt;X_TILED&lt;/code&gt; 我就能把你用在“叠加平面”上”，这样 client 就可以决定切换它的 buffer 格式。我大概在一年前给 Wayland 提了一个&lt;a href=&#34;https://patchwork.freedesktop.org/series/52370/&#34;&gt;补丁&lt;/a&gt;，这个补丁新增的信息能被 libliftoff 识别出来，在这种情况下，合成器可以把这个信息转发给 clients。&lt;/p&gt;
&lt;p&gt;接下来是关于内核 API 的问题，现在只有一个办法能让我们知道是否可以使用“叠加平面”，那就是用 atomic 相关的 test 接口测试（译者注：atomic 是 DRM 提供的一系列原子操作接口，支持事务），这个做法相当的不清真，因为我们必须得遍历尝试多个组合条件，基本上是暴力求解。除此之外，比较好的做法都要跟具体的硬件相关。&lt;/p&gt;
&lt;p&gt;为了搞定这个问题，我们必须让内核提供更多的信息，但是因为不同硬件的差异性很大，设计一个足够通用的 API 将是一个很蛋疼的事情，另一个解决方案是在 libliftoff 中添加特定厂商的插件，允许每个驱动添加自己的代码，以此更好的匹配自己的硬件。目前来说，貌似这是最好的方案了。&lt;/p&gt;
&lt;p&gt;这是本次讨论的总结(&lt;a href=&#34;https://fs.emersion.fr/protected/presentations/present.html?src=libliftoff-xdc2019/index.md&#34;&gt;PPT&lt;/a&gt;)：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://youtu.be/JIry8jpbPUY&#34;&gt;视频：XDC 2019 - Day 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Scott Anderson 也写了一些&lt;a href=&#34;https://lists.freedesktop.org/archives/wayland-devel/2019-October/040924.html&#34;&gt;总结&lt;/a&gt;，是关于 Wayland 协议的一些想法的详细介绍。&lt;/p&gt;
&lt;h1 id=&#34;分配器&#34; &gt;分配器
&lt;span&gt;
    &lt;a href=&#34;#%e5%88%86%e9%85%8d%e5%99%a8&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;在三年前的 XDC 上，Nvidia 的 James Jones 提出了这个&lt;a href=&#34;https://www.x.org/wiki/Events/XDC2016/Program/jones_unix_device_mem_alloc/&#34;&gt;分配器项目&lt;/a&gt;，用于解决 GBM/EGLStreams 现有的问题。今年，他做了一个关于 GBM、Nouveau 和 transitions 的&lt;a href=&#34;https://xdc2019.x.org/event/5/contributions/335/&#34;&gt;新的分享&lt;/a&gt;。跟之前的提案相比，这个提案的目的是在现有 API 的基础上建立一种叫 transition 的机制。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://youtu.be/HYa4UvVtMOE&#34;&gt;视频：XDC 2019 - Day 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;transition 可以用来减少渲染引擎的带宽占用。某些 GPU 支持使用压缩的 buffer，与未压缩的 buffer 相比，GPU 可以在这些压缩后的 buffer 上直接执行 OpenGL/Vulkan 的操作，这样做效率很高。所以，我们应该在渲染时使用压缩后的 buffers。&lt;/p&gt;
&lt;p&gt;不过，压缩后的 buffers 不能直接用在“叠加平面”上，需要先将它们解压才能使用，有一个方法是把它解压为一个新的 buffer，但是这样需要复制数据，速度会很慢。&lt;/p&gt;
&lt;p&gt;在某些 GPU 上有个更好的方法，它们支持在“叠加平面”中直接解压 buffer。这指的是：当一个 Wayland client 在压缩后的 buffer 上渲染，如果这个 buffer 可以在“叠加平面”中解压，并且之后可以直接使用它，那我们就将这种“buffer 就地解压的程序”称为 transition。&lt;/p&gt;
&lt;p&gt;之后我们在一个研讨会中继续探讨 transitions，讨论了应该在何时何地进行 transition 操作。想法一是：如果合成器准备把一个 buffer 用到“叠加平面”中，那就在合成器中做这个事，可以用 libliftoff 判断什么时候应该使用 transition。另一个想法是：在 client 把 buffer 提交给合成器之前做这个事，client 需要从合成器那获取一个信息，以便知道应该什么时候执行操作（这就是我之前提到的那个 &lt;a href=&#34;https://patchwork.freedesktop.org/series/52370/&#34;&gt;Wayland 协议的补丁&lt;/a&gt;可以做的事情）。&lt;/p&gt;
&lt;p&gt;这是这次研讨会的总结：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://youtu.be/JIry8jpbPUY&#34;&gt;视频：XDC 2019 - Day 3&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;可变刷新率vrr&#34; &gt;可变刷新率（VRR）
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%af%e5%8f%98%e5%88%b7%e6%96%b0%e7%8e%87vrr&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;AMD 的 Harry Wentland 搞了一个关于“自适应同步”（DisplayPort 的技术）、“可变刷新率”（HDMI 的技术）和“FreeSync”（AMD 的技术）的研讨会。所有这些技术都是为了适配屏幕的功能，允许它从“固定为 60Hz”的模式变成“可以稍微等待下一帧”的模式（译者注：动态刷新率的模式）。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://youtu.be/HYa4UvVtMOE&#34;&gt;视频：XDC 2019 - Day 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这东西的主要使用场景是游戏应用。游戏通常是用动态刷新率的方式更新画面（依赖于场景的复杂度），下一帧可以更快也可以更慢。游戏还希望能降低延迟，即避免渲染到在屏幕上显示的时间差过长。&lt;/p&gt;
&lt;p&gt;在屏幕固定刷新率的情况下，如果有一帧的渲染用时稍微长了一点，它错过了显示器的刷新时机，这样就会导致这一帧的显示有些滞后，而 VRR 则允许增加屏幕刷新时的等待时间，以免丢帧。&lt;/p&gt;
&lt;p&gt;另一种场景是视频播放。视频有固定的帧率，但是通常会与屏幕的刷新率不同，视频播放器需要用帧插值的方式，才能适应屏幕的刷新率，而 VRR 则允许降低刷新率以匹配能完美播放视频的时序。&lt;/p&gt;
&lt;p&gt;屏幕内容通常都是处于静止状态，在这种情况下，VRR 还可以降低电量消耗。比如用户在使用文本编辑器时，完全不需要 60FPS，这时候合成器可以降低屏幕的刷新率。&lt;/p&gt;
&lt;p&gt;游戏的使用场景比较简单，合成器可以比 deadline 稍微晚一点点提交帧，硬件可以搞定这种情况。其它场景需要做更多的工作，要想把 VRR 用在视频播放器上，我们需要某种定时提交帧的 API。要想用 VRR 做节能，合成器要能支持帧率改变时的平滑过渡，否则屏幕会有闪烁问题（这是支持 VRR 功能的屏幕的限制）。&lt;/p&gt;
&lt;p&gt;在我看来，我们目前应该把精力放在 Wayland 协议对游戏场景的支持上，因为搞其它两种场景需要做的事情太多了，它们不仅需要更多的新 API（内核和 Wayland 的都需要），还需要做很多的实验工作。&lt;/p&gt;
&lt;h1 id=&#34;chamelium&#34; &gt;Chamelium
&lt;span&gt;
    &lt;a href=&#34;#chamelium&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h1&gt;&lt;p&gt;我在 Intel 实习期间，参与了一个叫 Chamelium 的项目，它是一个基础的屏幕模拟器，你可以通过网络向它发送命令，它已经被用在了 ChromeOS 的 i915 显卡环境的 CI 服务中。&lt;/p&gt;
&lt;p&gt;我搞了一个关于这个项目以及我在项目内的工作的研讨会：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://youtu.be/JIry8jpbPUY&#34;&gt;视频：XDC 2019 - Day 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;除了这三个主题，我们还讨论了很多其它的东西，但是不能全塞到这一篇博客中讲，所以就说到这吧。感谢所有参加 XDC 的人，感谢 Mark Filion 组织了这次活动，还要感谢所有赞助商，没有你们的赞助就不会有的这次活动！&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/screensaver/">
      <title>开发DDE的屏保</title>
      <link>https://blog.deepin.org/posts/screensaver/</link>
      <pubDate>Tue, 03 Sep 2019 20:41:00 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/screensaver/</guid>
      <description>&lt;p&gt;还记得刚推出屏幕保护功能那会儿，我偶逛论坛，围观大家对这个功能的评价。其中让我印象最深的一句话就是：“一股Windows98风”，总之，评价总结出来就是一个字：“吃藕”。&lt;/p&gt;
&lt;p&gt;大家追求美好事物的诚挚之心深深地打动了我，而且，我个人做事情的风格是喜欢未雨绸缪，在屏幕保护程序开发之初，就已经定好了易于扩展的架构，所以我当时就下定了决心，为大家开发一个非Windows98风格的屏保。&lt;/p&gt;
&lt;p&gt;为了达到绝对“非Windows98”的目的，我特意选择了Windows10中的默认屏保作为参考，在无数个周末的战斗下，最终成功将名为“泡泡”的屏保应用发布到了商店（项目地址：&lt;a href=&#34;https://github.com/zccrs/screensaver-pp&#34;&gt;https://github.com/zccrs/screensaver-pp&lt;/a&gt; ）。&lt;/p&gt;
&lt;p&gt;本着“授人以鱼不如授人以渔”的理念，特地整理了这篇文章协助大家开发一款属于自己的时尚屏保应用。&lt;/p&gt;
&lt;h2 id=&#34;正文&#34; &gt;正文
&lt;span&gt;
    &lt;a href=&#34;#%e6%ad%a3%e6%96%87&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;在Linux+X11生态环境中，xscreensaver是最“流行”的屏幕保护程序，有着非常多的屏保资源，所以deepin-screensaver必然要兼容它的资源。&lt;/p&gt;
&lt;p&gt;但是，xscreensaver对屏保资源的扩展方式并不符合deepin的开发理念，因此，deepin-screensaver实现了一套全新的屏保扩展方式。&lt;/p&gt;
&lt;p&gt;支持使用Qt qml模块编写屏保应用，一个标准的屏保应用只需要包含一个 &amp;ldquo;xx.rcc&amp;rdquo; 文件，将文件安装到 /usr/lib/deepin-screensaver/resources目录。&lt;/p&gt;
&lt;p&gt;rcc 格式是一个编译之后的Qt资源文件，在这个资源文件中至少要包含两个文件：qml代码文件、屏保封面图。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://user-images.githubusercontent.com/13449038/218910865-688f689f-6d0c-49dd-b916-5b6a75bd354c.png&#34; alt=&#34;image&#34;&gt;&lt;/p&gt;
&lt;p&gt;图中文件名括号内为其别名，也就是屏保主应用加载文件时能读取到的文件名。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;qml代码文件：屏保应用的代码入口，会被屏保主程序加载显示&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;屏保封面图：设置屏保入口显示的预览图，支持svg png jpeg bmp等格式
所有的文件必须以特定的目录结构组织到一个Qt资源文件（qrc文件），以“泡泡”屏保为例：qml.qrc 为其资源文件，包含三个前缀路径&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/deepin-screensaver/modules：放置屏保应用的主qml文件，此路径下的所有qml文件都会被当做一个独立的屏保应用，因此，项目中的其它文件需要额外建立新的前缀放置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/deepin-screensaver/modules/cover：放置屏保应用封面图文件，文件名称必须和modules目录中的qml文件一致，且包含它的 &amp;ldquo;.qml&amp;rdquo; 后缀。如图上，qml文件全名为：&amp;ldquo;pp.qml&amp;rdquo;，封面图全名为：&amp;ldquo;pp.qml.svg&amp;rdquo;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/deepin-screensaver/modules/pp：此前缀不是必须的，用于放置项目中的其它文件。为了不与其它项目产生冲突，建议使用项目名作为目录名称
资源文件最好以项目名称命名，避免和其它屏保应用冲突。另外，大家可能已经发现了，这三个前缀都有一个共同点，那就是以 &amp;ldquo;/deepin-screensaver/modules&amp;quot;开头，的确，这是一个格式要求，不能随意更改路径。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;主qml文件作为屏保应用的入口，它的根元素一定要设置&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;anchors.fill: parent
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样才能确保屏保应用充满整个屏幕。在多屏的情况下下，会创建多个窗口示例，可根据屏幕绘制不同的屏保内容。
项目编译其实很简单，只需要使用Qt提供的rcc命令将qrc文件编译为rcc文件即可，使用qmake构建系统时，可以在pro文件中调用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;system(rcc --binary $$_PRO_FILE_PWD_/xx.qrc -o $$_PRO_FILE_PWD_/xx.rcc)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当然，最后不要忘记将 xx.rcc 文件安装到deepin-screensaver所要求的目录。做完这所有的步骤后，回到桌面，在右键菜单中选择“壁纸与屏保”，切换到屏保设置后即可看到新添加的屏保应用。
另外，deepin-screensaver为qml提供了获取当前屏幕截图的接口，只需要为Image项指定特定的路径即可：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;Image {
    anchors.fill: parent
    source: &amp;#34;image://deepin-screensaver/screen/&amp;#34; + Screen.name
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;由于要获取屏幕名称，上述代码需要 &amp;ldquo;import QtQuick.Window 2.2&amp;rdquo; 使用&lt;/p&gt;
&lt;h2 id=&#34;后记&#34; &gt;后记
&lt;span&gt;
    &lt;a href=&#34;#%e5%90%8e%e8%ae%b0&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;屏保封面图最佳比例为：8:5，推荐使用svg格式，以更好的适应高分屏缩放。&lt;/p&gt;
&lt;p&gt;推荐大家使用Qt Creator作为项目的开发工具，可以方便的编辑 qrc 文件。&lt;/p&gt;
&lt;p&gt;泡泡屏保是一个完整的demo，有任何疑问的地方都可以以其作为参考&lt;/p&gt;
&lt;h2 id=&#34;参考&#34; &gt;参考
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%82%e8%80%83&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;“泡泡”屏保项目：&lt;a href=&#34;https://github.com/zccrs/screensaver-pp&#34;&gt;https://github.com/zccrs/screensaver-pp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Qt资源文件：&lt;a href=&#34;https://doc.qt.io/qt-5/resources.html&#34;&gt;https://doc.qt.io/qt-5/resources.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/">
      <title>修理 FreeRDP</title>
      <link>https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/</link>
      <pubDate>Mon, 17 Dec 2018 14:00:00 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/%E4%BF%AE%E7%90%86-freerdp/</guid>
      <description>&lt;p&gt;头段时间入了一个大坑儿，大概被坑了有一个月之久，出来之后同事还不忘嘲讽一番：”这么个事情就搞了一个月，看吧，你果然是老了“。听了这句话，心里真是百般滋味，但转念一想，”我年轻的时候做事好像也不怎么快“，顿时也就释怀了 🙂&lt;/p&gt;
&lt;p&gt;这个坑就是”给 FreeRDP 的 RAIL 模式添加托盘支持“，当然，跟所有的需求一样，这么具有总结性而又直指根源的需求描述，绝对不是它最原始的模样——我刚接到这个坑的时候，它是这样的：FreeRDP 的 RAIL 模式下，应用的托盘在我们 DDE 下不显示。请注意这里说得是&lt;strong&gt;不显示&lt;/strong&gt;，而不是后来发现的&lt;strong&gt;压根儿没有支持&lt;/strong&gt;！&lt;/p&gt;
&lt;h3 id=&#34;freerdp&#34; &gt;FreeRDP
&lt;span&gt;
    &lt;a href=&#34;#freerdp&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;说到这，可能有读者还不了解 FreeRDP 和 RAIL，所以先简单介绍一下。&lt;/p&gt;
&lt;p&gt;RDP 其实是一个协议名称，全称 Remote Desktop Protocol（远程桌面协议），是微软公司开发的一套用于远程桌面展示和操作的协议，FreeRDP 就是它在开源世界的实现咯。而 RAIL 的全称是 Remote Application Integrated Locally （远程应用本地集成），其实就是非常类似大家熟悉的虚拟机的”无缝模式“，通过将应用的显示跟本地环境相融合，让用户完全感受不到这个应用其实不在本机运行——就是这么一种技术。&lt;/p&gt;
&lt;p&gt;问题也就出在这，我当时第一反应是这么老的技术实现肯定比较完整了，托盘没有显示出来应该是跟 DDE 的兼容性有点小问题，稍微修一下就完了，三下五除二的事情，所以满口答应了下来……&lt;/p&gt;
&lt;h2 id=&#34;经过&#34; &gt;经过
&lt;span&gt;
    &lt;a href=&#34;#%e7%bb%8f%e8%bf%87&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;既然答应了，硬着头皮也要顶下去的。何况调 BUG 这种事情——不管是不是我们自己的问题——在深度都是家常便饭。慢慢地，调各种项目的 BUG 竟然成了我的一种乐趣——每次开始接手一个新的项目的时候，我都把自己当成了福尔摩斯或者胡八一，或者也可以是其他全世界最聪明的那类人 ?，在通过代码找寻问题线索的过程中，慢慢成为这个项目世界中的主宰，解开真相……&lt;/p&gt;
&lt;p&gt;额……不好意思，白日梦又发作了一会儿。总之，这次也不例外，而且刚好这次在调问题的过程中有记录几个关键环节，所以打算把中间的过程写成日记性质的记录，看看能不能有更好的阅读效果：&lt;/p&gt;
&lt;h3 id=&#34;2018-11-14&#34; &gt;2018-11-14
&lt;span&gt;
    &lt;a href=&#34;#2018-11-14&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;从”沈老板“那收到需求，说 FreeRDP 在我们系统上有问题，应用的托盘显示不出来，QQ之类的程序关闭了窗口以后就没办法显示出来了，无法使用。这丫的又拿刘老大来压我……呵呵，想削他。不过看在他快要当爸爸的份上，还是算了。问了下时间要求，大概需要两周左右有初步的结果。不过我自己最近没有什么时间，先把锅丢给了印象中还比较熟悉网络协议的 @Blumia 同学。&lt;/p&gt;
&lt;h3 id=&#34;2018-11-15&#34; &gt;2018-11-15
&lt;span&gt;
    &lt;a href=&#34;#2018-11-15&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;从 @Blumia 那收到反馈，可能 FreeRDP 没有实现托盘图标这部分的功能，我怕他一个人搞不定，简单翻了翻 FreeRDP 的项目 wiki 和 RDP 的一些介绍，给了他，让他先帮忙找一下需要补充实现部分的代码结构。&lt;/p&gt;
&lt;h3 id=&#34;2018-11-16&#34; &gt;2018-11-16
&lt;span&gt;
    &lt;a href=&#34;#2018-11-16&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;没时间处理。&lt;/p&gt;
&lt;p&gt;@Blumia 搭了测试环境。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;中间几天两&lt;strong&gt;&lt;strong&gt;个&lt;/strong&gt;&lt;/strong&gt;人都没有时间处理 FreeRDP&lt;strong&gt;&lt;strong&gt;的&lt;/strong&gt;&lt;/strong&gt;事情。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;2018-11-22&#34; &gt;2018-11-22
&lt;span&gt;
    &lt;a href=&#34;#2018-11-22&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;留了少部分时间，看了 FreeRDP 的代码，大概找到了托盘图标相关处理应该在的位置。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RAIL 主要接口的实现都在 &lt;code&gt;xf_rail.c&lt;/code&gt; 中。&lt;/li&gt;
&lt;li&gt;托盘图标相关的处理在 &lt;code&gt;xf_rail_no``t``ify_icon_*&lt;/code&gt; 相关的函数，这些函数在 &lt;code&gt;xf_rail_re``gi``s``t``er_``u``pdate_call``b``a``c``ks&lt;/code&gt; 里面被注册到 &lt;code&gt;rdpWind``o``wUpdate&lt;/code&gt; 对象上。&lt;/li&gt;
&lt;li&gt;部署了一份测试服务器的虚拟机。&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;wf_rail.``c&lt;/code&gt; 中发现一个 &lt;code&gt;PrintRailI``c``onInfo&lt;/code&gt; 函数，放在 &lt;code&gt;xf_``r``ail_notify_i``c``on_common&lt;/code&gt; 中打印了获取到的图标的信息，发现能正常获取一些图标的数据。&lt;/li&gt;
&lt;li&gt;xf 应该是 x11 freerdp 的缩写，而 wf 应该是 windows freerdp 的缩写。
&lt;strong&gt;中间又是几天没有时间处理 FreeRDP 的事情。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2018-11-27&#34; &gt;2018-11-27
&lt;span&gt;
    &lt;a href=&#34;#2018-11-27&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;有半天的时间看 FreeRDP 的代码，同时跟 FreeRDP 的邮件列表发了&lt;a href=&#34;https://sourceforge.net/p/freerdp/mailman/message/36477842/&#34;&gt;邮件&lt;/a&gt;询问相关技术问题，主要是为了验证自己的想法，没有指望有回复或者什么比较大用处的信息，只是希望如果自己想法是错的，有人及时纠正一下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;图标显示的问题不打算优先处理，现在的问题变成如何让服务端知道了本地用户点了托盘图标。&lt;/li&gt;
&lt;li&gt;搜了一下 event 相关的文件，发现 &lt;code&gt;x``f_event.c&lt;/code&gt; ，怀疑 X 相关的事件都是在这里面处理的，这个也不用急着去证明，先看看 client 怎么让 server 感知本地的事件。&lt;/li&gt;
&lt;li&gt;没有头绪，只好看了一下 RAIL 的 &lt;a href=&#34;https://msdn.microsoft.com/en-us/library/cc242568/&#34;&gt;主要协议&lt;/a&gt;，发现 &lt;code&gt;Cli``e``nt Notify Ev``en``t&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;怀疑托盘图标在 client 端（本地端）的事件是通过 ClientNotifyEvent 发送给 server 端的。&lt;/li&gt;
&lt;li&gt;ClientNotifyEvent 相关：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rail_main.``c&lt;/code&gt; 中的 &lt;code&gt;Vi``r``tualChann``e``lE``n``tryEx&lt;/code&gt; 应该是 RDP 中 RAIL 相关的 channel 处理的函数。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2018-11-28&#34; &gt;2018-11-28
&lt;span&gt;
    &lt;a href=&#34;#2018-11-28&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;上午继续看了 FreeRDP 的代码。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;V``i``rtualCha``n``nelEntryEx&lt;/code&gt; 中给 &lt;code&gt;RailClientContext&lt;/code&gt; 设置的哪些成员函数，有些函数（Server开头的）都是需要真正的 client 去实现的，Client 开头的函数（包括 ClientNotifyEvent）都是默认有实现，但是这些 Client 开头的函数都是在哪调用的呢？
找到重要线索：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;/**
* The position of the X window can become out of sync with the RDP window
* if the X window is moved locally by the window manager.  In this event
* send an update to the RDP server informing it of the new window position
* and size.
*/
void xf_rail_adjust_position(xfContext* xfc, xfAppWindow* appWindow)
{
RAIL_WINDOW_MOVE_ORDER windowMove;

if (!appWindow-&amp;gt;is_mapped || appWindow-&amp;gt;local_move.state != LMS_NOT_ACTIVE)
    return;

/* If current window position disagrees with RDP window position, send update to RDP server */
if (appWindow-&amp;gt;x != appWindow-&amp;gt;windowOffsetX ||
    appWindow-&amp;gt;y != appWindow-&amp;gt;windowOffsetY ||
    appWindow-&amp;gt;width != appWindow-&amp;gt;windowWidth ||
    appWindow-&amp;gt;height != appWindow-&amp;gt;windowHeight)
{
    windowMove.windowId = appWindow-&amp;gt;windowId;
    /*
     * Calculate new size/position for the rail window(new values for windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server
     */
    windowMove.left = appWindow-&amp;gt;x;
    windowMove.top = appWindow-&amp;gt;y;
    windowMove.right = windowMove.left + appWindow-&amp;gt;width;
    windowMove.bottom = windowMove.top + appWindow-&amp;gt;height;
    xfc-&amp;gt;rail-&amp;gt;ClientWindowMove(xfc-&amp;gt;rail, &amp;amp;windowMove);
}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;其中有主动调用 &lt;code&gt;RailClientContext&lt;/code&gt; 的 &lt;code&gt;ClientWindowMove&lt;/code&gt; 函数。这个函数又是 &lt;code&gt;xf_``e``v``en``t.``c&lt;/code&gt; 中 &lt;code&gt;xf_``e``v``en``t_C``o``nfig``u``r``e``Notify&lt;/code&gt; 有调用，再加上这个函数的注释说明，差不多能证明所有的 X事件相关的都是在 &lt;code&gt;xf_event.c&lt;/code&gt; 中处理的，跟之前的猜测一致。&lt;/li&gt;
&lt;li&gt;那样的话如果想发送事件到 server，应该就是在 &lt;code&gt;xf_event.c&lt;/code&gt; 中收到我们自己创建的托盘图标的点击事件后，发送一个 ClientNofityEvent 。
尝试在收到 &lt;code&gt;xf_r``a``il_``no``ti``f``y_``i``con_u``p``d``a``te&lt;/code&gt; 的时候主动调用一次 &lt;code&gt;Cli``ent``NotifyEvent&lt;/code&gt; 看看会发生什么。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;RAIL_NOTIFY_EVENT_ORDER notifyEvent;
notifyEvent.windowId = orderInfo-&amp;gt;windowId;
notifyEvent.notifyIconId = orderInfo-&amp;gt;notifyIconId;
notifyEvent.message = NIN_SELECT;

xfContext* xfc = (xfContext*) context;
xfc-&amp;gt;rail-&amp;gt;ClientNotifyEvent(xfc-&amp;gt;rail, &amp;amp;notifyEvent);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;选择 message 为 &lt;code&gt;NIN_SELECT&lt;/code&gt; 是因为根据 &lt;code&gt;r``ail.h&lt;/code&gt; 里面仅有的零星注释，只能推测这个可能是针对托盘的。&lt;/li&gt;
&lt;li&gt;试了下，没有任何反应。尝试换成 NIN_KEYSELECT ，更不行。&lt;/li&gt;
&lt;li&gt;硬着头皮又翻了一下协议，发现 &lt;code&gt;Notificat``i``o``n``I``c``on Info``r``mation&lt;/code&gt; 这段，随便翻了一下，看起来没有有用信息。&lt;/li&gt;
&lt;li&gt;偶发奇想搜了一下 select 关键字想看一下这个到底是什么意思，偶然发现 &lt;code&gt;Cli``en``t Notify E``ve``nt PDU&lt;/code&gt; 这一节（能跟源码 &lt;code&gt;r``ai``l``.h&lt;/code&gt; 里面的一些注释对应上），里面有 WM_LBUTTONDOWN 、 WM_LBUTTONUP 等针对托盘图标的动作定义。&lt;/li&gt;
&lt;li&gt;真是对自己做事情毛毛躁躁的行为无语了，要不是心血来潮，差点就错过这么重要的信息。&lt;/li&gt;
&lt;li&gt;把 message 改成 &lt;code&gt;WM_LBUTTONDOWN&lt;/code&gt; 和 &lt;code&gt;WM_LBUTTONUP&lt;/code&gt; ，满怀期待。&lt;/li&gt;
&lt;li&gt;测试还是无效果……仍旧不死心，怀疑测试程序（@Blumia 同学搞的一个音乐程序）的稳健程度。&lt;/li&gt;
&lt;li&gt;使用 TIM 再试，还是不行，不能唤出主窗口。&lt;/li&gt;
&lt;li&gt;感觉走进了死胡同。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2018-11-29&#34; &gt;2018-11-29
&lt;span&gt;
    &lt;a href=&#34;#2018-11-29&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;继续看 FreeRDP 的问题，主窗口隐藏后不能显示的问题太奇怪了，得找一个简单点的程序，排除复杂影响。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;让 @zccrs 写了一个简单的窗口程序，定时隐藏、显示窗口。&lt;/li&gt;
&lt;li&gt;发现程序窗口隐藏后无法再显示出来……&lt;/li&gt;
&lt;li&gt;赶紧给上游报了一个 &lt;a href=&#34;https://github.com/FreeRDP/FreeRDP/issues/5078&#34;&gt;issu&lt;/a&gt;&lt;a href=&#34;https://github.com/FreeRDP/FreeRDP/issues/5078&#34;&gt;e&lt;/a&gt; ，希望上游能修复。但是也不能期望上游很快能修复这个问题，所以自己还是尝试看代码……&lt;/li&gt;
&lt;li&gt;上游回复还挺快的，但是对方好像是 FreeRDP 目前的维护者，说自己对 RAIL 这部分协议本身还不是特别熟悉。&lt;/li&gt;
&lt;li&gt;不能依赖的上游不是好上游，继续看代码，发现在窗口的显示隐藏主要是通过 &lt;code&gt;WINDOW_STATE_ORDER&lt;/code&gt; 中的 &lt;code&gt;s``h``o``wState&lt;/code&gt; 控制的，处理的函数是 &lt;code&gt;xf_``r``ail_window_``c``o``m``mon&lt;/code&gt; ，里面调用了 &lt;code&gt;xf_ShowWin``d``ow&lt;/code&gt; 这个函数，但是这个函数在该显示窗口的时候只是调整了一下窗口的最大化、最小化状态，并没有 Map 这个本地窗口。&lt;/li&gt;
&lt;li&gt;加了 &lt;code&gt;XMapW``in``dow(xf``c``-&amp;gt;di``s``pl``a``y,&lt;/code&gt; &lt;code&gt;appWi``n``dow-&amp;gt;ha``n``dl``e``)&lt;/code&gt; 这行，满心期待 bugfix。&lt;/li&gt;
&lt;li&gt;编译代码测试，发现窗口连关闭都不能关闭了……&lt;/li&gt;
&lt;li&gt;xfContext 的 appWindow 是本地窗口的一个抽象表示。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2018-12-03&#34; &gt;2018-12-03
&lt;span&gt;
    &lt;a href=&#34;#2018-12-03&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;觉得这个事情没有什么太大的希望了，不过既然已经知道托盘图标的显示方式和事件的发送，但是没有实际实现，到时候”沈老板“来问，也不好说都是在脑子里，干脆先把之前测通但是没有实现的内容实现一下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;托盘窗口加好了，事件也都加上了。&lt;/li&gt;
&lt;li&gt;眼看着都快要完美了，就差那么一点问题没有解决，实在是不甘心，继续死磕那个问题。&lt;/li&gt;
&lt;li&gt;尝试了各种手段调试，跟整个程序的命令传递，都没有能解决问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2018-12-04&#34; &gt;2018-12-04
&lt;span&gt;
    &lt;a href=&#34;#2018-12-04&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;调试了一天，一遍又一遍看窗口事件，一点一点排除事件处理函数，终于发现了上游犯的一个低级错误，我很怀疑当时作者有没有测试一下 🙁&lt;/p&gt;
&lt;p&gt;做了修复，提交了 &lt;a href=&#34;https://github.com/FreeRDP/FreeRDP/pull/5097/&#34;&gt;PR&lt;/a&gt;，并且顺利合并。&lt;/p&gt;
&lt;p&gt;心情终于舒畅了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;中间有事请假一天&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;2018-12-06&#34; &gt;2018-12-06
&lt;span&gt;
    &lt;a href=&#34;#2018-12-06&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;托盘图标也画上了，不过怎么感觉颜色有点偏。&lt;/p&gt;
&lt;p&gt;调了一下颜色的格式（RGBA -&amp;gt; BGRA），图标显示正常了。&lt;/p&gt;
&lt;p&gt;事情终于告一段路了。&lt;/p&gt;
&lt;h2 id=&#34;结束&#34; &gt;结束
&lt;span&gt;
    &lt;a href=&#34;#%e7%bb%93%e6%9d%9f&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;折腾了这么长时间，事情终于搞定了，这应该是最近一年里面时间拉的最长的 BUG 了。&lt;/p&gt;
&lt;p&gt;实现算是完了，也能使用。但是还有一些细节没有特别完善，已提交提交到上游 &lt;a href=&#34;https://github.com/FreeRDP/FreeRDP/pull/5110&#34;&gt;一个新&lt;/a&gt;&lt;a href=&#34;https://github.com/FreeRDP/FreeRDP/pull/5110&#34;&gt;的&lt;/a&gt;&lt;a href=&#34;https://github.com/FreeRDP/FreeRDP/pull/5110&#34;&gt;PR&lt;/a&gt; ，希望能早日合并造福一方用户。&lt;/p&gt;
&lt;h2 id=&#34;感想&#34; &gt;感想
&lt;span&gt;
    &lt;a href=&#34;#%e6%84%9f%e6%83%b3&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;p&gt;感觉我之前对 wine 有偏见，一直比较拒绝使用（或者大量使用）wine 的东西，但是实际上在修复 FreeRDP 的过程中，我竟然觉得这也是一种不错的解决方案……仔细想想，还是 wine 方便一点，至少不需要依赖一个服务端。&lt;/p&gt;
&lt;p&gt;准备入坑 wine 啦 ~(≧▽≦)/~&lt;/p&gt;</description>
    </item>
    
    <item xml:base="https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/">
      <title>用Flex和Bison自制“五仁”语言</title>
      <link>https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/</link>
      <pubDate>Wed, 04 May 2016 14:00:00 +0800</pubDate>
      
      <guid>https://blog.deepin.org/posts/%E7%94%A8flex%E5%92%8Cbison%E8%87%AA%E5%88%B6%E4%BA%94%E4%BB%81%E8%AF%AD%E8%A8%80/</guid>
      <description>&lt;ul&gt;
&lt;li&gt;“五仁”官方名称：zScript&lt;/li&gt;
&lt;li&gt;变量使用关键字&amp;quot;var&amp;quot;进行声明，变量本身无类型&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;变量声明可放在源码中任何位置，可在任何位置被调用，例如，var a在源码中最后一行，也可以在第一行使用变量a&lt;/li&gt;
&lt;li&gt;每条语句的结尾必须是&#39;;&amp;lsquo;或换行符(后面准备改成强制使用&amp;rsquo;;&amp;lsquo;结尾)&lt;/li&gt;
&lt;li&gt;目前支持的数据类型：int、double、bool、string、list、tuple、object 、undefined&lt;/li&gt;
&lt;li&gt;函数也是object类型&lt;/li&gt;
&lt;li&gt;string的定义为 &#39;&amp;rsquo; 或 &amp;quot;&amp;quot; 中所包裹的内容，支持C和JavaScript风格的字符转义&lt;/li&gt;
&lt;li&gt;支持的运算符： =、+、-、&lt;em&gt;、/、%、&amp;amp;、|、~、^、&amp;amp;&amp;amp;、||、+=、-=、&lt;/em&gt;=、/=、%=、&amp;amp;=、|=、~=、^=、&amp;amp;&amp;amp;=、||=、!、&amp;gt;、&amp;lt;、=、==、===、!=、!==、++、&amp;ndash;&lt;/li&gt;
&lt;li&gt;支持匿名函数，函数闭包，多值返回&lt;/li&gt;
&lt;li&gt;支持的分支结构：if else&lt;/li&gt;
&lt;li&gt;支持的循环结构：goto while for（后面准备加入do while、foreach）&lt;/li&gt;
&lt;li&gt;while for 循环结构中使用break跳出循环，continue立马开始执行下一次循环，break和continue可组合使用
例如：break, break 则可以直接跳出两层循环，组合使用时continue只能放在末尾&lt;/li&gt;
&lt;li&gt;支持switch case语句，可枚举int bool string undefined&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;flexlexical-analyzar&#34; &gt;Flex(Lexical Analyzar)
&lt;span&gt;
    &lt;a href=&#34;#flexlexical-analyzar&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Lex是一个产生词法分析器的工具(最早是Eric Emerson Schmidt和Mike Lesk制作）是许多UNIX系统的标准词法分析器（lexical analyzer）产生程序，而且这个工具所作的行为被详列为POSIX标准的一部分。而Flex就是由Vern Paxon实现的Lex&lt;/li&gt;
&lt;li&gt;FLex读进一个代表词法分析器规则的输入字符串流，然后输出C/C++语言的词法分析器源代码。&lt;/li&gt;
&lt;li&gt;wiki：&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;https://&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;en&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;.wik&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;i&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;p&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;e&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;dia.o&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;r&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;g/w&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;i&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;ki/F&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;l&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;ex&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;_&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;%28lex&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;ical&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;_&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;an&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;a&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;ly&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;s&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;er&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;_g&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;e&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;n&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;erat&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;o&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;r&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Flex_%28lexical_analyser_generator%29&#34;&gt;%29&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;flex的文件结构&#34; &gt;Flex的文件结构
&lt;span&gt;
    &lt;a href=&#34;#flex%e7%9a%84%e6%96%87%e4%bb%b6%e7%bb%93%e6%9e%84&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;文件分成三个区块，均以一个只有两个百分比符号（%%）的单行来分隔，如下：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;定义区域
%%
规则区域
%%
C/C++代码区&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;定义区块是用来定义宏以及导入C写成的头文件所在区块。在这里面也可以写一些C代码，这一些代码会被复制到产生出来的C源代码的开头部分。&lt;/li&gt;
&lt;li&gt;规则区块是最重要的区块；这里将样式与C的陈述（statement）串连在一起。这一些样式都是正则表达式。当lexer看到输入里面有合乎给定的样式时，则会操作相对应的C代码。这就是flex运作的基础。&lt;/li&gt;
&lt;li&gt;C代码区块的内容会原封不动的照搬到产生出来的C源代码里面。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;小例子&#34; &gt;小例子
&lt;span&gt;
    &lt;a href=&#34;#%e5%b0%8f%e4%be%8b%e5%ad%90&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;%{
 #include 
%}
number [0-9]+
%option noyywrap
%% // 第一部分结束
{number} {
    printf(&amp;#34;number: %s\n&amp;#34;, yytext);
}
%% // 第二部分结束
int main()
{
    yylex();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;%option noyywrap用于指明未定义yywrap函数，此函数用于在文件（或输入）的末尾调用。如果函数的返回值是1，就停止解析。否则等待输入后继续解析
注：正则表达式是使用单个字符串来描述、匹配一系列字符串的规则&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;wiki：&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;h&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;ttp&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;s://zh.wiki&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;p&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;e&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;di&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;a.&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;o&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;rg/&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;w&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;iki/%E6%&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;AD&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;%A3%E5%88%99%&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;E&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;8%A1%A8%&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;E&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F&#34;&gt;8%BE%BE%E5%BC%8F&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;bison&#34; &gt;Bison
&lt;span&gt;
    &lt;a href=&#34;#bison&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Bison是GNU版本的Yacc（Lexical Analyzar），用于做语法分析，它与Yacc兼容&lt;/li&gt;
&lt;li&gt;参考手册：&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;ht&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;t&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;p://www.gn&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;u&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;.o&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;r&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;g&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;/&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;so&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;f&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;twa&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;re&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;/biso&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;n&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;/manual/b&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;i&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;so&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;n&lt;/a&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;.html&lt;/a&gt;
中译版：&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;htt&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;p&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;://bl&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;o&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;g.c&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;s&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;dn.ne&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;t&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;/s&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;i&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;r&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;o&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;u&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;ni&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;2003/&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;ar&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;ticl&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;e&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;/d&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;e&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;tail&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;s&lt;/a&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672&#34;&gt;/400672&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;wiki：&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;h&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;t&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;tps://en.&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;wi&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;kipe&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;d&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;ia.&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;o&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;rg/&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;wi&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;k&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;i&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;/GNU_bis&lt;/a&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/GNU_bison&#34;&gt;on&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;bison的文件结构&#34; &gt;Bison的文件结构
&lt;span&gt;
    &lt;a href=&#34;#bison%e7%9a%84%e6%96%87%e4%bb%b6%e7%bb%93%e6%9e%84&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;和Flex一样，也分为三个部分&lt;/li&gt;
&lt;li&gt;分别是声明、语法规则和代码段，使用%%分开&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;定义区域
%%
规则区域
%%
C/C++代码区&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;第一部分和Flex一样，用于存放终结符的声明、符号优先级和符号类型等定义&lt;/li&gt;
&lt;li&gt;第二部分用来定义文法规则&lt;/li&gt;
&lt;li&gt;第三部分也是用来存放C/C++代码&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;来个小例子&#34; &gt;来个小例子
&lt;span&gt;
    &lt;a href=&#34;#%e6%9d%a5%e4%b8%aa%e5%b0%8f%e4%be%8b%e5%ad%90&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;%{
#include 
#define YYSTYPE int

void yyerror(char const *);
int yylex(void);
%}

%token NUMBER

%debug

%%

start:  { printf(&amp;#34;yylval = %d\n&amp;#34;, yylval);}
    | NUMBER {
        printf(&amp;#34;NUMBER %d\n&amp;#34;, yylval);
    }
    start NUMBER {
        printf(&amp;#34;start NUMBER %d\n&amp;#34;, yylval);
    }
    ;

%%

void yyerror(char const *msg)
{
    printf(&amp;#34;error: %s\n&amp;#34;, msg);
}

int yylex()
{
    printf(&amp;#34;&amp;amp;gt;&amp;amp;gt;&amp;amp;gt;&amp;#34;);

    char str[10] = {};

if(scanf(&amp;#34;%s\n&amp;#34;, str) == EOF)
    return 0;

int number = atoi(str);

printf(&amp;#34;%d\n&amp;#34;, number);

yylval = number;

return NUMBER;
}

int main()
{
return yyparse();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;%token用于定义一个终结符&lt;/li&gt;
&lt;li&gt;%debug指定bison在生成的代码中加入debug输出（和bison的-t参数一样）&lt;/li&gt;
&lt;li&gt;yyerror在分析过程中有语法错误时被调用&lt;/li&gt;
&lt;li&gt;yylex用于给分析器返回token（在生成的代码中自动被调用）
注：终结符是指文法规则中不能再被分解的最小单位，非终结符则是相反的概念。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如上面的文法规则中“start”就是非终结符，NUMBER就是一个终结符。&lt;/p&gt;
&lt;p&gt;wiki：&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;h&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;tt&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;ps://zh.w&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;i&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;k&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;ipe&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;d&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;i&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;a.&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;o&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;rg/wik&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;i&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;/%E7%B5%82%E7%B5%90%&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;E&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;7%AC%&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;A&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;6%&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;E&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;8%88%87%&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;E&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;9%9&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;D&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;%9&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;E&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;%E7%BB%88%E7%B5%90%&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;E&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E7%B5%82%E7%B5%90%E7%AC%A6%E8%88%87%E9%9D%9E%E7%BB%88%E7%B5%90%E7%AC%A6#&#34;&gt;7%AC%A6#&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;语法介绍&#34; &gt;语法介绍
&lt;span&gt;
    &lt;a href=&#34;#%e8%af%ad%e6%b3%95%e4%bb%8b%e7%bb%8d&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;h3 id=&#34;变量的定义&#34; &gt;变量的定义
&lt;span&gt;
    &lt;a href=&#34;#%e5%8f%98%e9%87%8f%e7%9a%84%e5%ae%9a%e4%b9%89&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;变量使用关键字“var”进行定义。&lt;/p&gt;
&lt;p&gt;zScript提供了两种形式的变量定义语句。&lt;/p&gt;
&lt;p&gt;1.直接定义的形式，该语句的一般形式为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;var 标识符;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的含义是：定义变量名为“标识符”的变量。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;var a;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的作用是：定义一个名为“a”的变量。
2.定义同时给变量初始化赋值，该语句的一般形式为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;var 标识符 = 表达式;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的含义是：定义变量名为“标识符”的变量，且给变量赋值为表达式的结果。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;var a = 1 + 2;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的作用是：定义一个名为“a”的变量，且赋值为3。
“var”关键字可同时定义多个变量，每个变量之间使用符号“,”分割。&lt;/p&gt;
&lt;h3 id=&#34;匿名函数的定义&#34; &gt;匿名函数的定义
&lt;span&gt;
    &lt;a href=&#34;#%e5%8c%bf%e5%90%8d%e5%87%bd%e6%95%b0%e7%9a%84%e5%ae%9a%e4%b9%89&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;匿名函数定义的一般形式为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;(形参列表) {
    语句组
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的含义是：生成一个函数对象，例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;(a) {
    console.log(a);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的作用是：定义一个函数，它有一个名为“a”的形参，执行此函数会将a的值打印到屏幕上。
形参列表，可为空、一个、多个，多个形参之间使用“,”分割。&lt;/p&gt;
&lt;h3 id=&#34;数组的定义&#34; &gt;数组的定义
&lt;span&gt;
    &lt;a href=&#34;#%e6%95%b0%e7%bb%84%e7%9a%84%e5%ae%9a%e4%b9%89&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;数组定义的一般形式为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;var a = [表达式1, 表达式2, ...];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的含义是：生成一个数组，数组元素为中括号中表达式的结果。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;var a = [1, 2, 3];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的作用是：定义一个包含三个元素的数组，元素的值分别为1、2、3，然后赋值给变量a；
数组使用“[”“]”定义，中间内容可以为空、一个表达式、多个表达式，多个表达式之间使用“,”隔开。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;a = a[0];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;取数组元素语法和C语言一样，都是中括号中写入数组下标。&lt;/p&gt;
&lt;h3 id=&#34;选择控制语句&#34; &gt;选择控制语句
&lt;span&gt;
    &lt;a href=&#34;#%e9%80%89%e6%8b%a9%e6%8e%a7%e5%88%b6%e8%af%ad%e5%8f%a5&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;一个选择结构，包括一组或若干组操作，每组操作称为一个分支。通过选择控制语句可以实现选择结构。选择控制语句包括if语句、switch语句及起辅助控制作用的break语句。&lt;/p&gt;
&lt;p&gt;If语句用于计算给定的表达式，根据表达式的值是否为假，决定是否执行某一组操作。&lt;/p&gt;
&lt;p&gt;Switch语句首先求解一个表达式，然后根据计算结果的值，从哈希表中查询该从哪一组操作开始执行。&lt;/p&gt;
&lt;p&gt;Break语句用于switch结构中，用于终止当前switch结构的执行。&lt;/p&gt;
&lt;h4 id=&#34;if语句&#34; &gt;if语句
&lt;span&gt;
    &lt;a href=&#34;#if%e8%af%ad%e5%8f%a5&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;zScript提供了两种形式的if语句。&lt;/p&gt;
&lt;p&gt;1.单if子句的if语句。该if语句的一般形式为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;if(表达式)
{
    语句组
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的含义是：只有表达式的值为非零值时，才执行其内部的语句组。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;if ( a &amp;amp;gt; b )
{
    console.log(“hello”);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的作用是：当a的值大于b的值时（此时，“a&amp;gt;b”的值为真，为非假值），在屏幕上显示“hello”；否则，不显示“hello”。
2.带else子句的if语句。该if语句的一般形式为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;if ( 表达式 )
{
    语句组1
} else {
    语句组2
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的含义是：当表达式的值为非假时，执行语句组1，而不执行语句组2；否则，即表达式的值为假时，执行语句组2，而不执行语句组1。例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;if ( a &amp;amp;gt; b )
{
    console.log(“hello1”);
} else {
    console.log(“hello2”);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该语句的作用是：若a的值大于b的值（此时“a&amp;gt;b”为真，为非假值），则在屏幕上显示“hello1”，而不显示“hello2”；否则，即表达式的值为假时，显示“hello2”，而不显示“hello1”。&lt;/p&gt;
&lt;h4 id=&#34;switch语句&#34; &gt;switch语句
&lt;span&gt;
    &lt;a href=&#34;#switch%e8%af%ad%e5%8f%a5&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;Switch语句与if语句一样，也可以实现分支选择。但if语句是判断一个表达式的值是否为假，决定是否执行某个分支；而switch语句是计算一个表达式的值，根据计算结果，从哈希表查询从哪个分支开始执行代码。Switch语句的一般形式为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;switch( 表达式 )
{
    case 常量1：
    语句组1
    case 常量2：
    语句组2
    ...
    case 常量n：
    语句组n
    default:
    语句组 n + 1
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;switch语句的执行过程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1.求解“表达式”的值；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2.如果“表达式”的值与某个case后面的“常量”的值相同，则从这里开始顺序执行语句。结果switch执行有两种形式：一是遇到break语句为止；二是未遇到break语句，则程序依次执行完所有语句组。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.如果“表达式”的值与任何一个case后面的“常量”的值都不相同，当有default子句时，则执行default后面的语句，如果没有default子句，则结束switch。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中break的一般形式为&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;break;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;循环控制语句&#34; &gt;循环控制语句
&lt;span&gt;
    &lt;a href=&#34;#%e5%be%aa%e7%8e%af%e6%8e%a7%e5%88%b6%e8%af%ad%e5%8f%a5&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;h4 id=&#34;while语句&#34; &gt;while语句
&lt;span&gt;
    &lt;a href=&#34;#while%e8%af%ad%e5%8f%a5&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;while语句的一般形式为&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;while( 表达式 )
{
    循环体
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;while语句的执行过程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1.求解小括号中表达式的值。如果表达式的值为真，转第2步；否则转第3步。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2.执行循环体，然后转第1步。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.执行while语句后面的语句。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;小括号中表达式的值是否为假，决定着循环体是终止还是继续循环。因此，该表达式的值为循环条件。while循环语句的执行特点是，先判断循环条件是否成立，然后决定是否执行循环体。&lt;/p&gt;
&lt;p&gt;当while语句的循环体只包含一条语句时，包含该循环体的“{}”可以省略。&lt;/p&gt;
&lt;h4 id=&#34;for语句&#34; &gt;for语句
&lt;span&gt;
    &lt;a href=&#34;#for%e8%af%ad%e5%8f%a5&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h4&gt;&lt;p&gt;在两种循环语句中，for语句最为灵活。for语句的一般形式为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;for (表达式1; 表达式2; 表达式3)
{
    循环体
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;for语句的执行过程：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1.求解表达式1的值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2.求解表达式2的值，若其值为假，则结束循环，转第4步；若其值为真，则执行循环体。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3.求解表达式3。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;4.结束循环。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;对象的定义&#34; &gt;对象的定义
&lt;span&gt;
    &lt;a href=&#34;#%e5%af%b9%e8%b1%a1%e7%9a%84%e5%ae%9a%e4%b9%89&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h3&gt;&lt;p&gt;对象定义的一般形式为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;{
    属性名1: 属性的值,
    属性名2: 属性的值,
    ...
    属性名n: 属性的值,
    属性名n+1: 属性的值
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;var object = {
    name: &amp;#34;张三&amp;#34;,
    age: 18
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;变量object即是一个对象，它包含两个属性。
注：当对一个对象不存在的属性赋值时会将此属性加入到对象中，例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;object.sex = &amp;#39;男&amp;#39;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对象object中就会多出一个“sex”属性。&lt;/p&gt;
&lt;h2 id=&#34;推荐&#34; &gt;推荐
&lt;span&gt;
    &lt;a href=&#34;#%e6%8e%a8%e8%8d%90&#34;&gt;
        &lt;svg viewBox=&#34;0 0 28 23&#34; height=&#34;100%&#34; width=&#34;19&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;&lt;path d=&#34;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;path d=&#34;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&#34; fill=&#34;none&#34; stroke-linecap=&#34;round&#34; stroke-miterlimit=&#34;10&#34; stroke-width=&#34;2&#34;/&gt;&lt;/svg&gt;
    &lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;GNU&lt;/a&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; (&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html)%5BB%5D(http://www.gnu.org/software/bison/manual/bison.html)%5Bi%5D(http://www.gnu.org/software/bison/manual/bison.html)%5Bso%5D(http://www.gnu.org/software/bison/manual/bison.html)%5Bn%5D(http://www.gnu.org/software/bison/manual/bison.html)%5B&#34;&gt;http://www.gnu.org/software/bison/manual/bison.html)[B](http://www.gnu.org/software/bison/manual/bison.html)[i](http://www.gnu.org/software/bison/manual/bison.html)[so](http://www.gnu.org/software/bison/manual/bison.html)[n](http://www.gnu.org/software/bison/manual/bison.html)[&lt;/a&gt;手册](&lt;a href=&#34;http://www.gnu.org/software/bison/manual/bison.html&#34;&gt;http://www.gnu.org/software/bison/manual/bison.html&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://blog.csdn.net/sirouni2003/article/details/400672#SEC103&#34;&gt;中译版&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/zccrs/zScript&#34;&gt;zScript项目地址&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
  </channel>
</rss>
