WSL 安装实践:WSL2+Ubuntu-22.04+KDE 配置开发环境
本文写于 2022 年 8 月 27 日,文章内容有效性可能随时间发生变化。
本人是 Linux 新手,文章可能有不成熟之处。
作者:Taivas Jumala
update on 2022-12-20, 22:13:48 (UTC +08:00)
WSL 的 systemd 得到了 Microsoft 官方的支持,本文一大部分仅剩参考意义。
有鉴于此,我以后应该写一些不那么有时效性的,更有含金量一点的东西。
而且,我在"Pro WSL"这本书里发现了很多差不多的内容,属于是重复造轮子了。
什么是 WSL?为什么使用 WSL2?
WSL 全称 Windows Subsystem for Linux (适用于 Linux 的 Windows 子系统), 它可让开发人员按原样运行 GNU/Linux 环境 - 包括大多数命令行工具、实用工具和应用程序 - 且不会产生传统虚拟机或双启动设置开销。它的主要目标是提高文件系统性能,以及添加完全的系统调用兼容性。这一新的体系结构改变了这些 Linux 二进制文件与Windows 和计算机硬件进行交互的方式,但仍然提供与 WSL 1(当前广泛可用的版本)中相同的用户体验。
单个 Linux 分发版可以在 WSL 1 或 WSL 2 体系结构中运行。 每个分发版可随时升级或降级,并且你可以并行运行 WSL 1 和 WSL 2 分发版。 WSL 2 使用全新的体系结构,该体系结构受益于运行真正的 Linux 内核。1
使用WSL2您可以:
- 在 Microsoft Store 中选择你偏好的 GNU/Linux 分发版。
- 运行常用的命令行软件工具(例如
grep、sed、awk)或其他 ELF-64 二进制文件。 - 运行 Bash shell 脚本和 GNU/Linux 命令行应用程序,包括:
- 工具:vim、emacs、tmux
- 语言: NodeJS 、JavaScript、 Python 、Ruby、C/C++、C# 与 F#、Rust、Go 等
- 服务:SSHD、 MySQL 、Apache、lighttpd、 MongoDB 、 PostgreSQL 。
- 使用自己的 GNU/Linux 分发包管理器安装其他软件。
- 使用类似于 Unix 的命令行 shell 调用 Windows 应用程序。
- 在 Windows 上调用 GNU/Linux 应用程序。
- 运行直接集成到 Windows 桌面的 GNU/Linux 图形应用程序
- 将 GPU 加速 用于机器学习、数据科学场景等
在享受 Linux 带来的开发便捷的同时,您同时可以享受到 Windows 上的办公软件、通讯工具,同时进行游戏娱乐。
传统虚拟机 vs WSL2 2
当然,在 Windows 上使用 VirtualBox 或是 VMWare 等安装 Linux 虚拟机是可行的。即使对于 WSL2(本质为 Hyper-V 虚拟机),它们相较于裸机运行 Linux 都是有所妥协的。
使用虚拟机运行 Linux 的缺点
安装安装 Linux 并不适合胆小的人,而且需要相当长的时间。但 WSL2 能让你运行最新的 Linux 发行版,理想情况下只需几下鼠标点击。
启动时间 现如今 Linux 能相当快地启动,但是仍然需要十数秒(或因计算机性能而有所区别)。启动虚拟机再启动 Linux 则要花更长时间。WSL2 则只需几秒钟,可以随着 Windows 一起启动。(实际上,启用 WSL2 后,Windows 本身也运行在 Hyper-V 上,并因此有些许性能损失。)
内存分配传统虚拟机会要求你在配置虚拟机时指明要分配的内存大小。假设你为你的虚拟机分配了 2GB 的内存,这 2GB 将不再能被你的 Windows 主机使用。而 WSL2 根据需要由 WSL 系统分配、释放内存。早期的 WSL2 并不是很积极的让分配的内存释放,最近的版本有了很大的改善。同时,你可以指定能被分配的最大内存。
和 Windows 几乎没有任何集成交互基本上你只能通过 Samba 来让 Windows 和 Linux 虚拟机来分享文件。如果你知道如何配置这当然是可行的。而 WSL2 为你做好了一切,而且易于使用,您可以用 Windows 的文件资源管理器访问 Linux 文件,同时也可以从 WSL2 的 Linux 发行版中挂载的文件系统访问 Windows 文件,虽然 I/O 效率不是很高。
Microsoft Visual Studio Code 允许你在 Linux 开发 (例如 NGINX web 服务器或是 Docker 容器) 但使用 Windows 端的 GUI 来获得近似 Linux 原生的开发体验。
使用 X Windows 配置,你可以让 Linux GUI 应用显示在你的 Windows 任务栏中,来获得更好的体验。
WSL2 的一些问题
根据 内置WSL 2的Windows 10可以完全取代桌面版Linux吗? - 于国瑞的回答 - 知乎
- 缺少 IPV6 的完全支持
- WSL 内部不能访问 Windows 的 localhost(然而从 Windows 可以访问 Linux 中的 localhost)
- 缺少 Systemd (这也是文章之后将着重解决的问题)
为什么选择 Ubuntu
作为可见的最受欢迎的 Linux 发行版(现在也许是 Arch Linux 了),Ubuntu 教程丰富,相对没有复杂的配置,易于新人上手。我个人也建议您之后去尝试 Fedora、Debian、Open SUSE等(大蜥蜴!我好喜欢你啊🥵!)。当然也可以尝试一下 Arch Linux。即使使用其他发行版,我本文对您也许也会有所帮助。
Ubuntu 22.04 LTS 最新代号为 Jammy Jellyfish,带来了几个新功能:3
针对数据驱动的解决方案进行了优化
数据分析和处理是当今每个业务的核心。要做到这一点,您需要强大的计算能力。Ubuntu 22.04 LTS 带来了开箱即用的NVIDIA 虚拟 GPU (vGPU)驱动程序支持。这意味着您可以利用 NVIDIA 虚拟 GPU 软件,使您能够在从物理 GPU 服务器共享的虚拟机中使用 GPU 计算能力。
实时内核支持
此外,Canonical 在 Ubuntu 22.04 LTS 版本中发布的一个有趣的公告是提供“real-time”内核选项,该选项现在是beta 测试版。对于电信和其他行业,对时间敏感的工作需要低延迟的操作系统。因此,考虑到这一点以及渗透这些领域的愿景,Ubuntu 22.04 LTS 带来了一个应用了 PREEMPT_RT 补丁的实时内核构建。它适用于 x86_64 和 AArch64 架构。
最新的应用程序、软件包和驱动程序
除了上述更改之外,此版本还带来了大量的软件包和工具链升级列表。例如,此版本根据使用情况带来了多种 Linux Kernel 类型,例如 Ubuntu 桌面可以选择加入Kernel 5.17,而硬件支持 Kernel 仍然是 5.15。
不仅如此,Ubuntu Server 还具有长期支持的 Kernel 5.15,而 Ubuntu Cloud 映像可以选择与云提供商合作使用更优化的 Kernel。
此外,如果您是 NVIDIA 用户,值得了解的是 ARM64 上的 NVIDIA 驱动程序的 Linux 限制模块现在可用(在 x86_64中已经可用)。您可以使用 ubuntu-drivers程序来安装和配置 NVIDIA 驱动程序。
由于核心模块和子系统的存在,一个完整的操作系统可以完美运行。因此,考虑到这一点,Ubuntu 22.04 LTS 升级了所有这些以迎合这个伟大的版本。下面是一个简短的介绍。
GNU/Linux 核心
- GCC 11.2.0
- binutils 2.38
- glibc 2.35
编程工具链
- Python 3.10.4
- Perl 5.34.0
- LLVM 14
- golang 1.18
- rustc 1.58
- OpenJDK 11 (option to use OpenJDK 18)
- Ruby 3.0PHP 8.1.2
- Apache 2.4.52
- PostgreSQL 14.2
- Django 3.2.12
- MySQL 8.0
- Updated NFS and Samba Server
- Systemd 249.11
- OpenSSL 3.0
虚拟化
qemu 6.2.0
libvirt 8.0.0
virt-manager 4.0.0
为什么选择 KDE 而不是 GNOME
诚然,GNOME 是 Ubuntu 的默认桌面环境,但是由于各种原因我仍然选择了 KDE 作为我们的桌面环境。
首先,由于 WSL2 缺少 Systemd,安装 GNOME 的过程中其会尝试配置大量相关服务,实际上这个过程会让你相当折磨,如果您仍然想要尝试, Running Ubuntu Desktop in WSL2 这篇文章可能对您有所帮助。
其次,KDE 有着华丽的桌面效果和高可定制性,同时 Reddit 用户 u/FriendlyJewThrowaway 发布了使用 VcXsrv 作为 X-Server 运行 KDE 非常流畅的说明,在实践中,其确实工作良好,只有在网页浏览器中滚动页面时才会遇到可见的延迟,而 Dolphin 等工作正常并且 Nextcloud 客户端工作良好甚至并且不像 GNOME, 其非常好地集成进了任务栏。
最后……
作者:霜空 链接:https://www.zhihu.com/question/20238837/answer/2645973880 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
KDE因为qt的协议问题没有被大资本选中,因此成不了开源良心,后来因为财政问题测试不到位,结果在一段时间内坏了相当的口碑,好在面向用户的方向没错,之后就一路向好了。
在最近几年,其实Gnome3要不是资本在后面强推,几乎被所有主流发行版作为默认的话,早被KDE碾成渣了。
以前我只是觉得Gnome3设计脑残,比如说全屏菜单,比如说快速启动栏的功能重叠,比如说上面任务栏占了宽屏宝贵的横向空间却又把所有程序缩成一点……
后来遇到高人交流才知道还是自己拿衣服了。Gnome3的设计初衷就和KDE或者传统Windows桌面不同,不是以提高桌面工作效率而定的;就像是现在的网站不像是10年前那样,不是为了最大限度的提高用户信息获取效率而设计,虽然有了多媒体加持仿佛变得多姿多彩了,实质使用起来却极其原始落后,大多采用了非常落后、信息含量极低的图片(图标)和点击为主的方式,连像十年那般根据时间和类别之类的硬指标排序都不行,操作效率极差,信息获取效率也极差。
原因就在于09年以后深度学习的成熟,在之后几乎所有新设计出的交互界面都有且只有一个目的,那就是为了清晰和明确指向的单一信息释放与采集。说白了就是采集用户的数据来训练机器,同时又反过来利用训练好的模型来规范用户。如果不信的话,比如说可以做一个小实验,去下载主流的linux发行版的live镜像,gnome3和kde的,用虚拟机打开联网,然后打开系统监视器,接着什么都不做。然后就可以看到几乎所有的gnome3发行版都在偷偷摸摸的不断发数据包收数据包,而kde的大多数不会有网络活动。
手机和平板的界面不说了,整个软硬件体系都是为之设计的,早没救了;只说计算机桌面,现在不管是Gnome3,还是windows10、11,甚至mac的新界面,都在朝着这个方向飞速发展,至于KDE只能说且用且珍惜吧,一旦用户量到达一定级别,背后的基金会被收购控制是注定的事情。
同时,如果你选择 Arch Linux 进行本文的实践,几乎无可避免的会选择 KDE。折腾小子当然只会一直折腾下去啦(不是主要原因)!
此外,如果选择 XFCE 作为您的桌面环境,对于 WSL 来说许多人认为它是一个不错的选择。
安装 WSL24
本人目前使用 Windows 11 22H2 (OS 内部版本22622.586),处于 Windows Insider Preview(Windows 预览体验计划) Beta 渠道。
相关内容除 Microsoft 官方文档外互联网上也有很多相关内容,读者也可以自行尝试。
开始前,最好在’启用或关闭 Windows 功能’(win+s 搜索或在控制面板中查找)中 开启「 Hyper-V」 和「适用于 Linux 的 Windows 子系统」功能。
安装 WSL 的方式方法根据您的 Windows 版本而有所不同。这部分内容部分参考微软的官方文档: 使用 WSL 在 Windows 上安装 Linux
先决条件:必须运行 Windows 10 版本 2004 及更高版本(内部版本 19041 及更高版本)或 Windows 11。
若要检查 Windows 版本及内部版本号,选择 Windows 徽标键 + R,然后键入“winver”,选择“确定” 。 可通过选择“开始”>“设置”>“Windows 更新”>“ 检查更新 ”来更新到最新的 Windows 版本。
选项一:Windows Subsystem for Linux Preview
使用本选项需要一些先决条件:
需要Windows 11内部版本 22000 或更高版本才能访问此功能。(可能需要加入 Windows 预览体验计划)。
已安装适用于 vGPU 的驱动程序
若要运行 Linux GUI 应用,应首先安装与以下系统匹配的驱动程序。 这样,就可以使用虚拟 GPU (vGPU),使你可受益于硬件加速 OpenGL 渲染。
然后使用 Microsoft Store 搜索 Windows Subsystem for Linux Preview 进行安装,将会自动配置并获得 GUI 支持。
有可能您需要使用 “
Win+S" 搜索「启用或关闭Windows功能」并勾选 「Hyper-V」和「适用于 Windows 的 Linux 子系统」选项。
选项二:从命令行安装
必须运行 Windows 10 版本 2004 及更高版本(内部版本 19041 及更高版本)或 Windows 11。
现在,可以在管理员 PowerShell 或 Windows 命令提示符中输入此命令,然后重启计算机来安装运行适用于 Linux 的 Windows 子系统 (WSL) 所需的全部内容。
wsl --install
此命令将启用运行 WSL 并安装 Linux 的 Ubuntu 发行版所需的功能。 ( 可以更改此默认发行版 )。
首次启动新安装的 Linux 发行版时,将打开一个控制台窗口,要求你等待将文件解压缩并存储到计算机上。 未来的所有启动时间应不到一秒。
上述命令仅在完全未安装 WSL 时才有效,如果运行
wsl --install并查看 WSL 帮助文本,请尝试运行wsl --list --online以查看可用发行版列表并运行wsl --install -d <DistroName>以安装发行版。 若要卸载 WSL,请参阅 卸载旧版 WSL 或 注销或卸载 Linux 发行版 。
选项三:旧版本的手动安装步骤
为简单起见,通常建议使用
wsl --install
安装适用于 Linux 的 Windows 子系统,但如果运行的是旧版 Windows,则可能不支持这种方式。
具体而详地介绍安装 WSL 不是本文的主要目的,详细信息请参考官方文档: 旧版 WSL 的手动安装步骤
安装完成后,通过以下命令检查是否成功。
wsl --status
例如,我执行完后,返回如下结果
默认版本: 2 WSL 版本: 0.66.2.0 内核版本: 5.15.57.1 WSLg 版本: 1.0.42 MSRDC 版本: 1.2.3401 Direct3D 版本: 1.606.4 DXCore 版本: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp Windows版本: 10.0.22622.586
您应该会看到类似的结果。同时如果安装有问题,该命令还会提供一些额外信息帮助您解决问题。更多帮助可以参考官方文档。
在开始前还可以执行以下命令,防止可能出现的问题:
wsl --update
wsl --set-default-version 2
安装 Ubuntu-22.04
在开始之前强烈建议您使用 Windows Terminal 获得最好的开发体验。如果是 Windows 10 用户,您可能需要从 Microsoft Store 搜索 Windows Terminal 进行安装,如果您是 Windows 11 用户,Windows Terminal 已成为 Windows 11 的默认终端程序。5
Windows 徽标键 + R,键入 ‘wt’ 可以快捷地打开,或是在 Windows 11 中右键开始菜单/按下 ‘win+x’,选择「终端」。
Microsoft Store 搜索 Ubuntu,就目前而言选择 Ubuntu 或是 Ubuntu 22.04.01 LTS 没有多大区别,但是本文大部分内容针对 Ubuntu 22.04 进行说明,如果担心兼容问题可以考虑安装 Ubuntu 22.04.01 LTS,目前我选择的 Ubuntu 进行安装。
如果您通过选项一安装 WSL2,将会默认安装 WSLg,安装 Ubuntu 后,选择打开可能会弹出 GUI 安装界面。因为 WSLg 仍有一些问题,可以在解压完(Unpacking the distro… 结束后)直接关闭,Ubuntu 将继续在命令行界面中继续安装步骤。(也可在.wslconfig 中禁用 GUI Apps)
此外,您也可以在 PowerShell 中使用以下命令:
wsl --install Ubuntu
设置 Linux 用户名和密码6
如果您第一次启动您的 Linux 发行版(这里我们的 Linux 发行版为 Ubuntu)。 系统将要求你为您的 Linux 发行版创建「用户名」和「密码」。
- 此用户名和密码特定于安装的每个单独的 Linux 分发版,与 Windows 用户名无关。
- 请注意,输入 密码时,屏幕上不会显示任何内容。 这称为盲目键入。 不会看到正在键入的内容,这完全正常。
- 创建用户名和密码后,该帐户将是分发版的默认用户,并将在启动时自动登录。
- 此帐户将被视为 Linux 管理员,能够运行
sudo(Super User Do) 管理命令。 - 在 WSL 上运行的每个 Linux 发行版都有其自己的 Linux 用户帐户和密码。 每当添加分发版、重新安装或重置时,都必须配置一个 Linux 用户帐户。
随 WSL 一起安装的 Linux 发行版是按用户安装,不可与其他 Windows 用户帐户共享。
如果需要额外的帮助,例如更改或重置密码,可以参考引用,并善用搜索引擎。
设置中文
对于一些用户可能希望使用中文,如果安装时没有选择或没有提供选择,可以通过执行一下命令修改:
sudo dpkg-reconfigure locales
之后输入密码,根据提示操作。
在之后安装 KDE 过程中,如果想让 KDE 默认中文,也可以使用这一命令。
如果您是 Linux 新手,您应该有一点 bash 基础,我建议学习 MIT 的课程 The Missing Semester of Your CS Education 。相关课程为 Course Shell 。如果您访问 Youtube 或是 Github 有困难,或是对英语缺乏自信,这一节的内容在哔哩哔哩上也有汉化组的翻译,见 @Missing_Semi_中译组 。目前只完成了$4/11$的课程视频资源翻译,并且在持续当鸽子。当然也可以通过其它的方式学习。
更换软件源
高级打包工具(英语:Advanced Packaging Tools,缩写为APT)是 Debian 及其派生的 Linux软件包 管理器。APT可以自动下载,配置,安装二进制或者源代码格式的 软件包 ,因此简化了 Unix 系统上管理软件的过程。APT最早被设计成 dpkg 的前端,用来处理 deb 格式的软件包。现在经过APT-RPM组织修改,APT已经可以安装在支持 RPM 的系统管理 RPM 包。7
由于 Ubuntu 配置的默认源并不是国内的服务器,下载更新软件都比较慢。可能需要换源来在使用 APT 时获得更好的下载速度。本人目前正在上海的学校学习,因此选择上海交通大学 Ubuntu 软件源镜像服务作为说明。如果您位于其他地区,可以参考您的地理位置选择合适的软件源。如果没有主意,国内可以选择清华源、中科大源或者阿里源等等进行配置,其官网都有对应的说明。
您可以通过
lsb_release -a
确定 Ubuntu 版本来选择合适的源。
以下使用方式参考https://mirrors.sjtug.sjtu.edu.cn/docs/ubuntu:
使用
sudo nano /etc/apt/sources.list打开镜像源列表文件。将类似于http://cn.archive.ubuntu.com/ubuntu或http://cn.archive.ubuntu.com/ubuntu的地址改成https://mirror.sjtu.edu.cn/ubuntu,之后按Ctrl-O 回车保存,按Ctrl-X退出。最后执行sudo apt update即可。镜像站一天同步一次 Ubuntu 镜像。为了取得最新的安全更新,我们不建议您将
security.ubuntu.com换成镜像源。您也可使用
sudo sed -i 's/http:\/\/cn.archive.ubuntu.com/https:\/\/mirror.sjtu.edu.cn/g' /etc/apt/sources.list指令直接批量修改。以下为修改后的镜像源列表文件的示例。可使用
gedit等文本编辑器修改/etc/apt/sources.list。修改后使用sudo apt update更新软件源。
Ubuntu 22.04
deb https://mirror.sjtu.edu.cn/ubuntu/ jammy main restricted universe multiverse
# deb-src https://mirror.sjtu.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb https://mirror.sjtu.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
# deb-src https://mirror.sjtu.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb https://mirror.sjtu.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
# deb-src https://mirror.sjtu.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
deb https://mirror.sjtu.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
# deb-src https://mirror.sjtu.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
# deb https://mirror.sjtu.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
# deb-src https://mirror.sjtu.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
其中deb-src为源码镜像,默认注释以提高速度。可将原镜像列表备份后替换为上述内容。
安装 Kubuntu 桌面组件
这一步建议按照顺序,在进行之后的操作之前完成。
执行:
sudo apt update
sudo apt upgrade
sudo apt install kubuntu-desktop
中途会提示一次「正在设定 encfs」,选择确定。
这一步会花点时间,取决于网络连接、电脑性能等等,可以先休息喝口水。
此外,之后的内容说明默认使用 Vim 作为文本编辑器进行说明,读者同样可以在 MIT 课程 The Missing Semester of Your CS Education 中学习到,或是善用互联网资源。
对于选择其他发行版进行实践的读者,即使不是 Arch Linux 的用户,Arch Linux 的 Wiki 也值得参考。当然,如果本身有好的文档,直接拿来用吧。
关于 Systemd 和 wsl
如果您按照文章的安装了 Ubuntu-22.04, 并且您对为何选择 genie 来让 WSL 启动 Systemd 不感兴趣,可以跳过这部分内容。若使用其他发行版,以下内容可能会对您有所帮助。
即使 WSL2 默认不会被全部安装,并且 Systemd 因为 WSL2 的启动方式的原因不能被安装在 WSL2 中。(简单来说 Systemd 需要运行在 PID 1。)虽然是这样设计的,但并不意味着通常在启动时运行的 GUI 不能正常的工作。对于想了解 WSL 与 Systemd 问题读者,可以参考以下资料:
对于 Systemd,目前有许多 workarounds, 在 wsl2 中启用 systemd 的方法至少有如下三种:
本文将使用 genie 作为说明,并会解释原因。
genie
genie 使得你能运行 systemd 作为 PID 1,同时启动所有的配套服务(这在之后的配置中节省了很多麻烦)。它通过创建一个 PID 命名空间和一个容器,让 Systemd 在其中运行来实现这一点,同时提供一些有用的快捷操作。
genie 只能运行在 WSL2 中,同时作者还为 Debian 系的发行版(当然包括 Linux)、Arch Linux 和 Fedora 提供维护,如果想要使用其他发行版,genie也当然值得一试。如果出现问题可以先在 GitHub 相关 issues 里寻找解决方法,当然也可以转而使用 wsl-distrod。
genie 经过几年打磨已经是个成熟的项目,相对于另外两个方法有比较多的文档可以参考,对于 Debian 系的发行版来说比较友好。
subsystemctl
灵感来源于 arkane-systems/genie 但是使用 Rust 编写。
然而 subsystemctl 是一个比较新的项目,不兼容命令行。除了 Arch Linux 外有打好的包外,Debian/Ubuntu 需要用 cargo 自行编译。相对于 genie 主要使用 Python, 可能有更好的性能,但是需要完善。
同时使用 Networkd 会有一些别的问题。
wsl-distrod
简而言之,Distrod 是一个二进制文件,它会创建一个简单的容器,将 systemd 作为 init 进程,并在该容器中启动您的 wsl 会话。为了做到这一点, Distrod 做了以下事情:
- 修改具体发行版的 rootfs,以使其与 wsl 和 systemd 兼容。
- 修改 systemd 服务,使其与 wsl 兼容。
- 把
/opt/distrod/bin/distrod和其它资源放进 rootfs。- 把 Distrod 二进制文件注册为登录 shell。
- 当 Distrod 作为登录 shell 被 wsl 的 init 进程启动时,Distrod:
- 在简单的容器中启动 systemd
- 在这个容器中启动你实际的 shell
- 在 systemd 会话和 wsl 交互环境间建立桥梁。
在官方文档也有提到过:
把 Distrod 作为一个独立的一次性命令:在这种情况下, distrod 的工作方式就像 genie 和 subsystemctl。
但是,Distrod 在自启动方面做了更多的工作。通过上述操作,Distrod 做到了:
- 安装并启用后,启动 wsl 会自动启动 systmed。
- 添加
--start-on-windows-boot参数后,Distrod 会注册一个 windows 任务,使得 wsl 在 windows 开机时就会运行。
这样的做法虽然能被广泛的用于各种发行版,正如官方文档中它的安装方式一样:从
linuxcontainers.org
上安装。这导致了安装多个不同的发行版会有额外的困难,正如它的一篇文档介绍的那样:
Install and Run Multiple Distros at the same time
. 而 WSL 的一大优势在于同时安装、运行多个 Linux 发行版,并且通常仅仅需要从 Microsoft Store 中选择或从 wsl --list --online 中选合适的发行版。
此外,subsystemctl 和 wsl-distrod 都只会启动必要的几个服务,使用时需要额外的更多工作。
安装配置 SystemD-Genie89
正如仓库 README 介绍的那样,首次安装 genie 前最好阅读整个 README 页面,这会减少一大部分之后可能会遇到的困难,在开始之前,以下文章列表也许能帮助你更好地解决问题:
- arkane-systems / genie
- WSLg FAQ
- lengthy description of WSLg architecture here
- known-problematic systemd units list
- the genie wiki
- wsl-transdebian
- Windows 11 + Genie 2.3: Genie seems stuck
- WSL 基本命令
- WSL 中的高级配置
下面正式开始:
安装 .NET 运行时
参考 在 Ubuntu 上安装 .NET SDK 或 .NET 运行时 根据架构和 Ubuntu 版本选择合适的安装方式,使用其他发行版的可参照同一页面上的介绍进行安装。对于 Ubuntu 22.04 LTS, 只需执行:
sudo apt-get install -y dotnet-runtime-6.0
genie 的运行有赖于此。
添加 wsl-transdebian 仓库
wsl-transdebain 是一个仅适用于 wsl 的 apt 仓库, wsl-transdebian 列出了支持的发行版,如果你不是选择使用 Ubuntu 您也许需要检查一下是否支持您的发行版,否则参考 arkane-systems / genie 以继续安装。
首先,确保 lsb_release 可用
apt install lsb-release
接着配置 wsl-transdebian 仓库:
作为 root 用户 (sudo -s), 输入接下来的命令:
wget -O /etc/apt/trusted.gpg.d/wsl-transdebian.gpg https://arkane-systems.github.io/wsl-transdebian/apt/wsl-transdebian.gpg
chmod a+r /etc/apt/trusted.gpg.d/wsl-transdebian.gpg
cat << EOF > /etc/apt/sources.list.d/wsl-transdebian.list
deb https://arkane-systems.github.io/wsl-transdebian/apt/ $(lsb_release -cs) main
deb-src https://arkane-systems.github.io/wsl-transdebian/apt/ $(lsb_release -cs) main
EOF
apt update
在实践中,您也许注意到需要访问 GitHub,鉴于国内的网络情况(主要是第一行),这个过程可能十分缓慢,您也许需要为其配置代理。
本人使用 Clash for Windows 作为代理客户端,对于有相同情况的读者可以参考 WSL 配置代理 ,否则请善用互联网。
安装
只需执行:
sudo apt update
sudo apt install -y systemd-genie
解决一些问题
在开始前,请使用 genie -h 了解相关的命令:
usage: genie [-h] [-V] [-v] [-a USER] (-i | -s | -l | -c ... | -u | -r | -b)
Handles transitions to the "bottle" namespace for systemd under WSL.
optional arguments:
-h, --help show this help message and exit # 显示本条帮助信息然后退出
-V, --version show program's version number and exit # 显示版本号然后退出
-v, --verbose display verbose progress messages # 显示冗长的处理消息
-a USER, --as-user USER
specify user to run shell or command as (use with -s or -c) # 指明执行命令或启动 shell 的用户
commands:
-i, --initialize initialize the bottle (if necessary) only # 仅初始化容器
-s, --shell initialize the bottle (if necessary), and run a shell in it # 初始化容器并在其中打开一个 shell
-l, --login initialize the bottle (if necessary), and open a logon prompt in it # 初始化容器并打开一个登录命令行
-c ..., --command ...
initialize the bottle (if necessary), and run the specified command in it # 初始化容器并执行命令
-u, --shutdown shut down systemd and exit the bottle # 终止 systemd 并退出容器
-r, --is-running check whether systemd is running in genie, or not # 检测 systemd 是否运行在容器中
-b, --is-in-bottle check whether currently executing within the bottle, or not # 检测目前的 shell 是否在容器中
For more information, see https://github.com/arkane-systems/genie/
genie 的配置文件为 /etc/genie.ini 为方便调试,我们这里使用 Vim 对其进行修改:
sudo vim /etc/genie.ini
将会看到:
[genie]
secure-path=/lib/systemd:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
update-hostname=true
update-hostname-suffix=-wsl
clone-path=false
clone-env=WSL_DISTRO_NAME,WSL_INTEROP,WSLENV,DISPLAY,WAYLAND_DISPLAY,PULSE_SERVER
systemd-timeout=240
resolved-stub=false
target-warning=true
对于 systemd-timeout=240 选项,我们需要暂时将其修改为 5, 该选项决定了容器在 systemd 进入 running 状态前会等待多久。
genie 设计为 systemd 默认目标(``default target) 为 multi-user.target` 下工作,为此,需要执行:
sudo systemctl set-default multi-user.target
并显示:
Created symlink /etc/systemd/system/default.target → /lib/systemd/system/multi-user.target.
为了测试并解决问题,执行:
genie -s
如果遇到:
Waiting for systemd....!!!!!
genie: systemd did not enter running state (unknown) after 5 seconds
genie: this may be due to a problem with your systemd configuration
genie: information on problematic units is available at https://github.com/arkane-systems/genie/wiki/Systemd-units-known-to-be-problematic-under-WSL
genie: a list of failed units follows:
Failed to list units: 传输端点尚未连接
genie: systemd in unsupported state 'unknown'; cannot proceed
其中每新显示出一个 ‘!’ 代表着等待了 1 秒,正如我们在 genie 配置文件中配置的那样,genie 应该在 5s 内完成启动,如果您感觉到启动时间远超过此时间,并且出现 state 'unknown'则根据
,您需要重启电脑并执行:
sudo systemctl mask acpid
如果仍出现卡顿的情况,您可能需要参考
Windows 11 + Genie 2.3: Genie seems stuck
中
@PaulKGrimes
中提到那样配置 .wslconfig文件(参考
WSL 中的高级配置
)并重启WSL(参考
WSL 基本命令
)。
即在你的 Windows 用户文件夹(C:\Users<你的用户名>)下的.wslconfig 中添加一行kernelCommandLine=systemd.unified_cgroup_hierarchy=1
接着执行genie -s, 您应该会看到类似以下的说明:
Waiting for systemd....!!!!!
genie: systemd did not enter running state (degraded) after 5 seconds
genie: this may be due to a problem with your systemd configuration
genie: information on problematic units is available at https://github.com/arkane-systems/genie/wiki/Systemd-units-known-to-be-problematic-under-WSL
genie: a list of failed units follows:
UNIT LOAD ACTIVE SUB DESCRIPTION
● ssh.service loaded failed failed OpenBSD Secure Shell server
● systemd-remount-fs.service loaded failed failed Remount Root and Kernel File Systems
● systemd-sysusers.service loaded failed failed Create System Users
LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.
3 loaded units listed.
genie: WARNING: systemd is in degraded state, issues may occur!
其中列出了三个未能正常启动的服务:ssh.service、systemd-remount-fs.service 和 systemd-sysusers.service。对于不同的发行版可能会遇到不尽相同的错误,您需要参考
known-problematic systemd units list
和 Github issues 寻找解决方案,正如我们接下来所做的那样。
ssh.service
对于这个问题只需要简单地执行:
sudo ssh-keygen -A
systemd-remount-fs.service
这个问题在
known-problematic systemd units list
有详细的说明,这里为了方便起见只需要删除/etc/fstab中的全部内容
sudo vim /etc/fstab
/etc/fstab 中的内容:
LABEL=cloudimg-rootfs / ext4 discard,errors=remount-ro 0 1
systemd-sysusers.service
只需要在/usr/lib/systemd/system/systemd-sysusers.service 文件中这几行:
LoadCredential=passwd.hashed-password.root
LoadCredential=passwd.plaintext-password.root
LoadCredential=passwd.shell.root
后再添加一行
LoadCredential=
为了稳定,可以将 /etc/genie.ini 中的 systemd-timeout 重设为 20.
为了方便使用,参考
WSL 中的高级配置
在 /etc/wsl.conf 中添加:
[boot]
command = genie -i
这样在 Ubuntu 启动时就会自动启动 genie.
注意,您应该在 KDE 中设置永不锁屏。
备用手段
如果您完全没有任何思路解决启动时列出的 unit 的问题,用 sudo systemctl mask <服务名> 命令来禁止其启动。
使用 x410 结合 WSL2 运行 KDE 桌面环境
诚然,您可以用 VcXsrv 实现类似的效果,它是开源免费的,并且可以在 Sourceforge 上 下载 。
x410 有着更为现代的界面和更详细的文档。您可以在 Microsoft Store 上获取,价格 354 元(目前2022年8月27日发现正在进行 4.1 折的折扣,价格 144 元),而淘宝上也有售卖兑换码,价格为 68 元。
您可以通过微软商店获得试用版进行试用。
通过 x410 作为 X-Server 转发 GUI 在 Linux 中开发的一个好处是 WSL2 可以专注于解决编程需求,而不需要去折腾QQ、微信等,这些任务可以在 Windows 上完成。
开始之前您需要勾选 x410 中的 Hyper-V (Default-Switch)、WSL2、Hyper-V 和 WSL2 (experimental).
配置 DISPLAY 环境变量[^10]10
本小节主要参考 Using X410 with WSL2 ,其中列出了三种方式,这里只选择其中一种作为说明。如果想了解更多方法或这里列出的方法不起作用,可以参考那篇文章。
默认情况下,当年启动 WSL2, Windows 会自动在 Linux 目录下生成 /etc/resolv.conf文件。所以你可以从中解析出 Windows 的 IP 并通过以下方式赋给 DISPLAY 环境变量:
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0
我们考虑将其写入 ~/.profile 文件中,这样当年你每次以该用户身份登录 bash 时就会自动地更新地址。
之后切换 x410 处于 Floating Desktop 模式,在终端中输入:
genie -c bash --login -c tplasma-x11
应该就能看见 KDE 桌面启动。
如果仍有问题,您应该尝试文中的其他方法。
为你的 Kubuntu 设置快捷方式11
建议您将本节创建的文件存放在您的 OneDrive 中以获得跨设备同步。
首先,创建一个 Windows batch 文件(*.bat),它应该包含以下内容:
@echo off
REM ### 以桌面模式启动 x410
start /B x410.exe /desktop
REM ### 启动 KDE 桌面环境
ubuntu.exe run "genie -r;if [ $? == "0" ]; then genie -c bash --login -c startplasma-x11; else genie -i && genie -c bash --login -c startplasma-x11; fi"
您应该将 Ubuntu 换成您对应的发行版。不妨将其命名为kubuntu.bat.
接着,创建一个 VB 脚本文件来静默地执行上述的脚本,而不用忍受运行时弹出的一个黑黑的命令行界面:
If WScript.Arguments.Count <= 0 Then
WScript.Quit
End If
bat = Left(WScript.ScriptFullName, InStrRev(WScript.ScriptFullName, "\")) & WScript.Arguments(0) & ".bat"
arg = ""
If WScript.Arguments.Count > 1 Then
arg = WScript.Arguments(1)
End If
CreateObject("WScript.Shell").Run """" & bat & """ """ & arg & """", 0, False
不妨将其命名为 bat-launcher.vbs .
最后,我们来创建一个桌面快捷方式。
右键空白处,选择创建一个快捷方式,对象的位置填写:
wscript.exe "<您的VB脚本存放位置>\bat-launcher.vbs" "kubuntu"
您可以通过右键创建好的快捷方式选择「属性→快捷方式→更改图标」修改创建好的图标。
对于 Ubuntu 您可以通过 https://assets.ubuntu.com/v1/49a1a858-favicon-32x32.png 来获得 Ubuntu 的 PNG 格式图标,需要您自行寻找 ico、icl 或 dll 格式的图标(例如格式转换)。
最后,您还可以通过右键该快捷方式将其固定到 Windows 开始菜单或是任务栏。
安装 Firefox
也许您注意到了,在您的 Ubuntu 22.04 中没有出现 Firefox 的身影,这是因为从 Ubuntu 22.04 开始,将仅在 snap 中提供 Firefox12,而 snap 的运行有赖于 systemd,因此我们的 kubuntu 上没有 Firefox。
诚然,这样做有一些优点,但许多人还是更喜欢传统的从 APT 安装的方式,因此催生了一大批相关的文章介绍如何以传统的方式安装,而且在我们之后的实践中,snap 版本的 Firefox 存在一些问题,我们将参考 How to Install Latest Firefox as classic Deb in Ubuntu 22.04 使得您能使用 apt 安装 Firefox.
添加 Mozilla 基金会的 PPA (Personal Package Archive)
sudo add-apt-repository ppa:mozillateam/ppa受限于网络原因,这个过程可能有点缓慢。
因为 snap 版本的 Firefox 在 Ubuntu 仓库中有着更高的版本号,因此我们需要调整 PPA 版 Firefox 的优先级。
sudo vim /etc/apt/preferences.d/mozillateamppa这个命令会创建一个新的文件,请写入如下信息:
Package: firefox* Pin: release o=LP-PPA-mozillateam Pin-Priority: 501保存文件后,请执行命令
sudo apt update,这步非常重要,否则会安装为 Ubuntu 16.04 设计的旧版本的 Firefox.最后,使用 APT 安装您的 Firefox.
sudo apt install firefox
设置网络代理
本人使用 Clash for Windows 作为代理客户端。启动您 Windows 上的 Clash for Windows 的 TUN 模式,启动该模式后 CFW 会设置一张虚拟网卡接受整个系统的流量,您具体可以参考 TUN 模式 来开启。
之后,你可以通过 Allow Lan 旁的标志查看 netowork interface 找到 Clash 虚拟网卡的 IP地址,一般为 198.18.0.1.
接着,打开 KDE 的设置→网络设置→代理→使用手动配置的代理服务器→填入198.18.0.1→端口设置为7890→勾选所有协议都使用同样的代理服务器。
最后,因为一些原因,Firefox 和 Chrome 浏览器不会遵循系统的代理配置,所以类似地,您需要手动为其设置代理:
设置→常规→滚动到底部为其设置网络连接→同样的为其手动设置代理服务器。
同样的,我们准备一份firefox.bat :
@echo off
REM ### 以窗口模式启动 x410
start /B x410.exe /wm
REM ### 启动 firefox
ubuntu.exe run "genie -r;if [ $? == "0" ]; then genie -c bash --login -c firefox; else genie -i && genie -c bash --login -c firefox; fi"
注意: x410 的启动参数变成了 /wm 意味着 x410 将以窗口化应用启动。
同样的,您的 batch 脚本应该和 VBScript 脚本应该位于同一文件夹下,快捷方式选择为:
wscript.exe "<您的VB脚本存放位置>\bat-launcher.vbs" "firefox"
您可以从 Product Identity Assets 下载 Firefox 的 Logo,并设置其为您的快捷方式的图标。
通过类似的方式,您可以创建一批快捷方式,方便您迅速地打开 Linux 桌面应用。
现在的体验基本和 WSLg 没有区别了,如果您开启了 WSLg,那么为了防止冲突,我们这里参考
WSL 中的高级配置
中的说明,在 .wslconfig 文件中添加:
guiApplications=false
但是,我们的方法还有一点和 WSLg 有差距—— 声音。接下来将解释如何操作。
开启声音13
x410 的文档中有关于让您的 WSL 发声的办法,不过那篇文章写于 2018 年 6 月 1 日,且针对的 WSL1. 不过知乎老哥根据这篇文章写了新的方法,这里我们将参考这两篇文章让我们的体验更完善。
下载 Pulse-Audio for Windows 的压缩包: 下载 ,为防止变化放上下载页面: Pulse-Audio on Windows
将压缩包解压到合适的地方,你需要记住这个位置
在解压完的目录下,找到 .\etc\pulse\default.pa 文件,修改第 42 行的内容如下(实际上就是在末尾增加
record=0)load-module module-waveout sink_name=output source_name=input record=0继续修改第 61 行内容如下:
load-module module-native-protocol-tcp auth-anonymous=1该配置会允许局域网内所有匿名终端都可以访问
PulseAudioServer服务(使用tcp 4713端口),保存并关闭default.pa文件。再编辑
etc\pulse\daemon.conf文件,修改第39行,取消注释后改为下面内容:exit-idle-time = -1这个选项是配置
PulseAudio Server不会在空闲时自动退出。从命令行打开
bin\pulseaudio.exe进行测试,此时 Windows 安全中心会弹出防火墙警示,选择确认。回到 WSL2 中,编辑
~/.profile添加:export PULSE_SERVER=tcp:$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}')修改完后还需要刷新下
source ~/.profile修改我们的
firefox.bat脚本@echo off REM ### 以窗口模式启动 x410 start /B x410.exe /wm REM ### 启动 pulseaudio start "" /B "<您的 pulse-audio 的解压位置>\bin\pulseaudio.exe" REM ### 启动 firefox ubuntu.exe run "genie -r;if [ $? == "0" ]; then genie -c bash --login -c firefox; else genie -i && genie -c bash --login -c firefox; fi"
Visual Studio Code
Visual Studio Code 目前是对 WSL 支持最好的开发工具,我将向您介绍它如何提升 WSL 的开发体验。
当然,首先您需要下载安装 Visual Studio Code:
Vistual Studio Code “Code editing. Redefined."——Free. Built on open source. Runs everywhere.
一般情况是选择 Windows 64bit System Installer 下载安装。
VSCode 还有一个社区驱动的分支—— VSCodium ,移除了微软相关的服务防止信息被跟踪泄露,但同时也意味着无法通过微软的服务轻松获取插件和同步设置,对个人隐私有要求或有开源洁癖的同学可以了解下。
要让 VS Code 和 WSL 一起工作,您需要在您的 WSL 中安装 Remote-WSL 拓展。安装此拓展后,VS Code 会在您的 Linux 中安装 VS Code Server 你可以使用 Windows 上 VS Code 的 UI,而将你的工具、语言和编译器等放到 Linux 中。
- 按
F1打开命令面板,通过 Remote-WSL 的几条命令或是在 WSL 的 bash 里使用 code 命令打开文件夹或工作区。
C++: GCC+Clangd+CMake+Ninja
C/C++ 通常是许多人第一次接触到编程语言,我们以 C++ 开始作为配置编程的第一次实践。
sudo apt install gcc g++ gdb llvm lldb clang clang++ clangd clang-format clang-tidy cmake ninja
主要检查 clang++ 版本:
clang++ --v
会看到类似:
Ubuntu clang version 14.0.0-1ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/11 Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/11 Candidate multilib: .;@m64 Selected multilib: .;@m64
主要检查是否找到标准库,否则容易出现头文件未找到的问题,即 Found candidate GCC installation 字样。
接着安装 VS Code 拓展:clangd、CodeLLDB、CMake 和 CMake Tools 拓展。
检查 clangd 版本:
clangd --version
可以从官方网站获得帮助: https://clangd.llvm.org/
对于 clangd 插件,用 clangd --help-list l查看可用的参数,来配置其行为,下面是我的 settings.json 的相关部分, 供您参考:
{
"editor.inlayHints.enabled": "onUnlessPressed",
/*<-------------------------------------------------------------------------->*/
// Extesions' Settings
/*<---------------------------------------------------------------------->*/
// clangd
/**
* This setting is relevant to clangd's version, use command:
* 'clangd --version' to check
* and use `clangd --help-list` to get reference.
*
* Ubuntu clangd version 14.0.0-1ubuntu1
* Features: linux+grpc
* Platform: x86_64-pc-linux-gnu
*/
"clangd.arguments": [
/*<------------------------------------------------------------------>*/
// clangd compilation flags options:
/**
* pecify a path to look for compile_commands.json. If path is
* invalid, clangd will look in the current directory and parent
* paths of each source file.
*/
"--compile-commands-dir=${command:cmake.launchTargetPath}",
/**
* Comma separated list of globs for white-listing gcc-compatible
* drivers that are safe to execute. Drivers matching any of these
* globs will be used to extract system includes.
*/
// e.g. /usr/bin/**/clang-*,/path/to/repo/**/g++-*
// "--query-driver=<string>",
/*<------------------------------------------------------------------>*/
// clangd feature options:
/**
* If set to true, code completion will include index symbols that
* are not defined in the scopes (e.g. namespaces) visible from the
* code completion point. Such completions can insert scope qualifiers
*/
"--all-scopes-completion",
// Index project code in the background and persist index on disk
// "--background-index",
// Enable clang-tidy diagnostics
"--clang-tidy",
/**
* --completion-parse=<value>
* Whether the clang-parser is used for code-completion
* =always - Block until the parser can be used
* =auto - Use text-based completion if the parser is not ready
* =never - Always used text-based completion
*/
// "--completion-parse=<value>",
/**
* --completion-style=<value>
* Granularity of code completion suggestions
* =detailed - One completion item for each semantically distinct
* completion, with full type information
* =bundled - Similar completion items (e.g. function overloads)
* are combined. Type information shown where possible
*/
"-completion-style=detailed",
// Show origins of completion items
// "--debug-origin",
/**
* this option may differ from clang-format's version, check it with
* 'clang-format --version'
*
* Ubuntu clang-format version 14.0.0-1ubuntu1
*
* clang-format style to apply by default when no .clang-format file
* is found. Possible values:
* LLVM、Google、Chromium、Mozilla、Webkit、Microsoft、GNU
* InheriteParentConfig:Not a real style, but allows to use the
* .clang-format file from the parent directory (or its parent if
* there is none). If there is no parent file found it falls back to
* the fallback style, and applies the changes to that.
*/
"--fallback-style=Microsoft",
/**
* When disabled, completions contain only parentheses for function
* calls.
* When enabled, completions also contain placeholders formethod
* parameters.
*/
"--function-arg-placeholders",
/**
* --header-insertion=<value>
* Add #include directives when accepting code completions
=iwyu - Include what you use. Insert the owning header for
top-level symbols, unless the header is already
directly included or the symbol is forward-declared
=never - Never insert #include directives as part of code
completion
*/
"--header-insertion=iwyu",
/**
* Prepend a circular dot or space before the completion label,
* depending on whether an include line will be inserted or not
*/
// "--header-insertion-decorators",
/**
* Limit the number of references returned by clangd.
* 0 means no limit (default=1000)
*/
// "--limit-references=1000",
/**
* Limit the number of results returned by clangd.
* 0 means no limit (default=100)
*/
// "--limit-results=100",
/**
* Path to the project root. Requires remote-index-address to be set.
*/
// "--project-root=<string>",
/**
* --ranking-model=<value>
* Model to use to rank code-completion items
* =heuristics - Use hueristics to rank code completion items
* =decision_forest - Use Decision Forest model to rank completion
* items
*/
"--ranking-model=decision_forest",
/**
* Address of the remote index server
*/
// "--remote-index-address=<string>",
/*<------------------------------------------------------------------>*/
// clangd miscellaneous options:
/**
* Parse one file in isolation instead of acting as a language server.
* Useful to investigate/reproduce crashes or configuration problems.
* With --check=<filename>, attempts to parse a particular file.
*/
// "--check[=<string>]",
/**
* If specified, limits the range of tokens in -check file on which
* various features are tested.
* Example --check-lines=3-7 restricts testing to lines 3 to 7
* (inclusive) or --check-lines=5 to restrict to one line.
* Default is testing entire file.
*/
// "--check-lines[=<string>]",
/**
* Read user and project configuration from YAML files.
* Project config is from a .clangd file in the project directory.
* User config is from clangd/config.yaml in the following directories:
* Windows: %USERPROFILE%\AppData\Local
* Mac OS: ~/Library/Preferences/
* Others: $XDG_CONFIG_HOME, usually ~/.config
* Configuration is documented at https://clangd.llvm.org/config.html
*/
// "--enable-config",
/**
* Number of async workers used by clangd. Background index also uses
* this many workers.
*/
"-j=12",
/**
* Release memory periodically via malloc_trim(3).
*/
"--malloc-trim",
/**
* --pch-storage=<value>
* =disk - store PCHs on disk
* =memory - store PCHs in memory
* Storing PCHs in memory increases memory usages, but may improve
* performance
*/
"--pch-storage=memory",
/*<------------------------------------------------------------------>*/
// clangd protocol and logging options:
/**
* --log=<value>
* Verbosity of log messages written to stderr
* =error - Error messages only
* =info - High level execution tracing
* =verbose - Low level details
*/
// "--offset-encoding=<value>",
/**
* --offset-encoding=<value>
* Force the offsetEncoding used for character positions. This bypasses
* negotiation via client capabilities
* =utf-8 - Offsets are in UTF-8 bytes
* =utf-16 - Offsets are in UTF-16 code units
* =utf-32 - Offsets are in unicode codepoints
*/
// "--offset-encoding=<value>",
// Pretty-print JSON output
"--pretty",
],
}
测试
编译运行
打开 Remote-WSL,选择合适的文件夹,按 F1 输入 CMake: Quick Start ,CMake 会自动生成一个简单的模板项目,然后同样地执行 CMake: Select a kit , 选择 GCC,虽然 MSVC 率先一步实现了全部的 C++20 特性,但是感觉不如老牌的 GCC,而 GCC 相比 clang,已经基本支持了 C++20 的特性。然后我们编辑 main.cpp 文件:
#include <array>
#include <cstdio>
#include <iostream>
using std::cout;
int main()
{
auto arr = std::to_array({1, 2, 3, 4, 5});
for (auto i : arr)
{
std::printf("%d ", i);
}
cout << "\nHello World!\n";
return 0;
}
你可能注意到 std::to_array 被标注有误,抑或是 “header not found” 问题,修改 CMakeList.txt ,在 project(* VERSION 0.1.0)后添加这两行:
set(CMAKE_EXPORT_COMPILECOMMANDS ON)
set(CMAKE_CXX_STANDARD 20)
第一行生成 compile_commands.json 帮助 clangd 分析语法,第二行设置 C++ 标准为 C++20。
接着 Ctrl+Shift+B 或 F7 编译,shift+F5 运行,应当能在命令行看到:
1 2 3 4 5
Hello World!
debug
我们使用 lldb debug 前,按 F1 , CMake: Select Variant 应当为 debug。
新建 .vscode 文件夹,添加编辑 launch.json 文件夹:
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug",
"program": "${command:cmake.launchTargetPath}",
"args": [],
"cwd": "${workspaceFolder}",
"internalConsoleOptions": "neverOpen",
"console": "integratedTerminal",
}
]
}
这是个简单的配置,能让你用 lldb 为项目 debug,接着打个断点按 F5 试试看吧。
clang-format14
然后是 clang-format 配置,在你的项目新建一个 .clang-format 文件,它应该像这样,你可以根据官方文档查看对应版本的 clang-format 格式化风格选项修改:
# The style used for all options not specifically set in the configuration
BasedOnStyle: Microsoft
# The extra indent ou outdent of access modifiers
AccessModifierOffset: 0
# If true, horizontally aligns arguments after an open brackets
AlignAfterOpenBracket: Align
# If not None, when using initialization for an array of structs aligns the
# fields into columns
AlignArrayOfStructures: Right
# Style of aligning consective assignments
# AlignConsecutiveAssignments: Consecutive
# Style of aligning consecutive bit field
# AlignConsecutiveBitFields: Consecutive
# Style of aligning consecutive declarations
# AlignConsecutiveDeclarations: Consecutive
# Sytle of aligning consecutive macro definitions
AlignConsecutiveMacros: Consecutive
# Options for aligning backslashes in escaped newlines
AlignEscapedNewlines: Right
# If true, horizontally align operands of binary and ternary expressions
AlignOperands: AlignAfterOperator
# If true, aligns trailing comments
AlignTrailingComments: true
# If a function call or braced initializer list doesn't fit on a line, allow
# putting all arguments onto the next line, even if `BinPackArguments` is false
AllowAllArgumentsOnNextLine: false
# If the function declaration doesn’t fit on a line, allow putting all
# parameters of a function declaration onto the next line even if
# `BinPackParameters` is `false`.
AllowAllParametersOfDeclarationOnNextLine: false
# Dependent on the value, `while (true) {continue;}` can be put on a single line
AllowShortBlocksOnASingleLine: Always
# If true, short case labels will be contracted to a single line
AllowShortCaseLabelsOnASingleLine: true
# Allow short enums on a single line
AllowShortEnumsOnASingleLine: true
# Dependent on the value, `int f() { return 0; }` can be put on a single line
AllowShortFunctionsOnASingleLine: All
# Dependent on the value, `if (a) return`; can be put on a single line
AllowShortIfStatementsOnASingleLine: Never
# Dependent on the value, `auto lambda []() { return 0; }` can be put on a
# single line
# AllowShortLambdasOnASingleLine: Always
# If true, `while (true) continue;` can be put on a single line
AllowShortLoopsOnASingleLine: true
# The function declaration return type breaking style to use
AlwaysBreakAfterReturnType: None
# If true, always break before multiline string literals
AlwaysBreakBeforeMultilineStrings: false
# The template declaration breaking style to use
AlwaysBreakTemplateDeclarations: MultiLine
# A vector of strings that should be interpreted as attributes/qualifiers
# instead of identifiers. This can be useful for language extensions or static
# analyzer annotations
# AttributeMacros:
# If false, a function call’s arguments will either be all on the same line or
# will have one line each
BinPackArguments: true
# If false, a function declaration’s or function definition’s parameters will
# either all be on the same line or will have one line each
BinPackParameters: true
# The BitFieldColonSpacingStyle to use for bitfields
# BitFieldColonSpacing: Both
# The brace breaking style to use
BreakBeforeBraces: Custom
# Control of individual brace wrapping cases
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: false
AfterFunction: true
AfterNamespace: true
# AfterObjCDeclaration:
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: true
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
# Break after each annotation on a field in Java files
BreakAfterJavaFieldAnnotations: false
# The way to wrap binary operators
BreakBeforeBinaryOperators: NonAssignment
# If true, concept will be placed on a new line
# BreakBeforeConceptDeclarations: true
# If true, ternary operators will be placed after line breaks
BreakBeforeTernaryOperators: false
# The break constructor initializers style to use.
BreakConstructorInitializers: BeforeComma
# The inheritance list style to use
BreakInheritanceList: BeforeComma
# Allow breaking string literals when formatting
BreakStringLiterals: true
# The column limit. A column limit of 0 means that there is no column limit.
ColumnLimit: 80
# A regular expression that describes comments with special meaning, which
# should not be split into lines or otherwise changed
# CommentPragmas:
# If true, consecutive namespace declarations will be on the same line. If
# false, each namespace is declared on a new line.
CompactNamespaces: true
# The number of characters to use for indentation of constructor initializer
# lists as well as inheritance lists
# ConstructorInitializerIndentWidth:
# Indent width for line continuations
# ContinuationIndentWidth:
# If true, format braced lists as best suited for C++11 braced lists
Cpp11BracedListStyle: true
# Analyze the formatted file for the most used line ending (\r\n or \n).
# UseCRLF is only used as a fallback if none can be derived.
# DeriveLineEnding:
# If true, analyze the formatted file for the most common alignment of & and *.
# Pointer and reference alignment styles are going to be updated according to
# the preferences found in the file. PointerAlignment is then used only as
# fallback.
# DerivePointerAlignment:
# Disables formatting completely.
# DisableFormat: false
# Defines when to put an empty line after access modifiers.
# `EmptyLineBeforeAccessModifier` configuration handles the number of empty
# lines between two access modifiers.
EmptyLineAfterAccessModifier: Never
# Leave Keep existing empty lines after access modifiers.
# `MaxEmptyLinesToKeep` is applied instead.
# MaxEmptyLinesToKeep:
# Defines in which cases to put empty line before access modifiers
EmptyLineBeforeAccessModifier: Leave
# If true, clang-format detects whether function calls and definitions are
# formatted with one parameter per line.
# NOTE: This is an experimental flag, that might go away or be renamed.
# Do not use this in config files, etc. Use at your own risk.
# ExperimentalAutoDetectBinPacking: false
# If true, clang-format adds missing namespace end comments for short namespaces
# and fixes invalid existing ones. Short ones are controlled by
# “ShortNamespaceLines”.
FixNamespaceComments: false
# A vector of macros that should be interpreted as foreach loops instead of as
# function calls.
# ForEachMacros:['RANGES_FOR', 'FOREACH']
# A vector of macros that should be interpreted as conditionals instead of as
# function calls.
# IfMacros: ['IF']
# Dependent on the value, multiple `#include` blocks can be sorted as one and
# divided based on category.
IncludeBlocks: Preserve
# `Regroup` Merge multiple #include blocks together and sort as one. Then split
# into groups based on category priority.
# `POSIX extended` regular expressions are supported.
# Regular expressions denoting the different `#include` categories used for
# ordering `#includes`.
# IncludeCategories:
# - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
# Priority: 2
# SortPriority: 2
# CaseSensitive: true
# Specify a regular expression of suffixes that are allowed in the
# file-to-main-include mapping.
# IncludeIsMainRegex:
# Specify a regular expression for files being formatted that are allowed to be
# considered “main” in the file-to-main-include mapping.
# IncludeIsMainSourceRegex:
# Specify whether access modifiers should have their own indentation level.
IndentAccessModifiers: false
# Indent case label blocks one level from the case label
IndentCaseBlocks: false
# Indent case labels one level from the switch statement.
IndentCaseLabels: true
# IndentExternBlockStyle is the type of indenting of extern blocks
# IndentExternBlock: AfterExternBlock
# Indent goto labels.
IndentGotoLabels: true
# The preprocessor directive indenting style to use
IndentPPDirectives: BeforeHash
# Indent the requires clause in a template
# IndentRequires: true
# The number of columns to use for indentation
IndentWidth: 4
# Indent if a function definition or declaration is wrapped after the type
IndentWrappedFunctionNames: false
# It is currently only available for JavaScript and disabled by default
# InsertTrailingCommas: None
# A vector of prefixes ordered by the desired groups for Java imports
# JavaImportGroups: ['com.example', 'com', 'org']
# The JavaScriptQuoteStyle to use for JavaScript strings
# JavaScriptQuotes: Double
# Whether to wrap JavaScript import/export statements.
# JavaScriptWrapImports: true
# If true, the empty line at the start of blocks is kept.
KeepEmptyLinesAtTheStartOfBlocks: true
# The indentation style of lambda bodies
# LambdaBodyIndentation: Signature
# Language, this format style is targeted at
# Language: Cpp
# A regular expression matching macros that start a block
# MacroBlockBegin:
# MacroBlockEnd:
# MaxEmptyLinesToKeep
MaxEmptyLinesToKeep: 1
# The indentation used for namespaces
# NamespaceIndentation: All
# A vector of macros which are used to open namespace blocks
# NamespaceMacros:
# Controls bin-packing Objective-C protocol conformance list items into as few
# lines as possible when they go over `ColumnLimit`
# ObjCBinPackProtocolList: Auto
# The number of characters to use for indentation of ObjC blocks
# ObjCBlockIndentWidth: 4
# Break parameters list into lines when there is nested block parameters in a
# function call.
# ObjCBreakBeforeNestedBlockParam: false
# ObjCSpaceAfterProperty
# ObjCSpaceAfterProperty: true
# Add a space in front of an Objective-C protocol list, i.e. use Foo <Protocol>
# instead of Foo<Protocol>
# ObjCSpaceBeforeProtocolList: true
# The pack constructor initializers style to use.
PackConstructorInitializers: CurrentLine
# The penalty for breaking around an assignment operator
# PenaltyBreakAssignment:
# The penalty for breaking a function call after call(
# PenaltyBreakBeforeFirstCallParameter: 1
# The penalty for each line break introduced inside a comment
# PenaltyBreakComment:
# The penalty for breaking before the first <<
# PenaltyBreakFirstLessLess:
# The penalty for breaking after (.
# PenaltyBreakOpenParenthesis:
# The penalty for each line break introduced inside a string literal
# PenaltyBreakString:
# The penalty for breaking after template declaration
# PenaltyBreakTemplateDeclaration:
# The penalty for each character outside of the column limit
# PenaltyExcessCharacter:
# Penalty for each character of whitespace indentation (counted relative to
# leading non-whitespace column).
# PenaltyIndentedWhitespace:
# Penalty for putting the return type of a function onto its own line
# PenaltyReturnTypeOnItsOwnLine:
# Pointer and reference alignment style
PointerAlignment: Left
# Different ways to arrange specifiers and qualifiers (e.g. const/volatile)
# QualifierAlignment: Leave
# The order in which the qualifiers appear. Order is an array that can contain
# any of the following:
# const、inline、static、constexpr、volatile、restrict、type
# QualifierOrder: ['inline', 'static', 'type', 'const', 'volatile' ]
# Defines hints for detecting supported languages code blocks in raw strings
# RawStringFormats:
# Reference alignment style (overrides PointerAlignment for references)
# ReferenceAlignment:
# If true, clang-format will attempt to re-flow comments
# ReflowComments:
# Remove optional braces of control statements (if, else, for, and while) in C++
# according to the LLVM coding style.
# RemoveBracesLLVM: false
# Specifies the use of empty lines to separate definition blocks, including
# classes, structs, enums, and functions.
SeparateDefinitionBlocks: Always
# The maximal number of unwrapped lines that a short namespace spans. Defaults
# to 1.
# ShortNamespaceLines: 25
# Controls if and how clang-format will sort `#includes`
SortIncludes: CaseSensitive
# When sorting Java imports, by default static imports are placed before
# non-static imports.
# SortJavaStaticImport: after
# If true, clang-format will sort using declarations
SortUsingDeclarations: true
# If true, a space is inserted after C style casts
SpaceAfterCStyleCast: false
# If true, a space is inserted after the logical not operator (`!`).
SpaceAfterLogicalNot: false
# If true, a space will be inserted after the ‘template’ keyword
SpaceAfterTemplateKeyword: false
# Defines in which cases to put a space before or after pointer qualifiers
# SpaceAroundPointerQualifiers:
# If false, spaces will be removed before assignment operators
SpaceBeforeAssignmentOperators: true
# If false, spaces will be removed before case colon
SpaceBeforeCaseColon: false
# If true, a space will be inserted before a C++11 braced list used to
# initialize an object (after the preceding identifier or type).
SpaceBeforeCpp11BracedList: false
# If false, spaces will be removed before constructor initializer colon
# SpaceBeforeCtorInitializerColon:
# If false, spaces will be removed before inheritance colon
SpaceBeforeInheritanceColon: true
# Defines in which cases to put a space before opening parentheses
SpaceBeforeParens: ControlStatements
# Control of individual space before parentheses
# SpaceBeforeParensOptions:
# If false, spaces will be removed before range-based for loop colon
SpaceBeforeRangeBasedForLoopColon: true
# If true, spaces will be before [. Lambdas will not be affected. Only the first
# [ will get a space added.
SpaceBeforeSquareBrackets: false
# If true, spaces will be inserted into `{}`
SpaceInEmptyBlock: true
# If true, spaces may be inserted into `()`
SpaceInEmptyParentheses: false
# The number of spaces before trailing line comments (// - comments)
SpacesBeforeTrailingComments: 1
# The SpacesInAnglesStyle to use for template argument lists
SpacesInAngles: false
# If true, spaces may be inserted into C style casts
# SpacesInCStyleCastParentheses:
# If true, spaces will be inserted around if/for/switch/while conditions
SpacesInConditionalStatement: true
# If true, spaces are inserted inside container literals (e.g. ObjC and
# Javascript array and dict literals).
# SpacesInContainerLiterals: true
# How many spaces are allowed at the start of a line comment
SpacesInLineCommentPrefix:
Minimum: 1
# Maximum:
# If true, spaces will be inserted after `(` and before `)`
# SpacesInParentheses: false
# If true, spaces will be inserted after `[` and before `]`
SpacesInSquareBrackets: false
# Parse and format C++ constructs compatible with this standard
Standard: Auto
# Macros which are ignored in front of a statement, as if they were an attribute
StatementAttributeLikeMacros:
# A vector of macros that should be interpreted as complete statements
# StatementMacros:
# The number of columns used for tab stops
# TabWidth: 4
# A vector of macros that should be interpreted as type declarations instead of
# as function calls.
# TypenameMacros: ['STACK_OF', 'LIST']
# Use `\r\n` instead of `\n` for line breaks. Also used as fallback if
# `DeriveLineEnding` is true.
# UseCRLF:
# The way to use tab characters in the resulting file
UseTab: Never
# A vector of macros which are whitespace-sensitive and should not be touched
# WhitespaceSensitiveMacros: ['STRINGIZE', 'PP_STRINGIZE']
clang-tidy15
添加编辑 .clang-tidy 文件:
---
Checks: "*,
-abseil-*,
-altera-*,
-android-*,
-fuchsia-*,
-llvm*,
-modernize-use-trailing-return-type,
-zircon-*,
-readability-else-after-return,
-readability-static-accessed-through-instance,
-readability-avoid-const-params-in-decls,
-cppcoreguidelines-non-private-member-variables-in-classes,
-misc-non-private-member-variables-in-classes,
"
WarningsAsErrors: ''
HeaderFilterRegex: ''
FormatStyle: none
在你的main.cpp 中添加这个函数:
int test(int num)
{
int a = 3;
return num * a * 5;
}
你应该能看见 5 条信息:
Variable name 'a' is too short, expected at least 3 characters
5 is a magic number; consider replacing it with a named constant
5 is a magic number; consider replacing it with a named constant
Variable name 'i' is too short, expected at least 3 characters
Do not call c-style vararg functions
至此,我们的实践简单地告一段落,显然还有很多实践值得一讲,主要是因为我水累了。