最近打算开发一个工具,将我自制的os内核安装到物理磁盘中运行,毕竟在虚拟机上运行与在真实的物理机上运行还是有差别的,同时也方便后续在windows上进行开发试验。前期需要验证一下磁盘绝对扇区读写的可行性。本来尝试用C语言的fopen,fread,fwrite,fclose这一套函数来编写的,但是fwrite一直不成功,最后换成了win32的文件操作api: CreateFile, WriteFile,ReadFile竟然成功了。
实验步骤分三个部分,首先读取磁盘的第一个扇区并打印内容,然后从磁盘第一个扇区开始写入制作好包含系统内核镜像的img文件,最后再次读取磁盘的第一个扇区内容,对比是否写入成功,全部源码如下:
// DiskBuildTool.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#define DISK "\\\\.\\PhysicalDrive2"
// 参数:输出的字符串指针,开始位置,长度
// 返回值:读取的大小
int ReadDisk(unsigned char* buffer,DWORD start,DWORD size)
{
OVERLAPPED over = { 0 };
over.Offset = start;
HANDLE handle = CreateFile(TEXT(DISK), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (handle == INVALID_HANDLE_VALUE){
return (-1);
}
DWORD readsize;
if (ReadFile(handle, buffer, size, &readsize, &over) == 0)
{
CloseHandle(handle);
return (-1);
}
CloseHandle(handle);
return readsize;
}
//参数:写入字符串指针,开始位置,长度
//返回值:写入的大小
int WriteDisk(unsigned char* buffer,DWORD start,DWORD size)
{
OVERLAPPED over = { 0 };
over.Offset = start;
HANDLE handle = CreateFile(TEXT(DISK), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (handle == INVALID_HANDLE_VALUE){
return (-1);
}
DWORD writeensize;
if (WriteFile(handle, buffer, size, &writeensize, &over) == 0)
{
CloseHandle(handle);
return (-1);
}
CloseHandle(handle);
return writeensize;
}
int _tmain(int argc, _TCHAR* argv[])
{
unsigned char mbr[512];
FILE *f_img;
int len = ReadDisk(mbr, 0, 512); // 读取并打印一个扇区
if ( len > 0 )
{
printf("Read MBR from %s:", DISK);
for (int i = 0; i < len/16; i++){
printf("\n");
for ( int j = 0; j < 16; j++ ){
printf("%02X ", mbr[i*16+j]);
}
}
// 将img文件写入磁盘
fopen_s(&f_img, "E:\\LoverOS\\arch\\c.img", "rb");
if ( !f_img ){
perror("fopen()");
exit(-1);
}
unsigned long offset = 0;
while( !feof(f_img )){
if (fread(mbr,sizeof(mbr),1,f_img) != 1 ){
break;
}
WriteDisk(mbr,offset, 512);
offset += 512;
}
fclose(f_img);
}else
{
perror("");
exit(-1);
}
printf("\n");
memset(mbr,0,sizeof(mbr));
len = ReadDisk(mbr, 0, 512); // 再次读取一个扇区并打印
if ( len > 0 )
{
printf("Read MBR from %s:\n", DISK);
for (int i = 0; i < len/16; i++){
printf("\n");
for ( int j = 0; j < 16; j++ ){
printf("%02X ", mbr[i*16+j]);
}
}
printf("\n\nimg to disk done\n");
}else
{
perror("");
exit(-1);
}
return 0;
}
其中需要说明的是
#define DISK "\\\\.\\PhysicalDrive2"
这个定义的物理磁盘号,编号格式为PhysicalDrivenn,其中最后的n代表物理盘序号,从0开始,第一块磁盘就是PhysicalDrive0,第二块磁盘就是PhysicalDrive1,以此类推,我的电脑系统上一共有3块磁盘,第一块是256G的固态硬盘,第二块是500G的机械盘,因为我的系统分区都是MBR类型, 不敢往这两个磁盘写入数据,万一废了启动不了系统就麻烦了。 因此我用工具制作了一个内存硬盘,效果与真实物理磁盘一样的效果,这里它就是PhysicalDrive2。
实验结果如下
1,读取的第一个扇区:
写入文件后再读取:
镜像文件的第一个扇区数据:
看起来写入后再读取的内容与原文件内容是一致的,实验成功!!!
注意:如果写入失败,可能是由于权限不足导致, 可以尝试把vs以管理员身份运行,或者在vs中按以下配置修改一下工程属性,使得应用获得管理员权限:
到这里,忍不住直接插入一块U盘来试试,直接把
#define DISK "\\\\.\\PhysicalDrive2"
替换为
#define DISK "\\\\.\\PhysicalDrive3"
一切顺利!