故障现象

我移动硬盘有300G空间为ext4分区格式,而我的Ubuntu一直装在VMware的虚拟机里。这样我使用VMware的挂载物理磁盘的功能来使用移动硬盘上的300G空间。以前一直好好的,最近突然出现挂载错误的情况。具体表现就是,当我启动虚拟机的时候,VMware会弹出对话框:

\"VMware错误提示\"

如果我点“重试”,则一直弹出这个错误。如果点“继续”,大概点十来次就不见了,但是300G空间的数据变为只读。如果点“取消”,那么整个虚拟机就会崩溃。

问题所在

刚开始我以为磁盘损坏了,用LinuxReader工具读取分区,发现没什么问题。然后我就去网上搜,最终找到一篇:《Setting a disk to offline in order to make physical disk access work in [Windows 7] on [VMware]》。文章的整体的意思是,Windows7在磁盘在线状态下是无法使用VMware的物理磁盘挂载功能的,将磁盘脱机就可以了。经过我实验,发现文章的说法是错误的,但是歪打正着,还是解决了我的问题。

其实我碰到这个问题,是因为我的分区对应的卷脱机了。Windows下有磁盘脱机和卷脱机两种脱机状态。至于我的卷怎么脱机的我也不太清楚,估计是因为,我的ext4分区被Windows分配盘符后,我害怕不小心被格式化了,将盘符删除了,结果Windows顺便把我的卷标记为了脱机状态。关于卷和分区的关系,平时我们的普通分区仅仅是卷的一种,叫做基本卷。卷可以实现比分区更多的功能,比如跨磁盘、组建RAID等。类似于Linux下的LVM。

解决方法

以管理员方式运行cmd,然后运行diskpart,用list volume列出所有卷的状态,对于脱机的卷,找到其编号,然后用select volume <编号>命令选择这个卷,再使用online volume将选择的卷联机。

以下是操作的详细步骤:

C:>diskpart

Microsoft DiskPart 版本 6.3.9600

Copyright (C) 1999-2013 Microsoft Corporation.
在计算机上: VIRTAO-PC

DISKPART> list volume

  卷 ###      LTR  标签          FS      类型        大小     状态       信息
  ----------  ---  -----------  -----  ----------  -------  ---------  --------
  卷     0     Z                       DVD-ROM         0 B  无介质

  卷     1     E   系统         NTFS   磁盘分区         131 GB  正常     页面文件

  卷     2     F   软件         NTFS   磁盘分区         800 GB  正常

  卷     3     C               NTFS   磁盘分区          80 GB  正常     系统

  卷     4     D               NTFS   磁盘分区          31 GB  正常

  卷     5     G   我的SD卡     exFAT  可移动           29 GB  正常

  卷     6     K   虚拟机       NTFS   磁盘分区          85 GB  正常

  卷     7     J   移动存储     NTFS   磁盘分区         380 GB  正常

  卷     8     H                       可移动             0 B  无介质

  卷     9     I                       可移动             0 B  无介质


DISKPART> select volume 8

卷 8 是所选卷。

DISKPART> online volume

DiskPart 成功使所选卷联机。

DISKPART> exit

退出 DiskPart...

C:>

DiskPart

这个工具是Windows下的专业命令行磁盘管理工具,类似Linux下的fdisk。这里只介绍有关卷和磁盘状态修改的命令。

首先是list命令,可以列出系统中的磁盘、分区或者卷的各类信息,包括后文要用到的编号:

list disk
list partition
list volume

然后是select命令,这个命令用来选择当前要操作的磁盘或者卷。只有使用这个命令选择目标磁盘或者卷后,才能进行进一步的操作。

select disk <编号>
select partition <编号>
select volume <编号>

第三个是控制联机和脱机的命令。

online disk
online volume
offline disk
offline volume

使用之前要先选择分区。

其它命令的详细用法,可以输入命令后查看帮助,也可以用help命令。

Linux下如果/boot是独立分区,经常更新内核容易导致/boot分区空间满了。解决方法就是删除旧的内核,这里以CentOS为例。

1. 查看/boot空间

# df -h /boot

2. 查看系统正在使用的内核版本,防止删错了导致无法启动

# uname -a

3. 删除旧的内核

# rm -rf /boot/*2.6.32-279*

如果多次升级,则可能有多个版本的旧内核,注意区分,别删错了。

也可以将这些文件先转移到别的地方,防止改错后系统无法启动。

4. 修改/boot/grub/grub.conf文件,将和2.6.32-279相关的条目删除

这一步注意备份grub.conf,一旦改错了还能有后悔药。

Go中可以使用“+”合并字符串,但是这种合并方式效率非常低,每合并一次,就必须遍历一次字符串。Java中提供StringBuilder类来解决这个问题。Go中也有类似的机制,那就是Buffer。以下是示例代码:

package main

import (
    \"bytes\"
    \"fmt\"
)

func main() {
    var buffer bytes.Buffer

    for i := 0; i < 1000; i++ {
        buffer.WriteString(\"a\")
    }

    fmt.Println(buffer.String())
}

使用bytes.Buffer来组装字符串,不需要遍历,只需要将添加的字符串放在缓存末尾即可。

那么这种效率差别有多大呢?我们做个测试,组装10万个“a”。

package main

import (
    \"bytes\"
    \"fmt\"
    \"time\"
)

func main() {

    _, t := testBuf()

    //fmt.Println(s)
    fmt.Println(\"string buffer : \", t, \"ns\")

    _, t = testPlus()

    //fmt.Println(ss)
    fmt.Println(\"string plus : \", t, \"ns\")
}

func testPlus() (s string, t int64) {
    start := time.Now().UnixNano()
    for i := 0; i < 100000; i++ {
        s += \"a\"
    }
    end := time.Now().UnixNano()

    return s, end - start
}

func testBuf() (s string, t int64) {
    var buf bytes.Buffer
    start := time.Now().UnixNano()
    for i := 0; i < 100000; i++ {
        buf.WriteString(\"a\")
    }
    s = buf.String()
    end := time.Now().UnixNano()

    return s, end - start
}

测试结果:

string buffer :  3001700 ns
string plus :  1414015200 ns

使用Buffer仅仅用了3ms,而加号则使用了1.4s,差距巨大。而且使用Buffer时,三分之二的时间用在了buf.String()上,也就是说,组装时间仅仅有1ms,其余2ms花在了转换为字符串上。

我本来是组装1万个“a”的,结果使用Buffer的方式测不出时间(时间为0ns),不得已采用的10万,这也说明Buffer效率非常高。