自制操作系统(三、文件系统实现)

一、选择文件系统

通常来说,自制操作系统都是fat12,fat16,ext之类的

经过3周的努力,查阅了各方的资料,终于——失败

看着一堆的db,什么ata,我陷入了沉默

为什么不自制文件系统呢?

说干就干

尽管这是纯汇编操作系统,我还是决定用python构建文件系统

其实python只是为了编译方便...

fs.py

import os
import sys

def create_fs_asm(files):
    """完全保持原样的暴力文件系统实现,仅添加二进制支持"""
    asm_content = """; 完全保持原样的暴力文件系统
section .data
global fs_files_count
fs_files_count dd %d

; 文件系统API
global fs_init, fs_list_files, fs_read_file

section .text

fs_init:
    ret

fs_list_files:
    mov esi, file_names
    ret

fs_read_file:
    ; 直接暴力比较文件名
    mov edi, esi
""" % len(files)

    for i, filename in enumerate(files):
        if not os.path.exists(filename):
            continue
            
        base = os.path.splitext(filename)[0]
        clean_base = base.replace('.', '_').replace(' ', '_')
        
        asm_content += f"""
    ; 检查文件: {filename}
    mov esi, file_{clean_base}_name
    call str_compare
    je .found_{i+1}
"""

    asm_content += """
    ; 文件未找到
    stc
    ret
"""

    for i, filename in enumerate(files):
        if not os.path.exists(filename):
            continue
            
        base = os.path.splitext(filename)[0]
        clean_base = base.replace('.', '_').replace(' ', '_')
        
        asm_content += f"""
.found_{i+1}:
    mov esi, file_{clean_base}_content_str
    clc
    ret
"""

    asm_content += """
str_compare:
    push eax
    push esi
    push edi
.loop:
    mov al, [esi]
    cmp al, [edi]
    jne .not_equal
    test al, al
    jz .equal
    inc esi
    inc edi
    jmp .loop
.equal:
    xor eax, eax
    jmp .done
.not_equal:
    or eax, 1
.done:
    pop edi
    pop esi
    pop eax
    ret
"""

    asm_content += """
section .data
file_names db """
    
    name_list = []
    for filename in files:
        if not os.path.exists(filename):
            continue
        name_list.append(f"'{filename} '")
    asm_content += ', '.join(name_list) + ',0\n\n'

    for filename in files:
        if not os.path.exists(filename):
            continue
            
        base = os.path.splitext(filename)[0]
        clean_base = base.replace('.', '_').replace(' ', '_')
        
        # 二进制模式读取文件
        with open(filename, 'rb') as f:
            content = f.read()
        
        # 生成转义后的字符串
        escaped = []
        for byte in content:
            if byte == 0:  # 处理null字节
                escaped.append('\\0')
            elif byte == ord('\''):  # 处理单引号
                escaped.append('\\\'')
            elif byte == ord('\\'):  # 处理反斜杠
                escaped.append('\\\\')
            elif 32 <= byte <= 126:  # 可打印ASCII
                escaped.append(chr(byte))
            else:  # 其他不可打印字符保持原样
                escaped.append(f'\\x{byte:02x}')
        
        asm_content += f"""
; 文件: {filename}
file_{clean_base}_name db '{filename}',0
file_{clean_base}_content_str db '{''.join(escaped)}',0
"""

    with open("fs.asm", "w") as f:
        f.write(asm_content)
    print("Created original brute-force fs.asm")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python fs.py file1 [file2 ...]")
        sys.exit(1)
    
    create_fs_asm(sys.argv[1:])

本次的makefile

all:
	make bin
	make img
	make run

bin:
	nasm boot.asm -o boot.bin
	nasm loader.asm -o loader.bin
	#gcc -c -O0 -fno-builtin -m32 -fno-stack-protector -o main.o main.c
	nasm -f elf -o a.o a.asm
	i686-elf-ld -e _start -o a.run a.o
	python fs.py 123.txt qwe.txt a.run
	nasm -f elf -o kernel.o kernel.asm
	nasm -f elf -o io.o io.asm
	nasm -f elf -o shell.o shell.asm
	nasm -f elf -o file.o file.asm
	nasm -f elf -o fs.o fs.asm
	i686-elf-ld -s -Ttext 0x100000 -o kernel.bin kernel.o io.o file.o shell.o fs.o

img : boot.bin loader.bin kernel.bin
	#dd if=boot.bin of=a.img bs=512 count=1
	#dd if=loader.bin of=a.img bs=512 seek=1 conv=notrunc
	#python img.py
	edimg   imgin:1.img \
		wbinimg src:boot.bin len:512 from:0 to:0 \
		copy from:loader.bin to:@: \
		copy from:kernel.bin to:@: \
		imgout:a.img


run : a.img
	#qemu-system-i386 -fda a.img
	qemu-system-i386 -fda a.img #-hda disk.img -boot a

shell.asm

;shell.asm
[bits 32]

extern scroll_screen
[section .data]
; Shell界面
msg db "[root@Plain]-(/)# ", 0
cmd_buffer times 80 db 0

; 命令定义
cmd_echo db "echo", 0
cmd_help db "help", 0
cmd_ls   db "ls", 0
cmd_cat  db "cat", 0
cmd_write db "write", 0
cmd_clear db "clear", 0

cmd_time db "time", 0
time_str db "HH:MM:SS", 0
; 帮助信息
help_msg1 db "Available commands:", 0
help_msg2 db "  echo <message> - Display message", 0
help_msg3 db "  help          - Show this help", 0
help_msg4 db "  ls            - List files", 0
help_msg5 db "  cat <file>    - Show file content", 0
help_msg6 db "  write <file> > <content> - Write to file", 0
help_msg7 db "  clear         - Clear screen", 0

; 错误和信息消息
not_msg db "Error: Command not found: ", 0
error_msg db "ERROR: Disk operation failed", 0
dir_entry db "  [DIR] ", 0
write_success db "Write successful", 0
write_fail db "Write failed", 0
invalid_format_msg db "Invalid write format. Use: write filename > content", 0


[section .text]
extern print_str, put_char, get_key, clear_screen, fs_list_files, fs_files_count, fs_read_file 

extern scroll_screen

global shell
shell:
    cmp ebx, 25
    ja .scroll
    mov ecx, 0
    mov esi, msg
    mov ah, 0x0F
    call print_str
    
    ; 初始化命令缓冲区
    mov edi, cmd_buffer
    mov ecx, 18          ; 从第18列开始输入
    mov byte [edi], 0    ; 清空缓冲区
    
.input_loop:
    call get_key
    test al, al
    jz .input_loop

    ; 处理回车
    cmp al, 0x0A
    je .execute

    ; 处理退格
    cmp al, 0x08
    je .backspace

    ; 存储并显示字符
    mov [edi], al
    inc edi
    mov ah, 0x0F
    call put_char
    inc ecx
    jmp .input_loop

.backspace:
    ; 退格处理
    cmp edi, cmd_buffer
    je .input_loop       ; 忽略空退格
    dec edi
    dec ecx
    mov al, ' '
    mov ah, 0x0F
    call put_char
    jmp .input_loop
    
.scroll:
    call scroll_screen
    dec ebx
    jmp shell
.execute:
    ; 添加字符串结束符
    mov byte [edi], 0
    
    ; 检查空命令
    mov esi, cmd_buffer
    call is_empty
    je .empty_cmd
    
    ; 跳过前导空格
    call skip_spaces
    test al, al
    jz .empty_cmd
    
    ; 检查help命令
    mov edi, cmd_help
    call cmd_cmp
    je .show_help

    ; 检查echo命令
    mov edi, cmd_echo
    call cmd_cmp
    je .do_echo
    
    ; 检查echo命令
    mov edi, cmd_ls
    call cmd_cmp
    je do_ls
    
    
    mov edi, cmd_time
    call cmd_cmp
    je do_time

    mov edi, cmd_cat
    call cmd_cmp
    je do_cat
    
    ;mov edi, cmd_write
    ;call cmd_cmp
    ;je do_write

    ; 检查clear命令
    mov edi, cmd_clear
    call cmd_cmp
    je .do_clear

    ; 未知命令处理
    inc ebx
    mov ecx, 0
    mov esi, not_msg
    mov ah, 0x0C        ; 红色错误信息
    call print_str
    
    ; 只显示命令部分(第一个空格前的内容)
    mov esi, cmd_buffer
    call print_command_part
    
    inc ebx
    jmp shell

.empty_cmd:
    inc ebx
    mov ecx, 0
    jmp shell

.show_help:
    ; 显示帮助信息
    inc ebx
    mov ecx, 0
    mov esi, help_msg1
    mov ah, 0x0A        ; 绿色帮助信息
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg2
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg3
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg4
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg5
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg6
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg7
    call print_str
    
    inc ebx
    jmp shell

.do_echo:
    ; 跳过"echo"和后续空格
    add esi, 4
    call skip_spaces
    test al, al
    jz .no_args1         ; 无参数情况
    
    ; 显示echo参数
    inc ebx
    mov ecx, 0
    mov ah, 0x0F
    call print_str
    
.no_args1:
    inc ebx             ; 换行
    jmp shell

; === clear命令实现 ===
.do_clear:
    call clear_screen
    mov ebx, 0
    mov ecx, 0
    jmp shell

; === 辅助函数 ===

; 打印命令部分(第一个空格前的内容)
print_command_part:
    pusha
    mov ecx, 26         ; 错误信息后位置
.loop:
    lodsb
    test al, al
    jz .done
    cmp al, ' '
    je .done
    mov ah, 0x0F
    call put_char
    inc ecx
    jmp .loop
.done:
    popa
    ret

; 检查字符串是否为空或只有空格
is_empty:
    push esi
.loop:
    lodsb
    cmp al, ' '
    je .loop
    test al, al
    pop esi
    ret

; 跳过字符串中的空格
skip_spaces:
    lodsb
    cmp al, ' '
    je skip_spaces
    dec esi             ; 回退到第一个非空格字符
    ret

; 命令比较函数
cmd_cmp:
    pusha
.compare:
    mov al, [esi]
    mov bl, [edi]
    
    ; 检查命令是否结束(空格或字符串结束)
    cmp al, ' '
    je .check_cmd_end
    test al, al
    jz .check_cmd_end
    
    ; 转换为小写比较
    cmp al, 'A'
    jb .no_change1
    cmp al, 'Z'
    ja .no_change1
    add al, 0x20
.no_change1:
    cmp bl, 'A'
    jb .no_change2
    cmp bl, 'Z'
    ja .no_change2
    add bl, 0x20
    
.no_change2:
    cmp al, bl
    jne .not_equal
    inc esi
    inc edi
    jmp .compare
    
.check_cmd_end:
    ; 检查命令字符串是否也结束了
    cmp byte [edi], 0
    jne .not_equal
    
.equal:
    popa
    xor eax, eax  ; ZF=1
    ret
    
.not_equal:
    popa
    or eax, 1     ; ZF=0
    ret

; 显示固定数量的字符
print_nchars:
    pusha
    mov ah, 0x0F
.loop:
    lodsb
    call put_char
    loop .loop
    popa
    ret

print_hex:
    pushad
    mov ecx, 8
.loop:
    rol eax, 4
    mov ebx, eax
    and ebx, 0x0f
    mov bl, [hex_chars + ebx]
    mov ah, 0x0F
    call put_char
    loop .loop
    popad
    ret

do_time:
    call get_time
    inc ebx             ; 换行
    mov ecx, 0
    mov esi, time_str
    mov ah, 0x0F        ; 白色文字
    call print_str
    jmp shell

get_time:
    pushad

    ; 禁用NMI并读取小时
    mov al, 0x04        ; 小时寄存器
    or al, 0x80         ; 禁用NMI
    out 0x70, al
    in al, 0x71
    call bcd_to_ascii
    mov [time_str], dh
    mov [time_str+1], dl

    ; 读取分钟
    mov al, 0x02
    or al, 0x80
    out 0x70, al
    in al, 0x71
    call bcd_to_ascii
    mov [time_str+3], dh
    mov [time_str+4], dl

    ; 读取秒
    mov al, 0x00
    or al, 0x80
    out 0x70, al
    in al, 0x71
    call bcd_to_ascii
    mov [time_str+6], dh
    mov [time_str+7], dl

    popad
    ret

bcd_to_ascii:
    ; 将AL中的BCD码转换为两个ASCII字符,存储在DH和DL中
    mov dh, al
    shr dh, 4
    add dh, '0'
    mov dl, al
    and dl, 0x0F
    add dl, '0'
    ret

; === ls命令实现 ===
do_ls:
    call fs_list_files
    ; 显示文件名
    inc ebx
    mov ecx, 0
    mov ah, 0x0F
    call print_str
    
    ; 换行
    inc ebx
    mov ecx, 0
    
    jmp shell

; === cat命令实现 ===
do_cat:
    ; 跳过"cat"和空格
    add esi, 3
    call skip_spaces
    test al, al
    jz .no_filename
    
    ; 直接调用文件系统
    call fs_read_file
    jc .file_not_found
    
    ; 显示内容 (esi已指向内容字符串)
    inc ebx
    mov ecx, 0
    mov ah, 0x0F
    call print_str  ; 直接打印整个内容
    
    inc ebx
    jmp shell
    
.file_not_found:
    inc ebx
    mov ecx, 0
    mov esi, no_file_msg
    mov ah, 0x0C
    call print_str
    
    ; 显示尝试的文件名
    mov esi, cmd_buffer+3
    mov ah, 0x0F
    call print_str
    
    inc ebx
    jmp shell
    
.no_filename:
    inc ebx
    mov ecx, 0
    mov esi, cat_usage_msg
    mov ah, 0x0C
    call print_str
    jmp shell

section .bss
filename_buffer resb 32  ; 存储临时文件名

; === 数据区 ===
[section .data]
no_file_msg db "File not found: ", 0
cat_usage_msg db "Usage: cat <filename>", 0
hex_chars db "0123456789ABCDEF"

 

一个make直接编译

 

终于完成了文件系统!

我知道你很高兴,但你先别高兴

我们没有用其他文件系统,这意味着我们无法执行写入操作

只能修改内存里文件位置指针指向的文件内容(就是重启后没有数据)

但我们的目的很简单,只是运行程序

这将在下一节实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值