自制操作系统(三、文件系统实现)
一、选择文件系统
通常来说,自制操作系统都是fat12,fat16,ext之类的
经过3周的努力,查阅了各方的资料,终于——失败
看着一堆的db,什么ata,我陷入了沉默
为什么不自制文件系统呢?
说干就干
尽管这是纯汇编操作系统,我还是决定用python构建文件系统
其实python只是为了编译方便...
fs.py
import os
import sysdef 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_filesection .textfs_init:retfs_list_files:mov esi, file_namesretfs_read_file:; 直接暴力比较文件名mov edi, esi
""" % len(files)for i, filename in enumerate(files):if not os.path.exists(filename):continuebase = os.path.splitext(filename)[0]clean_base = base.replace('.', '_').replace(' ', '_')asm_content += f"""; 检查文件: {filename}mov esi, file_{clean_base}_namecall str_compareje .found_{i+1}
"""asm_content += """; 文件未找到stcret
"""for i, filename in enumerate(files):if not os.path.exists(filename):continuebase = os.path.splitext(filename)[0]clean_base = base.replace('.', '_').replace(' ', '_')asm_content += f"""
.found_{i+1}:mov esi, file_{clean_base}_content_strclcret
"""asm_content += """
str_compare:push eaxpush esipush edi
.loop:mov al, [esi]cmp al, [edi]jne .not_equaltest al, aljz .equalinc esiinc edijmp .loop
.equal:xor eax, eaxjmp .done
.not_equal:or eax, 1
.done:pop edipop esipop eaxret
"""asm_content += """
section .data
file_names db """name_list = []for filename in files:if not os.path.exists(filename):continuename_list.append(f"'{filename} '")asm_content += ', '.join(name_list) + ',0\n\n'for filename in files:if not os.path.exists(filename):continuebase = 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: # 可打印ASCIIescaped.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 binmake imgmake runbin:nasm boot.asm -o boot.binnasm loader.asm -o loader.bin#gcc -c -O0 -fno-builtin -m32 -fno-stack-protector -o main.o main.cnasm -f elf -o a.o a.asmi686-elf-ld -e _start -o a.run a.opython fs.py 123.txt qwe.txt a.runnasm -f elf -o kernel.o kernel.asmnasm -f elf -o io.o io.asmnasm -f elf -o shell.o shell.asmnasm -f elf -o file.o file.asmnasm -f elf -o fs.o fs.asmi686-elf-ld -s -Ttext 0x100000 -o kernel.bin kernel.o io.o file.o shell.o fs.oimg : 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.pyedimg 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.imgrun : a.img#qemu-system-i386 -fda a.imgqemu-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", 0cmd_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_screenglobal shell
shell:cmp ebx, 25ja .scrollmov ecx, 0mov esi, msgmov ah, 0x0Fcall print_str; 初始化命令缓冲区mov edi, cmd_buffermov ecx, 18 ; 从第18列开始输入mov byte [edi], 0 ; 清空缓冲区.input_loop:call get_keytest al, aljz .input_loop; 处理回车cmp al, 0x0Aje .execute; 处理退格cmp al, 0x08je .backspace; 存储并显示字符mov [edi], alinc edimov ah, 0x0Fcall put_charinc ecxjmp .input_loop.backspace:; 退格处理cmp edi, cmd_bufferje .input_loop ; 忽略空退格dec edidec ecxmov al, ' 'mov ah, 0x0Fcall put_charjmp .input_loop.scroll:call scroll_screendec ebxjmp shell
.execute:; 添加字符串结束符mov byte [edi], 0; 检查空命令mov esi, cmd_buffercall is_emptyje .empty_cmd; 跳过前导空格call skip_spacestest al, aljz .empty_cmd; 检查help命令mov edi, cmd_helpcall cmd_cmpje .show_help; 检查echo命令mov edi, cmd_echocall cmd_cmpje .do_echo; 检查echo命令mov edi, cmd_lscall cmd_cmpje do_lsmov edi, cmd_timecall cmd_cmpje do_timemov edi, cmd_catcall cmd_cmpje do_cat;mov edi, cmd_write;call cmd_cmp;je do_write; 检查clear命令mov edi, cmd_clearcall cmd_cmpje .do_clear; 未知命令处理inc ebxmov ecx, 0mov esi, not_msgmov ah, 0x0C ; 红色错误信息call print_str; 只显示命令部分(第一个空格前的内容)mov esi, cmd_buffercall print_command_partinc ebxjmp shell.empty_cmd:inc ebxmov ecx, 0jmp shell.show_help:; 显示帮助信息inc ebxmov ecx, 0mov esi, help_msg1mov ah, 0x0A ; 绿色帮助信息call print_strinc ebxmov ecx, 0mov esi, help_msg2call print_strinc ebxmov ecx, 0mov esi, help_msg3call print_strinc ebxmov ecx, 0mov esi, help_msg4call print_strinc ebxmov ecx, 0mov esi, help_msg5call print_strinc ebxmov ecx, 0mov esi, help_msg6call print_strinc ebxmov ecx, 0mov esi, help_msg7call print_strinc ebxjmp shell.do_echo:; 跳过"echo"和后续空格add esi, 4call skip_spacestest al, aljz .no_args1 ; 无参数情况; 显示echo参数inc ebxmov ecx, 0mov ah, 0x0Fcall print_str.no_args1:inc ebx ; 换行jmp shell; === clear命令实现 ===
.do_clear:call clear_screenmov ebx, 0mov ecx, 0jmp shell; === 辅助函数 ===; 打印命令部分(第一个空格前的内容)
print_command_part:pushamov ecx, 26 ; 错误信息后位置
.loop:lodsbtest al, aljz .donecmp al, ' 'je .donemov ah, 0x0Fcall put_charinc ecxjmp .loop
.done:poparet; 检查字符串是否为空或只有空格
is_empty:push esi
.loop:lodsbcmp al, ' 'je .looptest al, alpop esiret; 跳过字符串中的空格
skip_spaces:lodsbcmp al, ' 'je skip_spacesdec esi ; 回退到第一个非空格字符ret; 命令比较函数
cmd_cmp:pusha
.compare:mov al, [esi]mov bl, [edi]; 检查命令是否结束(空格或字符串结束)cmp al, ' 'je .check_cmd_endtest al, aljz .check_cmd_end; 转换为小写比较cmp al, 'A'jb .no_change1cmp al, 'Z'ja .no_change1add al, 0x20
.no_change1:cmp bl, 'A'jb .no_change2cmp bl, 'Z'ja .no_change2add bl, 0x20.no_change2:cmp al, bljne .not_equalinc esiinc edijmp .compare.check_cmd_end:; 检查命令字符串是否也结束了cmp byte [edi], 0jne .not_equal.equal:popaxor eax, eax ; ZF=1ret.not_equal:popaor eax, 1 ; ZF=0ret; 显示固定数量的字符
print_nchars:pushamov ah, 0x0F
.loop:lodsbcall put_charloop .looppoparetprint_hex:pushadmov ecx, 8
.loop:rol eax, 4mov ebx, eaxand ebx, 0x0fmov bl, [hex_chars + ebx]mov ah, 0x0Fcall put_charloop .looppopadretdo_time:call get_timeinc ebx ; 换行mov ecx, 0mov esi, time_strmov ah, 0x0F ; 白色文字call print_strjmp shellget_time:pushad; 禁用NMI并读取小时mov al, 0x04 ; 小时寄存器or al, 0x80 ; 禁用NMIout 0x70, alin al, 0x71call bcd_to_asciimov [time_str], dhmov [time_str+1], dl; 读取分钟mov al, 0x02or al, 0x80out 0x70, alin al, 0x71call bcd_to_asciimov [time_str+3], dhmov [time_str+4], dl; 读取秒mov al, 0x00or al, 0x80out 0x70, alin al, 0x71call bcd_to_asciimov [time_str+6], dhmov [time_str+7], dlpopadretbcd_to_ascii:; 将AL中的BCD码转换为两个ASCII字符,存储在DH和DL中mov dh, alshr dh, 4add dh, '0'mov dl, aland dl, 0x0Fadd dl, '0'ret; === ls命令实现 ===
do_ls:call fs_list_files; 显示文件名inc ebxmov ecx, 0mov ah, 0x0Fcall print_str; 换行inc ebxmov ecx, 0jmp shell; === cat命令实现 ===
do_cat:; 跳过"cat"和空格add esi, 3call skip_spacestest al, aljz .no_filename; 直接调用文件系统call fs_read_filejc .file_not_found; 显示内容 (esi已指向内容字符串)inc ebxmov ecx, 0mov ah, 0x0Fcall print_str ; 直接打印整个内容inc ebxjmp shell.file_not_found:inc ebxmov ecx, 0mov esi, no_file_msgmov ah, 0x0Ccall print_str; 显示尝试的文件名mov esi, cmd_buffer+3mov ah, 0x0Fcall print_strinc ebxjmp shell.no_filename:inc ebxmov ecx, 0mov esi, cat_usage_msgmov ah, 0x0Ccall print_strjmp shellsection .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直接编译
终于完成了文件系统!
我知道你很高兴,但你先别高兴
我们没有用其他文件系统,这意味着我们无法执行写入操作
只能修改内存里文件位置指针指向的文件内容(就是重启后没有数据)
但我们的目的很简单,只是运行程序
这将在下一节实现