<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Looper 的博客</title>
    <link>/</link>
    <description>Recent content on Looper 的博客</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-CN</language>
    <copyright>Copyright © Dylan(github.com/newlooper); all rights reserved.</copyright>
    <lastBuildDate>Thu, 23 Dec 2021 07:14:13 +0800</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Windows 10/11 虚拟桌面管理增强</title>
      <link>/post/original/cs/os/windows/virtualdesktop/</link>
      <pubDate>Thu, 23 Dec 2021 07:14:13 +0800</pubDate>
      
      <guid>/post/original/cs/os/windows/virtualdesktop/</guid>
      <description>
        
          
            意图 在 Win10、Win11 下，实现一个具有类似 MacOS 下 TotalSpace 功能的程序。
市面上免费的、收费的程序有不少，但都有不尽如人意的地方，尝试自行实现。
Q：为什么只支持 Win10、Win11？ A：Win10、Win11 系统中的虚拟桌面 (Virtual Desktop)，是新加入的机制，强调：轻量级与灵活/便利性。较之传统的通过调用 CreateDesktop 生成的桌面，实现某些功能时更加简单直接。详见后文。
原则与限制 既可以由程序完全控制所有桌面（这是 Dexpot 的实现方式），也可以封装并增强系统自带的虚拟桌面。综合考量程序的资源占用、性能、稳定性、兼容性、系统/代码库依赖、打包大小、扩展性、灵活性、便利性等因素，本实现选择后者。
此程序，依赖若干微软公开或非公开的 API；对于非公开的 API 微软可能会进行不兼容的调整（一般发生在 Windows 重大更新后），导致在不同版本的系统中出现未定义的行为，因此并不试图达到兼容性全覆盖（尽可能确保在最新的版本中正常运行）。
程序工作流程 枚举目标窗口 一般情况下，Windows 系统中运行的程序的窗口总数多达数百个或更多，而在要实现的这个程序中，只关注那些期望在桌面概览界面中显示的窗口，因此需要过滤诸如：
ShellWindow（即：顶层桌面） 不可见窗口/无标题窗口 任务栏窗口 IME 窗口 没有 Windows.UI.Core.CoreWindow 子窗口 的 ApplicationFrameWindow 窗口(有点绕，后文解释) 其他不适合显示的窗口，且可以定制 解释 具有 ApplicationFrameWindow 窗口类的窗口是 UWP 程序的沙盒容器(多见于 Windows Store) ，其包含一些与传统 Windows 窗口不同的特性。比如：当该容器包含一个窗口类为 Windows.UI.Core.CoreWindow 的子窗口时，该窗口可见，否则不可见。
如果，一个拥有 Windows.UI.Core.CoreWindow 窗口类的窗口是顶层窗口时，则需要另行判断。
记录并维护目标窗口的虚拟桌面归属 背景知识 自 Windows 10 起引入的虚拟桌面，与传统的通过 CreateDesktop 生成的桌面有很大不同。
传统的或曰经典的 Desktop，是建立在 Windows 的 Sessions、Windows Stations、Desktops 继承树结构之上的。更强调安全性而忽视便利性，比如——在传统 Desktop 中的众多窗口是与该 Desktop 严格绑定的，无法将属于某一个 Desktop 的窗口移动到另一个 Desktop 中。
          
          
        
      </description>
    </item>
    
    <item>
      <title>Hugo 添加 Staticman 评论功能</title>
      <link>/post/original/cs/blog/hugowithstaticman/</link>
      <pubDate>Tue, 11 May 2021 13:00:00 +0800</pubDate>
      
      <guid>/post/original/cs/blog/hugowithstaticman/</guid>
      <description>
        
          
            描述 能为静态站点提供评论功能的第三方模块有很多(排名无先后)
Disqus Talkyard HyperComments Remarkbox IntenseDebate Gitalk Gitment Vuukle Muut 且都各有其优缺点。要取舍这些模块，先明确功能与特性的需求：
免费 无广告 高度定制 评论信息保存在站点所有者可控的存储介质中 安全 迁移成本低 经过筛选，决定使用 staticman
该系统可以满足上述需求，唯一的缺点是部署步骤较多，因此撰写此文，方便参考
本文专注：无(低)成本的让 staticman 在 Hugo 中运行，因此诸如 Hugo 的安装配置，相关站点的注册等则省略
涉及应用 Name Description Ver Hugo 静态博客构建 0.83.1 Staticman 博客评论工具 Branch master Heroku 提供 Staticman 线上运行环境 cloud service Github 博客代码线上仓库、评论数据线上仓库 Netlify 博客发布平台 步骤 0、准备工作 在 https://signup.heroku.com/ 注册，从而为 staticman 提供云服务
若计划将 staticman 搭建在自己的平台或其他平台可省略此步骤
1、部署 staticman 假设 步骤0 已完成
进入 https://github.com/eduardoboucas/staticman
点击紫色的 Deploy on Heroku 按钮，进入 Heroku 站点
          
          
        
      </description>
    </item>
    
    <item>
      <title>EOF，到底怎么回事</title>
      <link>/post/original/cs/io/eof/</link>
      <pubDate>Mon, 03 Aug 2020 15:52:41 +0800</pubDate>
      
      <guid>/post/original/cs/io/eof/</guid>
      <description>
        
          
            TL;DR 首先，确未想到，为说清楚这个玩意儿，居然要用不少的篇幅；其次，当涉及对一些概念、原理的追溯时，递归到多深的地步，也不容易拿捏；好在，写这些文字主要是为了将来碰到某些反直觉的情况时可以有个快捷解答；最后若能得到碰巧逛到这里的同仁指点迷津，纠正错误，互通有无，就算赚到了 :-)
希望读完此文，能够消除一些关于 EOF 的疑惑，再碰到关于她的一些争论时，大家能够相视一笑。
愿此文，能解释
什么是 EOF？ 为什么需要 EOF？ 文件里包不包含 EOF？ 终端输入时的 EOF 的表示方式和处理行为是怎么样的? 不同计算机语言的 EOF 如何定义的？ …… may your blade never dull section-0 概念澄清 In computing, end-of-file (commonly abbreviated EOF) is a condition in a computer operating system where no more data can be read from a data source. The data source is usually called a file or stream.
——Wikipedia
难为下定义的人们，描述既不能太复杂，又要尽可能的说清一个事物的本质。
好，从上面的叙述中，我们萃取出关于 EOF：
范畴：计算机操作系统中，其他领域看来用不着这玩意儿 含义：一种状况，什么状况？表明从数据源(通常指文件或流)中已无数据可读 如果只看到这里，EOF 似乎只是抽象概念而已，她应该独立于操作系统的种类、也应该独立于能够在某种操作系统下编译的计算机语言，everything before &#39;but&#39; is bullshit。
          
          
        
      </description>
    </item>
    
    <item>
      <title>Learn Assembly Language 汇编语言学习(拙译)</title>
      <link>/post/trans/learn-assembly-language/</link>
      <pubDate>Sat, 01 Aug 2020 16:29:01 +0800</pubDate>
      
      <guid>/post/trans/learn-assembly-language/</guid>
      <description>
        
          
            Index-0 原址：https://asmtutor.com/ 环境：nasm on x64 linux
TL;DR 动机：程序员——多掌握几门计算机语言，还是有好处的 主题：汇编语言——有其不可替代的作用 呈示：天下语言逾千——汇编笑看沉舟侧畔 展开：欲知程序真相——反编译难，反汇编易 再现：大道器也不器——初见时如茶味甘苦，洞悉后若灌顶醍醐；原以为听多说多皆已昨，忽回首似曾相识又如陌；罢，风流不在谈峰健，相对无言味更长…… 原文作者自己说 『This project was put together to teach myself NASM assembly language on linux.』
欸~，原来是很窄众的哦。
写的虽然通俗，但依然能感到其面向的并不是毫无编程基础的人群，所谓“某子不能隐真恶”，无论怎样努力的将大量概念、原理、知识安排到看似聊天般的文字中，这里都要提醒读者注意，提防因为好奇心而陷入递归学习的泥潭……
Lesson 1 Hello, world! 背景知识 汇编语言是一种低级语言，汇编程序员与底层硬件之间唯一的接口只有内核本身。用汇编语言编程，涉及到 Linux 内核提供的系统调用机制。这些系统调用是操作系统内置的库函数，提供诸如读取键盘输入以及将输出显示到屏幕之类的功能。
当用户程序发起系统调用时，内核将立即挂起该程序，进而通过驱动程序让相关硬件完成用户程序所发起的任务请求，最后，将控制权交还给用户程序。
提示 驱动程序的驱动二字，形象的描述了内核对硬件的控制
在汇编语言中发起系统调用，需要向EAX寄存器写入相应调用的函数编号(也即：操作码OPCODE)，同时设置其他几个寄存器的值作为实际参数，一切准备停当后，指令INT发送一个软中断，内核收到中断请求后接受参数并执行相应的库函数。简单直接。
来写我们的第壹个汇编程序吧——美玉有瑕 还是从著名的例子——$$Hello, world! $$ 开始，我们的汇编程序将把这个让无数程序员产生我已经学会这种语言了的错觉的字符串打印到标准输出上。
首先，在数据段定义一个msg变量，并赋给其一个字符串类型的值作为程序的输出。而在代码段中，通过编写全局标签_start:，告诉内核我们(写的诗)程序开始的地方(没有远方)
实际参数通过以下寄存器传给内核：
EDX存储字符串的长度(字节数) ECX存储字符串的首地址(定义在数据段中的msg变量加载到内存后所在的位置) EBX存储字符串写操作的目标文件——本例中是STDOUT 数据类型和实际参数的含义可以在函数定义中查到。
1# https://github.com/torvalds/linux/blob/master/include/linux/syscalls.h 2asmlinkage long sys_write(unsigned int fd, const char __user *buf, size_t count); 接下来，编译、链接、运行程序
1; Hello World Program - asmtutor.
          
          
        
      </description>
    </item>
    
    <item>
      <title>WSL2 迁移 Linux 发行版</title>
      <link>/post/original/cs/vm/wsl/migrate_distributions/</link>
      <pubDate>Sat, 01 Aug 2020 12:19:50 +0800</pubDate>
      
      <guid>/post/original/cs/vm/wsl/migrate_distributions/</guid>
      <description>
        
          
            意图 备份还原 迁移 节省 C 盘空间 …… 步骤 导出 1wsl.exe --export &amp;lt;DistributionName&amp;gt; &amp;lt;FileName&amp;gt; 导入 1wsl.exe --import &amp;lt;DistributionName&amp;gt; &amp;lt;InstallLocation&amp;gt; &amp;lt;FileName&amp;gt; 提示：导入之后，执行 wsl -l -v 查看运行版本，如果是 1 的话，可以执行 wsl --set-version &amp;lt;DistributionName&amp;gt; 2 更新到 2
WSL 默认登录用户 如果迁移完毕后发现默认登录用户被置为：root，或者想要手动指定默认的登录用户，可以按如下操作设置：
注册表项：
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss
其中的 DefaultUid 一项，设置为对应子系统中的 Linux UID，UID 可以登录进 WSL 获取
或者：
这里有一个老外提供的，只需提供用户名，不需要登录也能设置 DefaultUid 的 powershell 函数，注意该用户必须存在于子系统中
Function WSL-SetDefaultUser ($distro, $user) { Get-ItemProperty Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss*\ DistributionName | Where-Object -Property DistributionName -eq $distro | Set-ItemProperty -Name DefaultUid -Value ((wsl -d $distro -u $user -e id -u) | Out-String); };
          
          
        
      </description>
    </item>
    
    <item>
      <title>分页内存地址转换点滴</title>
      <link>/post/original/cs/os/memory/memory_paging/</link>
      <pubDate>Mon, 22 Jun 2020 09:28:07 +0800</pubDate>
      
      <guid>/post/original/cs/os/memory/memory_paging/</guid>
      <description>
        
          
            环境与条件：CPU 和操作系统都为 32 位，主存按字节编址
一、页大小的确定 一言以蔽之——在页表所占内存和页内填充内存的耗费上做取舍、折中
以下极端情况的对比，用以阐明为什么要取舍、折中
A、页面大小 = 1Byte
颗粒度到达极致细微、系统永远不必为页面填充不需要的内存 但页表项目达到 2^32 个，占用了整个内存 B、页面大小 = 4GBytes
每当新进程启动时，都需要将 4G 内存交换到磁盘 页表中只有一个条目，因此几乎不占用任何内存 因此：
x86 设计人员发现 4K 大小的页面是很好的庸点
当然，随着 CPU 地址总线位数的扩张，系统中物理内存的膨胀，4K 也并不是总是合适的大小。
二、页大小 &amp;amp; 页内偏移量位数 已确定页大小，可算出页内偏移量需要多少位，如常见的页面尺寸 4K = 4096Bytes
$$ \log_{2}{4096} = 12，即：需要12_{bits} $$
已知页内偏移量占用 12 位，则可算出页大小
$$ 2^{12} = 4096_{Bytes} $$
页内偏移范围：
BIN HEX 000000000000 0x0000 000000000001 0x0001 ... ... 111111111111 0x0FFF 三、页大小 &amp;amp; 页框大小 &amp;amp; 页表项数 &amp;amp; 物理内存块数 $$ \begin{array}{l} 物理内存块数 = \frac{物理内存(寻址范围)\equiv 2^{N}}{页大小}，其中 N = 地址总线条数 \end{array} $$ $$ 有：页大小 \equiv 页框大小，且：页表项数 \equiv物理内存块数 $$
          
          
        
      </description>
    </item>
    
    <item>
      <title>The GNU ed line editor [译]</title>
      <link>/post/trans/cs/editor/ed/</link>
      <pubDate>Fri, 12 Jun 2020 17:20:59 +0800</pubDate>
      
      <guid>/post/trans/cs/editor/ed/</guid>
      <description>
        
          
            GNU ed 手册 (version 1.16, 2020-02-20).
原址：https://www.gnu.org/software/ed/manual/ed_manual.html
概览: ed 概览 行编辑简介: GNU ed 起步 调用 ed: 命令行界面 行寻址: 在缓冲区中指定行/范围 正则表达式: 文本选择模式 命令: GNU ed 识别命令 限制: GNU ed 的固有局限性 诊断: GNU ed 错误处理 问题: 报告 bugs GNU Free Documentation License: How you can copy and share this manual Copyright © 1993, 1994, 2006-2020 Free Software Foundation, Inc. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.
          
          
        
      </description>
    </item>
    
    <item>
      <title>GitLab on Docker 配置 SMTP 服务</title>
      <link>/post/original/gitlab/docker_smtp/</link>
      <pubDate>Sun, 03 Sep 2017 13:34:45 +0800</pubDate>
      
      <guid>/post/original/gitlab/docker_smtp/</guid>
      <description>
        
          
            起因 gitlab 官方 docker 容器部署完毕。容器中的服务越少越好，所以使用外部 SMTP 发送邮件。
环境 撰写此文时：
Docker 17.06.1-ce GitLab Community Edition 9.5.2 ab97415 步骤 网易 163 邮箱 授权码设置 协议开启与服务器地址 容器中的 /etc/gitlab/gitlab.rb 1### Email Settings 2# gitlab_rails[&amp;#39;gitlab_email_enabled&amp;#39;] = true 3gitlab_rails[&amp;#39;gitlab_email_from&amp;#39;] = &amp;#39;&amp;lt;yourname&amp;gt;@163.com&amp;#39; 4# gitlab_rails[&amp;#39;gitlab_email_display_name&amp;#39;] = &amp;#39;Example&amp;#39; 5# gitlab_rails[&amp;#39;gitlab_email_reply_to&amp;#39;] = &amp;#39;noreply@example.com&amp;#39; 6# gitlab_rails[&amp;#39;gitlab_email_subject_suffix&amp;#39;] = &amp;#39;&amp;#39; 7gitlab_rails[&amp;#39;smtp_enable&amp;#39;] = true 8gitlab_rails[&amp;#39;smtp_address&amp;#39;] = &amp;#34;smtp.163.com&amp;#34; 9gitlab_rails[&amp;#39;smtp_port&amp;#39;] = 994 10gitlab_rails[&amp;#39;smtp_user_name&amp;#39;] = &amp;#34;&amp;lt;yourname&amp;gt;@163.com&amp;#34; 11gitlab_rails[&amp;#39;smtp_password&amp;#39;] = &amp;#34;&amp;lt;your 163 auth code&amp;gt;&amp;#34; 12gitlab_rails[&amp;#39;smtp_domain&amp;#39;] = &amp;#34;163.com&amp;#34; 13gitlab_rails[&amp;#39;smtp_authentication&amp;#39;] = :login 14gitlab_rails[&amp;#39;smtp_enable_starttls_auto&amp;#39;] = false 15gitlab_rails[&amp;#39;smtp_tls&amp;#39;] = true 16###!
          
          
        
      </description>
    </item>
    
    <item>
      <title>邻位对换法生成全排列</title>
      <link>/post/original/cs/math/permutation/</link>
      <pubDate>Thu, 02 Feb 2017 17:16:09 +0800</pubDate>
      
      <guid>/post/original/cs/math/permutation/</guid>
      <description>
        
          
            算法原理 相关算法——插入法 对于集合
$$ S=\{a_1, a_2, ..., a_n\} $$
若已知前 $n-1$ 个元素的全排列，则 $n$ 个元素的全排列
$$ p_i=\{p_1,p_2,...,p_{(n-1)!}\} $$
可以这样生成：将 $a_n$ 插入 $p_i$ 不同位置中，由此，得到集合 $S$ 的全排列
为什么这样操作能得到集合 $S$ 的全排列？因为每个 $p_i$ 的可能插入位置为 $n$ 个，所以总数是 $n!$ 又因为每个 $p_i$ 是不同的，因此，得到的排列必然没有重复
插入法有一个缺点：为了产生 $n$ 个元素的排列，必须知道并存储所有 $n-1$ 个元素的排列，然后才能产生出所有 $n$ 阶排列
邻位对换法的改进 依赖插入法能够生成全排列的事实，但邻位对换法不需要知道 $n-1$ 个元素的排列，只需要从某一个初始排列状态开始，进行特定的相邻元素交换即可生成全排列
算法正确性 假设算法对 $n$ 个元素能生成全排列，只需要证明其对 $n+1$ 个元素，也能生成全排列，对于新进来的元素，将其认为值最大，插入最右方，每次从右移到左，或者改变方向后从左移到右，就可以认为对于一个排列从不同位置插入生成一个新的排列，而原本 $n$ 个元素是全排列的，因此对于 $n+1$ 个元素也是全排列的，因此邻位对换法能生成全排列
以 $S=\{1, 2, 3, 4\}$ 为例。若 $\{1, 2, 3\}$ 的全排列为：
$p_1$ $p_2$ $p_3$ $p_4$ $p_5$ $p_6$ 123 132 312 321 231 213 那么，将 $4$ 按从尾到头的方式插入每一个排列，就得到：
          
          
        
      </description>
    </item>
    
  </channel>
</rss>
