ubuntu
15. Ubuntu应用¶
按我们最近的经验,cygwin这种模拟Linux接口的程序Bug挺多,不是可靠的学习平台。而devc++其实用的就是Cygwin的编译器,所以也不那么稳定。所以我们尽量还是直接用Ubuntu。
要用Ubuntu,对它基本的功能就有必要有基本的认识了。之前我们说过,Windows下安装的Ubuntu其实是一个虚拟机,它的内核还是Windows,只是这个内核模拟自己是个Linux,从而让Linux的程序可以在这上面运行。
反正你马上就要学习操作系统了,我们就对这个做一个简单的介绍。
操作系统是个什么东西呢?它是用来管理整台机器的软件。
这怎么说呢?你写一个程序helloworld.cpp,另一个人写另一个程序,叫word.cpp,这个程序都要运行,都要用内存,你也要用0-100的内存,他也要用0-100的内存,那这个内存到底谁用?所以操作系统负责管理所有的内容,谁要用就找OS分配,所以0-100的内容分配给你了,就不会分配给他。这就是操作系统的功能。
现代的操作系统都是有保护能力的,给你0-100的内存,那么word就连看都不能看。这通过硬件的一些设置来保证的,这个在计算机组成原来里面学,我们这里先不管。我们需要理解的是:硬件可以让部分软件有更高的权限,部分软件的权限低一点。所以,高权限的软件可以给人分配内存,而低权限的软件只能使用别人给它分配的内存。所以,一个操作系统分成了两个部分:用户和内核。硬件分时工作在不同的状态,用户态运行用户程序,内核态运行操作系统内核。
我们用一个Windows,Windows的核心功能都在内核中,其他看得见的,比如浏览器,写字板,这些都是用户态程序。Windows会留一些库在用户态,你的helloworld.cpp可以调用cout,这个cout的程序是Windows留在用户态的,它知道怎么请求Windows的内核,从而完成输出这个功能。
这个请求内核的行为,就叫“系统调用”。
cygwin(包括devc++),重写了这些用户态的库,所以,你以为你调用了cout是在调用Linux的接口,其实你这个cygwin的库会转换成Windows的系统调用,所以最终Windows的内核收到的是一个Windows请求。所以cygwin和devc++编译出来的程序都是.exe文件,它发出的请求就是Windows的系统调用。
一个真正的ubuntu操作系统是一个Linux操作系统。它也有自己的用户态程序,用户态库和Linux内核(其实Linux这个名字本来就是内核的名字,而不包括它在用户态的东西,我们把连用户态的完整Linux操作系统叫做GNU/Linux,而不是简单叫它Linux的)。
而Windows下装的Ubuntu称为WSL(Windows Subsystem for Linux),它是让Windows的内核模拟了一个接受Linux系统调用的接口。也就是说,你编译出来的就是一个Linux程序,发出的是Linux的系统调用,但这个调用被Windows内核重新解释了,按Linux的请求返回给Linux的用户态程序。
这个比cygwin可靠,是应为Windows内核有什么功能是固定的,它不可能和Linux的请求完全一样,所以有些功能是不那么稳定的。但WSL是Windows主动根据Linux的设计一个个接口做出来的,所以这些功能基本上是一一对应的,这个可靠性就高多了。
而WSL中的Ubuntu,除了没有Linux内核里面一些专门的功能,在用户态它和一个真正的Ubuntu是几乎一样的,你在这里编译的程序,甚至可以直接拷贝到一个真正的Ubuntu中运行,都是没有问题的。所以,你在WSL中学会的东西,都可以直接用于一个真正的Linux(除了内核的特殊功能,比如Linux内核支持ext4或者加密文件系统,这个WSL就是支持不了的)。
现在我们开始学习Ubuntu的基本用法,Ubuntu是一个GNU/Linux发行版。这是什么意思呢?一个基本的Linux,我们需要一个Linux内核,然后我们需要一些开发库(比如支持你做cout的那些库),这是Linux的基本部分。但你还需要很多软件,比如ls,vim,gcc,find等等,这些大部分都是GNU开源组织开发的,每个GNU/Linux发行版,都是组合了一堆这些软件才组成一个可以用的操作系统。每个不同的发行版,都会组合不同的软件,不同的软件版本。这些维护这些软件组合和版本的厂商,就会构成一个发行版。这些厂商还会在发行版上加上自己开发的一些软件,让操作系统更好用。
所以,发行版是一个选择一组软件的软件。一个软件,会包含很多文件,比如你写一个游戏,你需要运行程序,需要图片(用来显示),需要手册。这个软件可能还用了不同的库,这些库需要先安装其他软件才能安装。为了管理每个软件需要那些文件,需要依赖其他的什么软件。每个发行版最重要的一个特征,就是选择一个“包管理器”,这些包管理器管理一个软件(包)包含哪些文件,依赖哪些其他包,依赖它们的什么版本,以及安装完了,怎么设置这个软件和其他依赖的软件才能让它和其他软件配合得更好。所以,我们选择一个发行版,除了选择它使用的软件,更多时候,是选择它所使用的包管理器。
Ubuntu是其中一个发行版,它是基于Debian这个发行版开发的,所以它使用了一样的包管理系统(称为deb,debian的前三个字符),在这个基础上,它再选择和增加自己的软件包。所以,会用Ubuntu的包管理器,你也会用其他所有基于Debian的包管理器的发行版,比如KaliLinux,MindLinux等。我们先学怎么用这个包管理器,然后才学Ubuntu集成的软件。
deb包管理器包括两层,底层叫dpkg(Debian Package),用来管理单个软件包。高层叫apt(Advanced Package Tool),用来管理所有可以安装的软件包。你可以用dpkg管理本地已经安装的软件包::
dpkg -l # 列出所有已经安装的软件包
dpkg -L coreutils # 列出coreutils软件包包含哪些文件
dpkg -S /bin/ls # 查找/bin/ls这个文件是那个软件包提供的
dpkg -s coreutils # 显示coreutils软件包的状态
一般平时这些就沟通了。其他的看手册吧。
apt主要用来管理所有可以直接安装的软件包的。这一点和Windows这些操作系统不同。Windows的软件基本上每个都是收费的,所以互相之间没有什么合作,每个软件都是要把所有依赖的软件都放到一起去使用的。而Linux大部分是自由软件,这些软件反正免费,所以发行版就直接把他们一起放到他们自己的服务器上了。所以像apt这样的软件是Linux发行版特有的,里面包含非常丰富的软件包(数万个)。你可以用这些命令::
apt update # 更新服务器上所有文件的最新目录,这样你本地就可以查找
# 它们了,后面的命令都以这个命令为基础
apt search gcc # 查找包含gcc关键字的软件包
apt install gcc # 安装gcc软件包
apt remove gcc # 删除gcc软件包
apt upgrade # 根据最新的目录,更新所有旧的软件包(相当于升级)
apt dist-upgrade # 版本升级,更激进的upgrade(我一般都用这个)
这样就可以了,现在其实更流行另一个apt的替代品,叫nala,可以先用apt安装,其他功能和命令和apt几乎一样的(比如nala search,nala install这些)。这个没有apt稳定,但速度更快,界面也更好看。
Ubuntu也提供类似Windows的安装方式,称为snap,一个包里面包含所有依赖的东西。这个商业化气息很重,很多自由开发者都不喜欢它,觉得这是对自由软件的背叛。我建议不要用——关键是这东西的服务在国外,很慢,所以我这里就不说怎么用了。
正经学习Ubuntu,还需要学习它特有的软件,比如怎么配置网络,怎么配置防火墙,怎么播放音乐等等。但在Windows下你几乎都不需要,因为那些可以用Windows自己搞定,这样,你只要能在没有某个命令的时候,用apt search找一下是哪个软件包的,然后用apt install安装就可以了。
在Ubuntu下你需要的工具可能也就是:
编辑器:vim
开发工具:g++, make, gdb, sloccout, ctags, cscope, ftrace, strace, python3
压缩打包加密:tar, zip, gnupg
网络拷贝:wget, ssh, scp, ping
其他基本上都用不上。以后学汇编的时候,还可以装一下qemu,就够了。
我这里没有介绍最基本的命令,比如ls, mv之类的,这个要自己去找Linux命令行的书去学。其中最常用也最复杂的两个基本命令是find和grep,这个要花时间去学一下。前者用于找文件,后者用于找文件的内容。类似这样::
find /home/selina/work -name "test*.cpp" # 查找/home/selina/work目录中,名字是test*.cpp的文件
grep -Ir "test" /home/selina/work # 查找/home/selina/work目录中,内容包含test字符串的所有文件和所在的行
但如果你短时间记不住太复杂的语法,我会推荐这两个命令的模糊版本::
fzf
rg test
fzf是Fuzzy Find,用起来很简单,输入命令后会出来一个fzf自己的命令行,你输入一点接近的字符,它就能给你找,而且你一边输入它就已经在找了,用过就知道,这会简单很多。当然,你想严格找到想要的文件(特别是写脚本的时候),还是要学find的。
rg是Rip Grep(用apt install ripgrep安装),它也很简单,在后面简单写要找的字符串,它就在自动当前目录的所有文件中找对应的行了,不需要记住那么多东西。
无论用什么命令,命令行最大的麻烦还是切换目录,这个我推荐安装一下autojump,安装完在HOME目录的.bashrc中增加这样一行::
. /usr/share/autojump/autojump.sh
之后你进过的目录都可以快速跳过去,而不需要用cd命令一层层找了。比如你曾经进入过/usr/src/linux/Documentation目录,现在你回到/home/selina目录了。你要重新进前面那个目录,有了autojump,你不需要一层层cd过去的,你只要这样::
j Doc
或者
j doc
甚至
j tat
这样就行了,反正足够能区分开其他去过的目录就行。这样在命令上切换目录就可以很快。
15.1. 深入一点的WSL知识¶
这一章不是必须的,但如果你用多了,可能会关心这里的一些知识,我也放在这里作为参考了。
到2024年,WSL有两个版本,我们前面说的主要是WSL1,其实我们现在用得比较多的是WSL2。这两个版本的技术是不同的,前者其实有点类似cygwin,虽然它不是直接替换glibc,但它最后调用内核的时候,是靠Windows的内核去响应的。而WSL2用的是虚拟机技术,它是真有一个Linux的内核去响应你的要求的,所有,WSL的兼容性会更好一些。
两个版本都通过wsl.exe这个Windows下的命令去支持,你可以用:
wsl --set-default-version 2
把默认的版本设置为2。你可以装多个发行版,这通过下面的命令完成::
wsl --list --online # 查看有哪些Linux发行版
wsl --install -d <name> # 安装name表示的发行版
wsl --list --verbose # 看你安装了哪些发行版,包括看到它用什么wsl版本运行
wsl --set-version <name> 2 # 设置某个发行版用什么版本的wsl运行
所以,我们必须理解,在Windows上通过WSL运行Linux,其实是在两个系统上运行程序的,互相之间是独立的,但因为Windows把两者组合在一起了,所以很容易给你一个误会,好像两者是一个系统似的。
下面介绍这种集成的关系:
你在Windows的PowerShell或者命令行(cmd)中直接运行wsl或者bash,都是运行默认的发行版,所以,如果你在命令行用bash xx.sh运行脚本,这其实不是在运行一个Windows本地的bash,而干脆是在运行虚拟机中的wsl。这个bash.exe被放在Windows的system目录下面了。所以它看起来在运行一个windows的bash,但这个bash的作用是调用虚拟机,然后用虚拟机来运行你的bash脚本。
如果你有多个发行版,你运行bash.exe,它会用默认的发行版的默认用户来运行你的脚本。这可以通过如下命令来设置::
wsl --set-default # 设置默认发行版
wsl --user # 设置默认用户
所以,你可以这样实现在Windows一侧运行Linux一侧的命令::
wsl ls
wsl rm -Rf mydir
wsl find . -name "*.exe"
这些命令都会用Linux一侧的命令,而使用Windows一侧的目录来运行的。
反过来,在Linux中也可以运行Windows中的命令,只要带上.exe的路径就可以了。比如:
notepad.exe test.txt
explore.exe .
这里涉及的目录都是Linux一侧的目录。实际上,默认情况下,Windows一侧都可以用这样的方法访问Linux一侧的路径::
\\$wsl\\<发行版名字>\\...
而Linux一侧可以用这种路径访问Windows中的路径::
/mnt/c/...
/mnt/d/...
一般认为,跨系统访问对方的文件会稍慢一点点,但如果不是大文件拷贝,其实是感受不到的。
发行版默认安装在C盘,可以这样进行迁移或者复制::
wsl --exprot debian debian.tar
wsl --unregister debian
wsl --import debian1 d:\wsl xxx.tar
wsl --import debian2 d:\wsl xxx.tar
每个发行版的名字,会自动产生一个对应名字的.exe文件,比如debian1会生成一个debian1.exe,你可以用这个名字配置发行版的默认用户::
debian1 config --default-user kenny
每个发行版内部的/etc/wsl.conf目录用于配置这个发行版的默认参数,所以前面这个配置也可以直接修改这个文件,加入::
[user]
default=kenny