58、磁盘操作与文件管理的深入解析

磁盘操作与文件管理的深入解析

1. 磁盘扇区的读写操作

在磁盘操作中,扇区的读写是基础且关键的部分。程序的核心是 ReadSector 过程,它利用 INT 21h Function 7305h 从磁盘读取每个扇区。读取到的扇区数据会被存放在缓冲区中,然后通过 DisplaySector 过程进行显示。

以下是相关的代码片段:

ret
DisplaySector ENDP
;-----------------------------------------------
MoveCursor PROC
;
; Advance the cursor to the next column,
; check for possible wraparound on screen.
;-----------------------------------------------
cmp
curr_col,79
; last column?
jae
L1         
; yes: go to next row
inc
curr_col
; no: increment column
jmp
L2
L1:
mov
curr_col,0
; next row
inc
curr_row
L2:
INVOKE Setcursor,curr_row,curr_col
ret
MoveCursor ENDP
;-----------------------------------------------------
Setcursor PROC USES dx,
row:BYTE, col:BYTE
;
; Set the screen cursor position
;-----------------------------------------------------
mov
dh, row
mov
dl, col
call
Gotoxy
ret
Setcursor ENDP
END main

在显示扇区数据时,由于大多数扇区包含二进制数据,如果使用 INT 21h 来显示,ASCII 控制字符会被过滤,导致显示不连贯。因此,更适合使用 INT 10h Function 0Ah ,它能将 ASCII 码 0 到 31 作为图形字符显示。不过, Function 0Ah 不会自动移动光标,所以需要额外的代码在显示每个字符后将光标向右移动一列, SetCursor 过程简化了 Irvine16 库中 Gotoxy 过程的实现。

此外,扇区显示程序还有一些有趣的变体。例如,可以提示用户输入要显示的扇区编号范围,每个扇区可以以十六进制形式显示,还能让用户使用 PageUp PageDown 键在扇区之间前后滚动。

2. 系统级文件函数

在实地址模式下, INT 21h 提供了一系列系统服务,包括创建和更改目录、更改文件属性、查找匹配文件等,这些服务在高级编程语言库中通常是不可用的。调用这些服务时,函数编号会被放在 AH AX 中,其他寄存器可能包含输入参数。下面详细介绍几个常用的函数。

2.1 获取磁盘可用空间(7303h)

INT 21h Function 7303h 可用于查找 FAT16 或 FAT32 驱动器的磁盘卷大小和可用磁盘空间。信息会以标准结构 ExtGetDskFreSpcStruc 返回,其结构如下:

ExtGetDskFreSpcStruc STRUC
    StructSize                WORD  ?
    Level                     WORD  ?
    SectorsPerCluster         DWORD ?
    BytesPerSector            DWORD ?
    AvailableClusters         DWORD ?
    TotalClusters             DWORD ?
    AvailablePhysSectors      DWORD ?
    TotalPhysSectors          DWORD ?
    AvailableAllocationUnits  DWORD ?
    TotalAllocationUnits      DWORD ?
    Rsvd                      DWORD 2 DUP (?)
ExtGetDskFreSpcStruc ENDS

各字段的简要说明如下:
| 字段 | 说明 |
| ---- | ---- |
| StructSize | 表示 ExtGetDskFreSpcStruc 结构的大小(以字节为单位),是返回值 |
| Level | 输入和返回级别值,必须初始化为零 |
| SectorsPerCluster | 每个簇中的扇区数 |
| BytesPerSector | 每个扇区的字节数 |
| AvailableClusters | 可用簇的数量 |
| TotalClusters | 卷上的总簇数 |
| AvailablePhysSectors | 卷上可用的物理扇区数,不考虑压缩 |
| TotalPhysSectors | 卷上的总物理扇区数,不考虑压缩 |
| AvailableAllocationUnits | 卷上可用的分配单元数,不考虑压缩 |
| TotalAllocationUnits | 卷上的总分配单元数,不考虑压缩 |
| Rsvd | 保留成员 |

调用该函数时,需要以下输入参数:
- AX 必须等于 7303h
- ES:DI 必须指向一个 ExtGetDskFreSpcStruc 变量。
- CX 必须包含 ExtGetDskFreSpcStruc 变量的大小。
- DS:DX 必须指向一个包含驱动器名称的以空字符结尾的字符串,可以使用 MS-DOS 类型的驱动器规范(如 “C:\”),也可以使用通用命名约定的卷规范(如 “\Server\Share”)。

如果函数执行成功,会清除进位标志并填充结构;否则,会设置进位标志。调用函数后,以下计算可能会有用:
- 要计算卷的大小(以千字节为单位),使用公式 (TotalClusters * SectorsPerCluster * BytesPerSector) / 1024
- 要计算卷上的可用空间(以千字节为单位),使用公式 (AvailableClusters * SectorsPerCluster * BytesPerSector) / 1024

以下是一个使用 INT 21h Function 7303h 获取 FAT 类型驱动器卷的可用空间信息的程序:

; Disk Free Space 
(DiskSpc.asm)
INCLUDE Irvine16.inc
.data
buffer ExtGetDskFreSpcStruc <>
driveName BYTE "C:\",0
str1 BYTE "Volume size (KB): ",0
str2 BYTE "Free space (KB): ",0
str3 BYTE "Function call failed.",0dh,0ah,0
.code
main PROC
mov
ax,@data
mov
ds,ax
mov
es,ax
mov
buffer.Level,0
; must be zero
mov
di,OFFSET buffer
; ES:DI points to buffer
mov
cx,SIZEOF buffer
; buffer size
mov
dx,OFFSET DriveName
; ptr to drive name
mov
ax,7303h
; get disk free space
int
21h
jc
error
; failed if CF = 1
mov
dx,OFFSET str1
; volume size
call
WriteString
call
CalcVolumeSize
call
WriteDec
call
Crlf
mov
dx,OFFSET str2
; free space
call
WriteString
call
CalcVolumeFree
call
WriteDec
call
Crlf
jmp
quit
error:
mov
dx,OFFSET str3
call
WriteString
quit:
exit
main ENDP
;------------------------------------------------------------
CalcVolumeSize PROC
;
; Calculate and return the disk volume size, in kilobytes.
; Receives: buffer variable, a ExtGetDskFreSpcStruc structure
; Returns:  EAX = volume size
; Remarks:  (SectorsPerCluster * 512 * TotalClusters) / 1024
;------------------------------------------------------------
mov
eax,buffer.SectorsPerCluster
shl
eax,9
; mult by 512
mul
buffer.TotalClusters
mov
ebx,1024
div
ebx
; return kilobytes
ret
CalcVolumeSize ENDP
;------------------------------------------------------------
CalcVolumeFree PROC
;
; Calculate and return the number of available kilobytes 
; on the given volume.
; Receives: buffer variable, a ExtGetDskFreSpcStruc structure
; Returns:  EAX = available space, in kilobytes
; Remarks: (SectorsPerCluster * 512 * AvailableClusters) / 1024
;-------------------------------------------------------------
mov
eax,buffer.SectorsPerCluster
shl
eax,9
; mult by 512
mul
buffer.AvailableClusters
mov
ebx,1024
div
ebx
; return kilobytes
ret
CalcVolumeFree ENDP
END main
2.2 创建子目录(39h)

INT 21h Function 39h 用于创建新的子目录。它接收一个指向包含路径规范的以空字符结尾的字符串的指针( DS:DX )。例如,以下代码在默认驱动器的根目录下创建一个名为 ASM 的新子目录:

.data
pathname BYTE "\ASM",0
.code
mov
ah,39h
; create subdirectory
mov
dx,OFFSET pathname
int
21h
jc
display_error

如果函数失败,进位标志会被设置,可能的错误返回代码是 3 和 5。错误 3(路径未找到)表示路径名的某些部分不存在;错误 5(访问被拒绝)表示提议的子目录已经存在,或者路径中的第一个目录是根目录且已满。

2.3 删除子目录(3Ah)

INT 21h Function 3Ah 用于删除目录。它接收一个指向所需驱动器和路径的指针( DS:DX ),如果省略驱动器名称,则假定为默认驱动器。以下代码从驱动器 C 中删除 \ASM 目录:

.data
pathname  BYTE 'C:\ASM',0
.code
mov
ah,3Ah 
; remove subdirectory
mov
dx,OFFSET pathname
int
21h
jc
display_error

如果函数失败,进位标志会被设置,可能的错误代码是 3(路径未找到)、5(访问被拒绝:目录包含文件)、6(无效句柄)和 16(尝试删除当前目录)。

2.4 设置当前目录(3Bh)

INT 21h Function 3Bh 用于设置当前目录。它接收一个指向包含目标驱动器和路径的以空字符结尾的字符串的指针( DS:DX )。例如,以下语句将当前目录设置为 C:\ASM\PROGS

.data
pathname  BYTE "C:\ASM\PROGS",0
.code
mov
ah,3Bh
; set current directory
mov
dx,OFFSET pathname
int
21h
jc
display_error
2.5 获取当前目录(47h)

INT 21h Function 47h 用于返回包含当前目录的字符串。它接收驱动器编号( DL ,0 表示默认,1 表示 A,2 表示 B 等)和一个指向 64 字节缓冲区的指针( DS:SI )。MS-DOS 会在这个缓冲区中放置一个以空字符结尾的字符串,包含从根目录到当前目录的完整路径名(省略驱动器字母和前导反斜杠)。如果函数返回时进位标志被设置, AX 中唯一可能的错误返回代码是 0Fh(无效驱动器规范)。

以下示例中,MS-DOS 返回默认驱动器上的当前目录路径:

.data
pathname  BYTE 64 dup(0)  
; path stored here by MS-DOS
.code
mov
ah,47h
; get current directory path
mov
dl,0
; on default drive
mov
si,OFFSET pathname     
int
21h
jc
display_error
2.6 获取和设置文件属性(7143h)

INT 21h Function 7143h 可用于检索或设置文件属性等任务。在 Windows 9x 中,它取代了旧的 MS-DOS INT 21h Function 39 。将文件名的偏移量传递给 DX 。要设置文件属性,将 BL 赋值为 1,并将 CX 设置为表 15 - 8 中列出的一个或多个属性。 _A_NORMAL 属性必须单独使用,其他属性可以使用 + 运算符组合。

以下代码将文件的属性设置为只读和隐藏:

mov
ax,7143h
mov
bl,1
mov cx,_A_HIDDEN + _A_RDONLY
mov
dx,OFFSET filename
int
21h

要获取文件的当前属性,将 BX 设置为 0 并调用相同的函数,属性值会以 2 的幂的组合形式返回在 CX 中。可以使用 TEST 指令来评估单个属性。例如:

test cx,_A_RDONLY
jnz
readOnlyFile
; file is read-only

文件属性的具体含义如下表所示:
| 值 | 含义 |
| ---- | ---- |
| _A_NORMAL (0000h) | 文件可以读写,此值仅在单独使用时有效。 |
| _A_RDONLY (0001h) | 文件可以读取,但不能写入。 |
| _A_HIDDEN (0002h) | 文件是隐藏的,不会出现在普通的目录列表中。 |
| _A_SYSTEM (0004h) | 文件是操作系统的一部分或由操作系统专用。 |
| _A_ARCH (0020h) | 文件是存档文件,应用程序使用此值来标记文件以进行备份或删除。 |

3. 磁盘操作流程总结

下面是磁盘操作相关函数调用的流程:

graph TD;
    A[选择操作] --> B{获取磁盘可用空间};
    A --> C{创建子目录};
    A --> D{删除子目录};
    A --> E{设置当前目录};
    A --> F{获取当前目录};
    A --> G{获取和设置文件属性};
    B --> B1[设置 AX=7303h];
    B --> B2[设置 ES:DI 指向 ExtGetDskFreSpcStruc 变量];
    B --> B3[设置 CX 为 ExtGetDskFreSpcStruc 变量大小];
    B --> B4[设置 DS:DX 指向驱动器名称字符串];
    B --> B5[调用 INT 21h];
    C --> C1[设置 ah=39h];
    C --> C2[设置 dx 指向路径名];
    C --> C3[调用 INT 21h];
    D --> D1[设置 ah=3Ah];
    D --> D2[设置 dx 指向路径名];
    D --> D3[调用 INT 21h];
    E --> E1[设置 ah=3Bh];
    E --> E2[设置 dx 指向路径名];
    E --> E3[调用 INT 21h];
    F --> F1[设置 ah=47h];
    F --> F2[设置 dl 为驱动器编号];
    F --> F3[设置 si 指向缓冲区];
    F --> F4[调用 INT 21h];
    G --> G1[设置 ax=7143h];
    G --> G2{设置属性还是获取属性};
    G2 --> G3[设置 BL=1,CX 为属性值,dx 指向文件名(设置属性)];
    G2 --> G4[设置 BX=0,dx 指向文件名(获取属性)];
    G3 --> G5[调用 INT 21h];
    G4 --> G5;

通过以上的介绍,我们对磁盘操作和文件管理的系统级函数有了更深入的了解,这些函数为我们在实地址模式下进行磁盘和文件操作提供了强大的工具。在实际应用中,我们可以根据具体需求灵活运用这些函数,实现各种复杂的磁盘和文件管理任务。同时,在进行编程练习时,一定要注意备份受影响的磁盘,避免数据丢失。

磁盘操作与文件管理的深入解析

4. 编程练习与实践

在掌握了磁盘操作和系统级文件函数的相关知识后,通过编程练习可以更好地巩固和应用这些知识。以下是一些具体的编程练习及其要求。

4.1 设置默认磁盘驱动器

编写一个程序,提示用户输入磁盘驱动器字母(A、B、C 或 D),然后将默认驱动器设置为用户选择的驱动器。

; 示例伪代码,具体实现需根据 INT 21h 相关功能完善
.data
prompt BYTE "请输入磁盘驱动器字母 (A, B, C, D): ", 0
.code
main PROC
    ; 显示提示信息
    mov dx, OFFSET prompt
    call WriteString
    ; 读取用户输入
    call ReadChar
    ; 根据输入设置默认驱动器(需调用 INT 21h 对应功能)
    ; ...
    exit
main ENDP
END main
4.2 获取磁盘总数据空间

编写一个名为 Get_DiskSize 的过程,它接收一个驱动器编号( AL ,0 表示 A,1 表示 B,2 表示 C 等),并返回所选磁盘驱动器的总数据空间(以字节为单位,结果存储在 DX:AX 中)。

; 示例伪代码,具体实现需根据相关功能完善
Get_DiskSize PROC
    ; 根据 AL 中的驱动器编号获取磁盘总数据空间
    ; ...
    ret
Get_DiskSize ENDP
4.3 获取磁盘可用空间

编写一个名为 Get_DiskFreespace 的过程,它接收一个指向包含驱动器说明的字符串的指针( DS:DX ),并返回所选磁盘驱动器的可用空间(以字节为单位,结果存储在 EDX:EAX 中)。同时编写一个测试程序,显示 64 位结果的十六进制表示。

; 示例伪代码,具体实现需根据相关功能完善
Get_DiskFreespace PROC
    ; 根据 DS:DX 中的驱动器说明获取磁盘可用空间
    ; ...
    ret
Get_DiskFreespace ENDP

; 测试程序
.code
main PROC
    ; 设置驱动器说明字符串指针
    mov dx, OFFSET driveSpec
    call Get_DiskFreespace
    ; 显示 64 位结果的十六进制表示
    ; ...
    exit
main ENDP
END main
4.4 显示文件属性

编写一个名为 ShowFileAttributes 的过程,它接收一个文件名的偏移量( DX ),并在控制台窗口中显示该文件的属性(正常、隐藏、只读和系统)。提示:使用 INT 21h Function 7143h

ShowFileAttributes PROC
    mov ax, 7143h
    mov bx, 0
    mov dx, [传入的文件名偏移量]
    int 21h
    ; 测试属性并显示
    test cx, _A_NORMAL
    jnz displayNormal
    test cx, _A_HIDDEN
    jnz displayHidden
    test cx, _A_RDONLY
    jnz displayReadOnly
    test cx, _A_SYSTEM
    jnz displaySystem
    ret
displayNormal:
    ; 显示 normal 属性
    ret
displayHidden:
    ; 显示 hidden 属性
    ret
displayReadOnly:
    ; 显示 read-only 属性
    ret
displaySystem:
    ; 显示 system 属性
    ret
ShowFileAttributes ENDP

; 调用示例
.code
main PROC
    mov dx, OFFSET filename
    call ShowFileAttributes
    exit
main ENDP
END main
4.5 磁盘可用空间(以簇为单位)

修改之前的磁盘可用空间程序,使其显示以下信息:
- 驱动器说明
- 每个扇区的字节数
- 每个簇的扇区数
- 总簇数
- 可用簇数

; 修改后的 Disk Free Space 程序示例
; Disk Free Space 
(DiskSpc.asm)
INCLUDE Irvine16.inc
.data
buffer ExtGetDskFreSpcStruc <>
driveName BYTE "C:\",0
str1 BYTE "Drive specification: ",0
str2 BYTE "Bytes per sector: ",0
str3 BYTE "Sectors per cluster: ",0
str4 BYTE "Total Number of clusters: ",0
str5 BYTE "Number of available clusters: ",0
.code
main PROC
    ; 初始化和调用 INT 21h Function 7303h 部分同之前程序
    ; ...
    ; 显示驱动器说明
    mov dx, OFFSET str1
    call WriteString
    mov dx, OFFSET driveName
    call WriteString
    call Crlf
    ; 显示每个扇区的字节数
    mov dx, OFFSET str2
    call WriteString
    mov eax, buffer.BytesPerSector
    call WriteDec
    call Crlf
    ; 显示每个簇的扇区数
    mov dx, OFFSET str3
    call WriteString
    mov eax, buffer.SectorsPerCluster
    call WriteDec
    call Crlf
    ; 显示总簇数
    mov dx, OFFSET str4
    call WriteString
    mov eax, buffer.TotalClusters
    call WriteDec
    call Crlf
    ; 显示可用簇数
    mov dx, OFFSET str5
    call WriteString
    mov eax, buffer.AvailableClusters
    call WriteDec
    call Crlf
    exit
main ENDP
END main
4.6 显示扇区编号

以扇区显示程序为基础,在屏幕顶部显示一个字符串,指示驱动器说明和当前扇区编号(以十六进制表示)。

; 示例伪代码,具体实现需根据扇区显示程序完善
; 在显示扇区数据前添加显示驱动器说明和扇区编号的代码
.code
main PROC
    ; 显示驱动器说明和扇区编号
    mov dx, OFFSET driveSpec
    call WriteString
    mov eax, currentSectorNumber
    call WriteHex
    call Crlf
    ; 显示扇区数据部分
    ; ...
    exit
main ENDP
END main
4.7 十六进制扇区显示

以扇区显示程序为基础,添加代码让用户按 F2 键以十六进制形式显示当前扇区,每行显示 24 个字节,并在每行开头显示第一个字节的偏移量。

; 示例伪代码,具体实现需根据扇区显示程序完善
.code
main PROC
    ; 主循环
loop:
    ; 检测 F2 键按下
    call ReadKey
    cmp al, [F2 键的扫描码]
    jne loop
    ; 以十六进制形式显示当前扇区
    ; 每行显示 24 个字节,显示偏移量
    ; ...
    jmp loop
    exit
main ENDP
END main
5. 总结与注意事项

在进行磁盘操作和文件管理的编程时,我们需要牢记以下几点:
- 数据安全 :在进行编程练习时,一定要备份受影响的磁盘,避免数据丢失。特别是在删除目录、修改文件属性等操作时,要确保操作的正确性。
- 错误处理 :在调用 INT 21h 相关函数时,要注意检查进位标志(CF),根据不同的错误代码进行相应的错误处理,避免程序崩溃。
- 函数使用规范 :不同的 INT 21h 函数有不同的输入参数要求,在调用时要严格按照要求设置寄存器的值,确保函数正常执行。

通过对磁盘操作和文件管理系统级函数的学习和实践,我们能够在实地址模式下实现各种复杂的磁盘和文件管理任务。这些知识不仅有助于我们深入理解计算机系统的底层原理,还能为我们开发高效、稳定的磁盘和文件管理程序提供有力支持。

以下是不同 INT 21h 函数的总结表格:
| 函数编号 | 函数名称 | 功能 | 输入参数 | 可能的错误代码 |
| ---- | ---- | ---- | ---- | ---- |
| 7303h | 获取磁盘可用空间 | 查找 FAT16 或 FAT32 驱动器的磁盘卷大小和可用磁盘空间 | AX = 7303h,ES:DI 指向 ExtGetDskFreSpcStruc 变量,CX 为变量大小,DS:DX 指向驱动器名称字符串 | 无(CF 标志判断) |
| 39h | 创建子目录 | 创建新的子目录 | DS:DX 指向路径名 | 3(路径未找到),5(访问被拒绝) |
| 3Ah | 删除子目录 | 删除目录 | DS:DX 指向驱动器和路径 | 3(路径未找到),5(访问被拒绝),6(无效句柄),16(尝试删除当前目录) |
| 3Bh | 设置当前目录 | 设置当前目录 | DS:DX 指向目标驱动器和路径 | 无(CF 标志判断) |
| 47h | 获取当前目录 | 返回包含当前目录的字符串 | DL 为驱动器编号,DS:SI 指向 64 字节缓冲区 | 0Fh(无效驱动器规范) |
| 7143h | 获取和设置文件属性 | 检索或设置文件属性 | AX = 7143h,设置属性时 BL = 1,CX 为属性值,DX 指向文件名;获取属性时 BX = 0,DX 指向文件名 | 无(CF 标志判断) |

通过不断练习和实践,我们可以更加熟练地运用这些函数,提高自己的编程能力和对计算机系统的理解。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值