一直都在想,不管用谁的电脑,我都可以得到一个完全一致的工作环境,上面有我喜爱的软件,有我保存的重要资料,甚至浏览器的各种偏好都得一模一样!现在的云计算技术可以部分解决这个问题,但是远远不够。我的理想境界是,无论身处何地,一开机,看到的就是自己的电脑,或者相当于自己的电脑!自己可以任意处理自己的数据,不把隐私泄露给别人,当然也不要破坏人家已有的软件环境。要实现该理想,有几个办法:
1. 随时带一个笔记本或者上网本。到哪里都用它。
2. 带一个小小的U盘,里面装好操作系统和软件,到哪里都可以用它。
3. 只要有一个网络帐号,到哪里都可以得到一个专属于自己的安全的软件环境(含操作系统和应用软件,以及个人的数据)。
办法1太麻烦,办法3需要云计算技术的进一步发展,且网络带宽进一步提升。办法2最现实,也最好用。本文就集中精力来讲解如何在U盘上装一个Linux,自己的软件和数据都可以在上面永久保存。
A. 版本选择
Linux的版本很多,这里选择Ubuntu。理由就是好用,官方和民间的支持都不错,对PC或者笔记本的兼容性好,本身集成的内容已经能够满足桌面用户的基本需求,在上面用自带的软件包管理器获取软件也极其方便。不用像其他发行版一样,经常为了支持某个驱动绞尽脑汁,或者要安装某个特殊的软件不得不反复设置。
最新的稳定版是Ubuntu10.04。便携U盘的目的是提供一个方便的办公环境,所以应该选择桌面版,不需要选择服务器版,除非硬要做一个移动的服务器,呵呵。在32位和64位这两个版本之间我犹豫了一会儿。理论上64位的性能更好(所以服务器一般都用64位),但是32位的兼容性理所当然会超过64位(很多软件只有32位版本),且这个U盘系统有可能会跑在比较老的32位CPU上面,所以最终我最终选择了32位。
B. Live USB的制作
很多Linux都支持Live CD的制作,Ubuntu当然也不例外。Live CD不仅可以引导电脑安装系统,它本身就是一个功能丰富的Linux系统,预装了常用的软件,完全通过光盘启动可以进入桌面,浏览网页,在线聊天,编辑文件,样样都不在话下。甚至我们可以安装软件,不过,由于我们所做的改动都是在内存中进行的,重启电脑之后,新装的软件没有了,所有改动也都消失了。
现在的Ubuntu也支持Live USB的制作,省去了刻录光盘的麻烦。几乎所有较新的电脑都支持USB启动,所以,Live USB可以方便地用来引导系统。当然,它拥有Live CD的全部功能,更重要的是,U盘本身是可读可写的,我们甚至有机会用它来保存自己的更改,这比Live CD强多了。
我按照官方网站上的指导教程(
http://www.ubuntu.com/desktop/get-ubuntu/download
),下载了Universal USB Installer(版本1.7.9.8),在Windows上很方便地用4G U盘制作了一个Live USB。兴致冲冲地用它来启动电脑,果然看到了漂亮的Ubuntu桌面!于是,我给它配好网络,安装了一些常用的软件。过了一会儿,我关闭电脑,把U盘插在另一台电脑上面。启动之后,发现系统又变得干干净净,自己的改动完全没有保留!这,这不跟Live CD是一模一样的吗?
上网搜索了一番,有不少制作能够永久保留用户数据的U盘版Ubuntu的方法。比如,这篇文章就提到了好几种方法:
https://wiki.ubuntu.com/LiveUsbPendrivePersistent
。最直接的就是直接把U盘当成硬盘来安装。这样安装原理简单,但是对U盘的磨损也是最大的。我们都知道,U盘里面用来存储的芯片都是Nand Flash。Flash芯片的每一个块(block)都有一定的擦除/写入次数限制,超过这个限制该块就很可能被坏掉。典型的写入次数有1万次和10万次。平常我们使用的大容量U盘一般都用MLC Flash芯片,写入周期大概是1万次。U盘里的固件做了磨损平衡和坏块处理等算法,我们把它用作平常保存数据的介质,绰绰有余,也不用担心写1万次就会把U盘写坏的情况。但是如果用来直接跑系统,由于临时文件等诸多因素,其写入的压力会大很多,在这种情况下,我们不得不考虑U盘的使用时间。所以,直接用U盘当硬盘来装Linux不推荐。因为这样的话,即使禁掉了SWAP分区,/tmp分区还会在硬盘上面,该分区的临时文件会加速U盘的磨损。我按照另外一种办法
http://rudd-o.com/en/linux-and-free-software/a-better-way-to-create-a-customized-ubuntu-live-usb-drive
来制作了一个U盘,但是重启之后,只能停在GRUB的控制台里,无法启动Linux。正当我准备详细琢磨该办法的时候,我发现Universal USB Installer本来就支持制作能够持久保存数据的U盘,只是之前我没有注意而已!请看下图:
之前我忽略了Step 4,用的是No Persistence的默认值。其实该选项的目的就是让你选择合适的大小来保存自己的修改数据。我选择了2GB Casper-RW,这样,我对系统做的任何修改(包括新装软件,增加的文件等等),都会被存在casper-rw这个文件当中,该文件最大可以是2G。当然,用户完全可以不关心casper-rw这个文件,按照以往的用法来使用U盘的Ubuntu。
谈到这里,制作U盘版的Ubuntu就告一段落了。利用工具做好的U盘,几乎可以在任何主流的电脑上面启动。装好的软件也会按照原样保存下来,爽哉!如果你就像简单地尝试一下U盘Linux,看到这里就可以跳过本文其他部分了。如果你想了解有关U盘Linux的部分技术细节,想利用U盘来有效地移动办公,请继续看下去。
C. U盘的分区
U盘当然是容量越大体积越小就越小。现在4G和8G的U盘都很便宜了。我手头最多的U盘为4G的,所以本文中的U盘大小默认都为4G。我做好一个U盘,还可以把整个U盘的内容备份到一个文件里面,随时可以恢复系统,且可以方便地把它克隆到另外的U盘上,让我的移动工作环境进一步实用——它不需要物理上依赖于某个特定的U盘,只要我手头有备份的镜像文件,就可以随时恢复整个系统。
不过,4G的U盘实际容量并没有4G。这主要是单位换算不同造成的。在Windows,是按照1K=1024字节,1M=1024K,1G=1024来进行计算,U盘的厂商一般按照1K=1000字节,1M=1000K,1G=1000M来计算。所以,在Windows中看到的U盘的实际容量经常为3.7-3.8G(不同的U盘略有差别)。假定我有2个4G的U盘,一个容量是3.7G,一个是3.8G。如果我在3.7G的U盘上装好系统,然后复制到3.8G的U盘上,没有任何问题;但是反过来,就会有一部分数据丢掉,可能会导致潜在的问题。为了提高兼容性,我给U盘重新分区,划了一个3.5G的FAT32分区用来制作系统。Universal USB Installer制作U盘Linux的时候,似乎必须要求U盘本身已经被格成FAT格式,否则不能选择该盘。事实上,U盘Linux的分区本身采用的就是FAT格式,只是上面的文件可能在Linux启动之后又被当作块设备,以其他的文件系统格式再次被加载(稍后我会再讲)。大家再制作自己的Linux系统时,如果考虑到方便复制等兼容性因素,也可以给U盘保留一点点容量。
Windows的磁盘管理不能直接对U盘进行分区,我们可以借助第三方的工具来进行。我是用二进制编辑工具直接打开U盘,修改第一个扇区(MBR)的分区表来实现预留3.5G大小的分区的。剩下的200MB空间我就让它保留着好了。因为Flash需要用磨损平衡算法来提高使用寿命,这200MB额外的空间被用到全局的磨损平衡当中,对延长U盘的使用时间很可能有一定的好处。
D. aufs文件系统
启动U盘版Ubuntu,输入mount命令,得到如下的输出:
aufs on / type aufs (rw) none on /proc type proc (rw,noexec,nosuid,nodev) none on /sys type sysfs (rw,noexec,nosuid,nodev) none on /dev type devtmpfs (rw,mode=0755) none on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620) /dev/sdb1 on /cdrom type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=cp437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro) /dev/loop0 on /rofs type squashfs (ro,noatime) none on /sys/fs/fuse/connections type fusectl (rw) none on /sys/kernel/debug type debugfs (rw) none on /sys/kernel/security type securityfs (rw) none on /dev/shm type tmpfs (rw,nosuid,nodev) tmpfs on /tmp type tmpfs (rw,nosuid,nodev) none on /var/run type tmpfs (rw,nosuid,mode=0755) none on /var/lock type tmpfs (rw,noexec,nosuid,nodev) none on /lib/init/rw type tmpfs (rw,nosuid,mode=0755) binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noexec,nosuid,nodev) gvfs-fuse-daemon on /home/ubuntu/.gvfs type fuse.gvfs-fuse-daemon (rw,nosuid,nodev,user=ubuntu)
我们暂且不考虑proc,sysfs等用来对系统进行辅助管理的特殊文件系统,只考虑和U盘的实际存储打交道的部分。在上述列表里,没有看到我们最熟悉的ext3/ReiserFS等Linux下的主流文件系统。那到底U盘上建立的是什么文件系统,是如何进行数据保存的呢?
前面已经提到,制作U盘的时候,需要U盘上已有一个FAT32的分区。制作工具把必备的文件拷入到该分区,并不会对分区本身做修改。所以,现在的U盘理所当然地具有一个FAT32的分区。从mount的输出可以看到,设备/dev/sdb1(就是U盘的第一个分区,电脑的本地硬盘被识别成/dev/sda)被mount到/cdrom,格式为VFAT。顾名思义,该分区的功能类似于Live CD,我们可以简单地理解它里面包含了一张Ubuntu安装光盘所包含的必要内容。因为所有的软件和数据都保存在/dev/sdb1上,所以/cdrom理所当然地包含了我们需要的一切内容。
但是从/cdrom里面能够找到的只是有限的文件,似乎大部分都和安装有关,熟悉的就是/cdrom/casper目录下的内核文件vmlinuz以及initrd.lz。并不能找到我们所熟悉的其他Linux系统文件以及配置文件。既然/cdrom已经囊括全部了,那根目录下的/bin,/sbin,/etc又来自哪里呢?
/cdrom/casper下有一个巨大无比的文件filesystem.squashfs,大概700MB,从名字可以猜测,这个文件是不是本身就包含了运行Linux所必需的文件系统?且它的大小也能够容纳足够的应用程序。在Linux中,单一文件可以被加载成一个loop设备,然后它就可以当作普通的块设备使用,被mount成其他文件系统。(文件a可以作为块设备block_a,上面又存在一个文件b,那文件b又可以被作为块设备block_b,上面又存在一个文件c…一层套一层,这就是《盗梦空间》的文件系统版演绎。)用下面的命令查看当前使用的loop设备信息:
$ sudo losetup -a /dev/loop0: [0811]:27 (/cdrom/casper/filesystem.squashfs) /dev/loop1: [0811]:33 (/casper-rw-backing/casper-rw)
系统创建了两个loop设备,其中/dev/loop0就是基于filesystem.squashfs创建的。结合前面mount命令的输出,我们得以知道/dev/loop0被mount到/rofs目录,文件系统类型是squashfs。squashfs是一种只读压缩文件系统,在很多Linux上普遍使用,速度快,压缩比例高,乃Live CD制作的首选。在Ubuntu里简要查询了一下,/rofs下共有文件10万多个,总容量大概是1.7GB。想想原始的filesystem.squashfs的大小才700MB,squashfs的表现已经很不错。从这里也可以了解到Live CD(Live USB)上为什么可以放这么多的内容。
/rofs目录下有几乎我们所需要的一切,包括熟悉的bin,sbin,lib,etc,home等目录。俨然整个Linux系统就被它纳入囊中。可是它只是只读的(rofs表示read only file system),我们现在的U盘Linux对文件系统是可读可写的。而且,/rofs下面有一个bin,根目录下也有/bin,这两者是什么关系呢?比较了一下,/rofs/bin怎么和/bin似乎一模一样?
秘密在于aufs (Another Union FS)这个特殊的文件系统。它不是一个普通的文件系统,而是把现有的不同文件系统组合在一起,形成一个统一的文件系统。假设有有两个文件系统,fs_a和fs_b,它们所挂载的目录分别是/a和/b。利用aufs,可以让它们合并起来,/a和/b均可重新映射到根目录/。实际使用的时候,一般用户只需要关心根目录/所对应的文件系统是aufs即可,不必关心太多的底层细节。谈到这里,有人就有疑问了。比如,在aufs下创建一个文件,那该文件到底是存在于/a下面,还是/b下面呢?这不存在着很明显的冲突问题吗?
实际上,aufs并不具有把任意文件系统聚合在一起的功能。它一般只组合两个文件系统,其中一个是只读的,另外一个是可读可写的。读取文件的时候,如果仅仅只读的文件系统上有该文件,则从只读该处读取;如果可读写的文件系统上也有该文件,则从可读写的文件系统上读取。写文件的话,理所当然地就只能往可读写文件系统上写。只读文件系统可以认为是基础,可读写文件系统是补充,采取copy on write(COW,写时复制)的方式来保存用户的修改。因为仅仅保存修改部分,所以可以很大程度的节约空间。
就本文的例子来说,/rofs是一个只读文件系统,另外用了一个文件casper-rw来保存用户的更改数据。casper-rw是Universal USB Installer直接创建在U盘根目录下的。如果该文件的大小是2G,则可以保存2G的修改内容。如果U盘的空间允许,我们可以向这个文件的末尾追加内容,扩大文件以便增加可保存数据的容量。该文件本身被加载成一个loop设备,从losetup -a的输出来看,该设备为/dev/loop1。在此设备上创建好了ext2文件系统,被mount到/cow中。aufs把/rofs和/cow合并起来,一起加载到根目录/下。/rofs放的是原始文件,/cow保存用户修改。所以,我们可以看到根目录下的文件结构基本和/rofs下一模一样,只是新装的软件和新建的文件在/rofs下无法查看。 (aufs的创建位于initrd里的相关脚本,在启动之后,是无法直接查看/cow等目录的。)
我们可以这么简单理解,这个U盘版Ubuntu的基本系统存放于文件filesystem.squashfs当中,对它的修改都存放于文件casper-rw当中。
E. 减少对U盘的磨损
由于Flash芯片的擦除/写入次数限制,我们在保证满足自己需要的前提下,尽量减少U盘的写入次数。默认情况下,U盘版Ubuntu已经禁掉了SWAP分区,且把/tmp挂载到了内存,这些都是延长U盘使用时间的有效手段。
在系统中,默认会把最后一次访问文件的时间(last access time)给记录到该文件的元数据当中。这个操作的开销实际上很大,比如,我仅仅打开一个文件不对该文件做任何写操作,都会导致系统底层对磁盘至少有一次写操作。对U盘来说,last access time的更新也是降低性能,磨损U盘的一个重要因素。该时间对于普通的应用来说没有任何实际用处,所以我们可以把它禁掉。(有少数程序需要依赖于last access time来进行一些特殊判断,不过,从大局考虑,去掉它是U盘版Linux提高性能减少磨损的一个重要手段。)Linux的mount命令有一个noatime的选项,加上它可以实现禁用last access time的功能。(还有一个针对目录的禁用last access time选项,叫nodiratime。但是有人分析只要加了noatime,就会实现nodiratime的功能。)比如,如果把/dev/sdc加载成ext3文件系统,并禁掉last access time,我们可以这样调用mount:
mount -t ext3 -o noatime /dev/sdc /mnt/myfs
事实上,在制作U盘版Ubuntu的过程当中,/rofs已经被设置了noatime。加载到/cow打casper-rw以及aufs也都被设置了noatime(mount输出无法直接查看,但是在initrd.lz的相关脚本中可以看到)。另外,/cdrom是以relatime的方式来mount的。相比noatime来说,relatime是一个考虑性能和功能的折衷方案。只有当文件的last access time早于最后修改时间的时候,才会对它进行更新。因此,用relatime的方式,可以记录文件修改之后的第一次访问的时间。
日志文件系统(比如ext3)会额外向磁盘写入日志,以保证文件系统的完整性。但是对于U盘来说,增加的日志无疑会带来更多的写入操作。因此,如果对系统的稳定性不敏感的话,建议capser-rw上采用不带日志的ext2文件系统。事实上,该U盘版Ubuntu在初始化capser-rw时用的就是ext2.
Linux中,除了特别的需求,文件一般都是按异步模式写入磁盘当中。也就是说,应用程序写入的数据,会先放在页缓存(page cache)里面,等合适的时间再刷(flush)到磁盘当中。这种方式可以提高性能,同时也能够有效地降低写入的次数。Linux的内核导出了几个proc文件,可以供我们调节相关的flush参数。U盘版Ubuntu已经对Linux的默认参数做了一定的修改。相关的proc文件以及Ubuntu的设置值如下:
/proc/sys/vm/dirty_ratio: 20 /proc/sys/vm/dirty_background_ratio: 10 /proc/sys/vm/dirty_writeback_centisecs: 1500 /proc/sys/vm/dirty_expire_centisecs: 3000
简单解释如下:当page cache里的脏数据(dirty data)超过内存20%(dirty_ratio)的时候,会被马上刷到磁盘中;如果超过内存10%(dirty_background_ratio),进程pdflush会在后台异步地把数据刷到磁盘中。如果10%的阈值一直未达到,也不要紧,内核会每隔15秒(dirty_writeback_centisecs)启动一次flush操作,只要脏数据待在cache的时间超过了30秒(dirty_expire_centisecs),也会被刷到磁盘当中。
为了进一步减少写的次数,让脏数据在cache中待得更久,我们可以在启动脚本里更改部分参数,比如,修改dirty_writeback_centisecs为6000,表示把后台周期性flush的时间调成60秒一次。修改dirty_expire_centisecs为6000,表示脏数据的超时时间为60秒。另外两个参数可以不用修改,因为在小量写入的时候,脏数据一般也达不到内存总量的10%,在大量数据写入的时候,也没有必要把脏数据缓存更久。同时请注意,更改这些数据之后,系统突然断电等异常情况导致数据丢失的概率会相对较大一点。所以如何选择,得根据自己的需求权衡一下。
另外,U盘上请只装自己需要的软件,不要像普通Linux那样乱装一通。这是减少U盘写次数非常重要也非常简单的一个手段。
F. 备份和还原
U盘版Ubuntu做了一些设置,提高了性能,理论上也应该延长了U盘的使用时间。但是这些操作也会带来一定的风险,比如系统突然断电的情况下,最近修改的数据就更容易丢失。我们在此U盘上运行的是桌面系统,主要就是为了方便,功能够用,不像服务器那样要求很严格,也能够承担此风险(如果有更高的可靠性要求,请不要采用此U盘系统)。但是,即使U盘质量再好,我们自己也很注意,它总有可能会损坏,因此,强烈建议对U盘进行定期备份。备份之后的镜像文件,可以随时恢复到容量足够大的U盘上,让该系统具有迅速克隆的功能。即使你忘记带了U盘,也可以通过网络取得该镜像(网络速度要足够快),迅速恢复一个专属于自己的工作环境。
Windows下的很多工具都具有整盘复制功能。Linux下用dd也可以方便地进行备份(假设U盘的设备名为/dev/sda):
dd if=/dev/sdb of=/mnt/backup/ubuntu_bak1.img bs=1M
块大小(bs)设置大一点有助于提高性能,尤其是往U盘写入数据的时候。(大块的写入有利于降低U盘的底层IO。对于一般的Flash来说,一次写1个字节和一次写4K所产生的IO是一样的,甚至更多。)假设插入另外一块U盘/dev/sdc,可以把备份的镜像烧录到该U盘上,让此U盘立马成为我的定制版Ubuntu:
dd if=/mnt/backup/ubuntu_bak1.img of=/dev/sdc bs=1M
当然,如果想加快备份速度的话,也可以单独备份casper-rw文件,因为自己所作的修改全部保存在该文件当中。