1.1、问题的引出

​ 对于虚拟机来说,硬盘其实就是一个文件。虚拟机里面的操作系统对硬盘的所有操作都是对该硬盘的操作。说白了就是虚拟机欺骗的操作系统,让它认贼作父。当然这个贼并没有任何的恶意,相反对于用户来说,反而是好事。比如我们备份虚拟机的硬盘就非常方便,把虚拟硬盘文件备份一下就可以了。当然,虚拟机也可以使用真实硬盘。这可是千真万确的呦。但是,很不幸的是Bochs并没有提供这种支持。
​ 当然啦,大多数情况下,我们使用Bochs都是用来调试自己写的小操作系统。所以,也没有使用真实硬盘的需求。但是,对于各种虚拟机来说,都自成一家,分别定义了自己的虚拟硬盘文件格式。这样一来,我们使用VBox安装的操作系统就无法在Qemu里面使用,当然也无法在Bochs里面使用。当然,我们完全可以在Qemu里面再装一个嘛。硬盘空间问题先不说,Bochs里面怎么安装个Windows Xp需要多长时间啊。真的是很难以估算啊(因为我还从来没有尝试成功过,即便是我耐心的等了两三个小时)。而,有时候我们又特别想看看Windows XP的引导过程,怎么办呢?调试一下呗。用什么调试?当然是Bochs啦。但是,怎么在Bochs里面安装Windows XP啊。
很早的时候,我们想了一个办法。那就是用Qemu安装,然后再用Bochs来运行并调试。之所以这样做是因为,Qemu的一种虚拟硬盘格式和Bochs是相同的。那就是Flat类型。但是,如果想看看Vista的呢?怎么办?还用老办法?告诉你,行不通喽。因为在Qemu里面安装不了Vista(原因不再我们的讨论之列)。
怎么办呢?是不是没解了?
​ 怎么会没解呢?别忘了Bochs可是开源的。开源就意味着你完全可以对Bochs进行改造。既然它原本不支持,那么就给他添加上嘛?(会不会很难呢?接着看吧。)

1.2、Bochs现在已经支持的硬盘格式

​ Bochs已经支持flat, concat, external, dll, sparse, vmware3, vmware4, undoable, growing, volatile, z-undoable, z-volatile等12种之多。对于各种类型的详细描述可以查看Bochs的使用手册。我们经常使用的应该就是flat和growing。不过要说的是上面列出的类型中很多已经被禁用掉了。比如dll类型,这种类型就是通过一个dll去读写一个虚拟硬盘文件。只是很可惜,该类型被禁用掉了。原因我就清楚了。因为,即便是打开这个宏后就无法编译成功。那么,我们不妨给它添加一种新的类型。

1.3、为Bochs添加一种新的硬盘格式

​ 对于每个虚拟机都会有一个配置文件(例如bochsrc.bxrc)。其中,会有一行描述了硬盘的信息:
ata0-master: type=disk, mode=flat, path="c.img", cylinders=615, heads=6, spt=17
这一行的意思就是第1个IDE接口上的主硬盘(ata0-master)插的是一个硬盘(type=disk),其类型是平坦型(mode=flat)的,虚拟硬盘文件是c.img,该硬盘的CHS参数为:615,6,17。
​ 于是,Bochs启动的时候就会执行如下代码:
iodev\harddrv.cpp==>void bx_hard_drive_c::init(void)第282行

if (SIM->get_param_enum("type", base)->get() == BX_ATA_DEVICE_DISK) {BX_DEBUG(("Hard-Disk on target %d/%d",channel,device));BX_HD_THIS channels[channel].drives[device].device_type = IDE_DISK;sprintf(sbtext, "HD:%d-%s", channel, device?"S":"M");BX_HD_THIS channels[channel].drives[device].statusbar_id =bx_gui->register_statusitem(sbtext);int cyl = SIM->get_param_num("cylinders", base)->get();int heads = SIM->get_param_num("heads", base)->get();int spt = SIM->get_param_num("spt", base)->get();Bit64u disk_size = (Bit64u)cyl * heads * spt * 512;/* instantiate the right class */image_mode = SIM->get_param_enum("mode", base)->get();switch (image_mode) {case BX_ATA_MODE_FLAT:BX_INFO(("HD on ata%d-%d: '%s' 'flat' mode ", channel, device,SIM->get_param_string("path", base)->getptr()));channels[channel].drives[device].hard_drive = new default_image_t();break;......default:BX_PANIC(("HD on ata%d-%d: '%s' unsupported HD mode : %s:%d", channel, device,SIM->get_param_string("path", base)->getptr(),atadevice_mode_names[image_mode]));break;}

​ 上面的代码的意思很清楚,就是从配置文件中读出配置信息,如CHS参数。然后根据硬盘模式判断到底是哪种类型。然后将该硬盘的hard_drive指向相关的类。比如Flat类型的是default_image_t,Growing就是growing_image_t……而这些类又都有一个相同的基类device_image_t。所以,无论格式如何变化,读写接口都是相同的。如果找不到呢?那就通知用户,您的虚拟硬盘类型不支持。
​ 现在应该就很明了了。如果我们想添加一种新的格式,只需要新定义一种类型。然后对应的对应一个相关的访问类即可。
​ 考虑到,我们的虚拟硬盘格式可能会复杂多变。我们不妨把读写文件的代码写到一个Dll里面。这样,以后当我们需要变换硬盘格式时就很简单了。只需要把我们写的那个Dll换一下就可以了。那么,如果我们想扩展多个类型呢?反不能每多一种都改一次Bochs的源码吧?解决起来很简单,我们只需要把Dll的名字和硬盘的类型名结合起来,然后在Default里面进行处理,再通过类型名加载对应的Dll不就可以了。例如我们添加VBoxGrowing类型的硬盘其Dll就叫做VBoxGrowing.dll。为了防止重名,我们规定这些Dll都被放在程序所在的VDisk子目录下。所以,我们将上面的代码修改如下:

......default:BX_INFO(("HD on ata%d-%d: '%s' '%s' mode ", channel, device,SIM->get_param_string("path", base)->getptr()));channels[channel].drives[device].hard_drive = new dll2_image_t(SIM->get_param_string("path", base)->getptr());break;}

​ 注意,上面所说的仅仅是主要改动。并不是全部改动。其实,主要是对配置文件的读取部分修改了一点,将mode的enum类型改成了string类型。
​ dll2_image_t就是我们用来调用对应Dll的类。定义如下:

typedef int (*dll2_open)(const char * pathname, int flag);
typedef void (*dll2_close)();
typedef ULONG64 (*dll2_seek)(ULONG64 offset, int whence);
typedef ULONG64 (*dll2_size)();
typedef long (*dll2_read)(void* buf, size_t count);
typedef long (*dll2_write)(const void* buf, size_t count);
class dll2_image_t : public device_image_t
{
public:dll2_image_t(const char* dllName);~dll2_image_t();// Open a image. Returns non-negative if successful.int open(const char* pathname){return open(pathname, O_RDWR);};// Open an image with specific flags. Returns non-negative if successful.int open(const char* pathname, int flags);// Close the image.void close(){m_close();};// Position ourselves. Return the resulting offset from the// beginning of the file.Bit64s lseek(Bit64s offset, int whence){return m_seek(offset, whence);};// Read count bytes to the buffer buf. Return the number of// bytes read (count).ssize_t read(void* buf, size_t count){return m_read(buf, count);};// Write count bytes from buf. Return the number of bytes// written (count).ssize_t write(const void* buf, size_t count){return m_write(buf, count);};private:dll2_open m_open;dll2_close m_close;dll2_seek m_seek;dll2_read m_read;dll2_write m_write;HMODULE m_hDll;};dll2_image_t::dll2_image_t(const char* dllName):device_image_t()
{char szDllName[MAX_PATH] = {0};sprintf(szDllName, "vdisk\\%s.dll", dllName);m_hDll = LoadLibrary(szDllName);if (m_hDll){m_open = (dll2_open)GetProcAddress(m_hDll, "vdOpen");if (!m_open){BX_PANIC(("No vdOpen() in vdisk.dll!"));}m_close = (dll2_close)GetProcAddress(m_hDll, "vdClose");if (!m_close){BX_PANIC(("No vdClose() in vdisk.dll!"));}m_seek = (dll2_seek)GetProcAddress(m_hDll, "vdSeek");if (!m_seek){BX_PANIC(("No vdSeek() in vdisk.dll!"));}m_read = (dll2_read)GetProcAddress(m_hDll, "vdRead");if (!m_read){BX_PANIC(("No vdRead() in vdisk.dll!"));}m_write = (dll2_write)GetProcAddress(m_hDll, "vdWrite");if (!m_write){BX_PANIC(("No vdWrite() in vdisk.dll!"));}}else{BX_PANIC(("Can't find out Hard Drive DLL:%s!", szDllName));m_open = NULL;m_close = NULL;m_seek = NULL;m_read = NULL;m_write = NULL;}
}dll2_image_t::~dll2_image_t()
{if (m_hDll){FreeLibrary(m_hDll);m_hDll = NULL;}
}int dll2_image_t::open(const char* pathname, int flag)
{if (!m_open){return -1;}int iRet = m_open(pathname, flag);if (iRet < 0){BX_ERROR(("Open file(%s:%d) failed!", pathname, flag));}dll2_size size = (dll2_size)GetProcAddress(m_hDll, "vdSize");if (!size){BX_PANIC(("No vdSize() in vdisk.dll!"));return -1;}hd_size = size();return iRet;
}

​ 代码非常简单。就不再详细描述了。基本逻辑就是通过类型名找到对应的Dll并导出相关的函数。然后,依次调用而已。
​ 下面我们再看看一个简单的硬盘类型格式,其实就是Flat格式。我们不妨定义它为myFlat类型。

#include <stdarg.h>
#include <stdio.h>
HANDLE g_hFile = NULL;
char g_szFileName[MAX_PATH] = {0};#define LOG_FILE "myFlat.log"void LOG(LPTSTR lpszFormat, ...)
{va_list arg;char szBuf[1024] = {0};va_start (arg, lpszFormat);vsprintf (szBuf, lpszFormat, arg);va_end (arg);OutputDebugString(szBuf);FILE * f = fopen(LOG_FILE, "a+");if (f){fwrite(szBuf, 1, strlen(szBuf), f);fclose(f);f = NULL;}
}int vdOpen(const char * pathname, int flag)
{g_hFile = CreateFile(pathname,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,0);if (g_hFile == INVALID_HANDLE_VALUE){LOG("myFlat::Open file %s failed!flag=%d, ErrorNO=%ld",pathname,flag,GetLastError());return -1;}strcpy(g_szFileName, pathname);return (int)g_hFile;
}void vdClose()
{if (g_hFile != INVALID_HANDLE_VALUE){CloseHandle(g_hFile);g_hFile = INVALID_HANDLE_VALUE;memset(g_szFileName, 0, sizeof(g_szFileName));}
}ULONG64 vdSeek(ULONG64 offset, int whence)
{LARGE_INTEGER liOffset;liOffset.QuadPart = offset;liOffset.LowPart = SetFilePointer(g_hFile, liOffset.LowPart, &liOffset.HighPart, whence);if (liOffset.LowPart == INVALID_FILE_SIZE){LOG("vdisk::SetFilePointer(%s, %I64u, %d) failed!", g_szFileName, liOffset.QuadPart, whence);}return liOffset.QuadPart;
}#define HDDEV_HEAD "\\\\.\\physicaldrive"ULONG64 vdSize()
{if (strncmp(g_szFileName, HDDEV_HEAD, strlen(HDDEV_HEAD)) == 0){// TODO:支持获取的大小硬盘return 0;}DWORD dwHigh = 0;LARGE_INTEGER liSize;liSize.LowPart = GetFileSize(g_hFile, &dwHigh);if (liSize.LowPart == INVALID_FILE_SIZE){LOG("myFlat::GetFileSize(%s) failed!", g_szFileName);return 0;}return liSize.QuadPart;
}long vdRead(void* buf, size_t count)
{DWORD dwRead = 0;if (!ReadFile(g_hFile, buf, count, &dwRead, 0)){LOG("myFlat::ReadFile(%s, %lu) failed!", g_hFile, count);return 0;}return dwRead;
}long vdWrite(const void* buf, size_t count)
{DWORD dwWrite = 0;if (!WriteFile(g_hFile, buf, count, &dwWrite, 0)){LOG("myFlat::WriteFile(%s, %lu) failed!", g_hFile, count);return 0;}return dwWrite;
}

​ 同样,我也不对这段代码进行详细的注解(它们实在是太简单了,不是吗?)。需要说的是,这个格式可是支持真实硬盘的,不信的话你就把你的虚拟硬盘文件指定为:\.\physicaldrive0试一下。如果CHS参数正确的话,你肯定可以看到你操作系统的引导画面。不过,这样做是有一定危险的喔。你想想啊,你现在正在运行着的系统在哪儿啊?不就是在\.\physicaldrive0这个文件里面吗?如果再在虚拟机里面运行一遍会怎样呢?可以告诉的是,我试过,且已经很到了Windows的引导进度条,并且没有造成恶劣后果(但是,我可不保证你能和我一样好运)。

​ 怎么样?很简单吧。源码之下了无秘密。那就赶紧动手吧。

为Bochs添加新的虚拟硬盘格式相关推荐

  1. VirtualBox中虚拟Ubuntu添加新的虚拟硬盘

    VirtualBox中装好Ubuntu后,发现硬盘空间不够使用 了.以下是搜集整理的解决办法: 1. 添加新硬盘 设置 -> Storage -> SATA控制器->右击,选择&qu ...

  2. Debian10 Centos7 ProxmoxVE 虚拟硬盘格式转换

    测试:vmdk格式转为qcow2格式 # 下载测试用vmdk wget https://firmware.koolshare.cn/LEDE_X64_fw867/虚拟机转盘或PE下写盘专用/openw ...

  3. VirtualBox之vdi、vhd、vmdk虚拟硬盘格式相互转换

    Windows7的引导程序能够引导vhd格式的虚拟硬盘,而VirtualBox创建的虚拟硬盘文件是vdi格式的,怎么办呢? 以前要借助其他软件才能实现,但是VirtualBox早就悄悄为我们带来了一个 ...

  4. VirtualBox虚拟机下vdi、vhd、vmdk虚拟硬盘格式的相互转换

    Windows7的引导程序能够引导vhd格式的虚拟硬盘,而VirtualBox创建的虚拟硬盘文件是vdi格式的,怎么办呢?以前要借助其他软件才能实现,但是VirtualBox早就悄悄为我们带来了一个V ...

  5. Vm挂载虚拟硬盘(傻瓜式教程)

    Vm挂载虚拟硬盘(傻瓜式教程) 第一步:添加虚拟磁盘 打开vm,单机选择红帽的系统 编辑虚拟机设置 点击下面的添加 选择硬盘然后下一步 如果没有特殊的磁盘格式要求就默认推荐就好了 使用物理硬盘:需要一 ...

  6. Vm挂载虚拟硬盘(手把手教程)

    注意:下面还有一些知识的补充  一:前置工作 首先,打开我们的Vm,右击我们要测试的系统,点击设置 然后我们点击添加, 我这里之前已经添加了一块5G的虚拟硬盘 在这里默认选择,硬盘,一直下一步 使用物 ...

  7. xenserver 安装新硬盘_怎么为为 XenServer 添加新磁盘

    要在XenServer主机上安装一台虚拟机有三种方法--从顶部的菜单栏安装:在AC上部右击主机安装:从AC上部的任务栏安装.当选择安装虚拟机时,在标签窗口会出现第五个标签--"安装XenVM ...

  8. KVM虚拟机添加虚拟硬盘

    给KVM中虚拟机添加虚拟硬盘 背景 解决方法 背景 最近开始在网上找MySQL的课程学习,一开始听讲师说一般DBA在服务器存储数据时一般都会再挂载一个额外的硬盘来存储数据,在我们公司的DB部门的同事那 ...

  9. 指定计算机上的虚拟硬盘,初始化新加的虚拟硬盘

    初始化新加的虚拟硬盘 (2015-01-06 10:04:58) 标签: parallels 初始化新虚拟硬盘 在Parallels Desktop中,将新的空白虚拟硬盘添加到虚拟机配置后,对于安装在 ...

最新文章

  1. Python记录-基础语法入门
  2. Windows Phone APP中禁用截图
  3. SAP BSP和JSP页面里UI元素的ID生成逻辑
  4. 使用所有对象共有的方法
  5. 剖析Docker Swarm和Mesos:是什么?如何结合?有什么优势?
  6. 需要显卡还是cpu_装机应该在哪个硬件上省钱, CPU还是显卡, 看完本文就知道了...
  7. matlab线旋转成面,用matlab怎么绘制一条曲线绕z轴旋转生成的曲面?
  8. java获取weblogic路径_weblogic下java web项目获取根路径
  9. Rhino在java中的用法
  10. python允许无止境的循环_ParisGabriel:Python无止境 day07
  11. 如何保障企业业务流程的落地实施?
  12. sqlserver如何快速生成不重复的随机数据
  13. 最近发现一个不错的网站,可以用它来赚钱
  14. 员工缺乏责任心的四大原因
  15. 计算机毕设(附源码)JAVA-SSM基于Internet快递柜管理系统
  16. python的图形界面库wxpython的快速简单使用
  17. CCF 201709-2 公共钥匙盒 (Java 100分)
  18. Atollic TrueSTDIO下修改STM32L475VE的变量分配地址
  19. Springboot实现识别pdf信息
  20. WinXP原版与VOL版的区别

热门文章

  1. vue组件-echarts地图显示柱状图并给柱状图添加点击事件(支持自定义地图)
  2. 安盟信息如何用纷享销客CRM实现企业从线索到客户成功全流程管理
  3. Gated Bi-directional CNN for Object Detection
  4. 计算机配置及性能测试,配置性能测试及总结_一体电脑评测-中关村在线
  5. win7自带屏幕录像工具
  6. 【年终总结】辞旧迎新,2020,我们再出发
  7. 我的世界服务器插件文件夹,我的世界服务器管理程式(ServerUtils)插件
  8. 三个简短的动画实现饿了么红包滑动效果
  9. 一步一步教你写股票走势图——K线图五(高亮联动二)
  10. 动物芯片扫码器使用方法