├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── launch.json └── tasks.json ├── LICENSE ├── README.md ├── alinux.code-workspace ├── devel ├── bochs-gdb │ ├── PKGBUILD │ └── fix-build.patch ├── bochs │ ├── PKGBUILD │ └── fix-build.patch ├── bochsrc ├── bochsrc.gdb ├── bochsrcn.gdb ├── floppya.img ├── hda.img └── makefile ├── docs ├── 01 配置开发环境.md ├── 02 启动引导程序.md ├── 03 内核初始化.md ├── images │ ├── Floppy_disk_2009_G1.jpg │ ├── bochs-vscode.jpg │ ├── bochs.jpg │ └── qemu.jpg ├── 参考资料.md └── 硬件设备基础 │ ├── 01 软盘驱动器.md │ ├── 02 硬盘驱动器.md │ ├── 03 VGA 显示器.md │ ├── images │ ├── DRAM_SIMM_8x4x1M.jpg │ ├── block_diagram.png │ ├── i486DX2.jpg │ ├── i486_die_anno2.jpg │ ├── motherboard_anno.jpg │ └── x86_history.drawio.svg │ ├── readme.md │ └── x86 体系结构.md ├── linux-0.11 ├── Makefile ├── Option.mk ├── boot │ ├── bootsect.s │ ├── head.s │ └── setup.s ├── fs │ ├── Makefile │ ├── bitmap.c │ ├── block_dev.c │ ├── buffer.c │ ├── char_dev.c │ ├── exec.c │ ├── fcntl.c │ ├── file_dev.c │ ├── file_table.c │ ├── inode.c │ ├── ioctl.c │ ├── namei.c │ ├── open.c │ ├── pipe.c │ ├── read_write.c │ ├── stat.c │ ├── super.c │ └── truncate.c ├── include │ ├── a.out.h │ ├── asm │ │ ├── io.h │ │ ├── memory.h │ │ ├── segment.h │ │ └── system.h │ ├── const.h │ ├── ctype.h │ ├── errno.h │ ├── fcntl.h │ ├── linux │ │ ├── config.h │ │ ├── fdreg.h │ │ ├── fs.h │ │ ├── hdreg.h │ │ ├── head.h │ │ ├── kernel.h │ │ ├── mm.h │ │ ├── sched.h │ │ ├── sys.h │ │ └── tty.h │ ├── signal.h │ ├── stdarg.h │ ├── stddef.h │ ├── string.h │ ├── sys │ │ ├── stat.h │ │ ├── times.h │ │ ├── types.h │ │ ├── utsname.h │ │ └── wait.h │ ├── termios.h │ ├── time.h │ ├── unistd.h │ └── utime.h ├── init │ └── main.c ├── kernel │ ├── Makefile │ ├── asm.s │ ├── blk_drv │ │ ├── Makefile │ │ ├── blk.h │ │ ├── floppy.c │ │ ├── hd.c │ │ ├── ll_rw_blk.c │ │ └── ramdisk.c │ ├── chr_drv │ │ ├── Makefile │ │ ├── console.c │ │ ├── keyboard.S │ │ ├── rs_io.s │ │ ├── serial.c │ │ ├── tty_io.c │ │ └── tty_ioctl.c │ ├── exit.c │ ├── fork.c │ ├── math │ │ ├── Makefile │ │ └── math_emulate.c │ ├── mktime.c │ ├── panic.c │ ├── printk.c │ ├── sched.c │ ├── signal.c │ ├── sys.c │ ├── system_call.s │ ├── traps.c │ └── vsprintf.c ├── lib │ ├── Makefile │ ├── _exit.c │ ├── close.c │ ├── ctype.c │ ├── dup.c │ ├── errno.c │ ├── execve.c │ ├── malloc.c │ ├── open.c │ ├── setsid.c │ ├── string.c │ ├── wait.c │ └── write.c ├── mm │ ├── Makefile │ ├── memory.c │ └── page.s └── tools │ └── build.c └── tests ├── bochsrc ├── boot.asm └── makefile /.gitignore: -------------------------------------------------------------------------------- 1 | backup 2 | 3 | *.o 4 | *.a 5 | linux-0.11/tools/build 6 | linux-0.11/tools/system 7 | linux-0.11/kernel/chr_drv/keyboard.s 8 | linux-0.11/boot/setup 9 | linux-0.11/boot/bootsect 10 | linux-0.11/System.map 11 | linux-0.11/Image 12 | 13 | devel/bx_enh_dbg.ini 14 | 15 | tests/boot.bin 16 | tests/bx_enh_dbg.ini 17 | 18 | devel/bochs-gdb/bochs-2.6.11.tar.gz 19 | devel/bochs-gdb/src 20 | devel/bochs-gdb/pkg 21 | devel/bochs-gdb/*.zst 22 | 23 | devel/bochs/bochs-2.6.11.tar.gz 24 | devel/bochs/src 25 | devel/bochs/pkg 26 | devel/bochs/*.zst 27 | 28 | *.lock 29 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [{ 3 | "name": "Linux", 4 | "includePath": [ 5 | "${workspaceFolder}/linux-0.11/include" 6 | ], 7 | "defines": [], 8 | "compilerPath": "/usr/bin/clang", 9 | "cStandard": "c17", 10 | "cppStandard": "c++14", 11 | "intelliSenseMode": "linux-clang-x64" 12 | }], 13 | "version": 4 14 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [{ 7 | "name": "(gdb) kernel nogui", 8 | "type": "cppdbg", 9 | "request": "launch", 10 | "program": "${workspaceFolder}/linux-0.11/tools/system", 11 | "args": [], 12 | "stopAtEntry": false, 13 | "cwd": "${workspaceFolder}/linux-0.11", 14 | "environment": [], 15 | "externalConsole": false, 16 | "MIMode": "gdb", 17 | "miDebuggerPath": "/usr/bin/gdb", 18 | "miDebuggerServerAddress": "localhost:1234", 19 | "setupCommands": [{ 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | }, 24 | { 25 | "description": "ignore SIGUSR2 signal", 26 | "text": "handle SIGUSR2 nostop noprint nopass" 27 | } 28 | ], 29 | "preLaunchTask": "bochsgn" 30 | }, 31 | { 32 | "name": "(gdb) kernel", 33 | "type": "cppdbg", 34 | "request": "launch", 35 | "program": "${workspaceFolder}/linux-0.11/tools/system", 36 | "args": [], 37 | "stopAtEntry": false, 38 | "cwd": "${workspaceFolder}/linux-0.11", 39 | "environment": [], 40 | "externalConsole": false, 41 | "MIMode": "gdb", 42 | "miDebuggerPath": "/usr/bin/gdb", 43 | "miDebuggerServerAddress": "localhost:1234", 44 | "setupCommands": [{ 45 | "description": "Enable pretty-printing for gdb", 46 | "text": "-enable-pretty-printing", 47 | "ignoreFailures": true 48 | }, 49 | { 50 | "description": "ignore SIGUSR2 signal", 51 | "text": "handle SIGUSR2 nostop noprint nopass" 52 | } 53 | ], 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [{ 6 | "type": "cppbuild", 7 | "label": "bochsgn", 8 | "command": "/usr/bin/make", 9 | "args": [ 10 | "bochsgn" 11 | ], 12 | "options": { 13 | "cwd": "${workspaceFolder}/devel" 14 | }, 15 | "problemMatcher": [ 16 | "$gcc" 17 | ], 18 | "group": { 19 | "kind": "build", 20 | "isDefault": true 21 | }, 22 | "detail": "Task generated by Debugger." 23 | }, ] 24 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # alinux 2 | Linux 源码分析 3 | 4 | [本项目地址](https://github.com/StevenBaby/alinux) 5 | 6 | > 欢迎大家以 Markdown 的形式提交有关硬件内容的 PR!!! 7 | 8 | ## 配置开发环境 9 | 10 | 克隆代码,执行命令 11 | 12 | git clone https://github.com/StevenBaby/alinux.git 13 | 14 | 然后可以在 devel 目录执行 15 | 16 | make bochs 17 | 18 | ![](./docs/images/bochs.jpg) 19 | 20 | 或者 21 | 22 | make qemu 23 | 24 | ![](./docs/images/qemu.jpg) 25 | 26 | 来执行模拟程序; 27 | 28 | 如需要调试,则可执行: 29 | 30 | make bochsg 31 | 32 | 用以启动 `bochs-gdb`,然后在 vscode 中单步调试 33 | 34 | ![](./docs/images/bochs-vscode.jpg) 35 | 36 | 具体实现的细节,请看 [配置开发环境](./docs/01%20配置开发环境.md) 37 | 38 | ## 硬件设备基础 39 | 40 | - [硬件设备基础](./docs/硬件设备基础/readme.md) 41 | - [软盘驱动器](./docs/硬件设备基础/01%20软盘驱动器.md) 42 | - [硬盘驱动器](./docs/硬件设备基础/02%20硬盘驱动器.md) 43 | - [VGA 显示器](./docs/硬件设备基础/03%20VGA%20显示器.md) 44 | 45 | ## 源码解析 46 | 47 | - [配置开发环境](./docs/01%20配置开发环境.md) 48 | - [启动引导程序](./docs/02%20启动引导程序.md) 49 | 50 | ## 参考资料 51 | 52 | - [参考资料](./docs/参考资料.md) -------------------------------------------------------------------------------- /alinux.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [{ 3 | "path": "." 4 | }], 5 | "settings": {} 6 | } -------------------------------------------------------------------------------- /devel/bochs-gdb/PKGBUILD: -------------------------------------------------------------------------------- 1 | # (C) Copyright 2021 Steven; 2 | # @author: Steven kangweibaby@163.com 3 | # @date: 2021-04-17 4 | # 用于调试 C 程序 以下内容是从 Archlinux AUR 包和 bochs 包抠出来的,专门用于 32 位系统的调试, 5 | # 默认的是 64 位操作系统所以会有一些问题 6 | # 安装使用 makepkg -si 7 | 8 | pkgname=bochs-gdb 9 | pkgver=2.6.11 10 | pkgrel=3 11 | pkgdesc="A portable x86 PC emulation software package with gdbstub" 12 | arch=('x86_64') 13 | url="http://bochs.sourceforge.net/" 14 | license=('LGPL') 15 | depends=('gcc-libs' 'libxrandr' 'libxpm' 'gtk2') 16 | source=("http://downloads.sourceforge.net/sourceforge/bochs/bochs-$pkgver.tar.gz") 17 | md5sums=('61dbf6d5c0384712e1f3e51e88381b4c') 18 | 19 | prepare() { 20 | cd "$srcdir/bochs-$pkgver" 21 | # 4.X kernel is basically 3.20 22 | sed -i 's/2\.6\*|3\.\*)/2.6*|3.*|4.*)/' configure* 23 | 24 | patch -p1 < ../../fix-build.patch # https://sourceforge.net/p/bochs/bugs/1411/ 25 | } 26 | 27 | build() { 28 | cd "$srcdir/bochs-$pkgver" 29 | 30 | ./configure \ 31 | --prefix=/usr \ 32 | --without-wx \ 33 | --with-x11 \ 34 | --with-x \ 35 | --with-term \ 36 | --disable-docbook \ 37 | --enable-cpu-level=6 \ 38 | --enable-fpu \ 39 | --enable-3dnow \ 40 | --enable-disasm \ 41 | --enable-long-phy-address \ 42 | --enable-disasm \ 43 | --enable-pcidev \ 44 | --enable-usb \ 45 | --with-sdl \ 46 | --enable-all-optimizations \ 47 | --enable-gdb-stub \ 48 | --with-nogui \ 49 | # --enable-plugins \ 50 | # --enable-smp \ 51 | # --enable-x86-debugger \ 52 | # --enable-debugger \ 53 | # --enable-x86-64 \ 54 | # --enable-avx \ 55 | # --enable-evex \ 56 | sed -i 's/^LIBS = /LIBS = -lpthread/g' Makefile 57 | make -j 1 58 | } 59 | 60 | package() { 61 | cd "$srcdir/bochs-$pkgver" 62 | make DESTDIR="$pkgdir" install 63 | install -Dm644 .bochsrc "$pkgdir/etc/bochsrc-sample.txt" 64 | 65 | cd "$pkgdir/usr/bin/" 66 | mv bochs bochs-gdb 67 | rm -rf bochs-gdb-a20 68 | rm bximage 69 | cd "$pkgdir/usr/" 70 | rm -rfv share 71 | cd "$pkgdir" 72 | rm -rfv etc 73 | } 74 | -------------------------------------------------------------------------------- /devel/bochs-gdb/fix-build.patch: -------------------------------------------------------------------------------- 1 | Description: Fix the build with SMP enabled 2 | Origin: https://sourceforge.net/p/bochs/code/13778/ 3 | 4 | Index: bochs/bx_debug/dbg_main.cc 5 | =================================================================== 6 | --- bochs/bx_debug/dbg_main.cc (revision 13777) 7 | +++ bochs/bx_debug/dbg_main.cc (working copy) 8 | @@ -1494,11 +1494,11 @@ 9 | { 10 | char cpu_param_name[16]; 11 | 12 | - Bit32u index = BX_ITLB_INDEX_OF(laddr); 13 | + Bit32u index = BX_CPU(dbg_cpu)->ITLB.get_index_of(laddr); 14 | sprintf(cpu_param_name, "ITLB.entry%d", index); 15 | bx_dbg_show_param_command(cpu_param_name, 0); 16 | 17 | - index = BX_DTLB_INDEX_OF(laddr, 0); 18 | + index = BX_CPU(dbg_cpu)->DTLB.get_index_of(laddr); 19 | sprintf(cpu_param_name, "DTLB.entry%d", index); 20 | bx_dbg_show_param_command(cpu_param_name, 0); 21 | } 22 | 23 | Index: bochs/gdbstub.cc 24 | =================================================================== 25 | +++ bochs/gdbstub.cc 26 | @@ -489,6 +489,10 @@ static void debug_loop(void) 27 | { 28 | write_signal(&buf[1], SIGTRAP); 29 | } 30 | + else if (last_stop_reason == GDBSTUB_STOP_NO_REASON) 31 | + { 32 | + write_signal(&buf[1], 31); 33 | + } 34 | else 35 | { 36 | write_signal(&buf[1], 0); 37 | -------------------------------------------------------------------------------- /devel/bochs/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Kyle Keen 2 | # Contributor: Tom Newsom 3 | # Contributor: Kevin Piche 4 | 5 | pkgname=bochs 6 | pkgver=2.6.11 7 | pkgrel=1 8 | pkgdesc="A portable x86 PC emulation software package, including GUI debugger" 9 | arch=('x86_64') 10 | url="http://bochs.sourceforge.net/" 11 | license=('LGPL') 12 | depends=('gcc-libs' 'libxrandr' 'libxpm' 'gtk2') 13 | source=("https://downloads.sourceforge.net/sourceforge/$pkgname/$pkgname-$pkgver.tar.gz" 14 | "fix-build.patch") 15 | sha256sums=('63897b41fbbbdfb1c492d3c4dee1edb4224282a07bbdf442a4a68c19bcc18862' 16 | 'SKIP') 17 | 18 | prepare() { 19 | cd "$srcdir/$pkgname-$pkgver" 20 | # 4.X kernel is basically 3.20 21 | sed -i 's/2\.6\*|3\.\*)/2.6*|3.*|4.*)/' configure* 22 | 23 | patch -p1 < ../fix-build.patch # https://sourceforge.net/p/bochs/bugs/1411/ 24 | } 25 | 26 | build() { 27 | cd "$srcdir/$pkgname-$pkgver" 28 | 29 | ./configure \ 30 | --prefix=/usr \ 31 | --without-wx \ 32 | --with-x11 \ 33 | --with-x \ 34 | --with-term \ 35 | --disable-docbook \ 36 | --enable-cpu-level=6 \ 37 | --enable-fpu \ 38 | --enable-3dnow \ 39 | --enable-disasm \ 40 | --enable-smp \ 41 | --enable-x86-64 \ 42 | --enable-avx \ 43 | --enable-evex \ 44 | --enable-long-phy-address \ 45 | --enable-disasm \ 46 | --enable-pcidev \ 47 | --enable-usb \ 48 | --enable-debugger \ 49 | --with-sdl \ 50 | --enable-x86-debugger \ 51 | --enable-all-optimizations \ 52 | --with-nogui \ 53 | --enable-plugins 54 | sed -i 's/^LIBS = /LIBS = -lpthread/g' Makefile 55 | make -j 1 56 | } 57 | 58 | package() { 59 | cd "$srcdir/$pkgname-$pkgver" 60 | make DESTDIR="$pkgdir" install 61 | install -Dm644 .bochsrc "$pkgdir/etc/bochsrc-sample.txt" 62 | } 63 | -------------------------------------------------------------------------------- /devel/bochs/fix-build.patch: -------------------------------------------------------------------------------- 1 | Description: Fix the build with SMP enabled 2 | Origin: https://sourceforge.net/p/bochs/code/13778/ 3 | 4 | Index: bochs/bx_debug/dbg_main.cc 5 | =================================================================== 6 | --- bochs/bx_debug/dbg_main.cc (revision 13777) 7 | +++ bochs/bx_debug/dbg_main.cc (working copy) 8 | @@ -1494,11 +1494,11 @@ 9 | { 10 | char cpu_param_name[16]; 11 | 12 | - Bit32u index = BX_ITLB_INDEX_OF(laddr); 13 | + Bit32u index = BX_CPU(dbg_cpu)->ITLB.get_index_of(laddr); 14 | sprintf(cpu_param_name, "ITLB.entry%d", index); 15 | bx_dbg_show_param_command(cpu_param_name, 0); 16 | 17 | - index = BX_DTLB_INDEX_OF(laddr, 0); 18 | + index = BX_CPU(dbg_cpu)->DTLB.get_index_of(laddr); 19 | sprintf(cpu_param_name, "DTLB.entry%d", index); 20 | bx_dbg_show_param_command(cpu_param_name, 0); 21 | } 22 | 23 | Index: bochs/gui/gtk_enh_dbg_osdep.cc 24 | =================================================================== 25 | --- bochs/gui/gtk_enh_dbg_osdep.cc 26 | +++ bochs/gui/gtk_enh_dbg_osdep.cc 27 | @@ -819,7 +819,7 @@ void ShowDListCols (int totcols) 28 | while (++i < firsthide) 29 | gtk_tree_view_column_set_visible(AllCols[i], TRUE); 30 | while (i < 23) 31 | - gtk_tree_view_column_set_visible(AllCols[i], FALSE); 32 | + gtk_tree_view_column_set_visible(AllCols[i++], FALSE); 33 | } 34 | -------------------------------------------------------------------------------- /devel/bochsrc: -------------------------------------------------------------------------------- 1 | # configuration file generated by Bochs 2 | plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true 3 | config_interface: textconfig 4 | display_library: x, options="gui_debug" 5 | memory: host=32, guest=32 6 | romimage: file="/usr/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none 7 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 8 | boot: a 9 | floppy_bootsig_check: disabled=0 10 | floppya: 1_44="floppya.img", status=inserted 11 | # no floppyb 12 | ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 13 | ata0-master: type=disk, path="hda.img", mode=flat 14 | ata0-slave: type=none 15 | ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15 16 | ata1-master: type=none 17 | ata1-slave: type=none 18 | ata2: enabled=false 19 | ata3: enabled=false 20 | optromimage1: file=none 21 | optromimage2: file=none 22 | optromimage3: file=none 23 | optromimage4: file=none 24 | optramimage1: file=none 25 | optramimage2: file=none 26 | optramimage3: file=none 27 | optramimage4: file=none 28 | pci: enabled=1, chipset=i440fx 29 | vga: extension=vbe, update_freq=5, realtime=1 30 | cpu: count=1:1:1, ips=4000000, quantum=16, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 31 | cpuid: level=6, stepping=3, model=3, family=6, vendor_string="AuthenticAMD", brand_string="AMD Athlon(tm) processor" 32 | cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true 33 | cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, avx_f16c=false 34 | cpuid: avx_fma=false, bmi=0, xop=false, fma4=false, tbm=false, x86_64=true, 1g_pages=false 35 | cpuid: pcid=false, fsgsbase=false, smep=false, smap=false, mwait=true 36 | print_timestamps: enabled=0 37 | debugger_log: - 38 | magic_break: enabled=1 39 | port_e9_hack: enabled=0 40 | private_colormap: enabled=0 41 | clock: sync=none, time0=local, rtc_sync=0 42 | # no cmosimage 43 | log: - 44 | logprefix: %t%e%d 45 | debug: action=ignore 46 | info: action=report 47 | error: action=report 48 | panic: action=ask 49 | keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none 50 | mouse: type=ps2, enabled=false, toggle=ctrl+mbutton 51 | speaker: enabled=true, mode=system 52 | parport1: enabled=true, file=none 53 | parport2: enabled=false 54 | com1: enabled=true, mode=null 55 | com2: enabled=false 56 | com3: enabled=false 57 | com4: enabled=false 58 | -------------------------------------------------------------------------------- /devel/bochsrc.gdb: -------------------------------------------------------------------------------- 1 | # configuration file generated by Bochs 2 | plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true 3 | config_interface: textconfig 4 | display_library: x 5 | memory: host=32, guest=32 6 | romimage: file="/usr/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none 7 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 8 | boot: a 9 | floppy_bootsig_check: disabled=0 10 | floppya: 1_44="floppya.img", status=inserted 11 | # no floppyb 12 | ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 13 | ata0-master: type=disk, path="hda.img", mode=flat 14 | ata0-slave: type=none 15 | ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15 16 | ata1-master: type=none 17 | ata1-slave: type=none 18 | ata2: enabled=false 19 | ata3: enabled=false 20 | optromimage1: file=none 21 | optromimage2: file=none 22 | optromimage3: file=none 23 | optromimage4: file=none 24 | optramimage1: file=none 25 | optramimage2: file=none 26 | optramimage3: file=none 27 | optramimage4: file=none 28 | pci: enabled=1, chipset=i440fx 29 | vga: extension=vbe, update_freq=5, realtime=1 30 | cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 31 | cpuid: level=6, stepping=3, model=3, family=6, vendor_string="AuthenticAMD", brand_string="AMD Athlon(tm) processor" 32 | cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true 33 | cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, smep=false 34 | cpuid: smap=false, mwait=true 35 | print_timestamps: enabled=0 36 | gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0 37 | port_e9_hack: enabled=0 38 | private_colormap: enabled=0 39 | clock: sync=none, time0=local, rtc_sync=0 40 | # no cmosimage 41 | log: - 42 | logprefix: %t%e%d 43 | debug: action=ignore 44 | info: action=report 45 | error: action=report 46 | panic: action=ask 47 | keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none 48 | mouse: type=ps2, enabled=false, toggle=ctrl+mbutton 49 | speaker: enabled=true, mode=system 50 | parport1: enabled=true, file=none 51 | parport2: enabled=false 52 | com1: enabled=true, mode=null 53 | com2: enabled=false 54 | com3: enabled=false 55 | com4: enabled=false 56 | -------------------------------------------------------------------------------- /devel/bochsrcn.gdb: -------------------------------------------------------------------------------- 1 | # configuration file generated by Bochs 2 | plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true 3 | config_interface: textconfig 4 | # display_library: x 5 | display_library: nogui 6 | memory: host=32, guest=32 7 | romimage: file="/usr/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none 8 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 9 | boot: a 10 | floppy_bootsig_check: disabled=0 11 | floppya: 1_44="floppya.img", status=inserted 12 | # no floppyb 13 | ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 14 | ata0-master: type=disk, path="hda.img", mode=flat 15 | ata0-slave: type=none 16 | ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15 17 | ata1-master: type=none 18 | ata1-slave: type=none 19 | ata2: enabled=false 20 | ata3: enabled=false 21 | optromimage1: file=none 22 | optromimage2: file=none 23 | optromimage3: file=none 24 | optromimage4: file=none 25 | optramimage1: file=none 26 | optramimage2: file=none 27 | optramimage3: file=none 28 | optramimage4: file=none 29 | pci: enabled=1, chipset=i440fx 30 | vga: extension=vbe, update_freq=5, realtime=1 31 | cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 32 | cpuid: level=6, stepping=3, model=3, family=6, vendor_string="AuthenticAMD", brand_string="AMD Athlon(tm) processor" 33 | cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true 34 | cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, smep=false 35 | cpuid: smap=false, mwait=true 36 | print_timestamps: enabled=0 37 | gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0 38 | port_e9_hack: enabled=0 39 | private_colormap: enabled=0 40 | clock: sync=none, time0=local, rtc_sync=0 41 | # no cmosimage 42 | log: - 43 | logprefix: %t%e%d 44 | debug: action=ignore 45 | info: action=report 46 | error: action=report 47 | panic: action=ask 48 | keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none 49 | mouse: type=ps2, enabled=false, toggle=ctrl+mbutton 50 | speaker: enabled=true, mode=system 51 | parport1: enabled=true, file=none 52 | parport2: enabled=false 53 | com1: enabled=true, mode=null 54 | com2: enabled=false 55 | com3: enabled=false 56 | com4: enabled=false 57 | -------------------------------------------------------------------------------- /devel/floppya.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/devel/floppya.img -------------------------------------------------------------------------------- /devel/hda.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/devel/hda.img -------------------------------------------------------------------------------- /devel/makefile: -------------------------------------------------------------------------------- 1 | 2 | HARDDISK = ./hda.img 3 | IMAGE = ./floppya.img 4 | 5 | .PHONY: ../linux-0.11/Image 6 | ../linux-0.11/Image: 7 | make -C ../linux-0.11 8 | 9 | $(IMAGE): ../linux-0.11/Image 10 | cp $< $@ 11 | 12 | .PHONY: bochs 13 | bochs: $(IMAGE) 14 | bochs -q -unlock -f bochsrc 15 | 16 | .PHONY: bochsg 17 | bochsg: $(IMAGE) 18 | bochs-gdb -q -unlock -f bochsrc.gdb 19 | 20 | .PHONY: bochsgn 21 | bochsgn: kbochsg $(IMAGE) 22 | bochs-gdb -q -unlock -f bochsrcn.gdb 1>/dev/null 2>&1 & 23 | 24 | .PHONY: kbochsg 25 | kbochsg: 26 | ps -ef | grep bochs-gdb | grep -v grep | awk '{print $$2}' | xargs -r kill -9 27 | 28 | .PHONY: qemu 29 | qemu: $(IMAGE) 30 | qemu-system-i386 \ 31 | -m 16M \ 32 | -boot a \ 33 | -fda $(IMAGE) \ 34 | -hda $(HARDDISK) 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf $(IMAGE) 39 | 40 | .PHONY: clean-all 41 | clean-all: clean 42 | make -C ../linux-0.11 clean 43 | -------------------------------------------------------------------------------- /docs/01 配置开发环境.md: -------------------------------------------------------------------------------- 1 | # 配置开发环境 2 | 3 | ## bochs 4 | 5 | 如果只进行模拟的话,可以直接安装 bochs 6 | 7 | pacman -S bochs 8 | 9 | 不过,目前(2021-08) 官方 bochs 有 bug,也就是点击 Stack 及其他功能时,会卡死; 10 | 11 | 遂附赠安装程序补丁包,及 `PKGBUILD` 在 `devel/bochs` 目录中,执行: 12 | 13 | makepkg -si 14 | 15 | 即可安装。 16 | 17 | 安装完成之后,可在 `devel` 目录中执行: 18 | 19 | make bochs 20 | 21 | 即可模拟操作系统 22 | 23 | ![](./images/bochs.jpg) 24 | 25 | --- 26 | 27 | ## qemu 28 | 29 | 安装 `qemu` 的方式比较简单,只需要执行如下命令即可: 30 | 31 | pacman -S qemu 32 | pacman -S qemu-arch-extra 33 | 34 | 其中 `qemu-arch-extra` 有众多其他架构的模拟器,最重要的就是 `qemu-system-i386`,我们需要这个。 35 | 36 | 安装完成之后,可在 `devel` 目录执行: 37 | 38 | make qemu 39 | 40 | 即可模拟操作系统 41 | 42 | ![](./images/qemu.jpg) 43 | 44 | > 希望以后可以实现以下其他架构,比如 arm 😄 45 | 46 | --- 47 | 48 | ## bochs-gdb 49 | 50 | 如果已经配置了 `bochs-gdb` 可以首先在 主机上执行 `make bochsg`,此时,bochs-gdb 会处于等待连接的状态。 51 | 52 | 然后在 vscode 中点击 `F5`,开始对内核的单步调试。 53 | 54 | ### Archlinux 的安装方法 55 | 56 | 可在 `devel/bochs-gdb` 目录直接执行命令: 57 | 58 | makepkg -si 59 | 60 | 安装完成之后,就会有 `bochs-gdb` 命令。 61 | 62 | --- 63 | 64 | 注:默认 bochs 模拟时,会在 PageFault 的情况下中断,所以使用 `SIGUSR2` 来替代默认的信号 0。 65 | 66 | 我当前 gdb 的版本是 10.1,通过研究 gdb 源码找到了 `SIGUSR2` 定义的数字是 `31`,定义在文件 `/gdb/alpha-linux-tdep.c` 中的枚举 `ALPHA_LINUX_SIGUSR2 = 31`,这个与 bochs 源码中定义的有所不同,不过目前看起来一切运行良好。 67 | 68 | `fix-build.patch` 经过了优化,去掉了 PageFault 信号在 gdb 中引起的中断;不过首先需要在 gdb 中执行: 69 | 70 | [-exec] handle SIGUSR2 nostop noprint nopass 71 | 72 | 其中 `-exec` 是 vscode 中的必要选项,也可以在 vscode `launch.json` 文件 setupCommands 中加入下面的内容: 73 | 74 | ```json 75 | { 76 | "description": "ignore SIGUSR2 signal", 77 | "text": "handle SIGUSR2 nostop noprint nopass" 78 | } 79 | ``` 80 | 81 | 目前不知道这样做的其他后果,对于 gdb 的调试功能还不是很清楚,这里需要做进一步研究。 82 | 83 | 安装完成之后,可在 `devel` 目录执行: 84 | 85 | make bochsg 86 | 87 | 启动 bochs,然后就可以在 vscode 中调试了,效果如下: 88 | 89 | ![](./images/bochs-vscode.jpg) 90 | 91 | --- 92 | 93 | ## 关于 hda.img 的处理 94 | 95 | 由于实现提交了 根文件系统 `hda.img`,但是开发调试的过程中会改动 hda.img,但是我又不想再提交这个文件,于是可以执行下面的命令来再 `git commit` 时去掉该文件。 96 | 97 | git update-index --no-assume-unchanged hda.img 98 | 99 | ## 参考资料 100 | 101 | - 102 | - bochs与gdb联调时忽略 page fault 信号 103 | 104 | - diff & patch 制作及打补丁 105 | 106 | -------------------------------------------------------------------------------- /docs/02 启动引导程序.md: -------------------------------------------------------------------------------- 1 | # 启动引导程序 2 | 3 | ## BIOS int 0x13 4 | 5 | - AH = 0 / 重置磁盘 6 | - AH = 2 / 以 CHS 模式读磁盘 7 | - AH = 3 / 以 CHS 模式写磁盘 8 | - AH = 0x15 / 检测第二磁盘 9 | - AH = 0x41 / 测试扩展功能 10 | - AH = 0x42 / 如果支持扩展功能,则以 LBA 模式读磁盘 11 | - AH = 0x43 / 如果支持扩展功能,则以 LBA 模式写磁盘 12 | 13 | ### 重置磁盘 14 | 15 | 参数: 16 | 17 | 1. AH = 0 : 表示重置磁盘驱动器 18 | 2. DL = 驱动器号,如果是硬盘则位 7 要置位 19 | 20 | 返回值: 21 | 22 | 1. 如果出错,CF = 1 23 | 24 | ### 以 CHS 模式读取软盘 25 | 26 | 参数: 27 | 28 | 1. 设置 AH = 2:表示需要读磁盘 29 | 2. AL = 需要读取的数量, 30 | - 不能超过 es 64K 界限(由于只能使用 BX 访问 64K) 31 | - 或者柱面界限(由于需要重新寻道), 32 | - 必须 < 128 (128 * 512 = 64K,最多访问 64KB的空间) 33 | 3. CH = 柱面号的低 8 位 34 | 4. CL 35 | - 0 - 5 位:开始的扇区 36 | - 6 - 7 位:柱面号的高2位 37 | 5. DH = 磁头号 38 | 6. DL = 驱动器号,如果是硬盘则位 7 要置位 39 | 7. ES:BX -> 读入的内存缓冲区 40 | 8. `int 0x13` 41 | 42 | 返回值 43 | 44 | 1. 如果出错,CF = 1 45 | 46 | ```s 47 | read_mbr: 48 | ; 读取主引导扇区 49 | 50 | ; 缓冲区位 es:bx = 0x10000 51 | mov ax, 0x1000 52 | mov es, ax 53 | mov bx, 0 54 | 55 | mov ah, 2; 表示读磁盘 56 | mov al, 1; 读取一个扇区 57 | mov ch, 0; 磁道号 0 58 | mov cl, 1; 第一个扇区,注意扇区号从 1 开始 59 | mov dh, 0; 第 0 个磁头(盘面) 60 | mov dl, 0; 驱动器号 0 61 | int 0x13 62 | jnc .success 63 | .error: 64 | ; 读取失败 65 | jmp .error 66 | .success: 67 | ; 此时读取成功 68 | xchg bx, bx 69 | ``` 70 | 71 | > 注:如果要写磁盘,设置 AH = 3 即可,整个过程就是将缓存区 `ES:BX` 的内容写入磁盘。 72 | 73 | 比如将刚才读入的数据稍微改改,再回去 74 | 75 | ```s 76 | mov word es:[508], 0x1122 77 | mov bx, 0 78 | 79 | mov ah, 3; 表示写磁盘 80 | mov al, 1; 写一个扇区 81 | mov ch, 0; 磁道号 0 82 | mov cl, 1; 第一个扇区,注意扇区号从 1 开始 83 | mov dh, 0; 第 0 个磁头(盘面) 84 | mov dl, 0; 驱动器号 0 85 | int 0x13 86 | ``` 87 | 88 | ## BIOS int 0x10 89 | 90 | - AH = 1 / 设置光标类型 91 | - AH = 2 / 设置光标位置 92 | - AH = 3 / 获取光标位置 93 | - AH = 0xE / 显示字符 94 | - AH = 0xF / 获取当前状态 95 | - AH = 0x11 / 字符生成器 96 | - AH = 0x12 / 检测 EGA/VGA 97 | - AH = 0x13 / 显示字符串 98 | 99 | ### 获取光标位置 100 | 101 | 参数: 102 | 103 | - AH = 3 : 表示获取光标位置 104 | - BH: 页数以 0 开始 105 | 106 | 返回值: 107 | 108 | - (DH, DL): (行, 列) 请求页的位置 109 | - (CH, CL): 当前光标类型 110 | 111 | ### 获取显卡当前状态 112 | 113 | 参数: 114 | 115 | - AH = 0x0F:表示获取显卡当前状态 116 | 117 | 返回值: 118 | 119 | - AL - 当前模式 120 | - AH - 字符列数 121 | - BH - 当前页数 122 | 123 | ### 获取显卡信息 124 | 125 | 参数: 126 | 127 | - AH = 0x12: 获取 EGA 的功能特性 128 | - BL = 0x10: 获取 EGA 的信息 129 | 130 | 返回值: 131 | 132 | - BH: 133 | - 0x00 / 彩色模式 / IO 端口 3Dx 134 | - 0x01 / 单色模式 / IO 端口 3Bx 135 | - BL: 内存大小 136 | - 0x00 / 64KB 137 | - 0x01 / 128KB 138 | - 0x02 / 192KB 139 | - 0x03 / 256KB 140 | - CX: 显示卡特性参数 141 | 142 | ### 打印字符串 143 | 144 | 参数: 145 | 146 | - AH = 0x13:表示打印字符串 147 | - ES:BP -> 指向需要打印字符串的内存 148 | - CX: 需要打印的字符数量 149 | - DX: 字符串开始的光标位置,一般获取光标位置得到 150 | - BH: 页码 151 | - BL: 字符样式 152 | - AL 153 | - 00: 不移动光标 154 | - 01: 移动光标 155 | 156 | 没有返回值 157 | 158 | ## BIOS int 0x15 159 | 160 | ### 获取内存大小 161 | 162 | 参数: 163 | 164 | - AH = 0x88 165 | 166 | 返回值: 167 | 168 | - AX 为内存容量,单位 1KB (也就是说该调用最多检测 64MB 的内存,不过在那个年代,也够用了) 169 | - 若 CF = 1,则出错 170 | 171 | --- 172 | 173 | 检测内存最好的方式是 `int 0x15` 的 `0xe820` 调用,但并不是所有机器都支持。 174 | 175 | ## 硬盘基本参数表 176 | 177 | 中断向量表中,`int 0x41` 的中断向量位置(0x0000:0x0104)存放的并不是中断程序的地址,而是第一个硬盘的基本参数表。 178 | 179 | 对于 100% 兼容的 BIOS 来说,这里存放着硬盘参数表阵列的首地址 F000h:E401h 180 | 181 | 第二个硬盘的基本参数表入口地址存于 `int 0x46` 中断向量位置处 182 | 183 | **硬盘基本参数信息表** 184 | 185 | | 位移 | 大小 | 说明 | 186 | | ---- | ---- | -------------------------------------------- | 187 | | 0x00 | 字 | 柱面数 | 188 | | 0x02 | 字节 | 磁头数 | 189 | | 0x03 | 字 | 开始减小写电流的柱面(仅 PC XT 使用,其它为0) | 190 | | 0x05 | 字 | 开始写前预补偿柱面号(乘4) | 191 | | 0x07 | 字节 | 最大 ECC 猝发长度(仅XT 使用,其它为0) | 192 | | 0x08 | 字节 | 控制字节(驱动器步进选择) | 193 | | 0x09 | 字节 | 标准超时值(仅 XT 使用,其它为0) | 194 | | 0x0a | 字节 | 格式化超时值(仅 XT 使用,其它为0) | 195 | | 0x0b | 字节 | 检测驱动器超时值(仅 XT 使用,其它为0) | 196 | | 0x0c | 字 | 磁头着陆(停止)柱面号 | 197 | | 0x0e | 字节 | 每磁道扇区数 | 198 | | 0x0f | 字节 | 保留 | 199 | 200 | 控制字节 201 | 202 | | 位 | 描述 | 203 | | --- | ---------------------------------------- | 204 | | 0 | 未用 | 205 | | 1 | 保留 (0) (关闭IRQ) | 206 | | 2 | 允许复位 | 207 | | 3 | 若磁头数大于 8 则置 1 | 208 | | 4 | 未用 (0) | 209 | | 5 | 若在柱面数 +1 处有生产商的坏区图,则置 1 | 210 | | 6 | 禁止 ECC 重试 | 211 | | 7 | 禁止访问重试 | 212 | 213 | ## bootsect.s 214 | 215 | 主引导扇区代码,主要用于加载 `setup` 和内核 `system` 216 | 217 | ## setup.s 218 | 219 | setup 程序读取并保留的参数 220 | 221 | | 内存地址 | 长度(字节) | 名称 | 描述 | 222 | | -------- | ---------- | ---------- | ------------------------------------------------ | 223 | | 0x90000 | 2 | 光标位置 | 列号(0x00-最左端),行号(0x00-最顶端) | 224 | | 0x90002 | 2 | 扩展内存数 | 系统从 1M 开始的扩展内存数值(KB)。 | 225 | | 0x90004 | 2 | 显示页面 | 当前显示页面 | 226 | | 0x90006 | 1 | 显示模式 | 227 | | 0x90007 | 1 | 字符列数 | 228 | | 0x90008 | 2 | 功能支持 | 是否支持 EGA 功能 | 229 | | 0x9000A | 1 | 显示内存 | 显示内存(0x00-64k,0x01-128k,0x02-192k,0x03=256k) | 230 | | 0x9000B | 1 | 显示状态 | 0x00-彩色,I/O=0x3dX;0x11-单色,I/O=0x3bX | 231 | | 0x9000C | 2 | 特性参数 | 显示卡特性参数 | 232 | | ... | 233 | | 0x90080 | 16 | 硬盘参数表 | 第 1 个硬盘的参数表 | 234 | | 0x90090 | 16 | 硬盘参数表 | 第 2 个硬盘的参数表(如果没有,则清零) | 235 | | 0x901FC | 2 | 根设备号 | 根文件系统所在的设备号(`bootsec.s` 中设置) | 236 | 237 | ## 参考资料 238 | 239 | - Linux 内核源码漫游 240 | 241 | - 242 | - IBM PS 2 and PC BIOS Interface Technical Reference 243 | - 244 | - 245 | - 246 | - 247 | - 248 | -------------------------------------------------------------------------------- /docs/03 内核初始化.md: -------------------------------------------------------------------------------- 1 | # 内核初始化 2 | 3 | ## 参考资料 4 | 5 | - Using the GNU Compiler Collection 6 | 7 | -------------------------------------------------------------------------------- /docs/images/Floppy_disk_2009_G1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/docs/images/Floppy_disk_2009_G1.jpg -------------------------------------------------------------------------------- /docs/images/bochs-vscode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/docs/images/bochs-vscode.jpg -------------------------------------------------------------------------------- /docs/images/bochs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/docs/images/bochs.jpg -------------------------------------------------------------------------------- /docs/images/qemu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/docs/images/qemu.jpg -------------------------------------------------------------------------------- /docs/参考资料.md: -------------------------------------------------------------------------------- 1 | # 参考资料 2 | 3 | - 4 | 5 | - 6 | 7 | - bootimage 8 | 9 | - 10 | 11 | - 赵炯 - 《Linux内核完全注释》 12 | 13 | - How to mount a QEMU virtual disk image 14 | 15 | 16 | - Make partition using sfdisk 17 | 18 | 19 | - Install 20 | 21 | - How to fix Git error: object file is empty? 22 | 23 | -------------------------------------------------------------------------------- /docs/硬件设备基础/01 软盘驱动器.md: -------------------------------------------------------------------------------- 1 | 2 | # 软盘驱动器 3 | 4 | ![软盘](../images/Floppy_disk_2009_G1.jpg) 5 | 6 | ## 背景 7 | 8 | 软盘 (Floppy Disk) 是一种碟盘存储,主要部分是一张薄软的磁存储介质盘片,盘片封装在矩形塑料壳中,内衬有用于清理灰尘的纤维织物。读写软盘需要借助软盘驱动器。 9 | 10 | 第一个软盘是由 IBM 于 1971 年开发出的,直径 8 英寸(上图黑色的软盘)。随着硬件技术的发展与使用的需要,又派生出 5.25 英寸的软盘(上图中橙色的软盘),并广泛使用在 Apple II、IBM PC 及其他兼容电脑上。苹果 1984 年在Mac 机开始采用 3.5 英寸软盘,此时容量还不到 1MB,后来,由日本索尼的 3.5英寸软盘片容量有 1.44MB 所取代,这种软盘在 80 至 90 年代盛行,直至 2000 年代以前,3.5 英寸软盘驱动器仍是电脑普及设备之一,但是在1998年后渐渐被淘汰。 11 | 12 | > 注:1英寸 = 2.54 厘米 13 | 14 | 软盘直至 2000 年代仍为电脑必备设备之一,因为把它制作成电脑引导碟以及更新 BIOS 时需要用到。但是由于软盘读取方式的局限,磁头在读写软盘资料时 **必须接触碟片**,而不是像硬盘那样 **悬空读写**,因此软盘已经难以满足大量和高速的资料存储,而且软盘的存储稳定性也较差,一张正常的软盘,容易受到外界环境影响,如受热、受潮、多次读写,均使之寿命减少。后来虽然有很多升级产品如 zip Drive、 SuperDisk(LS-120) 及 Jaz Drive 等,但是都难以同时解决兼容性和速度容量两者直接的矛盾。随着光盘、U盘、移动硬盘等移动存储接口的应用,5.25 英寸及 8 英寸的软盘已极为罕见,3.5 英寸的软盘使用也渐被淘汰。 15 | 16 | 2010年代,尽管大多数大厂已经宣布不再生产软盘驱动器,然而,仍有厂商推出传统内接式或是 USB 接口的外接式软盘驱动器,以供应市场上的需求,软盘片之所以在市场上仍占一席之地,系因在许多的企业、政府机关、研究单位,仍然需要在一些较旧型电脑或工业电脑等特殊环境使用软盘驱动器来做资料文件的传输。 17 | 18 | ## 尺寸 19 | 20 | | 软盘类型 | 尺寸(毫米) | 容量 | 21 | | -------- | ------------------- | ------- | 22 | | 8 | 203.0 x 203.0 x 1.6 | 242 KB | 23 | | 5.25 | 133.3 x 133.3 x 1.6 | 1.2 MB | 24 | | 3.5 | 93.7 x 90.0 x 3.3 | 1.44 MB | 25 | 26 | ## 种类 27 | 28 | > 注:通常 29 | > - **磁道** 和 **柱面** 是同义词 30 | > - **盘面** 和 **磁头** 是同义词 31 | 32 | - 5.25英寸软盘 33 | - 160kB,单面 / 40 磁道 / 8扇区 34 | - 180kB,单面 / 40 磁道 / 9扇区 35 | - 320kB,双面 / 40 磁道 / 8 扇区 36 | - 360kB,双面 / 40 磁道 / 9 扇区 37 | - 1.2MB,双面 / 80 磁道 / 15 扇区 38 | - 3.5英寸软盘 39 | - 360kB,单面 / 80 磁道 / 9 扇区 40 | - 720kB,双面 / 80 磁道 / 9 扇区 41 | - 1.44MB,双面 / 80 磁道 / 18 扇区 42 | - 2.88MB,双面 / 80 磁道 / 36 扇区 43 | 44 | ### 扇区寻址方式 45 | 46 | 软盘驱动器只能使用 CHS (Cylinders Heads Sector) 的寻址方式,默认总是有两个磁头,但是驱动器需要知道多少有多少磁道和多少扇区。 47 | 48 | 典型的,通常使用的 1.44M,3.5 寸软盘有 80 磁道,每磁道有 18 扇区。 49 | 50 | 磁道和磁头从 0 开始计数,而扇区从 1 开始计数 51 | 52 | 但是,很多逻辑地址是以 LBA(Logical Block Address) 模式记录的,也就死从逻辑上将所有的扇区排序,第一个扇区编号为 0,两种模式的转换方式如下: 53 | 54 | --- 55 | 56 | > 逻辑扇区地址 = (( 第几个柱面 * 柱面对应的磁头数 + 第几个磁头 ) * 每磁道扇区数 ) + 第几个扇区 - 1 57 | 58 | 假设我们从头到尾将磁盘的所有扇区都写满, 59 | 60 | - 先选择没有写满的柱面,这样相当于选择了好几个磁道,这些磁道在不同盘面的同一个位置 61 | - 再从这些磁道中选择一个没有写满的磁道,相当于选择一个磁头。 62 | - 再从这个磁道中选择没有写入的扇区 63 | 64 | > 这里说一个 **错误** 的直观感觉: 65 | > 66 | > 对我来说,直观的感觉应该是先选择一个盘面可劲儿的写,写完这个盘面,然后再选择下一个盘面,然后再写;如果磁盘是好多个盘面垒起来的圆柱体,这种方式就像是先从下往上,再从外向内,依次写入,有点像一楼写满了,再写二楼;从逻辑上来说可以这么做,但是却没有,事实不是这样的; 67 | > 68 | > 下面是 **正确** 的 69 | > 70 | > 应该是先选择柱面,然后选择磁道。如果磁盘还是上面的圆柱体,这种方式就像是 先从外向内,再从下到上依次写入; 71 | > 72 | > 这么做的主要原因是,**磁盘的主要性能瓶颈是寻道时间**,也就是移动磁头的时间,如果以上面那种方式写的话,就需要频繁的移动磁头,这样就会拖慢性能。而下面这种方式,在写满所有盘面的同一磁道时不需要移动磁头,只有写完所有磁道之后,才需要移动磁头,所以就会比较快。 73 | 74 | 75 | ## 参考资料 76 | 77 | - 78 | - 79 | - -------------------------------------------------------------------------------- /docs/硬件设备基础/02 硬盘驱动器.md: -------------------------------------------------------------------------------- 1 | # 硬盘驱动器 2 | 3 | ## 参考资料 4 | -------------------------------------------------------------------------------- /docs/硬件设备基础/03 VGA 显示器.md: -------------------------------------------------------------------------------- 1 | # 03 VGA 显示器 -------------------------------------------------------------------------------- /docs/硬件设备基础/images/DRAM_SIMM_8x4x1M.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/docs/硬件设备基础/images/DRAM_SIMM_8x4x1M.jpg -------------------------------------------------------------------------------- /docs/硬件设备基础/images/block_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/docs/硬件设备基础/images/block_diagram.png -------------------------------------------------------------------------------- /docs/硬件设备基础/images/i486DX2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/docs/硬件设备基础/images/i486DX2.jpg -------------------------------------------------------------------------------- /docs/硬件设备基础/images/i486_die_anno2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/docs/硬件设备基础/images/i486_die_anno2.jpg -------------------------------------------------------------------------------- /docs/硬件设备基础/images/motherboard_anno.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/alinux/06bbe5b7111e7c3bbb357092e52307b84c7ce6fb/docs/硬件设备基础/images/motherboard_anno.jpg -------------------------------------------------------------------------------- /docs/硬件设备基础/readme.md: -------------------------------------------------------------------------------- 1 | # 硬件设备基础 2 | 3 | 这里介绍必要的硬件,一些背景知识。可以在大环境上烘托出一种气氛。 4 | 5 | 由于 Linux 0.11 对于计算机来说已经是好几个地质纪年之前的事情了,所以有必要回到那个年代,来感受一下当时的硬件设备,以及对现代计算机的影响。 6 | 7 | 我们主要的 CPU 还是以 Intel 80486 为主,所以我觉得我需要了解以下与 80486 相关的一些硬件,操作系统是用来直接操作硬件的软件系统,所以了解硬件实现的细节挺重要的,不过这部分内容对于计算机专业来说不是那么容易,更偏向电子相关的专业,需要处理很多电器工程的问题。 8 | 9 | > 以下内容可能有误,我无从试验,主要是从网络摘抄的!!! 10 | 11 | ## 主板 (80486) 12 | 13 | ![](./images/motherboard_anno.jpg) 14 | 15 | ## CPU 16 | 17 | ![](./images/i486DX2.jpg) 18 | 19 | ![](./images/block_diagram.png) 20 | 21 | ![](./images/i486_die_anno2.jpg) 22 | 23 | ## 内存 24 | 25 | ![](./images/DRAM_SIMM_8x4x1M.jpg) 26 | 27 | --- 28 | 29 | ## 芯片组 30 | 31 | ## BIOS 32 | 33 | ## 键盘及控制器 34 | 35 | ## 时钟生成器 36 | 37 | ## 二级缓存 38 | 39 | ## I/O 端口映射 40 | 41 | IO 端口通常用作 x86 IO 总线上的一个特定地址,总线为设备提供一种固定大小和顺序的通信方式,是一种内存映射的替代方式。在许多其他的架构中,并没有预定义的总线来作类似的通信,而是使用内存映射的方式。它同样越来越多地出现在现代 x86 硬件中。 42 | 43 | 如果你发现了一个端口号,但是不知它作何之用,这里是一个常用端口号的列表: 44 | 45 | | 端口号范围 | 描述 | 46 | | --------------- | ----------------------------- | 47 | | 0x0000 ~ 0x001F | 第一 DMA 控制器,一般用于软盘 | 48 | | 0x0020 ~ 0x0021 | 第一可编程中断控制器 | 49 | | 0x0040 ~ 0x0047 | 可编程定时器 | 50 | | 0x0060 ~ 0x0064 | 键盘和鼠标 | 51 | | 0x0070 ~ 0x0071 | 实时时钟 | 52 | | 0x0080 ~ 0x008F | DMA (Page registers) | 53 | | 0x0092 | 快速 A20 门 | 54 | | 0x00A0 ~ 0x00A1 | 第二可编程中断控制器 | 55 | | 0x00C0 ~ 0x00DF | 第二 DMA 控制器,一般用于声卡 | 56 | | 0x0170 ~ 0x0177 | 从硬盘控制器 | 57 | | 0x01F0 ~ 0x01F7 | 主硬盘控制器 | 58 | | 0x0278 ~ 0x027A | 并口 | 59 | | 0x02F8 ~ 0x02FF | 第二串口 | 60 | | 0x03B0 ~ 0x03DF | IBM VGA | 61 | | 0x03F0 ~ 0x03F7 | 软盘控制器 | 62 | | 0x03F8 ~ 0x03FF | 第一串口 | 63 | 64 | --- 65 | 66 | ## ISA 总线 67 | 68 | ISA总线: (Industry Standard Architecture:工业标准体系结构)是为 PC/AT 电脑而制定的总线标准,为 8/16 位体系结构,只能支持 16 位的 I/O 设备,数据传输率大约是 16MB/s。也称为 AT 标准。 69 | 70 | --- 71 | 72 | ## VESA 局部总线 73 | 74 | VESA 局部总线是由 60 家附件卡制造商联合推出的一种局部总线,简称为 VL(VESA local bus) 总线。 75 | 76 | 它的推出为微机系统总线体系结构的革新奠定了基础。 77 | 78 | VESA 总线主要目的是用于视频插卡,以提高视频性能。 79 | 80 | 该总线系统考虑到 CPU 与主存和 Cache 的直接相连,通常把这部分总线称为 CPU 总线或主总线,其他设备通过 VL 总线与 CPU 总线相连,所以 VL 总线被称为局部总线。 81 | 82 | 它定义了 32 位数据线,且可通过扩展槽扩展到 64 位,使用 33MHz 时钟频率,最大传输率达 132MB/s,可与 CPU 同步工作。是一种高速、高效的局部总线,可支持 386SX、386DX、486SX、486DX 及 奔腾微处理器。 83 | 84 | --- 85 | 86 | ## 其他细节 87 | 88 | - [软盘驱动器](./01%20软盘驱动器.md) 89 | - [硬盘驱动器](./02%20硬盘驱动器.md) 90 | - [VGA 显示器](./03%20VGA%20显示器.md) 91 | 92 | 93 | ## 参考资料 94 | 95 | - 96 | - 97 | - 98 | -------------------------------------------------------------------------------- /docs/硬件设备基础/x86 体系结构.md: -------------------------------------------------------------------------------- 1 | # x86 体系结构 2 | 3 | 我们所研究的是 x86 体系结构,那么有必要先研究一下英特尔的 CPU,机器相关的架构,这样我觉得可能会好一点。 4 | 5 | ## 英特尔 CPU 历史 6 | 7 | ![](./images/x86_history.drawio.svg) 8 | 9 | ### 8086: x86 的始祖 10 | 11 | ![](https://cdn.mos.cms.futurecdn.net/Hn3JNRhpuNZddkfWcE7qK9-970-80.jpg) 12 | 13 | Intel 第一款 16 位的处理器,比起之前的处理器在性能上有了很大的提升,不仅仅是时钟频率的提升,而且使用了 16 位的外部数据总线和更长的 6字节预取指队列,使得它可以运行 16 位的任务(尽管当时的大多数软件是为 8 位处理器而设计的),地址总线被扩展到 20 位,使得 8086 可以访问 1MB 的内存而因此提升性能。 14 | 15 | 8086 同样是 x86 系列的第一块处理器,使用了第一版的 x86 指令集 (ISA Instruction Set Architechiture) 16 | 17 | ### 80386 18 | 19 | ![](https://cdn.mos.cms.futurecdn.net/9BSdhnaYY2KxHxzHmsg6XV-970-80.jpg) 20 | 21 | Intel 第一块 32位处理器是 80386,于 1985 年发布。一个关键的优势是它有 32 位的地址总线允许访问 4GB 的内存空间。因此它在发布的时候,远远大于同时代的处理器。内存的限制常常是引发性能问题的关键,不像当代的CPU,在 80386 处理器发布时,更多的内存总是被认为是更高的性能。 22 | 23 | ### 80486: 集成了 FPU 24 | 25 | ![](https://cdn.mos.cms.futurecdn.net/rFZk5af5bVPUsQ5uBevL5B-970-80.jpg) 26 | 27 | 80486 是另一个提升性能的关键,关键在于成功的在CPU中集成了更多的组件,80486 是第一个有 L1 缓存的处理器。 28 | 29 | 80486 还集成了 FPU(浮点处理单元) 和 MMU (内存管理单元) 30 | 31 | ## 参考资料 32 | 33 | - The History Of Intel CPUs: Updated! 34 | 35 | - 36 | -------------------------------------------------------------------------------- /linux-0.11/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # 如果你要使用 RAM 盘设备的话,就定义块的大小 3 | # 4 | 5 | RAMDISK = #-DRAMDISK=512 6 | 7 | # 8086 汇编器 8 | # -0 生成 8086 目标程序; 9 | # -a 生成与 gas 和 gld 部分兼容的代码 10 | AS86 =as86 -0 -a 11 | 12 | # 8086 连接器 13 | LD86 =ld86 -0 14 | 15 | # 这里导入通用的编译工具以及编译选项 16 | # 与源文件不同之处在于将所有 makefile 文件中的公共部分抽取出来 17 | # 这样如果有变动的话,只需要修改 Option.mk 文件就可以了 18 | include Option.mk 19 | 20 | # GNU 连接器 ld 运行时用到的选项 21 | # -Ttext 0 表示内核入口地址为 0 22 | # -e startup_32 表示内核入口为 startup_32 23 | # 意味着 startup_32 这个函数将被加载到 内存开始的位置 24 | LDFLAGS += -Ttext 0 -e startup_32 25 | 26 | # 添加 include 选项 27 | CFLAGS += $(RAMDISK) -Iinclude 28 | CPP += -Iinclude 29 | 30 | # 31 | # ROOT_DEV 指定在创建内核映像 (image) 文件时所使用的默认根文件系统所在的设备, 32 | # 这可以是软盘(FLOPPY)、/dev/xxxx 或者干脆空着 33 | # 空着时 build 程序(在tools/目录中)就使用默认值 /dev/hd6 34 | # 对应 bochs 中的 ata0-master: type=disk, path="hda.img", mode=flat 35 | # 36 | 37 | ROOT_DEV= #FLOPPY 38 | 39 | # kernel 目录、mm 目录和 fs 目录所产生的目标代码文件 40 | # 为了方便引用在这里将它们用 ARCHIVES(归档文件)标识符表示 41 | ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o 42 | 43 | # 块和字符设备库文件 44 | # .a 表示该文件是个归档文件,通俗的说就是静态链接库, 45 | # 也即包含有许多可执行二进制代码子程序集合的库文件, 46 | # 通常是用 GNU 的 ar 程序生成 47 | # ar 是 GNU 的二进制文件处理程序,用于创建、修改以及从归档文件中抽取文件 48 | DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a 49 | 50 | # 数学运算库文件 51 | MATH =kernel/math/math.a 52 | 53 | # 由 lib/ 目录中的文件所编译生成的通用库文件 54 | LIBS =lib/lib.a 55 | 56 | # make 老式的隐式后缀规则 57 | # 该行指示 make 利用下面的命令将所有的 .c 文件编译生成 .s 汇编程序 58 | # ':' 表示下面是该规则的命令 59 | # 这里的命令相当于 %s: %c 60 | .c.s: 61 | $(CC) $(CFLAGS) -S -o $*.s $< 62 | 63 | # 表示将所有 .s 汇编程序文件编译成 .o 目标文件 64 | .s.o: 65 | $(AS) -o $*.o $< 66 | 67 | # 类似上面,*.c 文件 -> *.o 目标文件 68 | .c.o: 69 | $(CC) $(CFLAGS) -c -o $*.o $< 70 | 71 | # all 表示创建 Makefile 所知的最顶层的目标,这里即是 Image 文件 72 | all: Image 73 | 74 | # 说明目标(Image 文件)是由冒号后面的 4 个元素产生 75 | # objcopy 用于生成加载到内存中的二进制文件 76 | # 默认 ELF 文件需要解析,特别是对于 bss 段的解析 77 | # objcopy 将 bss 段也存入文件中,尽管全部都是 0 78 | # 这样避免了 ELF 文件的解析,加载到内存中就可以直接执行 79 | # -R 表示删除特定的段 80 | Image: boot/bootsect boot/setup tools/system tools/build 81 | objcopy -O binary -R .note -R .comment -R .note.gnu.property tools/system tools/kernel 82 | tools/build boot/bootsect boot/setup tools/kernel $(ROOT_DEV) > Image 83 | rm tools/kernel -f 84 | sync # 同步命令是迫使缓冲块数据立即写盘并更新超级块 85 | 86 | # 表示disk 这个目标要由 Image 产生,这个目前没用 87 | disk: Image 88 | dd bs=8192 if=Image of=/dev/fd0 89 | 90 | # 由 tools 目录下的 build.c 程序生成执行程序 build 91 | tools/build: tools/build.c 92 | gcc -m32 -o tools/build tools/build.c 93 | 94 | # 生成 head.o 目标文件 95 | boot/head.o: boot/head.s 96 | gcc $(DEBUG) -m32 -I./include -c $< -o $@ 97 | 98 | # 生成 system 文件 99 | # nm 命令是 names 的缩写,用于生成符号文件 100 | tools/system: boot/head.o init/main.o \ 101 | $(ARCHIVES) $(DRIVERS) $(MATH) $(LIBS) 102 | $(LD) $(LDFLAGS) boot/head.o init/main.o \ 103 | $(ARCHIVES) \ 104 | $(DRIVERS) \ 105 | $(MATH) \ 106 | $(LIBS) \ 107 | -o tools/system 108 | nm tools/system | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)'| sort > System.map 109 | 110 | # 数学协处理函数文件 math.a 由下一行上的命令实现 111 | kernel/math/math.a: 112 | (cd kernel/math; make) 113 | 114 | # 块设备函数文件 blk_drv.a 115 | kernel/blk_drv/blk_drv.a: 116 | (cd kernel/blk_drv; make) 117 | 118 | # 字符设备函数文件 chr_drv.a 119 | kernel/chr_drv/chr_drv.a: 120 | (cd kernel/chr_drv; make) 121 | 122 | # 内核目标模块 kernel.o 123 | kernel/kernel.o: 124 | (cd kernel; make) 125 | 126 | # 内存管理模块 mm.o 127 | mm/mm.o: 128 | (cd mm; make) 129 | 130 | # 文件系统目标模块 fs.o 131 | fs/fs.o: 132 | (cd fs; make) 133 | 134 | # 库函数 lib.a 135 | lib/lib.a: 136 | (cd lib; make) 137 | 138 | # 这里开始的三行是使用 8086 汇编和连接器 139 | boot/setup: boot/setup.s 140 | $(AS86) -o boot/setup.o boot/setup.s 141 | $(LD86) -s -o boot/setup boot/setup.o 142 | 143 | # 同上,生成 bootsect.o 磁盘引导块 144 | boot/bootsect: boot/bootsect.s 145 | $(AS86) -o boot/bootsect.o boot/bootsect.s 146 | $(LD86) -s -o boot/bootsect boot/bootsect.o 147 | 148 | # 这里用来计算 system 的大小,写入 bootsect,但是目前用不到,可以忽略 149 | tmp.s: boot/bootsect.s tools/system 150 | (echo -n "SYSSIZE = (";ls -l tools/system | grep system \ 151 | | cut -c25-31 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.s 152 | cat boot/bootsect.s >> tmp.s 153 | 154 | # 清理生成的文件 155 | clean: 156 | rm -f Image System.map tmp_make core boot/bootsect boot/setup 157 | rm -f init/*.o tools/system tools/build boot/*.o 158 | (cd mm;make clean) 159 | (cd fs;make clean) 160 | (cd kernel;make clean) 161 | (cd lib;make clean) 162 | 163 | # 该规则将首先执行上面的 clean 规则,然后对 linux 目录进行压缩 164 | # 生成 backup.Z 压缩文件。'cd .. ' 表示退到linux 的上一级(父)目录; 165 | # 'tar cf - linux' 表示对 linux 目录执行 tar 归档程序 166 | # -cf 表示需要创建新的归档文件 '| compress -' 表示将 tar 程序的执行通过管道操作 ('|') 167 | # 传递给压缩程序 compress,并将压缩程序的输出存成 backup.Z 文件 168 | backup: clean 169 | (cd .. ; tar cf - linux | compress16 - > backup.Z) 170 | sync 171 | 172 | # 该目标或规则用于各文件之间的依赖关系 173 | # 创建的这些依赖关系是为了给 make 用来确定是否需要要重建一个目标对象的 174 | # 比如当某个头文件被改动过后,make 就通过生成的依赖关系, 175 | # 重新编译与该头文件有关的所有 *.c 文件。具体方法如下: 176 | # 使用字符串编辑程序sed 对 Makefile 文件(这里即是自己)进行处理, 177 | # 输出为删除 Makefile文件中 '### Dependencies' 行后面的所有行,并生成 tmp_make 临时文件 178 | # 然后对 init 目录下的每一个 C 文件(其实只有一个文件 main.c)执行gcc 预处理操作, 179 | # -M 标志告诉预处理程序输出描述每个目标文件相关性的规则 180 | # 并且这些规则符合 make 语法。对于每一个源文件,预处理程序输出一个 make 规则, 181 | # 其结果形式是相应源程序文件的目标文件名加上其依赖关系,该源文件中包含的所有头文件列表 182 | # 下面的 $$i 实际上是 $($i) 的意思。这里 $i 是这句前面的 shell 变量的值 183 | # make 变量如果只有一个字母可以省略圆括号 也就是 $(i) = $i 184 | # 然后把预处理结果都添加到临时文件 tmp_make 中,然后将该临时文件复制成新的 Makefile 文件 185 | dep: 186 | sed '/\#\#\# Dependencies/q' < Makefile > tmp_make 187 | (for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done) >> tmp_make 188 | cp tmp_make Makefile 189 | (cd fs; make dep) 190 | (cd kernel; make dep) 191 | (cd mm; make dep) 192 | 193 | ### Dependencies: 194 | init/main.o : init/main.c include/unistd.h include/sys/stat.h \ 195 | include/sys/types.h include/sys/times.h include/sys/utsname.h \ 196 | include/utime.h include/time.h include/linux/tty.h include/termios.h \ 197 | include/linux/sched.h include/linux/head.h include/linux/fs.h \ 198 | include/linux/mm.h include/signal.h include/asm/system.h include/asm/io.h \ 199 | include/stddef.h include/stdarg.h include/fcntl.h 200 | -------------------------------------------------------------------------------- /linux-0.11/Option.mk: -------------------------------------------------------------------------------- 1 | 2 | # 需要添加调试信息 3 | DEBUG = -g 4 | 5 | # GNU 的汇编程序 6 | AS = as --32 $(DEBUG) 7 | 8 | # GNU 的二进制文件处理程序,用于创建、修改以及从归档文件中抽取文件 9 | AR = ar 10 | 11 | # GNU 的连接程序 12 | LD = ld 13 | 14 | # 连接程序所有的参数,默认 32 位程序 15 | LDFLAGS = -m elf_i386 16 | 17 | # GNU C 语言编译器 18 | CC = gcc 19 | 20 | # C 预处理选项。-E 只运行 C 预处理 21 | # 对所有指定的 C 程序进行预处理并将处理结果输出到标准输出设备或指定的输出文件中; 22 | CPP = cpp -nostdinc 23 | 24 | # C 编译程序选项 25 | # -Wall 显示所有的警告信息; 26 | # -O 优化选项,优化代码长度和执行时间; 27 | # -fstrength-reduce 优化循环执行代码,排除重复变量; 28 | # -fomit-frame-pointer 省略保存不必要的框架指针; 29 | # -fcombine-regs 合并寄存器,减少寄存器类的使用; 30 | # -finline-functions 将所有简单短小的函数代码嵌入调用程序中; 31 | # -nostdinc 不使用默认路径中的包含文件; 32 | 33 | CFLAGS = $(DEBUG) -m32 \ 34 | -fno-builtin \ 35 | -fno-pic \ 36 | -fno-stack-protector \ 37 | -fomit-frame-pointer \ 38 | -fstrength-reduce \ 39 | -nostdinc 40 | -------------------------------------------------------------------------------- /linux-0.11/fs/Makefile: -------------------------------------------------------------------------------- 1 | include ../Option.mk 2 | 3 | LDFLAGS += -r 4 | CFLAGS += -I../include 5 | CPP += -I../include 6 | 7 | .c.s: 8 | $(CC) $(CFLAGS) \ 9 | -S -o $*.s $< 10 | .c.o: 11 | $(CC) $(CFLAGS) \ 12 | -c -o $*.o $< 13 | .s.o: 14 | $(AS) -o $*.o $< 15 | 16 | OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o \ 17 | block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \ 18 | bitmap.o fcntl.o ioctl.o truncate.o 19 | 20 | fs.o: $(OBJS) 21 | $(LD) $(LDFLAGS) -o fs.o $(OBJS) 22 | 23 | clean: 24 | rm -f core *.o *.a tmp_make 25 | for i in *.c;do rm -f `basename $$i .c`.s;done 26 | 27 | dep: 28 | sed '/\#\#\# Dependencies/q' < Makefile > tmp_make 29 | (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make 30 | cp tmp_make Makefile 31 | 32 | ### Dependencies: 33 | bitmap.o: bitmap.c ../include/string.h ../include/linux/sched.h \ 34 | ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ 35 | ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h 36 | block_dev.o: block_dev.c ../include/errno.h ../include/linux/sched.h \ 37 | ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ 38 | ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ 39 | ../include/asm/segment.h ../include/asm/system.h 40 | buffer.o: buffer.c ../include/stdarg.h ../include/linux/config.h \ 41 | ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ 42 | ../include/sys/types.h ../include/linux/mm.h ../include/signal.h \ 43 | ../include/linux/kernel.h ../include/asm/system.h ../include/asm/io.h 44 | char_dev.o: char_dev.c ../include/errno.h ../include/sys/types.h \ 45 | ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ 46 | ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ 47 | ../include/asm/segment.h ../include/asm/io.h 48 | exec.o: exec.c ../include/errno.h ../include/string.h \ 49 | ../include/sys/stat.h ../include/sys/types.h ../include/a.out.h \ 50 | ../include/linux/fs.h ../include/linux/sched.h ../include/linux/head.h \ 51 | ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ 52 | ../include/asm/segment.h 53 | fcntl.o: fcntl.c ../include/string.h ../include/errno.h \ 54 | ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ 55 | ../include/sys/types.h ../include/linux/mm.h ../include/signal.h \ 56 | ../include/linux/kernel.h ../include/asm/segment.h ../include/fcntl.h \ 57 | ../include/sys/stat.h 58 | file_dev.o: file_dev.c ../include/errno.h ../include/fcntl.h \ 59 | ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \ 60 | ../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \ 61 | ../include/linux/kernel.h ../include/asm/segment.h 62 | file_table.o: file_table.c ../include/linux/fs.h ../include/sys/types.h 63 | inode.o: inode.c ../include/string.h ../include/sys/stat.h \ 64 | ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \ 65 | ../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \ 66 | ../include/linux/kernel.h ../include/asm/system.h 67 | ioctl.o: ioctl.c ../include/string.h ../include/errno.h \ 68 | ../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.h \ 69 | ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ 70 | ../include/signal.h 71 | namei.o: namei.c ../include/linux/sched.h ../include/linux/head.h \ 72 | ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ 73 | ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h \ 74 | ../include/string.h ../include/fcntl.h ../include/errno.h \ 75 | ../include/const.h ../include/sys/stat.h 76 | open.o: open.c ../include/string.h ../include/errno.h ../include/fcntl.h \ 77 | ../include/sys/types.h ../include/utime.h ../include/sys/stat.h \ 78 | ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ 79 | ../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \ 80 | ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h 81 | pipe.o: pipe.c ../include/signal.h ../include/sys/types.h \ 82 | ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ 83 | ../include/linux/mm.h ../include/asm/segment.h 84 | read_write.o: read_write.c ../include/sys/stat.h ../include/sys/types.h \ 85 | ../include/errno.h ../include/linux/kernel.h ../include/linux/sched.h \ 86 | ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ 87 | ../include/signal.h ../include/asm/segment.h 88 | stat.o: stat.c ../include/errno.h ../include/sys/stat.h \ 89 | ../include/sys/types.h ../include/linux/fs.h ../include/linux/sched.h \ 90 | ../include/linux/head.h ../include/linux/mm.h ../include/signal.h \ 91 | ../include/linux/kernel.h ../include/asm/segment.h 92 | super.o: super.c ../include/linux/config.h ../include/linux/sched.h \ 93 | ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ 94 | ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ 95 | ../include/asm/system.h ../include/errno.h ../include/sys/stat.h 96 | truncate.o: truncate.c ../include/linux/sched.h ../include/linux/head.h \ 97 | ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ 98 | ../include/signal.h ../include/sys/stat.h 99 | -------------------------------------------------------------------------------- /linux-0.11/fs/block_dev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/block_dev.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // 数据块写函数 - 向指定设备从给定偏移处写入指定长度字节数据 15 | // 参数: 16 | // dev - 设备号; 17 | // pos - 设备文件中偏移量指针; 18 | // buf - 用户地址空间中缓冲区地址; 19 | // count - 要传送的字节数; 20 | // 对于内核来说,写操作是向高速缓冲区中写入数据 21 | // 什么时候数据最终写入设备是由高速缓冲管理程序决定并处理的 22 | // 另外,因为设备是以块为单位进行读写的,因此对于写开始位置不处于块起始处时 23 | // 需要先将开始字节所在的整个块读出,然后将需要写的数据从写开始处填写满该块 24 | // 再将完整的一块数据写盘(即交由高速缓冲程序去处理) 25 | int block_write(int dev, long *pos, char *buf, int count) 26 | { 27 | // 由 pos 地址换算成开始读写块的块序号 block 28 | // 并求出需读第 1 字节在该块中的偏移位置 offset 29 | int block = *pos >> BLOCK_SIZE_BITS; 30 | int offset = *pos & (BLOCK_SIZE - 1); 31 | int chars; 32 | int written = 0; 33 | struct buffer_head *bh; 34 | register char *p; 35 | 36 | // 针对要写入的字节数 count,循环执行以下操作,直到全部写入 37 | while (count > 0) 38 | { 39 | // 计算在该块中可写入的字节数,如果需要写入的字节数填不满一块,则只需写 count 字节 40 | chars = BLOCK_SIZE - offset; 41 | if (chars > count) 42 | chars = count; 43 | 44 | // 如果正好要写 1 块数据,则直接申请 1 块高速缓冲块 45 | // 否则需要读入将被修改的数据块,并预读下两块数据,然后将块号递增 1 46 | if (chars == BLOCK_SIZE) 47 | bh = getblk(dev, block); 48 | else 49 | bh = breada(dev, block, block + 1, block + 2, -1); 50 | block++; 51 | 52 | // 如果缓冲块操作失败,则返回已写字节数,如果没有写入任何字节,则返回出错号(负数) 53 | if (!bh) 54 | return written ? written : -EIO; 55 | 56 | // p 指向读出数据块中开始写的位置 57 | // 若最后写入的数据不足一块,则需从块开始填写(修改)所需的字节 58 | // 因此这里需置 offset 为零 59 | p = offset + bh->b_data; 60 | offset = 0; 61 | 62 | // 将文件中偏移指针前移已写字节数 63 | // 累加已写字节数 chars 64 | // 传送计数值减去此次已传送字节数 65 | *pos += chars; 66 | written += chars; 67 | count -= chars; 68 | 69 | // 从用户缓冲区复制 chars 字节到 p 指向的高速缓冲区中开始写入的位置 70 | while (chars-- > 0) 71 | *(p++) = get_fs_byte(buf++); 72 | 73 | // 置该缓冲区块已修改标志,并释放该缓冲区(也即该缓冲区引用计数递减1) 74 | bh->b_dirt = 1; 75 | brelse(bh); 76 | } 77 | 78 | // 返回已写入的字节数,正常退出 79 | return written; 80 | } 81 | 82 | // 数据块读函数 - 从指定设备和位置读入指定字节数的数据到高速缓冲中 83 | int block_read(int dev, unsigned long *pos, char *buf, int count) 84 | { 85 | 86 | // 由 pos 地址换算成开始读写块的块序号 block 87 | // 并求出需读第 1 字节在该块中的偏移位置 offset 88 | int block = *pos >> BLOCK_SIZE_BITS; 89 | int offset = *pos & (BLOCK_SIZE - 1); 90 | int chars; 91 | int read = 0; 92 | struct buffer_head *bh; 93 | register char *p; 94 | 95 | // 针对要读入的字节数 count,循环执行以下操作,直到全部读入 96 | while (count > 0) 97 | { 98 | // 计算在该块中需读入的字节数,如果需要读入的字节数不满一块,则只需读 count 字节 99 | chars = BLOCK_SIZE - offset; 100 | if (chars > count) 101 | chars = count; 102 | 103 | // 读入需要的数据块,并预读下两块数据 104 | // 如果读操作出错,则返回已读字节数 105 | // 如果没有读入任何字节,则返回出错号,然后将块号递增 1 106 | if (!(bh = breada(dev, block, block + 1, block + 2, -1))) 107 | return read ? read : -EIO; 108 | block++; 109 | 110 | // p 指向从设备读出数据块中需要读取的开始位置 111 | // 若最后需要读取的数据不足一块,则需从块开始读取所需的字节 112 | // 因此这里需将 offset 置零 113 | p = offset + bh->b_data; 114 | offset = 0; 115 | 116 | // 将文件中偏移指针前移已读出字节数 chars 117 | // 累加已读字节数,传送计数值减去此次已传送字节数 118 | *pos += chars; 119 | read += chars; 120 | count -= chars; 121 | 122 | // 从高速缓冲区中 p 指向的开始位置 123 | // 复制 chars 字节数据到用户缓冲区,并释放该高速缓冲区 124 | while (chars-- > 0) 125 | put_fs_byte(*(p++), buf++); 126 | brelse(bh); 127 | } 128 | 129 | // 返回已读取的字节数,正常退出 130 | return read; 131 | } 132 | -------------------------------------------------------------------------------- /linux-0.11/fs/char_dev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/char_dev.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | // 终端读 17 | extern int tty_read(unsigned minor, char *buf, int count); 18 | 19 | // 终端写 20 | extern int tty_write(unsigned minor, char *buf, int count); 21 | 22 | // 定义字符设备读写函数指针类型 23 | typedef int (*crw_ptr)(int rw, unsigned minor, char *buf, int count, off_t *pos); 24 | 25 | // 串口终端读写操作函数 26 | // 参数:rw - 读写命令;minor - 终端子设备号;buf - 缓冲区;cout - 读写字节数; 27 | // pos - 读写操作当前指针,对于终端操作,该指针无用 28 | // 返回:实际读写的字节数。 29 | static int rw_ttyx(int rw, unsigned minor, char *buf, int count, off_t *pos) 30 | { 31 | return ((rw == READ) ? tty_read(minor, buf, count) : tty_write(minor, buf, count)); 32 | } 33 | 34 | // 终端读写操作函数 35 | // 同上rw_ttyx(),只是增加了对进程是否有控制终端的检测 36 | static int rw_tty(int rw, unsigned minor, char *buf, int count, off_t *pos) 37 | { 38 | // 若进程没有对应的控制终端,则返回出错号 39 | if (current->tty < 0) 40 | return -EPERM; 41 | 42 | // 否则调用终端读写函数 rw_ttyx(),并返回实际读写字节数 43 | return rw_ttyx(rw, current->tty, buf, count, pos); 44 | } 45 | 46 | // 内存数据读写,未实现 47 | static int rw_ram(int rw, char *buf, int count, off_t *pos) 48 | { 49 | return -EIO; 50 | } 51 | 52 | // 内存数据读写操作函数,未实现 53 | static int rw_mem(int rw, char *buf, int count, off_t *pos) 54 | { 55 | return -EIO; 56 | } 57 | 58 | // 内核数据区读写函数,未实现 59 | static int rw_kmem(int rw, char *buf, int count, off_t *pos) 60 | { 61 | return -EIO; 62 | } 63 | 64 | // 端口读写操作函数 65 | // 参数:rw - 读写命令;buf - 缓冲区;cout - 读写字节数;pos - 端口地址; 66 | // 返回:实际读写的字节数。 67 | static int rw_port(int rw, char *buf, int count, off_t *pos) 68 | { 69 | int i = *pos; 70 | 71 | // 对于所要求读写的字节数,并且端口地址小于 64k 时,循环执行单个字节的读写操作 72 | while (count-- > 0 && i < 65536) 73 | { 74 | // 若是读命令,则从端口 i 中读取一字节内容并放到用户缓冲区中 75 | if (rw == READ) 76 | put_fs_byte(inb(i), buf++); 77 | // 若是写命令,则从用户数据缓冲区中取一字节输出到端口 i 78 | else 79 | outb(get_fs_byte(buf++), i); 80 | 81 | // 前移一个端口 [??] 82 | i++; 83 | } 84 | 85 | // 计算读/写的字节数,并相应调整读写指针 86 | i -= *pos; 87 | *pos += i; 88 | 89 | // 返回读/写的字节数 90 | return i; 91 | } 92 | 93 | // 内存读写操作函数 94 | static int rw_memory(int rw, unsigned minor, char *buf, int count, off_t *pos) 95 | { 96 | // 根据内存设备子设备号,分别调用不同的内存读写函数 97 | switch (minor) 98 | { 99 | case 0: 100 | return rw_ram(rw, buf, count, pos); 101 | case 1: 102 | return rw_mem(rw, buf, count, pos); 103 | case 2: 104 | return rw_kmem(rw, buf, count, pos); 105 | case 3: 106 | return (rw == READ) ? 0 : count; /* rw_null */ 107 | case 4: 108 | return rw_port(rw, buf, count, pos); 109 | default: 110 | return -EIO; 111 | } 112 | } 113 | 114 | // 定义系统中设备种数 115 | #define NRDEVS ((sizeof(crw_table)) / (sizeof(crw_ptr))) 116 | 117 | // 字符设备读写函数指针表 118 | static crw_ptr crw_table[] = { 119 | NULL, // 无设备(空设备) 120 | rw_memory, // /dev/mem 等 121 | NULL, // /dev/fd 软驱 122 | NULL, // /dev/hd 硬盘 123 | rw_ttyx, // /dev/ttyx 串口终端 124 | rw_tty, // /dev/tty 终端 125 | NULL, // /dev/lp 打印机 126 | NULL}; // 未命名管道 127 | 128 | // 字符设备读写操作函数 129 | // 参数:rw - 读写命令;dev - 设备号;buf - 缓冲区;count - 读写字节数;pos -读写指针; 130 | // 返回:实际读/写字节数 131 | int rw_char(int rw, int dev, char *buf, int count, off_t *pos) 132 | { 133 | crw_ptr call_addr; 134 | 135 | // 如果设备号超出系统设备数,则返回出错码 136 | if (MAJOR(dev) >= NRDEVS) 137 | return -ENODEV; 138 | 139 | // 若该设备没有对应的读/写函数,则返回出错码 140 | if (!(call_addr = crw_table[MAJOR(dev)])) 141 | return -ENODEV; 142 | 143 | // 调用对应设备的读写操作函数,并返回实际读/写的字节数 144 | return call_addr(rw, MINOR(dev), buf, count, pos); 145 | } 146 | -------------------------------------------------------------------------------- /linux-0.11/fs/fcntl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/fcntl.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | /* #include */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | // 关闭文件系统调用 17 | extern int sys_close(int fd); 18 | 19 | // 复制文件句柄(描述符) 20 | // 参数 fd 是欲复制的文件句柄,arg 指定新文件句柄的最小数值 21 | // 返回新文件句柄或出错码 22 | static int dupfd(unsigned int fd, unsigned int arg) 23 | { 24 | // 如果文件句柄值大于一个程序最多打开文件数 NR_OPEN 25 | // 或者该句柄的文件结构不存在,则出错 26 | // 返回出错码并退出 27 | if (fd >= NR_OPEN || !current->filp[fd]) 28 | return -EBADF; 29 | 30 | // 如果指定的新句柄值 arg 大于最多打开文件数,则出错,返回出错码并退出 31 | if (arg >= NR_OPEN) 32 | return -EINVAL; 33 | 34 | // 在当前进程的文件结构指针数组中,寻找索引号大于等于 arg 但还没有使用的项 35 | while (arg < NR_OPEN) 36 | if (current->filp[arg]) 37 | arg++; 38 | else 39 | break; 40 | 41 | // 如果找到的新句柄值 arg 大于最多打开文件数,则出错,返回出错码并退出 42 | if (arg >= NR_OPEN) 43 | return -EMFILE; 44 | 45 | // 在执行时关闭标志位图中复位该句柄位,也即在运行 exec() 类函数时不关闭该句柄 46 | current->close_on_exec &= ~(1 << arg); 47 | 48 | // 令该文件结构指针等于原句柄 fd 的指针,并将文件引用计数增 1 49 | (current->filp[arg] = current->filp[fd])->f_count++; 50 | 51 | // 返回新的文件句柄 52 | return arg; 53 | } 54 | 55 | // 复制文件句柄系统调用函数 56 | // 复制指定文件句柄 oldfd,新句柄值等于 newfd。如果 newfd 已经打开,则首先关闭之 57 | int sys_dup2(unsigned int oldfd, unsigned int newfd) 58 | { 59 | // 若句柄 newfd 已经打开,则首先关闭之 60 | sys_close(newfd); 61 | return dupfd(oldfd, newfd); 62 | } 63 | 64 | // 复制文件句柄系统调用函数 65 | // 复制指定文件句柄 oldfd,新句柄的值是当前最小的未用句柄 66 | int sys_dup(unsigned int fildes) 67 | { 68 | return dupfd(fildes, 0); 69 | } 70 | 71 | // 文件控制系统调用函数 72 | // 参数 fd 是文件句柄,cmd 是操作命令 73 | int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) 74 | { 75 | struct file *filp; 76 | 77 | // 如果文件句柄值大于一个进程最多打开文件数 NR_OPEN 78 | // 或者该句柄的文件结构指针为空,则出错,返回出错码并退出 79 | if (fd >= NR_OPEN || !(filp = current->filp[fd])) 80 | return -EBADF; 81 | 82 | // 根据不同命令 cmd 进行分别处理 83 | switch (cmd) 84 | { 85 | // 复制文件句柄 86 | case F_DUPFD: 87 | return dupfd(fd, arg); 88 | 89 | // 取文件句柄的执行时关闭标志 90 | case F_GETFD: 91 | return (current->close_on_exec >> fd) & 1; 92 | 93 | // 设置句柄执行时关闭标志,arg 位 0 置位是设置,否则关闭 94 | case F_SETFD: 95 | if (arg & 1) 96 | current->close_on_exec |= (1 << fd); 97 | else 98 | current->close_on_exec &= ~(1 << fd); 99 | return 0; 100 | 101 | // 取文件状态标志和访问模式 102 | case F_GETFL: 103 | return filp->f_flags; 104 | 105 | // 设置文件状态和访问模式(根据 arg 设置添加、非阻塞标志) 106 | case F_SETFL: 107 | filp->f_flags &= ~(O_APPEND | O_NONBLOCK); 108 | filp->f_flags |= arg & (O_APPEND | O_NONBLOCK); 109 | return 0; 110 | // 以下未实现 111 | case F_GETLK: 112 | case F_SETLK: 113 | case F_SETLKW: 114 | return -1; 115 | default: 116 | return -1; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /linux-0.11/fs/file_dev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/file_dev.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // 取 a, b 中的最小值 15 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 16 | 17 | // 取 a, b 中的最大值 18 | #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 19 | 20 | // 文件读函数 - 根据 i 节点和文件结构,读设备数据 21 | // 由 i 节点可以知道设备号,由 filp 结构可以知道文件中当前读写指针位置 22 | // buf 指定用户态中缓冲区的位置,count 为需要读取的字节数 23 | // 返回值是实际读取的字节数,或出错号(小于0) 24 | int file_read(struct m_inode *inode, struct file *filp, char *buf, int count) 25 | { 26 | int left, chars, nr; 27 | struct buffer_head *bh; 28 | 29 | // 若需要读取的字节计数值小于等于零,则返回 30 | if ((left = count) <= 0) 31 | return 0; 32 | 33 | // 若还需要读取的字节数不等于 0,就循环执行以下操作,直到全部读出 34 | while (left) 35 | { 36 | // 根据 i 节点和文件表结构信息,取数据块文件当前读写位置在设备上对应的逻辑块号 nr 37 | // 若 nr 不为 0,则从 i 节点指定的设备上读取该逻辑块 38 | // 如果读操作失败则退出循环,若 nr 为 0,表示指定的数据块不存在,置缓冲块指针为 NULL 39 | if ((nr = bmap(inode, (filp->f_pos) / BLOCK_SIZE))) 40 | { 41 | if (!(bh = bread(inode->i_dev, nr))) 42 | break; 43 | } 44 | else 45 | bh = NULL; 46 | 47 | // 计算文件读写指针在数据块中的偏移值 nr,则该块中可读字节数为(BLOCK_SIZE-nr) 48 | // 然后与还需读取的字节数 left 作比较,其中小值即为本次需读的字节数 chars 49 | // 若 (BLOCK_SIZE-nr) 大则说明该块是需要读取的最后一块数据,反之则还需要读取一块数据 50 | nr = filp->f_pos % BLOCK_SIZE; 51 | chars = MIN(BLOCK_SIZE - nr, left); 52 | 53 | // 调整读写文件指针,指针前移此次将读取的字节数 chars,剩余字节计数相应减去 chars 54 | filp->f_pos += chars; 55 | left -= chars; 56 | 57 | // 若从设备上读到了数据,则将 p 指向读出数据块缓冲区中开始读取的位置 58 | // 并且复制 chars 字节到用户缓冲区 buf 中 59 | // 否则往用户缓冲区中填入 chars 个 0 值字节 60 | if (bh) 61 | { 62 | char *p = nr + bh->b_data; 63 | while (chars-- > 0) 64 | put_fs_byte(*(p++), buf++); 65 | brelse(bh); 66 | } 67 | else 68 | { 69 | while (chars-- > 0) 70 | put_fs_byte(0, buf++); 71 | } 72 | } 73 | 74 | // 修改该 i 节点的访问时间为当前时间 75 | // 返回读取的字节数,若读取字节数为 0,则返回出错号 76 | inode->i_atime = CURRENT_TIME; 77 | return (count - left) ? (count - left) : -ERROR; 78 | } 79 | 80 | // 文件写函数 - 根据 i 节点和文件结构信息,将用户数据写入指定设备 81 | // 由 i 节点可以知道设备号,由 filp 结构可以知道文件中当前读写指针位置 82 | // buf 指定用户态中缓冲区的位置,count 为需要写入的字节数 83 | // 返回值是实际写入的字节数,或出错号(小于0) 84 | int file_write(struct m_inode *inode, struct file *filp, char *buf, int count) 85 | { 86 | off_t pos; 87 | int block, c; 88 | struct buffer_head *bh; 89 | char *p; 90 | int i = 0; 91 | 92 | // ok,当许多进程同时写时,append 操作可能不行,但那又怎样 93 | // 不管怎样那样做会导致混乱一团 94 | 95 | // 如果是要向文件后添加数据,则将文件读写指针移到文件尾部,否则就将在文件读写指针处写入 96 | if (filp->f_flags & O_APPEND) 97 | pos = inode->i_size; 98 | else 99 | pos = filp->f_pos; 100 | 101 | // 若已写入字节数 i 小于需要写入的字节数 count,则循环执行以下操作 102 | while (i < count) 103 | { 104 | // 创建数据块号(pos/BLOCK_SIZE) 在设备上对应的逻辑块,并返回在设备上的逻辑块号 105 | // 如果逻辑块号=0,则表示创建失败,退出循环s 106 | if (!(block = create_block(inode, pos / BLOCK_SIZE))) 107 | break; 108 | 109 | // 根据该逻辑块号读取设备上的相应数据块,若出错则退出循环 110 | if (!(bh = bread(inode->i_dev, block))) 111 | break; 112 | 113 | // 求出文件读写指针在数据块中的偏移值 c 114 | // 将 p 指向读出数据块缓冲区中开始读取的位置 115 | // 置该缓冲区已修改标志 116 | c = pos % BLOCK_SIZE; 117 | p = c + bh->b_data; 118 | bh->b_dirt = 1; 119 | 120 | // 从开始读写位置到块末共可写入 c=(BLOCK_SIZE-c) 个字节 121 | // 若 c 大于剩余还需写入的字节数 (count-i) 122 | // 则此次只需再写入 c=(count-i) 即可 123 | c = BLOCK_SIZE - c; 124 | if (c > count - i) 125 | c = count - i; 126 | 127 | // 文件读写指针前移此次需写入的字节数 128 | // 如果当前文件读写指针位置值超过了文件的大小 129 | // 则修改 i 节点中文件大小字段,并置 i 节点已修改标志 130 | pos += c; 131 | if (pos > inode->i_size) 132 | { 133 | inode->i_size = pos; 134 | inode->i_dirt = 1; 135 | } 136 | 137 | // 已写入字节计数累加此次写入的字节数 c 138 | // 从用户缓冲区 buf 中复制 c 个字节到高速缓冲区中 p 指向开始的位置处,然后释放该缓冲区 139 | i += c; 140 | while (c-- > 0) 141 | *(p++) = get_fs_byte(buf++); 142 | brelse(bh); 143 | } 144 | 145 | // 更改文件修改时间为当前时间 146 | inode->i_mtime = CURRENT_TIME; 147 | 148 | // 如果此次操作不是在文件尾添加数据 149 | // 则把文件读写指针调整到当前读写位置 150 | // 并更改 i 节点修改时间为当前时间 151 | if (!(filp->f_flags & O_APPEND)) 152 | { 153 | filp->f_pos = pos; 154 | inode->i_ctime = CURRENT_TIME; 155 | } 156 | 157 | // 返回写入的字节数,若写入字节数为 0,则返回出错号-1 158 | return (i ? i : -1); 159 | } 160 | -------------------------------------------------------------------------------- /linux-0.11/fs/file_table.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/file_table.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #include 8 | 9 | // 文件表数组 10 | struct file file_table[NR_FILE]; 11 | -------------------------------------------------------------------------------- /linux-0.11/fs/ioctl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/ioctl.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | /* #include */ 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | // 终端 ioctl 14 | extern int tty_ioctl(int dev, int cmd, int arg); 15 | 16 | // 定义输入输出控制(ioctl)函数指针 17 | typedef int (*ioctl_ptr)(int dev, int cmd, int arg); 18 | 19 | // 定义系统中设备种数 20 | #define NRDEVS ((sizeof(ioctl_table)) / (sizeof(ioctl_ptr))) 21 | 22 | // ioctl 操作函数指针表 23 | static ioctl_ptr ioctl_table[] = { 24 | NULL, /* nodev */ 25 | NULL, /* /dev/mem */ 26 | NULL, /* /dev/fd */ 27 | NULL, /* /dev/hd */ 28 | tty_ioctl, /* /dev/ttyx */ 29 | tty_ioctl, /* /dev/tty */ 30 | NULL, /* /dev/lp */ 31 | NULL}; /* named pipes */ 32 | 33 | // 系统调用函数 - 输入输出控制函数 34 | // 参数:fd - 文件描述符;cmd - 命令码;arg - 参数; 35 | // 返回:成功则返回0,否则返回出错码 36 | int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) 37 | { 38 | struct file *filp; 39 | int dev, mode; 40 | 41 | // 如果文件描述符超出可打开的文件数,或者对应描述符的文件结构指针为空,则返回出错码,退出 42 | if (fd >= NR_OPEN || !(filp = current->filp[fd])) 43 | return -EBADF; 44 | 45 | // 取对应文件的属性,如果该文件不是字符文件,也不是块设备文件,则返回出错码,退出 46 | mode = filp->f_inode->i_mode; 47 | if (!S_ISCHR(mode) && !S_ISBLK(mode)) 48 | return -EINVAL; 49 | 50 | // 从字符或块设备文件的 i 节点中取设备号,如果设备号大于系统现有的设备数,则返回出错号 51 | dev = filp->f_inode->i_zone[0]; 52 | if (MAJOR(dev) >= NRDEVS) 53 | return -ENODEV; 54 | 55 | // 如果该设备在 ioctl 函数指针表中没有对应函数,则返回出错码 56 | if (!ioctl_table[MAJOR(dev)]) 57 | return -ENOTTY; 58 | 59 | // 否则返回实际 ioctl 函数返回码,成功则返回0,否则返回出错码 60 | return ioctl_table[MAJOR(dev)](dev, cmd, arg); 61 | } 62 | -------------------------------------------------------------------------------- /linux-0.11/fs/pipe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/pipe.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | #include /* for get_free_page */ 11 | #include 12 | 13 | // 管道读操作函数 14 | // 参数 inode 是管道对应的 i 节点,buf 是数据缓冲区指针,count 是读取的字节数 15 | int read_pipe(struct m_inode *inode, char *buf, int count) 16 | { 17 | int chars, size, read = 0; 18 | 19 | // 若欲读取的字节计数值 count 大于 0,则循环执行以下操作 20 | while (count > 0) 21 | { 22 | // 若当前管道中没有数据(size=0),则唤醒等待该节点的进程 23 | // 如果已没有写管道者,则返回已读字节数,退出 24 | // 否则在该 i 节点上睡眠,等待信息 25 | while (!(size = PIPE_SIZE(*inode))) 26 | { 27 | wake_up(&inode->i_wait); 28 | if (inode->i_count != 2) /* are there any writers? */ 29 | return read; 30 | sleep_on(&inode->i_wait); 31 | } 32 | 33 | // 取管道尾到缓冲区末端的字节数 chars 34 | // 如果其大于还需要读取的字节数 count,则令其等于 count 35 | // 如果 chars 大于当前管道中含有数据的长度 size,则令其等于 size 36 | chars = PAGE_SIZE - PIPE_TAIL(*inode); 37 | if (chars > count) 38 | chars = count; 39 | if (chars > size) 40 | chars = size; 41 | 42 | // 读字节计数减去此次可读的字节数 chars,并累加已读字节数 43 | count -= chars; 44 | read += chars; 45 | 46 | // 令 size 指向管道尾部,调整当前管道尾指针(前移 chars 字节) 47 | size = PIPE_TAIL(*inode); 48 | PIPE_TAIL(*inode) += chars; 49 | PIPE_TAIL(*inode) &= (PAGE_SIZE - 1); 50 | 51 | // 将管道中的数据复制到用户缓冲区中 52 | // 对于管道 i 节点,其 i_size 字段中是管道缓冲块指针 53 | while (chars-- > 0) 54 | put_fs_byte(((char *)inode->i_size)[size++], buf++); 55 | } 56 | 57 | // 唤醒等待该管道 i 节点的进程,并返回读取的字节数 58 | wake_up(&inode->i_wait); 59 | return read; 60 | } 61 | 62 | // 管道写操作函数 63 | // 参数 inode 是管道对应的 i 节点,buf 是数据缓冲区指针,count 是将写入管道的字节数 64 | int write_pipe(struct m_inode *inode, char *buf, int count) 65 | { 66 | int chars, size, written = 0; 67 | 68 | // 若将写入的字节计数值 count 还大于 0,则循环执行以下操作 69 | while (count > 0) 70 | { 71 | while (!(size = (PAGE_SIZE - 1) - PIPE_SIZE(*inode))) 72 | { 73 | // 若当前管道中没有已经满了(size=0),则唤醒等待该节点的进程 74 | // 如果已没有读管道者,则向进程发送 SIGPIPE 信号 75 | // 并返回已写入的字节数并退出 76 | // 若写入 0 字节,则返回 -1 77 | // 否则在该 i 节点上睡眠,等待管道腾出空间 78 | wake_up(&inode->i_wait); 79 | if (inode->i_count != 2) 80 | { /* no readers */ 81 | current->signal |= (1 << (SIGPIPE - 1)); 82 | return written ? written : -1; 83 | } 84 | sleep_on(&inode->i_wait); 85 | } 86 | 87 | // 取管道头部到缓冲区末端空间字节数 chars 88 | // 如果其大于还需要写入的字节数 count,则令其等于 count 89 | // 如果 chars 大于当前管道中空闲空间长度 size,则令其等于 size 90 | chars = PAGE_SIZE - PIPE_HEAD(*inode); 91 | if (chars > count) 92 | chars = count; 93 | if (chars > size) 94 | chars = size; 95 | 96 | // 写入字节计数减去此次可写入的字节数 chars,并累加已写字节数到 written 97 | count -= chars; 98 | written += chars; 99 | 100 | // 令 size 指向管道数据头部,调整当前管道数据头部指针(前移 chars 字节) 101 | size = PIPE_HEAD(*inode); 102 | PIPE_HEAD(*inode) += chars; 103 | PIPE_HEAD(*inode) &= (PAGE_SIZE - 1); 104 | 105 | // 从用户缓冲区复制 chars 个字节到管道中 106 | // 对于管道 i 节点,其 i_size 字段中是管道缓冲块指针 107 | while (chars-- > 0) 108 | ((char *)inode->i_size)[size++] = get_fs_byte(buf++); 109 | } 110 | 111 | // 唤醒等待该 i 节点的进程,返回已写入的字节数,退出 112 | wake_up(&inode->i_wait); 113 | return written; 114 | } 115 | 116 | // 创建管道系统调用函数 117 | // 在 fildes 所指的数组中创建一对文件句柄(描述符) 118 | // 这对文件句柄指向一管道 i 节点 119 | // fildes[0] 用于读管道中数据 120 | // fildes[1] 用于向管道中写入数据 121 | // 成功时返回 0,出错时返回 -1 122 | int sys_pipe(unsigned long *fildes) 123 | { 124 | struct m_inode *inode; 125 | struct file *f[2]; 126 | int fd[2]; 127 | int i, j; 128 | 129 | // 从系统文件表中取两个空闲项(引用计数字段为 0 的项),并分别设置引用计数为 1 130 | j = 0; 131 | for (i = 0; j < 2 && i < NR_FILE; i++) 132 | if (!file_table[i].f_count) 133 | (f[j++] = i + file_table)->f_count++; 134 | 135 | // 如果只有一个空闲项,则释放该项(引用计数复位) 136 | if (j == 1) 137 | f[0]->f_count = 0; 138 | 139 | // 如果没有找到两个空闲项,则返回 -1 140 | if (j < 2) 141 | return -1; 142 | 143 | // 针对上面取得的两个文件结构项,分别分配一文件句柄 144 | // 并使进程的文件结构指针分别指向这两个文件结构 145 | j = 0; 146 | for (i = 0; j < 2 && i < NR_OPEN; i++) 147 | if (!current->filp[i]) 148 | { 149 | current->filp[fd[j] = i] = f[j]; 150 | j++; 151 | } 152 | 153 | // 如果只有一个空闲文件句柄,则释放该句柄 154 | if (j == 1) 155 | current->filp[fd[0]] = NULL; 156 | 157 | // 如果没有找到两个空闲句柄,则释放上面获取的两个文件结构项(复位引用计数值),并返回 -1 158 | if (j < 2) 159 | { 160 | f[0]->f_count = f[1]->f_count = 0; 161 | return -1; 162 | } 163 | 164 | // 申请管道 i 节点,并为管道分配缓冲区(1 页内存) 165 | // 如果不成功,则相应释放两个文件句柄和文件结构项,并返回 -1 166 | if (!(inode = get_pipe_inode())) 167 | { 168 | current->filp[fd[0]] = 169 | current->filp[fd[1]] = NULL; 170 | f[0]->f_count = f[1]->f_count = 0; 171 | return -1; 172 | } 173 | 174 | // 初始化两个文件结构,都指向同一个 i 节点,读写指针都置零 175 | // 第 1 个文件结构的文件模式置为读 176 | // 第 2 个文件结构的文件模式置为写 177 | f[0]->f_inode = f[1]->f_inode = inode; 178 | f[0]->f_pos = f[1]->f_pos = 0; 179 | f[0]->f_mode = 1; /* read */ 180 | f[1]->f_mode = 2; /* write */ 181 | 182 | // 将文件句柄数组复制到对应的用户数组中,并返回 0,退出 183 | put_fs_long(fd[0], 0 + fildes); 184 | put_fs_long(fd[1], 1 + fildes); 185 | return 0; 186 | } 187 | -------------------------------------------------------------------------------- /linux-0.11/fs/read_write.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/read_write.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | // 字符设备读写函数 16 | extern int rw_char(int rw, int dev, char *buf, int count, off_t *pos); 17 | 18 | // 读管道操作函数 19 | extern int read_pipe(struct m_inode *inode, char *buf, int count); 20 | 21 | // 写管道操作函数 22 | extern int write_pipe(struct m_inode *inode, char *buf, int count); 23 | 24 | // 块设备读操作函数 25 | extern int block_read(int dev, off_t *pos, char *buf, int count); 26 | 27 | // 块设备写操作函数 28 | extern int block_write(int dev, off_t *pos, char *buf, int count); 29 | 30 | // 读文件操作函数 31 | extern int file_read(struct m_inode *inode, struct file *filp, 32 | char *buf, int count); 33 | 34 | // 写文件操作函数 35 | extern int file_write(struct m_inode *inode, struct file *filp, 36 | char *buf, int count); 37 | 38 | // 重定位文件读写指针系统调用函数 39 | // 参数 fd 是文件句柄,offset 是新的文件读写指针偏移值 40 | // origin 是偏移的起始位置,是下列三者之一 41 | // SEEK_SET (0,从文件开始处) 42 | // SEEK_CUR (1,从当前读写位置) 43 | // SEEK_END (2,从文件尾处) 44 | int sys_lseek(unsigned int fd, off_t offset, int origin) 45 | { 46 | struct file *file; 47 | int tmp; 48 | 49 | // 如果文件句柄值大于程序最多打开文件数 NR_OPEN(20) 50 | // 或者该句柄的文件结构指针为空,或者对应文件结构的 i 节点字段为空 51 | // 或者指定设备文件指针是不可定位的,则返回出错码并退出 52 | if (fd >= NR_OPEN || 53 | !(file = current->filp[fd]) || 54 | !(file->f_inode) || 55 | !IS_SEEKABLE(MAJOR(file->f_inode->i_dev))) 56 | return -EBADF; 57 | 58 | // 如果文件对应的 i 节点是管道节点,则返回出错码,退出;管道头尾指针不可随意移动! 59 | if (file->f_inode->i_pipe) 60 | return -ESPIPE; 61 | 62 | // 根据设置的定位标志,分别重新定位文件读写指针 63 | switch (origin) 64 | { 65 | // origin = SEEK_SET,要求以文件起始处作为原点设置文件读写指针 66 | // 若偏移值小于零,则出错返回错误码 67 | // 否则设置文件读写指针等于 offset 68 | case 0: 69 | if (offset < 0) 70 | return -EINVAL; 71 | file->f_pos = offset; 72 | break; 73 | 74 | // origin = SEEK_CUR,要求以文件当前读写指针处作为原点重定位读写指针 75 | // 如果文件当前指针加上偏移值小于0,则返回出错码退出 76 | // 否则在当前读写指针上加上偏移值 77 | case 1: 78 | if (file->f_pos + offset < 0) 79 | return -EINVAL; 80 | file->f_pos += offset; 81 | break; 82 | 83 | // origin = SEEK_END,要求以文件末尾作为原点重定位读写指针 84 | // 此时若文件大小加上偏移值小于零则返回出错码退出 85 | // 否则重定位读写指针为文件长度加上偏移值 86 | case 2: 87 | if ((tmp = file->f_inode->i_size + offset) < 0) 88 | return -EINVAL; 89 | file->f_pos = tmp; 90 | break; 91 | 92 | // origin 设置出错,返回出错码退出 93 | default: 94 | return -EINVAL; 95 | } 96 | 97 | // 返回重定位后的文件读写指针值 98 | return file->f_pos; 99 | } 100 | 101 | // 读文件系统调用函数 102 | // 参数 fd 是文件句柄,buf 是缓冲区,count 是欲读字节数 103 | int sys_read(unsigned int fd, char *buf, int count) 104 | { 105 | struct file *file; 106 | struct m_inode *inode; 107 | 108 | // 如果文件句柄值大于程序最多打开文件数 NR_OPEN 109 | // 或者需要读取的字节计数值小于 0 110 | // 或者该句柄的文件结构指针为空,则返回出错码并退出 111 | if (fd >= NR_OPEN || count < 0 || !(file = current->filp[fd])) 112 | return -EINVAL; 113 | 114 | // 若需读取的字节数 count 等于 0,则返回 0,退出 115 | if (!count) 116 | return 0; 117 | 118 | // 验证存放数据的缓冲区内存限制 119 | verify_area(buf, count); 120 | 121 | // 取文件对应的 i 节点 122 | // 若是管道文件,并且是读管道文件模式,则进行读管道操作 123 | // 若成功则返回读取的字节数,否则返回出错码,退出 124 | inode = file->f_inode; 125 | if (inode->i_pipe) 126 | return (file->f_mode & 1) ? read_pipe(inode, buf, count) : -EIO; 127 | 128 | // 如果是字符型文件,则进行读字符设备操作,返回读取的字符数 129 | if (S_ISCHR(inode->i_mode)) 130 | return rw_char(READ, inode->i_zone[0], buf, count, &file->f_pos); 131 | 132 | // 如果是块设备文件,则执行块设备读操作,并返回读取的字节数 133 | if (S_ISBLK(inode->i_mode)) 134 | return block_read(inode->i_zone[0], &file->f_pos, buf, count); 135 | 136 | // 如果是目录文件或者是常规文件,则首先验证读取数 count 的有效性并进行调整 137 | // 若读取字节数加上文件当前读写指针值大于文件大小 138 | // 则重新设置读取字节数为文件长度-当前读写指针值 139 | // 若读取数等于 0,则返回 0,退出 140 | // 然后执行文件读操作,返回读取的字节数并退出 141 | if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) 142 | { 143 | if (count + file->f_pos > inode->i_size) 144 | count = inode->i_size - file->f_pos; 145 | if (count <= 0) 146 | return 0; 147 | return file_read(inode, file, buf, count); 148 | } 149 | 150 | // 否则打印节点文件属性,并返回出错码退出 151 | printk("(Read)inode->i_mode=%06o\n\r", inode->i_mode); 152 | return -EINVAL; 153 | } 154 | 155 | // 写文件系统调用函数 156 | // 参数 fd 是文件句柄,buf 是缓冲区,count 是欲写字节数 157 | int sys_write(unsigned int fd, char *buf, int count) 158 | { 159 | struct file *file; 160 | struct m_inode *inode; 161 | 162 | // 如果文件句柄值大于程序最多打开文件数 NR_OPEN 163 | // 或者需要写入的字节计数小于0,或者该句柄的文件结构指针为空,则返回出错码并退出 164 | if (fd >= NR_OPEN || count < 0 || !(file = current->filp[fd])) 165 | return -EINVAL; 166 | 167 | // 若需读取的字节数 count 等于0,则返回 0,退出 168 | if (!count) 169 | return 0; 170 | 171 | // 取文件对应的 i 节点 172 | // 若是管道文件,并且是写管道文件模式,则进行写管道操作 173 | // 若成功则返回写入的字节数,否则返回出错码,退出 174 | inode = file->f_inode; 175 | if (inode->i_pipe) 176 | return (file->f_mode & 2) ? write_pipe(inode, buf, count) : -EIO; 177 | 178 | // 如果是字符型文件,则进行写字符设备操作,返回写入的字符数,退出 179 | if (S_ISCHR(inode->i_mode)) 180 | return rw_char(WRITE, inode->i_zone[0], buf, count, &file->f_pos); 181 | 182 | // 如果是块设备文件,则进行块设备写操作,并返回写入的字节数,退出 183 | if (S_ISBLK(inode->i_mode)) 184 | return block_write(inode->i_zone[0], &file->f_pos, buf, count); 185 | 186 | // 若是常规文件,则执行文件写操作,并返回写入的字节数,退出 187 | if (S_ISREG(inode->i_mode)) 188 | return file_write(inode, file, buf, count); 189 | 190 | // 否则,显示对应节点的文件模式,返回出错码,退出 191 | printk("(Write)inode->i_mode=%06o\n\r", inode->i_mode); 192 | return -EINVAL; 193 | } 194 | -------------------------------------------------------------------------------- /linux-0.11/fs/stat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/stat.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // 复制文件状态信息 16 | // 参数 inode 是文件对应的 i 节点,statbuf 是 stat 文件状态结构指针,用于存放取得的状态信息 17 | static void cp_stat(struct m_inode *inode, struct stat *statbuf) 18 | { 19 | struct stat tmp; 20 | int i; 21 | 22 | // 首先验证(或分配)存放数据的内存空间 23 | verify_area(statbuf, sizeof(*statbuf)); 24 | 25 | // 然后临时复制相应节点上的信息 26 | tmp.st_dev = inode->i_dev; // 文件所在的设备号 27 | tmp.st_ino = inode->i_num; // 文件 i 节点号 28 | tmp.st_mode = inode->i_mode; // 文件属性 29 | tmp.st_nlink = inode->i_nlinks; // 文件的连接数 30 | tmp.st_uid = inode->i_uid; // 文件的用户 id 31 | tmp.st_gid = inode->i_gid; // 文件的组 id 32 | tmp.st_rdev = inode->i_zone[0]; // 设备号(如果文件是特殊的字符文件或块文件) 33 | tmp.st_size = inode->i_size; // 文件大小(字节数)(如果文件是常规文件) 34 | tmp.st_atime = inode->i_atime; // 最后访问时间 35 | tmp.st_mtime = inode->i_mtime; // 最后修改时间 36 | tmp.st_ctime = inode->i_ctime; // 最后节点修改时间 37 | 38 | // 最后将这些状态信息复制到用户缓冲区中 39 | for (i = 0; i < sizeof(tmp); i++) 40 | put_fs_byte(((char *)&tmp)[i], &((char *)statbuf)[i]); 41 | } 42 | 43 | // 文件状态系统调用函数 - 根据文件名获取文件状态信息 44 | // 参数 filename 是指定的文件名,statbuf 是存放状态信息的缓冲区指针 45 | // 返回 0,若出错则返回出错码 46 | int sys_stat(char *filename, struct stat *statbuf) 47 | { 48 | struct m_inode *inode; 49 | 50 | // 首先根据文件名找出对应的 i 节点,若出错则返回错误码 51 | if (!(inode = namei(filename))) 52 | return -ENOENT; 53 | 54 | // 将 i 节点上的文件状态信息复制到用户缓冲区中,并释放该 i 节点 55 | cp_stat(inode, statbuf); 56 | iput(inode); 57 | return 0; 58 | } 59 | 60 | // 文件状态系统调用 - 根据文件句柄获取文件状态信息 61 | // 参数 fd 是指定文件的句柄(描述符),statbuf 是存放状态信息的缓冲区指针 62 | // 返回 0,若出错则返回出错码 63 | int sys_fstat(unsigned int fd, struct stat *statbuf) 64 | { 65 | struct file *f; 66 | struct m_inode *inode; 67 | 68 | // 如果文件句柄值大于一个程序最多打开文件数 NR_OPEN 69 | // 或者该句柄的文件结构指针为空,或者对应文件结构的 i 节点字段为空,则出错,返回出错码并退出 70 | if (fd >= NR_OPEN || !(f = current->filp[fd]) || !(inode = f->f_inode)) 71 | return -EBADF; 72 | 73 | // 将 i 节点上的文件状态信息复制到用户缓冲区中 74 | cp_stat(inode, statbuf); 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /linux-0.11/fs/truncate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/fs/truncate.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | 11 | // 释放一次间接块 12 | static void free_ind(int dev, int block) 13 | { 14 | struct buffer_head *bh; 15 | unsigned short *p; 16 | int i; 17 | 18 | // 如果逻辑块号为 0,则返回 19 | if (!block) 20 | return; 21 | 22 | // 读取一次间接块,并释放其上表明使用的所有逻辑块,然后释放该一次间接块的缓冲区 23 | if ((bh = bread(dev, block))) 24 | { 25 | // 指向数据缓冲区 26 | p = (unsigned short *)bh->b_data; 27 | 28 | // 每个逻辑块上可有 512 个块号 29 | for (i = 0; i < 512; i++, p++) 30 | if (*p) 31 | // 释放指定的逻辑块 32 | free_block(dev, *p); 33 | 34 | // 释放缓冲区 35 | brelse(bh); 36 | } 37 | 38 | // 释放设备上的一次间接块 39 | free_block(dev, block); 40 | } 41 | 42 | // 释放二次间接块 43 | static void free_dind(int dev, int block) 44 | { 45 | struct buffer_head *bh; 46 | unsigned short *p; 47 | int i; 48 | 49 | // 如果逻辑块号为 0,则返回 50 | if (!block) 51 | return; 52 | 53 | // 读取二次间接块的一级块,并释放其上表明使用的所有逻辑块,然后释放该一级块的缓冲区 54 | if ((bh = bread(dev, block))) 55 | { 56 | // 指向数据缓冲区 57 | p = (unsigned short *)bh->b_data; 58 | 59 | // 每个逻辑块上可连接 512 个二级块 60 | for (i = 0; i < 512; i++, p++) 61 | if (*p) 62 | // 释放所有一次间接块 63 | free_ind(dev, *p); 64 | 65 | // 释放缓冲区 66 | brelse(bh); 67 | } 68 | 69 | // 最后释放设备上的二次间接块 70 | free_block(dev, block); 71 | } 72 | 73 | // 将节点对应的文件长度截为 0,并释放占用的设备空间 74 | void truncate(struct m_inode *inode) 75 | { 76 | int i; 77 | 78 | // 如果不是常规文件或者是目录文件,则返回 79 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) 80 | return; 81 | 82 | // 释放 i 节点的 7 个直接逻辑块,并将这 7 个逻辑块项全置零 83 | for (i = 0; i < 7; i++) 84 | // 如果块号不为 0,则释放之 85 | if (inode->i_zone[i]) 86 | { 87 | free_block(inode->i_dev, inode->i_zone[i]); 88 | inode->i_zone[i] = 0; 89 | } 90 | 91 | // 释放一次间接块 92 | free_ind(inode->i_dev, inode->i_zone[7]); 93 | 94 | // 释放二次间接块 95 | free_dind(inode->i_dev, inode->i_zone[8]); 96 | 97 | // 逻辑块项 7、8 置零 98 | inode->i_zone[7] = inode->i_zone[8] = 0; 99 | 100 | // 文件大小置零 101 | inode->i_size = 0; 102 | 103 | // 置节点已修改标志 104 | inode->i_dirt = 1; 105 | 106 | // 重置文件和节点修改时间为当前时间 107 | inode->i_mtime = inode->i_ctime = CURRENT_TIME; 108 | } 109 | -------------------------------------------------------------------------------- /linux-0.11/include/a.out.h: -------------------------------------------------------------------------------- 1 | #ifndef _A_OUT_H 2 | #define _A_OUT_H 3 | 4 | #define __GNU_EXEC_MACROS__ 5 | 6 | // 执行文件头结构 7 | struct exec 8 | { 9 | unsigned long a_magic; // 执行文件魔数。使用 N_MAGIC 等宏访问 10 | unsigned a_text; // 代码长度,字节数 11 | unsigned a_data; // 数据长度,字节数 12 | unsigned a_bss; // 文件中的未初始化数据区长度,字节数 13 | unsigned a_syms; // 文件中的符号表长度,字节数 14 | unsigned a_entry; // 执行开始地址 15 | unsigned a_trsize; // 代码重定位信息长度,字节数 16 | unsigned a_drsize; // 数据重定位信息长度,字节数 17 | }; 18 | 19 | // 用于取执行结构中的魔数 20 | #ifndef N_MAGIC 21 | #define N_MAGIC(exec) ((exec).a_magic) 22 | #endif 23 | 24 | #ifndef OMAGIC 25 | 26 | // 指明为目标文件或者不纯的可执行文件的代号 27 | #define OMAGIC 0407 28 | /* Code indicating pure executable. */ 29 | 30 | // 历史上,最早在 PDP-11 计算机上,魔数(幻数)是八进制数 0407,它位于执行程序头结构的开始处 31 | // 原本是 PDP-11 的一条跳转指令,表示跳转到随后 7 个字后的代码开始处 32 | // 这样,加载程序(loader) 就可以在把执行文件放入内存后,直接跳转到指令开始处运行 33 | // 现在已经没有程序使用这种方法了,但这个八进制数却作为识别文件类型的标志(魔数)保留了下来 34 | // OMAGIC 可以认为是 Old Magic 的意思 35 | 36 | // 指明为纯可执行文件的代号 37 | // New Magic, 1975 年以后开始使用,涉及虚存机制 38 | #define NMAGIC 0410 39 | 40 | // 指明为需要分页处理的可执行文件 41 | #define ZMAGIC 0413 42 | #endif /* not OMAGIC */ 43 | 44 | // 另外还有一个 QMAGIC,是为了节约磁盘容量,把盘上执行文件的头结构与代码紧凑存放 45 | // 如果魔数不能被识别,则返回真 46 | 47 | #ifndef N_BADMAG 48 | #define N_BADMAG(x) \ 49 | (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC && N_MAGIC(x) != ZMAGIC) 50 | #endif 51 | 52 | #define _N_BADMAG(x) \ 53 | (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC && N_MAGIC(x) != ZMAGIC) 54 | 55 | // 程序头在内存中的偏移位置 56 | #define _N_HDROFF(x) (SEGMENT_SIZE - sizeof(struct exec)) 57 | 58 | // 代码起始偏移值 59 | #ifndef N_TXTOFF 60 | #define N_TXTOFF(x) \ 61 | (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof(struct exec) : sizeof(struct exec)) 62 | #endif 63 | 64 | // 数据起始偏移值 65 | #ifndef N_DATOFF 66 | #define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text) 67 | #endif 68 | 69 | // 代码重定位信息偏移值 70 | #ifndef N_TRELOFF 71 | #define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data) 72 | #endif 73 | 74 | // 数据重定位信息偏移值 75 | #ifndef N_DRELOFF 76 | #define N_DRELOFF(x) (N_TRELOFF(x) + (x).a_trsize) 77 | #endif 78 | 79 | // 符号表偏移值 80 | #ifndef N_SYMOFF 81 | #define N_SYMOFF(x) (N_DRELOFF(x) + (x).a_drsize) 82 | #endif 83 | 84 | // 字符串信息偏移值 85 | #ifndef N_STROFF 86 | #define N_STROFF(x) (N_SYMOFF(x) + (x).a_syms) 87 | #endif 88 | 89 | // 代码段加载到内存中后的地址 90 | #ifndef N_TXTADDR 91 | #define N_TXTADDR(x) 0 92 | #endif 93 | 94 | // 数据段加载到内存中后的地址 95 | // 注意,对于下面没有列出名称的机器,需要你自己来定义对应的 SEGMENT_SIZE 96 | #if defined(vax) || defined(hp300) || defined(pyr) 97 | #define SEGMENT_SIZE PAGE_SIZE 98 | #endif 99 | #ifdef hp300 100 | #define PAGE_SIZE 4096 101 | #endif 102 | #ifdef sony 103 | #define SEGMENT_SIZE 0x2000 104 | #endif /* Sony. */ 105 | #ifdef is68k 106 | #define SEGMENT_SIZE 0x20000 107 | #endif 108 | #if defined(m68k) && defined(PORTAR) 109 | #define PAGE_SIZE 0x400 110 | #define SEGMENT_SIZE PAGE_SIZE 111 | #endif 112 | 113 | #define PAGE_SIZE 4096 114 | #define SEGMENT_SIZE 1024 115 | 116 | // 以段为界的大小 117 | #define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1)) 118 | 119 | // 代码段尾地址 120 | #define _N_TXTENDADDR(x) (N_TXTADDR(x) + (x).a_text) 121 | 122 | // 数据开始地址 123 | #ifndef N_DATADDR 124 | #define N_DATADDR(x) \ 125 | (N_MAGIC(x) == OMAGIC ? (_N_TXTENDADDR(x)) \ 126 | : (_N_SEGMENT_ROUND(_N_TXTENDADDR(x)))) 127 | #endif 128 | 129 | // bss 段加载到内存以后的地址 130 | #ifndef N_BSSADDR 131 | #define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data) 132 | #endif 133 | 134 | // nlist 结构。符号表记录结构 135 | #ifndef N_NLIST_DECLARED 136 | struct nlist 137 | { 138 | union 139 | { 140 | char *n_name; 141 | struct nlist *n_next; 142 | long n_strx; 143 | } n_un; 144 | unsigned char n_type; 145 | char n_other; 146 | short n_desc; 147 | unsigned long n_value; 148 | }; 149 | #endif 150 | 151 | // 下面定义 exec 结构中的变量偏移值 152 | #ifndef N_UNDF 153 | #define N_UNDF 0 154 | #endif 155 | #ifndef N_ABS 156 | #define N_ABS 2 157 | #endif 158 | #ifndef N_TEXT 159 | #define N_TEXT 4 160 | #endif 161 | #ifndef N_DATA 162 | #define N_DATA 6 163 | #endif 164 | #ifndef N_BSS 165 | #define N_BSS 8 166 | #endif 167 | #ifndef N_COMM 168 | #define N_COMM 18 169 | #endif 170 | #ifndef N_FN 171 | #define N_FN 15 172 | #endif 173 | 174 | // 以下 3 个常量定义是 nlist 结构中 n_type 变量的屏蔽码 175 | #ifndef N_EXT 176 | #define N_EXT 1 177 | #endif 178 | #ifndef N_TYPE 179 | #define N_TYPE 036 180 | #endif 181 | #ifndef N_STAB 182 | #define N_STAB 0340 183 | #endif 184 | 185 | // 下面的类型指明了符号的定义作为对另一个符号的间接引用 186 | // 紧接该符号的其它的符号呈现为未定义的引用间接性是不对称的 187 | // 其它符号的值将被用于满足间接符号的请求,但反之不然 188 | // 如果其它符号并没有定义,则将搜索库来寻找一个定义 189 | #define N_INDR 0xa 190 | 191 | // 下面的符号与集合元素有关,所有具有相同名称 N_SET[ATDB] 的符号形成一个集合 192 | // 在代码部分中已为集合分配了空间,并且每个集合元素的值存放在一个字(word)的空间 193 | // 空间的第一个字存有集合的长度(集合元素数目),集合的地址被放入一个 N_SETV 符号,它的名称与集合同名 194 | // 在满足未定义的外部引用方面,该符号的行为象一个 N_DATA 全局符号 195 | 196 | // 以下这些符号在目标文件中是作为链接程序 LD 的输入 197 | 198 | // 绝对集合元素符号 199 | #define N_SETA 0x14 200 | 201 | // 代码集合元素符号 202 | #define N_SETT 0x16 203 | 204 | // 数据集合元素符号 205 | #define N_SETD 0x18 206 | 207 | // Bss 集合元素符号 208 | #define N_SETB 0x1A 209 | 210 | // 下面是 LD 的输出 211 | 212 | // 指向数据区中集合向量 213 | #define N_SETV 0x1C 214 | 215 | #ifndef N_RELOCATION_INFO_DECLARED 216 | 217 | // 下面的结构描述执行一个重定位的操作 218 | // 文件的代码重定位部分是这些结构的一个向量,所有这些适用于代码部分 219 | // 类似地,数据重定位部分适用于数据部分 220 | 221 | // 重定位信息结构 222 | struct relocation_info 223 | { 224 | // 需要重定位的地址(在段内) 225 | int r_address; 226 | 227 | // r_symbolnum 的含义与r_extern 有关 228 | unsigned int r_symbolnum : 24; 229 | 230 | // 非零意味着值是一个 pc 相关的偏移值 231 | // 因而在其自己地址空间以及符号或指定的节改变时,需要被重定位 232 | unsigned int r_pcrel : 1; 233 | 234 | // 需要被重定位的字段长度(是2 的次方),因此,若值是 2 则表示 1<<2 字节数 235 | unsigned int r_length : 2; 236 | 237 | // 1 => 以符号的值重定位 238 | // r_symbolnum 是文件符号表中符号的索引。 239 | // 0 => 以段的地址进行重定位 240 | // r_symbolnum 是N_TEXT、N_DATA、N_BSS 或 N_ABS 241 | // (N_EXT 比特位也可以被设置,但是毫无意义) 242 | unsigned int r_extern : 1; 243 | 244 | // 没有使用的 4 个比特位,但是当进行写一个目标文件时最好将它们清除掉 245 | unsigned int r_pad : 4; 246 | }; 247 | #endif /* no N_RELOCATION_INFO_DECLARED. */ 248 | 249 | #endif /* __A_OUT_GNU_H__ */ 250 | -------------------------------------------------------------------------------- /linux-0.11/include/asm/io.h: -------------------------------------------------------------------------------- 1 | // 硬件端口字节输出函数 2 | // 参数:value - 欲输出字节;port - 端口 3 | #define outb(value, port) \ 4 | __asm__("outb %%al,%%dx" ::"a"(value), "d"(port)) 5 | 6 | // 硬件端口字节输入函数 7 | // 参数:port - 端口; 8 | // 返回读取的字节 9 | #define inb(port) ( \ 10 | { \ 11 | unsigned char _v; \ 12 | __asm__ volatile("inb %%dx,%%al" \ 13 | : "=a"(_v) \ 14 | : "d"(port)); \ 15 | _v; \ 16 | }) 17 | 18 | // 带延迟的硬件端口字节输出函数 19 | // 参数:value - 欲输出字节;port - 端口 20 | #define outb_p(value, port) \ 21 | __asm__("outb %%al,%%dx\n" \ 22 | "\tjmp 1f\n" \ 23 | "1:\tjmp 1f\n" \ 24 | "1:" ::"a"(value), \ 25 | "d"(port)) 26 | 27 | // 带延迟的硬件端口字节输入函数 28 | // 参数:port - 端口; 29 | // 返回读取的字节 30 | #define inb_p(port) ( \ 31 | { \ 32 | unsigned char _v; \ 33 | __asm__ volatile("inb %%dx,%%al\n" \ 34 | "\tjmp 1f\n" \ 35 | "1:\tjmp 1f\n" \ 36 | "1:" \ 37 | : "=a"(_v) \ 38 | : "d"(port)); \ 39 | _v; \ 40 | }) 41 | -------------------------------------------------------------------------------- /linux-0.11/include/asm/memory.h: -------------------------------------------------------------------------------- 1 | // 注意!!! memcpy(dest,src,n) 假设段寄存器ds=es=通常数据段 2 | // 在内核中使用的所有函数都基于该假设(ds=es=内核空间,fs=局部数据空间,gs=null) 3 | // 具有良好行为的应用程序也是这样(ds=es=用户数据空间) 4 | // 如果任何用户程序随意改动了 es 寄存器而出错,则并不是由于系统程序错误造成的 5 | 6 | // 内存块复制:从源地址 src 处开始复制 n 个字节到目的地址 dest 处 7 | // 参数:dest - 复制的目的地址,src - 复制的源地址,n - 复制字节数 8 | // %0 - edi(目的地址dest),%1 - esi(源地址src),%2 - ecx(字节数n) 9 | #define memcpy(dest, src, n) ( \ 10 | { \ 11 | void *_res = dest; \ 12 | __asm__("cld \n rep \n movsb" \ 13 | : \ 14 | : "D"((long)(_res)), "S"((long)(src)), "c"((long)(n))); \ 15 | _res; \ 16 | }) 17 | -------------------------------------------------------------------------------- /linux-0.11/include/asm/segment.h: -------------------------------------------------------------------------------- 1 | // 该文件中定义了一些访问段寄存器或与段寄存器有关的内存操作函数 2 | 3 | // 读取 fs 段中指定地址处的字节 4 | // 参数:addr - 指定的内存地址 5 | // %0 - (返回的字节_v);%1 - (内存地址addr) 6 | // 返回:返回内存 fs:[addr] 处的字节 7 | static inline unsigned char get_fs_byte(const char *addr) 8 | { 9 | unsigned register char _v; 10 | 11 | __asm__("movb %%fs:%1,%0" 12 | : "=r"(_v) 13 | : "m"(*addr)); 14 | return _v; 15 | } 16 | 17 | // 读取fs 段中指定地址处的字 18 | // 参数:addr - 指定的内存地址 19 | // %0 - (返回的字_v);%1 - (内存地址addr) 20 | // 返回:返回内存 fs:[addr] 处的字 21 | static inline unsigned short get_fs_word(const unsigned short *addr) 22 | { 23 | unsigned short _v; 24 | 25 | __asm__("movw %%fs:%1,%0" 26 | : "=r"(_v) 27 | : "m"(*addr)); 28 | return _v; 29 | } 30 | 31 | // 读取 fs 段中指定地址处的长字(4 字节) 32 | // 参数:addr - 指定的内存地址 33 | // %0 - (返回的长字_v);%1 - (内存地址addr) 34 | // 返回:返回内存 fs:[addr] 处的长字 35 | static inline unsigned long get_fs_long(const unsigned long *addr) 36 | { 37 | unsigned long _v; 38 | 39 | __asm__("movl %%fs:%1,%0" 40 | : "=r"(_v) 41 | : "m"(*addr)); 42 | return _v; 43 | } 44 | 45 | // 将一字节存放在 fs 段中指定内存地址处 46 | // 参数:val - 字节值;addr - 内存地址 47 | // %0 - 寄存器(字节值 val);%1 - (内存地址 addr) 48 | static inline void put_fs_byte(char val, char *addr) 49 | { 50 | __asm__("movb %0,%%fs:%1" ::"r"(val), "m"(*addr)); 51 | } 52 | 53 | // 将一字存放在 fs 段中指定内存地址处 54 | // 参数:val - 字值;addr - 内存地址。 55 | // %0 - 寄存器(字值 val);%1 - (内存地址 addr) 56 | static inline void put_fs_word(short val, short *addr) 57 | { 58 | __asm__("movw %0,%%fs:%1" ::"r"(val), "m"(*addr)); 59 | } 60 | 61 | // 将一长字存放在 fs 段中指定内存地址处 62 | // 参数:val - 长字值;addr - 内存地址 63 | // %0 - 寄存器(长字值 val);%1 - (内存地址 addr) 64 | static inline void put_fs_long(unsigned long val, unsigned long *addr) 65 | { 66 | __asm__("movl %0,%%fs:%1" ::"r"(val), "m"(*addr)); 67 | } 68 | 69 | // 比我更懂 GNU 汇编的人应该仔细检查下面的代码 70 | // 这些代码能使用,但我不知道是否含有一些小错误 71 | // --- TYT,1991 年 11 月 24 日 72 | // [ 这些代码没有错误,Linus ] 73 | 74 | // 取 fs 段寄存器值(选择符) 75 | // 返回:fs 段寄存器值 76 | static inline unsigned long get_fs() 77 | { 78 | unsigned short _v; 79 | __asm__("mov %%fs,%%ax" 80 | : "=a"(_v) 81 | :); 82 | return _v; 83 | } 84 | 85 | // 取 ds 段寄存器值 86 | // 返回:ds 段寄存器值 87 | static inline unsigned long get_ds() 88 | { 89 | unsigned short _v; 90 | __asm__("mov %%ds,%%ax" 91 | : "=a"(_v) 92 | :); 93 | return _v; 94 | } 95 | 96 | // 设置 fs 段寄存器 97 | // 参数:val - 段值(选择符) 98 | static inline void set_fs(unsigned long val) 99 | { 100 | __asm__("mov %0,%%fs" ::"a"((unsigned short)val)); 101 | } 102 | -------------------------------------------------------------------------------- /linux-0.11/include/asm/system.h: -------------------------------------------------------------------------------- 1 | 2 | // 移动到用户模式运行 3 | // 该函数利用 iret 指令实现从内核模式移动到初始任务 0 中去执行 4 | 5 | // 保存堆栈指针 esp 到 eax 寄存器中 6 | // 首先将堆栈段选择子 (ss) 入栈 7 | // 然后将保存的堆栈指针值 esp 入栈 8 | // 将标志寄存器(eflags)内容入栈 9 | // 将 Task0 代码段选择子(cs) 入栈 10 | // 将下面标号1 的偏移地址(eip) 入栈 11 | // 执行中断返回指令,则会跳转到下面标号1 处 12 | // 此时开始执行任务0 13 | // 初始化段寄存器指向本局部表的数据段 14 | #define move_to_user_mode() \ 15 | __asm__("movl %%esp,%%eax\n\t" \ 16 | "pushl $0x17\n\t" \ 17 | "pushl %%eax\n\t" \ 18 | "pushfl\n\t" \ 19 | "pushl $0x0f\n\t" \ 20 | "pushl $1f\n\t" \ 21 | "iret\n" \ 22 | "1:\tmovl $0x17,%%eax\n\t" \ 23 | "movw %%ax,%%ds\n\t" \ 24 | "movw %%ax,%%es\n\t" \ 25 | "movw %%ax,%%fs\n\t" \ 26 | "movw %%ax,%%gs" :: \ 27 | : "ax") 28 | 29 | // 开中断 set interrupt 30 | #define sti() __asm__("sti" ::) 31 | 32 | // 关中断 clear interrupt 33 | #define cli() __asm__("cli" ::) 34 | 35 | // 空操作 no operation 36 | #define nop() __asm__("nop" ::) 37 | 38 | // 中断返回 39 | #define iret() __asm__("iret" ::) 40 | 41 | // 设置门描述符宏函数 42 | // 参数: 43 | // gate_addr -描述符地址; 44 | // type -描述符中类型域值; 45 | // dpl -描述符特权层值; 46 | // addr - 偏移地址 47 | // %0 - (由 dpl, type 组合成的类型标志字);%1 - (描述符低 4 字节地址); 48 | #define _set_gate(gate_addr, type, dpl, addr) \ 49 | __asm__("movw %%dx,%%ax\n\t" \ 50 | "movw %0,%%dx\n\t" \ 51 | "movl %%eax,%1\n\t" \ 52 | "movl %%edx,%2" \ 53 | : \ 54 | : "i"((short)(0x8000 + (dpl << 13) + (type << 8))), \ 55 | "o"(*((char *)(gate_addr))), \ 56 | "o"(*(4 + (char *)(gate_addr))), \ 57 | "d"((char *)(addr)), "a"(0x00080000)) 58 | 59 | // 设置中断门函数 60 | // 参数:n - 中断号;addr - 中断程序偏移地址 61 | // &idt[n]对应中断号在中断描述符表中的偏移值;中断描述符的类型是 14,特权级是 0 62 | #define set_intr_gate(n, addr) \ 63 | _set_gate(&idt[n], 14, 0, addr) 64 | 65 | // 设置陷阱门函数 66 | // 参数:n - 中断号;addr - 中断程序偏移地址 67 | // &idt[n] 对应中断号在中断描述符表中的偏移值;中断描述符的类型是 15,特权级是 0 68 | #define set_trap_gate(n, addr) \ 69 | _set_gate(&idt[n], 15, 0, addr) 70 | 71 | // 设置系统调用门函数 72 | // 参数:n - 中断号;addr - 中断程序偏移地址 73 | // &idt[n] 对应中断号在中断描述符表中的偏移值;中断描述符的类型是 15,特权级是 3 74 | #define set_system_gate(n, addr) \ 75 | _set_gate(&idt[n], 15, 3, addr) 76 | 77 | // 设置段描述符函数 78 | // 参数:gate_addr - 描述符地址;type - 描述符中类型域值;dpl - 描述符特权层值; 79 | // base - 段的基地址;limit - 段限长(参见段描述符的格式) 80 | #define _set_seg_desc(gate_addr, type, dpl, base, limit) \ 81 | { \ 82 | *(gate_addr) = ((base)&0xff000000) | \ 83 | (((base)&0x00ff0000) >> 16) | \ 84 | ((limit)&0xf0000) | \ 85 | ((dpl) << 13) | \ 86 | (0x00408000) | \ 87 | ((type) << 8); \ 88 | *((gate_addr) + 1) = (((base)&0x0000ffff) << 16) | \ 89 | ((limit)&0x0ffff); \ 90 | } 91 | 92 | // 在全局表中设置任务状态段/局部表描述符 93 | // 参数: 94 | // n - 在全局表中描述符项 n 所对应的地址; 95 | // addr - 状态段/局部表所在内存的基地址 96 | // type - 描述符中的标志类型字节 97 | // %0 - eax(地址 addr); 98 | // %1 - (描述符项 n 的地址); 99 | // %2 - (描述符项 n 的地址偏移 2 处); 100 | // %3 - (描述符项 n 的地址偏移 4 处); 101 | // %4 - (描述符项 n 的地址偏移 5 处); 102 | // %5 - (描述符项 n 的地址偏移 6 处); 103 | // %6 - (描述符项 n 的地址偏移 7 处); 104 | #define _set_tssldt_desc(n, addr, type) \ 105 | __asm__("movw $104,%1\n\t" \ 106 | "movw %%ax,%2\n\t" \ 107 | "rorl $16,%%eax\n\t" \ 108 | "movb %%al,%3\n\t" \ 109 | "movb $" type ",%4\n\t" \ 110 | "movb $0x00,%5\n\t" \ 111 | "movb %%ah,%6\n\t" \ 112 | "rorl $16,%%eax" ::"a"(addr), \ 113 | "m"(*(n)), "m"(*(n + 2)), "m"(*(n + 4)), \ 114 | "m"(*(n + 5)), "m"(*(n + 6)), "m"(*(n + 7))) 115 | 116 | // 在全局表中设置任务状态段描述符 117 | // n - 是该描述符的指针; 118 | // addr - 是描述符中的基地址值 119 | // 任务状态段描述符的类型是 0x89 120 | #define set_tss_desc(n, addr) _set_tssldt_desc(((char *)(n)), ((int)(addr)), "0x89") 121 | 122 | // 在全局表中设置局部表描述符 123 | // n - 是该描述符的指针; 124 | // addr - 是描述符中的基地址值 125 | // 局部表描述符的类型是 0x82 126 | #define set_ldt_desc(n, addr) _set_tssldt_desc(((char *)(n)), ((int)(addr)), "0x82") 127 | -------------------------------------------------------------------------------- /linux-0.11/include/const.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONST_H 2 | #define _CONST_H 3 | 4 | // 该文件定义了 i 节点中文件属性和类型 i_mode 字段所用到的一些标志位常量符号 5 | 6 | // 定义缓冲使用内存的末端(代码中没有使用该常量) 7 | #define BUFFER_END 0x200000 8 | 9 | // 指明 i 节点类型 10 | #define I_TYPE 0170000 11 | 12 | // 是目录文件。 13 | #define I_DIRECTORY 0040000 14 | 15 | // 常规文件,不是目录文件或特殊文件 16 | #define I_REGULAR 0100000 17 | 18 | // 块设备特殊文件 19 | #define I_BLOCK_SPECIAL 0060000 20 | 21 | // 字符设备特殊文件 22 | #define I_CHAR_SPECIAL 0020000 23 | 24 | // 命名管道 25 | #define I_NAMED_PIPE 0010000 26 | 27 | // 在执行时设置有效用户 id 类型 28 | #define I_SET_UID_BIT 0004000 29 | 30 | // 在执行时设置有效组 id 类型 31 | #define I_SET_GID_BIT 0002000 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /linux-0.11/include/ctype.h: -------------------------------------------------------------------------------- 1 | #ifndef _CTYPE_H 2 | #define _CTYPE_H 3 | 4 | // 该比特位用于大写字符[A-Z] 5 | #define _U 0x01 /* upper */ 6 | 7 | // 该比特位用于小写字符[a-z] 8 | #define _L 0x02 /* lower */ 9 | 10 | // 该比特位用于数字[0-9] 11 | #define _D 0x04 /* digit */ 12 | 13 | // 该比特位用于控制字符 14 | #define _C 0x08 /* cntrl */ 15 | 16 | // 该比特位用于标点字符 17 | #define _P 0x10 /* punct */ 18 | 19 | // 用于空白字符,如空格、\t、\n 等 20 | #define _S 0x20 /* white space (space/lf/tab) */ 21 | 22 | // 该比特位用于十六进制数字 23 | #define _X 0x40 /* hex digit */ 24 | 25 | // 该比特位用于空格字符(0x20) 26 | #define _SP 0x80 /* hard space (0x20) */ 27 | 28 | // 字符特性数组(表),定义了各个字符对应上面的属性 29 | extern unsigned char _ctype[]; 30 | 31 | // 一个临时字符变量 32 | extern char _ctmp; 33 | 34 | // 是字符或数字 [A-Z]、[a-z] 或 [0-9] 35 | #define isalnum(c) ((_ctype + 1)[c] & (_U | _L | _D)) 36 | 37 | // 是字符 38 | #define isalpha(c) ((_ctype + 1)[c] & (_U | _L)) 39 | 40 | // 是控制字符 41 | #define iscntrl(c) ((_ctype + 1)[c] & (_C)) 42 | 43 | // 是数字 44 | #define isdigit(c) ((_ctype + 1)[c] & (_D)) 45 | 46 | // 是图形字符 47 | #define isgraph(c) ((_ctype + 1)[c] & (_P | _U | _L | _D)) 48 | 49 | // 是小写字符 50 | #define islower(c) ((_ctype + 1)[c] & (_L)) 51 | 52 | // 是可打印字符 53 | #define isprint(c) ((_ctype + 1)[c] & (_P | _U | _L | _D | _SP)) 54 | 55 | // 是标点符号 56 | #define ispunct(c) ((_ctype + 1)[c] & (_P)) 57 | 58 | // 是空白字符如空格,\f,\n,\r,\t,\v 59 | #define isspace(c) ((_ctype + 1)[c] & (_S)) 60 | 61 | // 是大写字符 62 | #define isupper(c) ((_ctype + 1)[c] & (_U)) 63 | 64 | // 是十六进制数字 65 | #define isxdigit(c) ((_ctype + 1)[c] & (_D | _X)) 66 | 67 | // 是 ASCII 字符 68 | #define isascii(c) (((unsigned)c) <= 0x7f) 69 | 70 | // 转换成 ASCII 字符 71 | #define toascii(c) (((unsigned)c) & 0x7f) 72 | 73 | // 以上两个定义中,宏参数前使用了前缀(unsigned),因此 c 应该加括号,即表示成(c) 74 | // 因为在程序中 c 可能是一个复杂的表达式。例如,如果参数是 a + b,若不加括号 75 | // 则在宏定义中变成了:(unsigned) a + b,这显然不对 76 | // 加了括号就能正确表示成(unsigned)(a + b) 77 | 78 | // 转换成对应小写字符 79 | #define tolower(c) (_ctmp = c, isupper(_ctmp) ? _ctmp - ('A' - 'a') : _ctmp) 80 | 81 | // 转换成对应大写字符 82 | #define toupper(c) (_ctmp = c, islower(_ctmp) ? _ctmp - ('a' - 'A') : _ctmp) 83 | 84 | // 以上两个宏定义中使用一个临时变量 _ctmp 的原因是:在宏定义中,宏的参数只能被使用一次 85 | // 但对于多线程来说这是不安全的,因为两个或多个线程可能在同一时刻使用这个公共临时变量 86 | // 因此从 Linux 2.2.x 版本开始更改为使用两个函数来取代这两个宏定义 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /linux-0.11/include/errno.h: -------------------------------------------------------------------------------- 1 | #ifndef _ERRNO_H 2 | #define _ERRNO_H 3 | 4 | // ok,由于我没有得到任何其它有关出错号的资料,我只能使用与 minix 系统相同的出错号了 5 | // 希望这些是 POSIX 兼容的或者在一定程度上是这样的 6 | // 我不知道(而且 POSIX 没有告诉我 - 要获得他们的混蛋标准需要出钱) 7 | 8 | // 我们没有使用 minix 那样的 _SIGN 簇,所以内核的返回值必须自己辨别正负号 9 | 10 | // 注意!如果你改变该文件的话,记着也要修改 strerror() 函数 11 | 12 | // 系统调用以及很多库函数返回一个特殊的值以表示操作失败或出错 13 | // 这个值通常选择 -1 或者其它一些特定的值来表示 14 | // 但是这个返回值仅说明错误发生了, 15 | // 如果需要知道出错的类型,就需要查看表示系统出错号的变量 errno 16 | // 该变量即在 errno.h 文件中申明,在程序开始执行时该变量值被初始化为 0 17 | extern int errno; 18 | 19 | #define ERROR 99 // 一般错误 20 | #define EPERM 1 // 操作没有许可 21 | #define ENOENT 2 // 文件或目录不存在 22 | #define ESRCH 3 // 指定的进程不存在 23 | #define EINTR 4 // 中断的函数调用 24 | #define EIO 5 // 输入输出错误 25 | #define ENXIO 6 // 指定设备或地址不存在 26 | #define E2BIG 7 // 参数列表太长 27 | #define ENOEXEC 8 // 执行程序格式错误 28 | #define EBADF 9 // 文件描述符错误 29 | #define ECHILD 10 // 子进程不存在 30 | #define EAGAIN 11 // 资源暂时不可用 31 | #define ENOMEM 12 // 内存不足 32 | #define EACCES 13 // 没有许可权限 33 | #define EFAULT 14 // 地址错 34 | #define ENOTBLK 15 // 不是块设备文件 35 | #define EBUSY 16 // 资源正忙 36 | #define EEXIST 17 // 文件已存在 37 | #define EXDEV 18 // 非法连接 38 | #define ENODEV 19 // 设备不存在 39 | #define ENOTDIR 20 // 不是目录文件 40 | #define EISDIR 21 // 是目录文件 41 | #define EINVAL 22 // 参数无效 42 | #define ENFILE 23 // 系统打开文件数太多 43 | #define EMFILE 24 // 打开文件数太多 44 | #define ENOTTY 25 // 不恰当的 IO 控制操作(没有 tty 终端) 45 | #define ETXTBSY 26 // 不再使用 46 | #define EFBIG 27 // 文件太大 47 | #define ENOSPC 28 // 设备已满(设备已经没有空间) 48 | #define ESPIPE 29 // 无效的文件指针重定位 49 | #define EROFS 30 // 文件系统只读 50 | #define EMLINK 31 // 连接太多 51 | #define EPIPE 32 // 管道错 52 | #define EDOM 33 // 域(domain)出错 53 | #define ERANGE 34 // 结果太大 54 | #define EDEADLK 35 // 避免资源死锁 55 | #define ENAMETOOLONG 36 // 文件名太长 56 | #define ENOLCK 37 // 没有锁定可用 57 | #define ENOSYS 38 // 功能还没有实现 58 | #define ENOTEMPTY 39 // 目录不空 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /linux-0.11/include/fcntl.h: -------------------------------------------------------------------------------- 1 | #ifndef _FCNTL_H 2 | #define _FCNTL_H 3 | 4 | // 类型头文件 定义了基本的系统数据类型 5 | #include 6 | 7 | // open/fcntl - NOCTTY 和 NDELAY 现在还没有实现 8 | #define O_ACCMODE 00003 // 文件访问模式屏蔽码 9 | 10 | // 打开文件 open() 和文件控制 fcntl() 函数使用的文件访问模式,同时只能使用三者之一 11 | #define O_RDONLY 00 // 以只读方式打开文件 12 | #define O_WRONLY 01 // 以只写方式打开文件 13 | #define O_RDWR 02 // 以读写方式打开文件 14 | 15 | // 下面是文件创建标志,用于 open(),可与上面访问模式用 按位或 的方式一起使用 16 | #define O_CREAT 00100 // 如果文件不存在就创建 17 | #define O_EXCL 00200 // 独占使用文件标志 18 | #define O_NOCTTY 00400 // 不分配控制终端 19 | #define O_TRUNC 01000 // 若文件已存在且是写操作,则长度截为 0 20 | #define O_APPEND 02000 // 以添加方式打开,文件指针置为文件尾 21 | #define O_NONBLOCK 04000 // 非阻塞方式打开和操作文件 22 | #define O_NDELAY O_NONBLOCK 23 | 24 | // 下面定义了 fcntl 的命令,注意目前锁定命令还没有支持 25 | // 而其它命令实际上还没有测试过 26 | #define F_DUPFD 0 // 拷贝文件句柄为最小数值的句柄 27 | #define F_GETFD 1 // 取文件句柄标志 28 | #define F_SETFD 2 // 设置文件句柄标志 29 | #define F_GETFL 3 // 取文件状态标志和访问模式 30 | #define F_SETFL 4 // 设置文件状态标志和访问模式 31 | 32 | // 下面是文件锁定命令 fcntl() 的第三个参数 lock 是指向 flock 结构的指针 33 | #define F_GETLK 5 // 返回阻止锁定的flock 结构 /* not implemented */ 34 | #define F_SETLK 6 // 设置(F_RDLCK 或F_WRLCK)或清除(F_UNLCK)锁定 35 | #define F_SETLKW 7 // 等待设置或清除锁定 36 | 37 | /* for F_[GET|SET]FL */ 38 | // 在执行 exec() 簇函数时关闭文件句柄,(执行时关闭 - Close On EXECution) 39 | #define FD_CLOEXEC 1 // 实际上只要低位为1 即可 40 | 41 | // OK,以下是锁定类型,任何函数中都还没有实现,POSIX 标准要求这些类型 42 | #define F_RDLCK 0 // 共享或读文件锁定 43 | #define F_WRLCK 1 // 独占或写文件锁定 44 | #define F_UNLCK 2 // 文件解锁 45 | 46 | // 同样, 也还没有实现,但是... 47 | 48 | // 文件锁定操作数据结构,描述了受影响文件段的类型(l_type)、开始偏移(l_whence)、 49 | // 相对偏移(l_start)、锁定长度(l_len) 和实施锁定的进程 id 50 | struct flock 51 | { 52 | short l_type; // 锁定类型(F_RDLCK,F_WRLCK,F_UNLCK) 53 | short l_whence; // 开始偏移(SEEK_SET,SEEK_CUR 或SEEK_END) 54 | off_t l_start; // 阻塞锁定的开始处。相对偏移(字节数) 55 | off_t l_len; // 阻塞锁定的大小;如果是0 则为到文件末尾 56 | pid_t l_pid; // 加锁的进程id 57 | }; 58 | 59 | // 以下是使用上述标志或命令的函数原型 60 | // 创建新文件或重写一个已存在文件 61 | // 参数 filename 是欲创建文件的文件名,mode 是创建文件的属性 62 | extern int creat(const char *filename, mode_t mode); 63 | 64 | // 文件句柄操作,会影响文件的打开 65 | // 参数 fildes 是文件描述符,cmd 是操作命令 66 | extern int fcntl(int fildes, int cmd, ...); 67 | 68 | // 打开文件,在文件与文件句柄之间建立联系。 69 | // 参数 filename 是欲打开文件的文件名,flags 是打开标志组合 70 | extern int open(const char *filename, int flags, ...); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /linux-0.11/include/linux/config.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONFIG_H 2 | #define _CONFIG_H 3 | 4 | // 内核配置头文件,定义使用的键盘语言类型和硬盘类型(HD_TYPE)可选项 5 | 6 | // 根文件系统设备已不再是硬编码的了 7 | // 通过修改 boot/bootsect.s 文件中行 ROOT_DEV = XXX 8 | // 你可以改变根设备的默认设置值 9 | 10 | // 在这里定义你的键盘类型 11 | // KBD_FINNISH 是芬兰键盘 12 | // KBD_US 是美式键盘 13 | // KBD_GR 是德式键盘 14 | // KBD_FR 是法式键盘 15 | 16 | #define KBD_US 17 | /*#define KBD_GR */ 18 | /*#define KBD_FR */ 19 | /*#define KBD_FINNISH */ 20 | 21 | // 通常,Linux 能够在启动时从 BIOS 中获取驱动器的参数 22 | // 但是若由于未知原因而没有得到这些参数时,会使程序束手无策 23 | // 对于这种情况,你可以定义 HD_TYPE,其中包括硬盘的所有信息 24 | 25 | // HD_TYPE 宏应该象下面这样的形式: 26 | 27 | // #define HD_TYPE { head, sect, cyl, wpcom, lzone, ctl} 28 | 29 | // 对于有两个硬盘的情况,参数信息需用逗号分开: 30 | 31 | // #define HD_TYPE { h,s,c,wpcom,lz,ctl }, {h,s,c,wpcom,lz,ctl } 32 | 33 | // 下面是一个例子,两个硬盘,第 1 个是类型 2,第 2 个是类型 3: 34 | 35 | // #define HD_TYPE { 4,17,615,300,615,8 }, {6,17,615,300,615,0 } 36 | 37 | // 注意:对应所有硬盘 38 | // 若其磁头数<=8,则 ctl 等于 0, 39 | // 若磁头数多于 8 个,则 ctl=8 40 | 41 | // 如果你想让 BIOS 给出硬盘的类型,那么只需不定义 HD_TYPE,这是默认操作 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /linux-0.11/include/linux/fdreg.h: -------------------------------------------------------------------------------- 1 | // 该文件中含有一些软盘控制器的一些定义,这些信息有多处来源 2 | // 大多数取自 Sanches 和 Canton 编著的 《IBM 微型计算机:程序员手册》一书 3 | 4 | #ifndef _FDREG_H 5 | #define _FDREG_H 6 | 7 | // 一些软盘类型函数的原型说明 8 | extern int ticks_to_floppy_on(unsigned int nr); 9 | extern void floppy_on(unsigned int nr); 10 | extern void floppy_off(unsigned int nr); 11 | extern void floppy_select(unsigned int nr); 12 | extern void floppy_deselect(unsigned int nr); 13 | 14 | // 下面是有关软盘控制器一些端口和符号的定义 15 | 16 | // 软盘控制器(FDC)寄存器端口。摘自 S&C 书中约 340 页 17 | #define FD_STATUS 0x3f4 // 主状态寄存器端口 18 | #define FD_DATA 0x3f5 // 数据端口 19 | #define FD_DOR 0x3f2 // 数字输出寄存器(也称为数字控制寄存器) 20 | #define FD_DIR 0x3f7 // 数字输入寄存器 21 | #define FD_DCR 0x3f7 // 数据传输率控制寄存器 22 | 23 | // 主状态寄存器各比特位的含义 24 | #define STATUS_BUSYMASK 0x0F // 驱动器忙位(每位对应一个驱动器) 25 | #define STATUS_BUSY 0x10 // 软盘控制器忙 26 | #define STATUS_DMA 0x20 // 0 - 为 DMA 数据传输模式,1 - 为非 DMA 模式 27 | #define STATUS_DIR 0x40 // 传输方向:0 - CPU -> FDC,1 - 相反 28 | #define STATUS_READY 0x80 // 数据寄存器就绪位 29 | 30 | // 状态字节0(ST0)各比特位的含义 31 | #define ST0_DS 0x03 // 驱动器选择号(发生中断时驱动器号) 32 | #define ST0_HA 0x04 // 磁头号 33 | #define ST0_NR 0x08 // 磁盘驱动器未准备好 34 | #define ST0_ECE 0x10 // 设备检测出错(零磁道校准出错) 35 | #define ST0_SE 0x20 // 寻道或重新校正操作执行结束 36 | 37 | // 中断代码位(中断原因) 38 | // 00 - 命令正常结束; 39 | // 01 - 命令异常结束; 40 | // 10 - 命令无效; 41 | // 11 - FDD 就绪状态改变 42 | #define ST0_INTR 0xC0 43 | 44 | // 状态字节1(ST1)各比特位的含义 45 | #define ST1_MAM 0x01 // 未找到地址标志(ID AM) 46 | #define ST1_WP 0x02 // 写保护 47 | #define ST1_ND 0x04 // 未找到指定的扇区 48 | #define ST1_OR 0x10 // 数据传输超时(DMA 控制器故障) 49 | #define ST1_CRC 0x20 // CRC 检验出错 50 | #define ST1_EOC 0x80 // 访问超过一个磁道上的最大扇区号 51 | 52 | // 状态字节2(ST2)各比特位的含义 53 | #define ST2_MAM 0x01 // 未找到数据地址标志 54 | #define ST2_BC 0x02 // 磁道坏 55 | #define ST2_SNS 0x04 // 检索(扫描)条件不满足 56 | #define ST2_SEH 0x08 // 检索条件满足 57 | #define ST2_WC 0x10 // 磁道(柱面)号不符 58 | #define ST2_CRC 0x20 // 数据场CRC 校验错 59 | #define ST2_CM 0x40 // 读数据遇到删除标志 60 | 61 | // 状态字节3(ST3)各比特位的含义 62 | #define ST3_HA 0x04 // 磁头号 63 | #define ST3_TZ 0x10 // 零磁道信号 64 | #define ST3_WP 0x40 // 写保护 65 | 66 | // 软盘命令码 67 | #define FD_RECALIBRATE 0x07 // 重新校正(磁头退到零磁道) 68 | #define FD_SEEK 0x0F // 磁头寻道 69 | #define FD_READ 0xE6 // 读数据(MT 多磁道操作,MFM 格式,跳过删除数据) 70 | #define FD_WRITE 0xC5 // 写数据(MT,MFM) 71 | #define FD_SENSEI 0x08 // 检测中断状态 72 | #define FD_SPECIFY 0x03 // 设定驱动器参数(步进速率、磁头卸载时间等) 73 | 74 | // DMA 命令 75 | #define DMA_READ 0x46 // DMA 读盘,DMA 方式字(送DMA 端口12,11) 76 | #define DMA_WRITE 0x4A // DMA 写盘,DMA 方式字 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /linux-0.11/include/linux/hdreg.h: -------------------------------------------------------------------------------- 1 | 2 | // 本文件含有一些 AT 硬盘控制器的定义,来自各种资料 3 | // 请查证某些定义(带有问号的注释) 4 | 5 | #ifndef _HDREG_H 6 | #define _HDREG_H 7 | 8 | // 硬盘控制器寄存器端口。参见:IBM AT Bios 程序 9 | #define HD_DATA 0x1f0 /* _CTL when writing */ 10 | #define HD_ERROR 0x1f1 /* see err-bits */ 11 | #define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */ 12 | #define HD_SECTOR 0x1f3 /* starting sector */ 13 | #define HD_LCYL 0x1f4 /* starting cylinder */ 14 | #define HD_HCYL 0x1f5 /* high byte of starting cyl */ 15 | #define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */ 16 | #define HD_STATUS 0x1f7 /* see status-bits */ 17 | #define HD_PRECOMP HD_ERROR /* same io address, read=error, write=precomp */ 18 | #define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */ 19 | 20 | #define HD_CMD 0x3f6 // 控制寄存器端口 21 | 22 | // 硬盘状态寄存器各位的定义(HD_STATUS) 23 | #define ERR_STAT 0x01 // 命令执行错误 24 | #define INDEX_STAT 0x02 // 收到索引 25 | #define ECC_STAT 0x04 // ECC 校验错 26 | #define DRQ_STAT 0x08 // 请求服务 27 | #define SEEK_STAT 0x10 // 寻道结束 28 | #define WRERR_STAT 0x20 // 驱动器故障 29 | #define READY_STAT 0x40 // 驱动器准备好(就绪) 30 | #define BUSY_STAT 0x80 // 控制器忙碌 31 | 32 | // 硬盘命令值(HD_CMD) 33 | #define WIN_RESTORE 0x10 // 驱动器重新校正(驱动器复位) 34 | #define WIN_READ 0x20 // 读扇区 35 | #define WIN_WRITE 0x30 // 写扇区 36 | #define WIN_VERIFY 0x40 // 扇区检验 37 | #define WIN_FORMAT 0x50 // 格式化磁道 38 | #define WIN_INIT 0x60 // 控制器初始化 39 | #define WIN_SEEK 0x70 // 寻道 40 | #define WIN_DIAGNOSE 0x90 // 控制器诊断 41 | #define WIN_SPECIFY 0x91 // 建立驱动器参数 42 | 43 | // 错误寄存器各比特位的含义(HD_ERROR) 44 | // 执行控制器诊断命令时含义与其它命令时的不同,下面分别列出: 45 | // ================================================== 46 | // 诊断命令时 其它命令时 47 | // -------------------------------------------------- 48 | // 0x01 无错误 数据标志丢失 49 | // 0x02 控制器出错 磁道0 错 50 | // 0x03 扇区缓冲区错 51 | // 0x04 ECC 部件错 命令放弃 52 | // 0x05 控制处理器错 53 | // 0x10 ID 未找到 54 | // 0x40 ECC 错误 55 | // 0x80 坏扇区 56 | //--------------------------------------------------- 57 | #define MARK_ERR 0x01 /* Bad address mark ? */ 58 | #define TRK0_ERR 0x02 /* couldn't find track 0 */ 59 | #define ABRT_ERR 0x04 /* ? */ 60 | #define ID_ERR 0x10 /* ? */ 61 | #define ECC_ERR 0x40 /* ? */ 62 | #define BBD_ERR 0x80 /* ? */ 63 | 64 | // 硬盘分区表结构。参见下面列表后信息 65 | struct partition 66 | { 67 | unsigned char boot_ind; /* 0x80 - active (unused) */ 68 | unsigned char head; /* ? */ 69 | unsigned char sector; /* ? */ 70 | unsigned char cyl; /* ? */ 71 | unsigned char sys_ind; /* ? */ 72 | unsigned char end_head; /* ? */ 73 | unsigned char end_sector; /* ? */ 74 | unsigned char end_cyl; /* ? */ 75 | unsigned int start_sect; /* starting sector counting from 0 */ 76 | unsigned int nr_sects; /* nr of sectors in partition */ 77 | }; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /linux-0.11/include/linux/head.h: -------------------------------------------------------------------------------- 1 | #ifndef _HEAD_H 2 | #define _HEAD_H 3 | 4 | // 定义了段描述符的数据结构 5 | // 该结构仅说明每个描述符是由 8 个字节构成,每个描述符表共有 256 项 6 | typedef struct desc_struct 7 | { 8 | unsigned long a, b; 9 | } desc_table[256]; 10 | 11 | // 内存页目录数组。每个目录项为 4 字节。从物理地址 0 开始 12 | extern unsigned long pg_dir[1024]; 13 | 14 | // 中断描述符表,全局描述符表 15 | extern desc_table idt, gdt; 16 | 17 | #define GDT_NUL 0 // 全局描述符表的第 0 项,不用 18 | #define GDT_CODE 1 // 第 1 项,是内核代码段描述符项 19 | #define GDT_DATA 2 // 第 2 项,是内核数据段描述符项 20 | #define GDT_TMP 3 // 第 3 项,系统段描述符,Linux 没有使用 21 | 22 | #define LDT_NUL 0 // 每个局部描述符表的第 0 项,不用 23 | #define LDT_CODE 1 // 第 1 项,是用户程序代码段描述符项 24 | #define LDT_DATA 2 // 第 2 项,是用户程序数据段描述符项 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /linux-0.11/include/linux/kernel.h: -------------------------------------------------------------------------------- 1 | // 定义了一些常用函数的原型等 2 | 3 | // 验证给定地址开始的内存块是否超限,若超限则追加内存 4 | void verify_area(void *addr, int count); 5 | 6 | // 显示内核出错信息,然后进入死循环 7 | volatile void panic(const char *str); 8 | 9 | // 标准打印(显示)函数 10 | int printf(const char *fmt, ...); 11 | 12 | // 内核专用的打印信息函数,功能与 printf() 相同 13 | int printk(const char *fmt, ...); 14 | 15 | // 往 tty 上写指定长度的字符串 16 | int tty_write(unsigned ch, char *buf, int count); 17 | 18 | // 通用内核内存分配函数 19 | void *malloc(unsigned int size); 20 | 21 | // 释放指定对象占用的内存 22 | void free_s(void *obj, int size); 23 | 24 | #define free(x) free_s((x), 0) 25 | 26 | // 下面函数是以宏的形式定义的,但是在某方面来看它可以成为一个真正的子程序 27 | // 如果返回是 true 时它将设置标志 28 | // (如果使用 root 用户权限的进程设置了标志,则用于执行 BSD 方式的计帐处理) 29 | // 这意味着你应该首先执行常规权限检查,最后再检测 suser() 30 | 31 | #define suser() (current->euid == 0) 32 | -------------------------------------------------------------------------------- /linux-0.11/include/linux/mm.h: -------------------------------------------------------------------------------- 1 | #ifndef _MM_H 2 | #define _MM_H 3 | 4 | // 定义内存页面的大小(字节数) 5 | #define PAGE_SIZE 4096 6 | 7 | // 取空闲页面函数,返回页面地址,扫描页面映射数组 mem_map[] 取空闲页面 8 | extern unsigned long get_free_page(void); 9 | 10 | // 在指定物理地址处放置一页面,在页目录和页表中放置指定页面信息 11 | extern unsigned long put_page(unsigned long page, unsigned long address); 12 | 13 | // 释放物理地址 addr 开始的一页面内存,修改页面映射数组 mem_map[] 中引用次数信息 14 | extern void free_page(unsigned long addr); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /linux-0.11/include/linux/sys.h: -------------------------------------------------------------------------------- 1 | // sys.h 头文件列出了内核中所有系统调用函数的原型,以及系统调用函数指针表 2 | 3 | extern int sys_setup(); // 系统启动初始化设置函数 4 | extern int sys_exit(); // 程序退出 5 | extern int sys_fork(); // 创建进程 6 | extern int sys_read(); // 读文件 7 | extern int sys_write(); // 写文件 8 | extern int sys_open(); // 打开文件 9 | extern int sys_close(); // 关闭文件 10 | extern int sys_waitpid(); // 等待进程终止 11 | extern int sys_creat(); // 创建文件 12 | extern int sys_link(); // 创建一个文件的硬连接 13 | extern int sys_unlink(); // 删除一个文件名(或删除文件) 14 | extern int sys_execve(); // 执行程序 15 | extern int sys_chdir(); // 更改当前目录 16 | extern int sys_time(); // 取当前时间 17 | extern int sys_mknod(); // 建立块/字符特殊文件 18 | extern int sys_chmod(); // 修改文件属性 19 | extern int sys_chown(); // 修改文件宿主和所属组 20 | extern int sys_break(); // 21 | extern int sys_stat(); // 使用路径名取文件的状态信息 22 | extern int sys_lseek(); // 重新定位读/写文件偏移 23 | extern int sys_getpid(); // 取进程 id 24 | extern int sys_mount(); // 安装文件系统 25 | extern int sys_umount(); // 卸载文件系统 26 | extern int sys_setuid(); // 设置进程用户 id 27 | extern int sys_getuid(); // 取进程用户 id 28 | extern int sys_stime(); // 设置系统时间日期 29 | extern int sys_ptrace(); // 程序调试 30 | extern int sys_alarm(); // 设置报警 31 | extern int sys_fstat(); // 使用文件句柄取文件的状态信息 32 | extern int sys_pause(); // 暂停进程运行 33 | extern int sys_utime(); // 改变文件的访问和修改时间 34 | extern int sys_stty(); // 修改终端行设置 35 | extern int sys_gtty(); // 取终端行设置信息 36 | extern int sys_access(); // 检查用户对一个文件的访问权限 37 | extern int sys_nice(); // 设置进程执行优先权 38 | extern int sys_ftime(); // 取日期和时间 39 | extern int sys_sync(); // 同步高速缓冲与设备中数据 40 | extern int sys_kill(); // 终止一个进程 41 | extern int sys_rename(); // 更改文件名 42 | extern int sys_mkdir(); // 创建目录 43 | extern int sys_rmdir(); // 删除目录 44 | extern int sys_dup(); // 复制文件句柄 45 | extern int sys_pipe(); // 创建管道 46 | extern int sys_times(); // 取运行时间 47 | extern int sys_prof(); // 程序执行时间区域 48 | extern int sys_brk(); // 修改数据段长度 49 | extern int sys_setgid(); // 设置进程组 id 50 | extern int sys_getgid(); // 取进程组 id 51 | extern int sys_signal(); // 信号处理 52 | extern int sys_geteuid(); // 取进程有效用户 id 53 | extern int sys_getegid(); // 取进程有效组 id 54 | extern int sys_acct(); // 进程记帐 55 | extern int sys_phys(); // 56 | extern int sys_lock(); // 57 | extern int sys_ioctl(); // 设备控制 58 | extern int sys_fcntl(); // 文件句柄操作 59 | extern int sys_mpx(); // 60 | extern int sys_setpgid(); // 设置进程组id 61 | extern int sys_ulimit(); // 62 | extern int sys_uname(); // 显示系统信息 63 | extern int sys_umask(); // 取默认文件创建属性码 64 | extern int sys_chroot(); // 改变根系统 65 | extern int sys_ustat(); // 取文件系统信息 66 | extern int sys_dup2(); // 复制文件句柄 67 | extern int sys_getppid(); // 取父进程 id 68 | extern int sys_getpgrp(); // 取进程组 id,等于 getpgid(0) 69 | extern int sys_setsid(); // 在新会话中运行程序 70 | extern int sys_sigaction(); // 改变信号处理过程 71 | extern int sys_sgetmask(); // 取信号屏蔽码 72 | extern int sys_ssetmask(); // 设置信号屏蔽码 73 | extern int sys_setreuid(); // 设置真实与/或有效用户 id 74 | extern int sys_setregid(); // 设置真实与/或有效组 id 75 | 76 | fn_ptr sys_call_table[] = { 77 | sys_setup, 78 | sys_exit, 79 | sys_fork, 80 | sys_read, 81 | sys_write, 82 | sys_open, 83 | sys_close, 84 | sys_waitpid, 85 | sys_creat, 86 | sys_link, 87 | sys_unlink, 88 | sys_execve, 89 | sys_chdir, 90 | sys_time, 91 | sys_mknod, 92 | sys_chmod, 93 | sys_chown, 94 | sys_break, 95 | sys_stat, 96 | sys_lseek, 97 | sys_getpid, 98 | sys_mount, 99 | sys_umount, 100 | sys_setuid, 101 | sys_getuid, 102 | sys_stime, 103 | sys_ptrace, 104 | sys_alarm, 105 | sys_fstat, 106 | sys_pause, 107 | sys_utime, 108 | sys_stty, 109 | sys_gtty, 110 | sys_access, 111 | sys_nice, 112 | sys_ftime, 113 | sys_sync, 114 | sys_kill, 115 | sys_rename, 116 | sys_mkdir, 117 | sys_rmdir, 118 | sys_dup, 119 | sys_pipe, 120 | sys_times, 121 | sys_prof, 122 | sys_brk, 123 | sys_setgid, 124 | sys_getgid, 125 | sys_signal, 126 | sys_geteuid, 127 | sys_getegid, 128 | sys_acct, 129 | sys_phys, 130 | sys_lock, 131 | sys_ioctl, 132 | sys_fcntl, 133 | sys_mpx, 134 | sys_setpgid, 135 | sys_ulimit, 136 | sys_uname, 137 | sys_umask, 138 | sys_chroot, 139 | sys_ustat, 140 | sys_dup2, 141 | sys_getppid, 142 | sys_getpgrp, 143 | sys_setsid, 144 | sys_sigaction, 145 | sys_sgetmask, 146 | sys_ssetmask, 147 | sys_setreuid, 148 | sys_setregid, 149 | }; 150 | -------------------------------------------------------------------------------- /linux-0.11/include/linux/tty.h: -------------------------------------------------------------------------------- 1 | 2 | // 'tty.h'中定义了tty_io.c 程序使用的某些结构和其它一些定义 3 | 4 | // 注意!在修改这里的定义时,一定要检查 rs_io.s 或 con_io.s 程序中不会出现问题 5 | // 在系统中有些常量是直接写在程序中的(主要是一些 tty_queue 中的偏移值) 6 | 7 | #ifndef _TTY_H 8 | #define _TTY_H 9 | 10 | // 终端输入输出函数头文件 主要定义控制异步通信口的终端接口 11 | #include 12 | 13 | // tty 缓冲区(缓冲队列)大小 14 | #define TTY_BUF_SIZE 1024 15 | 16 | // tty 等待队列数据结构。用于tty_struc 结构中的读、写和辅助(规范)缓冲队列 17 | struct tty_queue 18 | { 19 | // 队列缓冲区中含有字符行数值(不是当前字符数) 20 | // 对于串口终端,则存放串行端口地址 21 | unsigned long data; 22 | unsigned long head; // 缓冲区中数据头指针 23 | unsigned long tail; // 缓冲区中数据尾指针 24 | struct task_struct *proc_list; // 等待进程列表 25 | char buf[TTY_BUF_SIZE]; // 队列的缓冲区 26 | }; 27 | 28 | // 以下定义了 tty 等待队列中缓冲区操作宏函数,tail 在前,head 在后 29 | // a 缓冲区指针前移 1 字节,若已超出缓冲区右侧,则指针循环 30 | #define INC(a) ((a) = ((a) + 1) & (TTY_BUF_SIZE - 1)) 31 | 32 | // a 缓冲区指针后退 1 字节,并循环 33 | #define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE - 1)) 34 | 35 | // 清空指定队列的缓冲区 36 | #define EMPTY(a) ((a).head == (a).tail) 37 | 38 | // 缓冲区还可存放字符的长度(空闲区长度) 39 | #define LEFT(a) (((a).tail - (a).head - 1) & (TTY_BUF_SIZE - 1)) 40 | 41 | // 缓冲区中最后一个位置 42 | #define LAST(a) ((a).buf[(TTY_BUF_SIZE - 1) & ((a).head - 1)]) 43 | 44 | // 缓冲区满(如果为1 的话) 45 | #define FULL(a) (!LEFT(a)) 46 | 47 | // 缓冲区中已存放字符的长度 48 | #define CHARS(a) (((a).head - (a).tail) & (TTY_BUF_SIZE - 1)) 49 | 50 | // 从 queue 队列项缓冲区中取一字符(从 tail 处,并且 tail += 1) 51 | #define GETCH(queue, c) \ 52 | (void)( \ 53 | { \ 54 | c = (queue).buf[(queue).tail]; \ 55 | INC((queue).tail); \ 56 | }) 57 | 58 | // 往 queue 队列项缓冲区中放置一字符(在 head 处,并且 head += 1) 59 | #define PUTCH(c, queue) \ 60 | (void)( \ 61 | { \ 62 | (queue).buf[(queue).head] = (c); \ 63 | INC((queue).head); \ 64 | }) 65 | 66 | // 判断终端键盘字符类型 67 | #define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR]) // 中断符 68 | #define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT]) // 退出符 69 | #define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE]) // 削除符 70 | #define KILL_CHAR(tty) ((tty)->termios.c_cc[VKILL]) // 终止符 71 | #define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF]) // 文件结束符 72 | #define START_CHAR(tty) ((tty)->termios.c_cc[VSTART]) // 开始符 73 | #define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP]) // 停止符 74 | #define SUSPEND_CHAR(tty) ((tty)->termios.c_cc[VSUSP]) // 挂起符 75 | 76 | // tty 数据结构 77 | struct tty_struct 78 | { 79 | struct termios termios; // 终端io 属性和控制字符数据结构 80 | int pgrp; // 所属进程组 81 | int stopped; // 停止标志 82 | void (*write)(struct tty_struct *tty); // tty 写函数指针 83 | struct tty_queue read_q; // tty 读队列 84 | struct tty_queue write_q; // tty 写队列 85 | struct tty_queue secondary; // tty 辅助队列(存放规范模式字符序列) 86 | }; // 可称为规范(熟)模式队列 87 | 88 | extern struct tty_struct tty_table[]; // tty 结构数组 89 | 90 | /* intr=^C quit=^| erase=del kill=^U 91 | eof=^D vtime=\0 vmin=\1 sxtc=\0 92 | start=^Q stop=^S susp=^Z eol=\0 93 | reprint=^R discard=^U werase=^W lnext=^V 94 | eol2=\0 95 | */ 96 | 97 | /* 中断 intr=^C 退出 quit=^| 删除 erase=del 终止 kill=^U 98 | 文件结束 eof=^D vtime= \0 vmin=\1 sxtc=\0 99 | 开始 start=^Q 停止 stop=^S 挂起 susp=^Z 行结束 eol=\0 100 | 重显 reprint=^R 丢弃 discard=^U werase=^W lnext=^V 101 | 行结束 eol2=\0 102 | */ 103 | 104 | // 控制字符对应的ASCII 码值 [8 进制] 105 | #define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" 106 | 107 | void rs_init(void); // 异步串行通信初始化 108 | void con_init(void); // 控制终端初始化 109 | void tty_init(void); // tty 初始化 110 | 111 | int tty_read(unsigned c, char *buf, int n); 112 | int tty_write(unsigned c, char *buf, int n); 113 | 114 | void rs_write(struct tty_struct *tty); 115 | void con_write(struct tty_struct *tty); 116 | 117 | void copy_to_cooked(struct tty_struct *tty); 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /linux-0.11/include/signal.h: -------------------------------------------------------------------------------- 1 | #ifndef _SIGNAL_H 2 | #define _SIGNAL_H 3 | 4 | #include 5 | 6 | // 定义信号原子操作类型 7 | typedef int sig_atomic_t; 8 | 9 | // 定义信号集类型 10 | typedef unsigned int sigset_t; /* 32 bits */ 11 | 12 | // 定义信号种类 -- 32 种 13 | #define _NSIG 32 14 | 15 | // NSIG = _NSIG 16 | #define NSIG _NSIG 17 | 18 | // 以下这些是 Linux 0.11 内核中定义的信号 19 | // 其中包括了 POSIX.1 要求的所有 20 个信号 20 | 21 | // 挂断控制终端或进程 22 | #define SIGHUP 1 23 | 24 | // 来自键盘的中断 25 | #define SIGINT 2 26 | 27 | // 来自键盘的退出 28 | #define SIGQUIT 3 29 | 30 | // 非法指令 31 | #define SIGILL 4 32 | 33 | // 跟踪断点 34 | #define SIGTRAP 5 35 | 36 | // 异常结束 37 | #define SIGABRT 6 38 | 39 | // 同上 40 | #define SIGIOT 6 41 | 42 | // 没有使用 43 | #define SIGUNUSED 7 44 | 45 | // 协处理器出错 46 | #define SIGFPE 8 47 | 48 | // 强迫进程终止 49 | #define SIGKILL 9 50 | 51 | // 用户信号 1,进程可使用 52 | #define SIGUSR1 10 53 | 54 | // 无效内存引用 55 | #define SIGSEGV 11 56 | 57 | // 用户信号 2,进程可使用 58 | #define SIGUSR2 12 59 | 60 | // 管道写出错,无读者 61 | #define SIGPIPE 13 62 | 63 | // 实时定时器报警 64 | #define SIGALRM 14 65 | 66 | // 进程终止 67 | #define SIGTERM 15 68 | 69 | // 栈出错(协处理器) 70 | #define SIGSTKFLT 16 71 | 72 | // 子进程停止或被终止 73 | #define SIGCHLD 17 74 | 75 | // 恢复进程继续执行 76 | #define SIGCONT 18 77 | 78 | // 停止进程的执行 79 | #define SIGSTOP 19 80 | 81 | // tty 发出停止进程,可忽略 82 | #define SIGTSTP 20 83 | 84 | // 后台进程请求输入 85 | #define SIGTTIN 21 86 | 87 | // 后台进程请求输出 88 | #define SIGTTOU 22 89 | 90 | // OK,我还没有实现 sigactions 的实现,但在头文件中仍希望遵守 POSIX 标准 91 | 92 | // 当子进程处于停止状态,就不对 SIGCHLD 处理 93 | #define SA_NOCLDSTOP 1 94 | 95 | // 不阻止在指定的信号处理程序(信号句柄)中再收到该信号 96 | #define SA_NOMASK 0x40000000 97 | 98 | // 信号句柄一旦被调用过就恢复到默认处理句柄 99 | #define SA_ONESHOT 0x80000000 100 | 101 | // 以下常量用于 sigprocmask(how, ) / 改变阻塞信号集(屏蔽码) 用于改变该函数的行为 102 | #define SIG_BLOCK 0 // 在阻塞信号集中加上给定的信号集 103 | #define SIG_UNBLOCK 1 // 从阻塞信号集中删除指定的信号集 104 | #define SIG_SETMASK 2 // 设置阻塞信号集(信号屏蔽码) 105 | 106 | // 以下两个常数符号都表示指向无返回值的函数指针,且都有一个 int 整型参数 107 | // 这两个指针值是逻辑上讲实际上不可能出现的函数地址值,可作为下面 signal 函数的第二个参数 108 | // 用于告知内核,让内核处理信号或忽略对信号的处理 109 | #define SIG_DFL ((void (*)(int))0) // 默认的信号处理程序(信号句柄) 110 | #define SIG_IGN ((void (*)(int))1) // 忽略信号的处理程序 111 | 112 | // 下面是 sigaction 的数据结构 113 | // sa_handler 是对应某信号指定要采取的行动 114 | // 可以用上面的 SIG_DFL,或 SIG_IGN 来忽略该信号 115 | // 也可以是指向处理该信号函数的一个指针 116 | // sa_mask 给出了对信号的屏蔽码,在信号程序执行时将阻塞对这些信号的处理 117 | // sa_flags 指定改变信号处理过程的信号集 118 | // sa_restorer 是恢复函数指针,由函数库 Libc 提供,用于清理用户态堆栈 119 | // 另外,引起触发信号处理的信号也将被阻塞,除非使用了 SA_NOMASK 标志 120 | struct sigaction 121 | { 122 | void (*sa_handler)(int); 123 | sigset_t sa_mask; 124 | int sa_flags; 125 | void (*sa_restorer)(void); 126 | }; 127 | 128 | // 下面 signal 函数用于是为信号 _sig 安装一新的信号处理程序(信号句柄),与 sigaction() 类似 129 | // 该函数含有两个参数:指定需要捕获的信号 _sig;具有一个参数且无返回值的函数指针 _func 130 | // 该函数返回值也是具有一个 int 参数(最后一个(int))且无返回值的函数指针 131 | // 它是处理该信号的原处理句柄 132 | void (*signal(int _sig, void (*_func)(int)))(int); 133 | 134 | // 下面两函数用于发送信号 135 | // raise() 用于向当前进程自身发送信号,其作用等价于 kill(getpid(),sig) 136 | int raise(int sig); 137 | // kill() 用于向任何进程或进程组发送任何信号 138 | int kill(pid_t pid, int sig); 139 | 140 | // 在进程的任务结构中,除有一个以比特位表示当前进程待处理的 32 位信号字段 signal 以外 141 | // 还有一个同样以比特位表示的用于屏蔽进程当前阻塞信号集合(屏蔽信号集)的字段 blocked,也是32 位 142 | // 每个比特代表一个对应的阻塞信号。修改进程的屏蔽信号集可以阻塞或解除阻塞所指定的信号 143 | // 以下五个函数就是用于操作进程屏蔽信号集,虽然简单实现起来很简单,但本版本内核中还未实现 144 | // sigaddset() 和 sigdelset() 用于对信号集中的信号进行增、删修改 145 | // sigaddset() 用于向 mask 指向的信号集中增加指定的信号 signo 146 | // sigdelset 则反之 147 | // sigemptyset() 和 sigfillset() 用于初始化进程屏蔽信号集 148 | // 每个程序在使用信号集前,都需要使用这两个函数之一对屏蔽信号集进行初始化 149 | // sigemptyset() 用于清空屏蔽的所有信号,也即响应所有的信号 150 | // sigfillset() 向信号集中置入所有信号,也即屏蔽所有信号 151 | // 当然 SIGINT 和 SIGSTOP 是屏蔽不了的 152 | 153 | int sigaddset(sigset_t *mask, int signo); 154 | int sigdelset(sigset_t *mask, int signo); 155 | int sigemptyset(sigset_t *mask); 156 | int sigfillset(sigset_t *mask); 157 | 158 | // sigismember() 用于测试一个指定信号是否在信号集中(1 - 是,0 - 不是,-1 - 出错) 159 | int sigismember(sigset_t *mask, int signo); /* 1 - is, 0 - not, -1 error */ 160 | 161 | // 对 set 中的信号进行检测,看是否有挂起的信号,在 set 中返回进程中当前被阻塞的信号集 162 | int sigpending(sigset_t *set); 163 | 164 | // 下面函数用于改变进程目前被阻塞的信号集(信号屏蔽码) 165 | // 若 oldset 不是 NULL,则通过其返回进程当前屏蔽信号集 166 | // 若 set 指针不是 NULL,则根据 how 指示修改进程屏蔽信号集 167 | int sigprocmask(int how, sigset_t *set, sigset_t *oldset); 168 | 169 | // 下面函数用 sigmask 临时替换进程的信号屏蔽码,然后暂停该进程直到收到一个信号 170 | // 若捕捉到某一信号并从该信号处理程序中返回,则该函数也返回,并且信号屏蔽码会恢复到调用调用前的值 171 | int sigsuspend(sigset_t *sigmask); 172 | 173 | // sigaction() 用于改变进程在收到指定信号时所采取的行动 174 | int sigaction(int sig, struct sigaction *act, struct sigaction *oldact); 175 | 176 | #endif /* _SIGNAL_H */ 177 | -------------------------------------------------------------------------------- /linux-0.11/include/stdarg.h: -------------------------------------------------------------------------------- 1 | #ifndef _STDARG_H 2 | #define _STDARG_H 3 | 4 | // 定义 va_list 是一个字符指针类型 5 | typedef char *va_list; 6 | 7 | // 下面给出了类型为 TYPE 的 arg 参数列表所要求的空间容量 8 | // TYPE 也可以是使用该类型的一个表达式 9 | 10 | // 下面这句定义了取整后的 TYPE 类型的字节长度值,是 int 长度(4)的倍数 11 | #define __va_rounded_size(TYPE) \ 12 | (((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int)) 13 | 14 | // 下面这个函数(用宏实现)使 AP 指向传给函数的可变参数表的第一个参数 15 | // 在第一次调用 va_arg 或 va_end 之前,必须首先调用该函数 16 | // __builtin_saveregs() 是在 gcc 的库程序 libgcc2.c 中定义的,用于保存寄存器 17 | #ifndef __sparc__ 18 | #define va_start(AP, LASTARG) \ 19 | (AP = ((char *)&(LASTARG) + __va_rounded_size(LASTARG))) 20 | #else 21 | #define va_start(AP, LASTARG) \ 22 | (__builtin_saveregs(), \ 23 | AP = ((char *)&(LASTARG) + __va_rounded_size(LASTARG))) 24 | #endif 25 | 26 | // 下面该宏用于被调用函数完成一次正常返回 27 | // va_end 可以修改 AP 使其在重新调用 va_start 之前不能被使用 28 | // va_end 必须在 va_arg 读完所有的参数后再被调用 29 | void va_end(va_list); /* Defined in gnulib */ 30 | #define va_end(AP) 31 | 32 | // 下面该宏用于扩展表达式,使其与下一个被传递参数具有相同的类型和值 33 | // 对于缺省值,va_arg 可以用字符、无符号字符和浮点类型 34 | // 在第一次使用 va_arg 时,它返回表中的第一个参数 35 | // 后续的每次调用都将返回表中的下一个参数 36 | // 这是通过先访问 AP,然后把它增加以指向下一项来实现的 37 | // va_arg 使用 TYPE 来完成访问和定位下一项,每调用一次 va_arg 38 | // 它就修改 AP 以指示表中的下一参数 39 | #define va_arg(AP, TYPE) \ 40 | (AP += __va_rounded_size(TYPE), \ 41 | *((TYPE *)(AP - __va_rounded_size(TYPE)))) 42 | 43 | #endif /* _STDARG_H */ 44 | -------------------------------------------------------------------------------- /linux-0.11/include/stddef.h: -------------------------------------------------------------------------------- 1 | #ifndef _STDDEF_H 2 | #define _STDDEF_H 3 | 4 | #ifndef _PTRDIFF_T 5 | #define _PTRDIFF_T 6 | typedef long ptrdiff_t; // 两个指针相减结果的类型 7 | #endif 8 | 9 | #ifndef _SIZE_T 10 | #define _SIZE_T 11 | typedef unsigned long size_t; // sizeof 返回的类型 12 | #endif 13 | 14 | #undef NULL 15 | #define NULL ((void *)0) // 空指针 16 | 17 | // 成员在类型中的偏移位置 18 | #define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER) 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /linux-0.11/include/string.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRING_H_ 2 | #define _STRING_H_ 3 | 4 | #ifndef NULL 5 | #define NULL ((void *)0) 6 | #endif 7 | 8 | #ifndef _SIZE_T 9 | #define _SIZE_T 10 | typedef unsigned int size_t; 11 | #endif 12 | 13 | extern char *strerror(int errno); 14 | 15 | // 这个字符串头文件以内联函数的形式定义了所有字符串操作函数使用 gcc 时 16 | // 同时假定了 ds=es=数据空间,这应该是常规的 17 | // 绝大多数字符串函数都是经手工进行大量优化的 18 | // 尤其是函数 strtok、strstr、str[c]spn,它们应该能正常工作 19 | // 但却不是那么容易理解,所有的操作基本上都是使用寄存器集来完成的,这使得函数即快又整洁 20 | // 所有地方都使用了字符串指令,这又使得代码 “稍微” 难以理解 😊 21 | 22 | extern inline char *strcpy(char *dest, const char *src); 23 | extern inline char *strcat(char *dest, const char *src); 24 | extern inline int strcmp(const char *cs, const char *ct); 25 | extern inline int strspn(const char *cs, const char *ct); 26 | extern inline int strcspn(const char *cs, const char *ct); 27 | extern inline char *strpbrk(const char *cs, const char *ct); 28 | extern inline char *strstr(const char *cs, const char *ct); 29 | extern inline int strlen(const char *s); 30 | 31 | // 用于临时存放指向下面被分析字符串1(s) 的指针 32 | extern char *___strtok; 33 | 34 | extern inline char *strtok(char *s, const char *ct); 35 | extern inline void *memcpy(void *dest, const void *src, int n); 36 | extern inline void *memmove(void *dest, const void *src, int n); 37 | extern inline void *memchr(const void *cs, char c, int count); 38 | #endif 39 | -------------------------------------------------------------------------------- /linux-0.11/include/sys/stat.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYS_STAT_H 2 | #define _SYS_STAT_H 3 | 4 | #include 5 | 6 | struct stat 7 | { 8 | dev_t st_dev; // 含有文件的设备号 9 | ino_t st_ino; // 文件i 节点号 10 | umode_t st_mode; // 文件类型和属性 11 | nlink_t st_nlink; // 指定文件的连接数 12 | uid_t st_uid; // 文件的用户(标识)号 13 | gid_t st_gid; // 文件的组号 14 | dev_t st_rdev; // 设备号(如果文件是特殊的字符文件或块文件) 15 | off_t st_size; // 文件大小(字节数)(如果文件是常规文件) 16 | time_t st_atime; // 上次(最后)访问时间 17 | time_t st_mtime; // 最后修改时间 18 | time_t st_ctime; // 最后节点修改时间 19 | }; 20 | 21 | // st_mode 值的符号名称 22 | // 文件类型 23 | #define S_IFMT 00170000 // 文件类型(8 进制表示) 24 | #define S_IFREG 0100000 // 常规文件 25 | #define S_IFBLK 0060000 // 块特殊(设备)文件,如磁盘 dev/fd0 26 | #define S_IFDIR 0040000 // 目录文件 27 | #define S_IFCHR 0020000 // 字符设备文件 28 | #define S_IFIFO 0010000 // FIFO 特殊文件 29 | 30 | // 文件属性位: 31 | // S_ISUID 用于测试文件的 set-user-ID 标志是否置位 32 | // 若该标志置位,则当执行该文件时,进程的有效用户ID 33 | // 将被设置为该文件宿主的用户 ID,S_ISGID 则是针对组 ID 进行相同处理 34 | #define S_ISUID 0004000 // 执行时设置用户ID(set-user-ID) 35 | #define S_ISGID 0002000 // 执行时设置组ID(set-group-ID) 36 | #define S_ISVTX 0001000 // 对于目录,受限删除标志 37 | 38 | #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) // 测试是否常规文件 39 | #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) // 是否目录文件 40 | #define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR) // 是否字符设备文件 41 | #define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK) // 是否块设备文件 42 | #define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO) // 是否FIFO 特殊文件 43 | 44 | // 文件访问权限 45 | #define S_IRWXU 00700 // 宿主可以读、写、执行/搜索 46 | #define S_IRUSR 00400 // 宿主读许可 47 | #define S_IWUSR 00200 // 宿主写许可 48 | #define S_IXUSR 00100 // 宿主执行/搜索许可 49 | 50 | #define S_IRWXG 00070 // 组成员可以读、写、执行/搜索 51 | #define S_IRGRP 00040 // 组成员读许可 52 | #define S_IWGRP 00020 // 组成员写许可 53 | #define S_IXGRP 00010 // 组成员执行/搜索许可 54 | 55 | #define S_IRWXO 00007 // 其他人读、写、执行/搜索许可 56 | #define S_IROTH 00004 // 其他人读许可 57 | #define S_IWOTH 00002 // 其他人写许可 58 | #define S_IXOTH 00001 // 其他人执行/搜索许可 59 | 60 | // 修改文件属性 61 | extern int chmod(const char *_path, mode_t mode); 62 | 63 | // 取指定文件句柄的文件状态信息 64 | extern int fstat(int fildes, struct stat *stat_buf); 65 | 66 | // 创建目录 67 | extern int mkdir(const char *_path, mode_t mode); 68 | 69 | // 创建管道文件 70 | extern int mkfifo(const char *_path, mode_t mode); 71 | 72 | // 取指定文件名的文件状态信息 73 | extern int stat(const char *filename, struct stat *stat_buf); 74 | 75 | // 设置属性屏蔽码 76 | extern mode_t umask(mode_t mask); 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /linux-0.11/include/sys/times.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIMES_H 2 | #define _TIMES_H 3 | 4 | #include 5 | 6 | struct tms 7 | { 8 | time_t tms_utime; // 用户使用的 CPU 时间 9 | time_t tms_stime; // 系统(内核)CPU 时间 10 | time_t tms_cutime; // 已终止的子进程使用的用户 CPU 时间 11 | time_t tms_cstime; // 已终止的子进程使用的系统 CPU 时间 12 | }; 13 | 14 | extern time_t times(struct tms *tp); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /linux-0.11/include/sys/types.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYS_TYPES_H 2 | #define _SYS_TYPES_H 3 | 4 | #ifndef _SIZE_T 5 | #define _SIZE_T 6 | typedef unsigned int size_t; // 用于对象的大小(长度) 7 | #endif 8 | 9 | #ifndef _TIME_T 10 | #define _TIME_T 11 | typedef long time_t; // 用于时间(以秒计) 12 | #endif 13 | 14 | #ifndef _PTRDIFF_T 15 | #define _PTRDIFF_T 16 | typedef long ptrdiff_t; 17 | #endif 18 | 19 | #ifndef NULL 20 | #define NULL ((void *)0) 21 | #endif 22 | 23 | typedef int pid_t; // 用于进程号和进程组号 24 | typedef unsigned short uid_t; // 用于用户号(用户标识号) 25 | typedef unsigned char gid_t; // 用于组号 26 | typedef unsigned short dev_t; // 用于设备号 27 | typedef unsigned short ino_t; // 用于文件序列号 28 | typedef unsigned short mode_t; // 用于某些文件属性 29 | typedef unsigned short umode_t; // 30 | typedef unsigned char nlink_t; // 用于连接计数 31 | typedef int daddr_t; 32 | typedef long off_t; // 用于文件长度(大小) 33 | typedef unsigned char u_char; // 无符号字符类型 34 | typedef unsigned short ushort; // 无符号短整数类型 35 | 36 | // 用于 DIV 除法操作 37 | typedef struct 38 | { 39 | int quot, rem; 40 | } div_t; 41 | 42 | // 用于长 DIV 除法操作 43 | typedef struct 44 | { 45 | long quot, rem; 46 | } ldiv_t; 47 | 48 | // 文件系统参数结构,用于 ustat() 函数 49 | // 最后两个字段未使用,总是返回 NULL 指针 50 | struct ustat 51 | { 52 | daddr_t f_tfree; // 系统总空闲块数 53 | ino_t f_tinode; // 总空闲 i 节点数 54 | char f_fname[6]; // 文件系统名称 55 | char f_fpack[6]; // 文件系统压缩名称 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /linux-0.11/include/sys/utsname.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYS_UTSNAME_H 2 | #define _SYS_UTSNAME_H 3 | 4 | #include 5 | 6 | struct utsname 7 | { 8 | char sysname[9]; // 本版本操作系统的名称 9 | char nodename[9]; // 与实现相关的网络中节点名称 10 | char release[9]; // 本实现的当前发行级别 11 | char version[9]; // 本次发行的版本级别 12 | char machine[9]; // 系统运行的硬件类型名称 13 | }; 14 | 15 | extern int uname(struct utsname *utsbuf); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /linux-0.11/include/sys/wait.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYS_WAIT_H 2 | #define _SYS_WAIT_H 3 | 4 | #include 5 | 6 | #define _LOW(v) ((v)&0377) // 取低字节(8 进制表示) 7 | #define _HIGH(v) (((v) >> 8) & 0377) // 取高字节 8 | 9 | // waitpid 的选项,其中 WUNTRACED 未被支持 10 | // [ 注:其实0.11 内核已经支持 WUNTRACED 选项。上面这条注释应该是以前内核版本遗留下来的 ] 11 | // 以下常数符号是函数 waitpid(pid_t pid, long *stat_addr, int options) 中 options 使用的选项 12 | #define WNOHANG 1 // 如果没有状态也不要挂起,并立刻返回 13 | #define WUNTRACED 2 // 报告停止执行的子进程状态 14 | 15 | // 以下宏定义用于判断 waitpid() 函数返回的状态字含义 16 | 17 | // 如果子进程正常退出,则为真 18 | #define WIFEXITED(s) (!((s)&0xFF) 19 | 20 | // 如果子进程正停止着,则为真 21 | #define WIFSTOPPED(s) (((s)&0xFF) == 0x7F) 22 | 23 | // 返回退出状态 24 | #define WEXITSTATUS(s) (((s) >> 8) & 0xFF) 25 | 26 | // 返回导致进程终止的信号值(信号量) 27 | #define WTERMSIG(s) ((s)&0x7F) 28 | 29 | // 返回导致进程停止的信号值 30 | #define WSTOPSIG(s) (((s) >> 8) & 0xFF) 31 | 32 | // 如果由于未捕捉到信号而导致子进程退出则为真 33 | #define WIFSIGNALED(s) (((unsigned int)(s)-1 & 0xFFFF) < 0xFF) 34 | 35 | // wait() 和 waitpid() 函数允许进程获取与其子进程之一的状态信息 36 | // 各种选项允许获取已经终止或停止的子进程状态信息 37 | // 如果存在两个或两个以上子进程的状态信息,则报告的顺序是不指定的 38 | // wait() 将挂起当前进程,直到其子进程之一退出(终止) 39 | // 或者收到要求终止该进程的信号 40 | // 或者是需要调用一个信号句柄(信号处理程序) 41 | // waitpid() 挂起当前进程,直到 pid 指定的子进程退出(终止) 42 | // 或者收到要求终止该进程的信号 43 | // 或者是需要调用一个信号句柄(信号处理程序) 44 | // 如果 pid = -1,options = 0 45 | // 则 waitpid() 的作用与wait()函数一样 46 | // 否则其行为将随 pid 和 options 47 | // 参数的不同而不同 48 | pid_t wait(int *stat_loc); 49 | pid_t waitpid(pid_t pid, int *stat_loc, int options); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /linux-0.11/include/time.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIME_H 2 | #define _TIME_H 3 | 4 | // 在 MINIX 中有一段对时间的描述很有趣: 5 | // 时间的处理较为复杂,比如什么是 GMT(Greenwich Mean Time)(格林威治标准时间)、本地时间或其它时间等 6 | // 尽管(天主教)主教 Ussher(1581-1656 年) 曾经计算过、 7 | // 根据圣经,世界开始之日是公元前 4004 年 10 月 12 日上午 9 点 8 | // 但在 UNIX 世界里,时间是从 GMT 1970 年 1 月 1 日午夜开始的 9 | // 在这之前,所有均是空无的和(无效的) 10 | 11 | #ifndef _TIME_T 12 | #define _TIME_T 13 | // 从 GMT 1970 年 1 月 1 日开始的以秒计数的时间(日历时间) 14 | typedef long time_t; 15 | #endif 16 | 17 | #ifndef _SIZE_T 18 | #define _SIZE_T 19 | typedef unsigned int size_t; 20 | #endif 21 | 22 | // 系统时钟滴答频率,100HZ 23 | #define CLOCKS_PER_SEC 100 24 | 25 | // 从进程开始系统经过的时钟滴答数 26 | typedef long clock_t; 27 | 28 | struct tm 29 | { 30 | int tm_sec; // 秒数 [0,59] 31 | int tm_min; // 分钟数 [0,59] 32 | int tm_hour; // 小时数 [0,59] 33 | int tm_mday; // 1 个月的天数 [0,31] 34 | int tm_mon; // 1 年中月份 [0,11] 35 | int tm_year; // 从 1900 年开始的年数 36 | int tm_wday; // 1 星期中的某天 [0,6] (星期天 =0) 37 | int tm_yday; // 1 年中的某天 [0,365] 38 | int tm_isdst; // 夏令时标志 39 | }; 40 | 41 | // 以下是有关时间操作的函数原型 42 | // 确定处理器使用时间,返回程序所用处理器时间(滴答数)的近似值 43 | clock_t clock(void); 44 | 45 | // 取时间(秒数),返回从 1970-01-01 00:00:00 开始的秒数(称为日历时间) 46 | time_t time(time_t *tp); 47 | 48 | // 计算时间差。返回时间 time2 与 time1 之间经过的秒数 49 | double difftime(time_t time2, time_t time1); 50 | 51 | // 将 tm 结构表示的时间转换成日历时间 52 | time_t mktime(struct tm *tp); 53 | 54 | // 将 tm 结构表示的时间转换成一个字符串,返回指向该串的指针 55 | char *asctime(const struct tm *tp); 56 | 57 | // 将日历时间转换成一个字符串形式,如“Wed Jun 30 21:49:08:1993\n” 58 | char *ctime(const time_t *tp); 59 | 60 | // 将日历时间转换成 tm 结构表示的 UTC 时间(UTC - 世界时间代码 Universal Time Code)。 61 | struct tm *gmtime(const time_t *tp); 62 | 63 | // 将日历时间转换成 tm 结构表示的指定时间区(timezone)的时间 64 | struct tm *localtime(const time_t *tp); 65 | 66 | // 将 tm 结构表示的时间,利用格式字符串 fmt 转换成最大长度为 smax 的字符串,并将结果存储在 s 中 67 | size_t strftime(char *s, size_t smax, const char *fmt, const struct tm *tp); 68 | 69 | // 初始化时间转换信息,使用环境变量 TZ,对 zname 变量进行初始化 70 | // 在与时间区相关的时间转换函数中将自动调用该函数 71 | void tzset(void); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /linux-0.11/include/utime.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTIME_H 2 | #define _UTIME_H 3 | 4 | #include // 我知道 - 不应该这样做,但是.. 5 | 6 | struct utimbuf 7 | { 8 | time_t actime; // 文件访问时间戳,从 1970-01-01 00:00:00 开始的秒数 9 | time_t modtime; // 文件修改时间戳,从 1970-01-01 00:00:00 开始的秒数 10 | }; 11 | 12 | // 设置文件访问和修改时间函数 13 | extern int utime(const char *filename, struct utimbuf *times); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /linux-0.11/kernel/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # FREAX 内核的 Makefile 文件 3 | 4 | # 注意!依赖关系是由'make dep'自动进行的,它也会自动去除原来的依赖信息 5 | # 不要把你自己的依赖关系信息放在这里,除非是特别文件的(也即不是一个.c 文件的信息) 6 | # (Linux 最初的名字叫FREAX,后来被 ftp.funet.fi 的管理员改成 Linux 这个名字) 7 | 8 | # 这里导入通用的编译工具以及编译选项 9 | include ../Option.mk 10 | 11 | LDFLAGS += -r 12 | 13 | CFLAGS += -I../include 14 | 15 | CPP += -I../include 16 | 17 | # 下面的规则指示 make 利用下面的命令将所有的 .c 文件编译生成 .s 汇编程序。 18 | # 该规则的命令使 gcc 采用 CFLAGS 所指定的选项对 C 代码编译后不进行汇编就停止(-S) 19 | # 从而产生与输入的各个 C 文件对应的汇编代码文件 20 | # 默认情况下所产生的汇编程序文件名是原 C 文件名去掉.c 而加上.s 后缀。-o 表示其后是输出文件的名称。 21 | # 其中 $*.s(或$@)是自动目标变量,$<代表第一个先决条件,这里即是符合条件 *.c 的文件 22 | 23 | .c.s: 24 | $(CC) $(CFLAGS) -S -o $*.s $< 25 | 26 | # 下面规则表示将所有 .s 汇编程序文件编译成 .o 目标文件 27 | .s.o: 28 | $(AS) -o $*.o $< 29 | 30 | # 类似上面,*.c 文件 -> *.o 目标文件,不进行连接 31 | .c.o: 32 | $(CC) $(CFLAGS) -c -o $*.o $< 33 | 34 | # 定义目标文件变量 OBJS 35 | OBJS = sched.o system_call.o traps.o asm.o fork.o \ 36 | panic.o printk.o vsprintf.o sys.o exit.o \ 37 | signal.o mktime.o 38 | 39 | # 在有了先决条件 OBJS 后使用下面的命令连接成目标 kernel.o 40 | kernel.o: $(OBJS) 41 | $(LD) $(LDFLAGS) -o kernel.o $(OBJS) 42 | sync 43 | 44 | # 下面的规则用于清理工作。当执行'make clean'时,就会执行命令,去除所有编译连接生成的文件。 45 | # 'rm'是文件删除命令,选项 -f 含义是忽略不存在的文件,并且不显示删除信息 46 | clean: 47 | rm -f core *.o *.a tmp_make keyboard.s 48 | for i in *.c;do rm -f `basename $$i .c`.s;done 49 | (cd chr_drv; make clean) # 进入 chr_drv/ 目录;执行该目录 Makefile 中的 clean 规则 50 | (cd blk_drv; make clean) 51 | (cd math; make clean) 52 | 53 | # 下面得目标或规则用于检查各文件之间的依赖关系。方法如下: 54 | # 使用字符串编辑程序 sed 对 Makefile 文件(这里即是自己)进行处理 55 | # 输出为删除 Makefile 文件中 '### Dependencies' 行后面的所有行 56 | # 并生成 tmp_make 临时文件;然后对 kernel/ 目录下的每一个 C 文件执行 gcc 预处理操作 57 | # -M 标志告诉预处理程序输出描述每个目标文件相关性的规则,并且这些规则符合 make 语法 58 | # 对于每一个源文件,预处理程序输出一个 make 规则, 59 | # 其结果形式是相应源程序文件的目标文件名加上其依赖关系 60 | # 该源文件中包含的所有头文件列表,把预处理结果都添加到临时文件 tmp_make 中 61 | # 然后将该临时文件复制成新的 Makefile 文件。 62 | dep: 63 | sed '/\#\#\# Dependencies/q' < Makefile > tmp_make 64 | (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ 65 | $(CPP) -M $$i;done) >> tmp_make 66 | cp tmp_make Makefile 67 | (cd chr_drv; make dep) # 对 chr_drv/ 目录下的 Makefile 文件也作同样的处理 68 | (cd blk_drv; make dep) 69 | 70 | ### Dependencies: 71 | exit.s exit.o: exit.c ../include/errno.h ../include/signal.h \ 72 | ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \ 73 | ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ 74 | ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \ 75 | ../include/asm/segment.h 76 | fork.s fork.o: fork.c ../include/errno.h ../include/linux/sched.h \ 77 | ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ 78 | ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ 79 | ../include/asm/segment.h ../include/asm/system.h 80 | mktime.s mktime.o: mktime.c ../include/time.h 81 | panic.s panic.o: panic.c ../include/linux/kernel.h ../include/linux/sched.h \ 82 | ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ 83 | ../include/linux/mm.h ../include/signal.h 84 | printk.s printk.o: printk.c ../include/stdarg.h ../include/stddef.h \ 85 | ../include/linux/kernel.h 86 | sched.s sched.o: sched.c ../include/linux/sched.h ../include/linux/head.h \ 87 | ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ 88 | ../include/signal.h ../include/linux/kernel.h ../include/linux/sys.h \ 89 | ../include/linux/fdreg.h ../include/asm/system.h ../include/asm/io.h \ 90 | ../include/asm/segment.h 91 | signal.s signal.o: signal.c ../include/linux/sched.h ../include/linux/head.h \ 92 | ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ 93 | ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h 94 | sys.s sys.o: sys.c ../include/errno.h ../include/linux/sched.h \ 95 | ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ 96 | ../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \ 97 | ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h \ 98 | ../include/sys/times.h ../include/sys/utsname.h 99 | traps.s traps.o: traps.c ../include/string.h ../include/linux/head.h \ 100 | ../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \ 101 | ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ 102 | ../include/asm/system.h ../include/asm/segment.h ../include/asm/io.h 103 | vsprintf.s vsprintf.o: vsprintf.c ../include/stdarg.h ../include/string.h 104 | -------------------------------------------------------------------------------- /linux-0.11/kernel/asm.s: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/asm.s 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | # asm.s 程序中包括大部分的硬件故障(或出错)处理的底层次代码 8 | # 页异常 PageFault 是由内存管理程序 mm 处理的,所以不在这里 9 | # 此程序还处理(希望是这样)由于 TS 位而造成的 fpu 异常 10 | # 因为fpu 必须正确地进行保存/恢复处理,这些还没有测试过 11 | 12 | # 这个文件主要处理 Intel 定义的 (中断/异常) 13 | # 软中断、外中断,异常 的实现方式都是一样的,都是一个中断函数,最后用 iret 返回 14 | # 区别在于 15 | # 异常是 Intel 在 CPU 中定义的 16 | # 外中断 由外部硬件触发,比如时钟,键盘 17 | # 内终端 由 int 指令触发 18 | 19 | # 本代码文件主要涉及对 Intel 保留的中断 int0 ~ int16 的处理 (int 17 ~ int 31 留作今后使用) 20 | # 以下是一些全局函数名的声明,其原形在 traps.c 中说明 21 | 22 | .globl divide_error,debug,nmi,int3,overflow,bounds,invalid_op 23 | .globl double_fault,coprocessor_segment_overrun 24 | .globl invalid_TSS,segment_not_present,stack_segment 25 | .globl general_protection,coprocessor_error,irq13,reserved 26 | 27 | # int 0 28 | # 下面是被零除出错 (divide_error) 处理代码。'do_divide_error'函数在traps.c 中 29 | # 那个年代的 gcc 编译生成的符号名,前面会加上下划线 _,但是现在已经不用了,故所有的前缀下划线都去掉了。 30 | divide_error: 31 | # 首先把将要调用的函数地址入栈,这段程序的出错号为 0 32 | pushl $do_divide_error 33 | 34 | # 这里是无出错号处理的入口处 35 | no_error_code: 36 | # do_divide_error 的地址 -> eax,eax 被交换入栈。 37 | xchgl %eax,(%esp) 38 | 39 | # 保存上下文 40 | pushl %ebx 41 | pushl %ecx 42 | pushl %edx 43 | pushl %edi 44 | pushl %esi 45 | pushl %ebp 46 | 47 | # 注:16 位的段寄存器入栈后也要占用 4 个字节 48 | push %ds 49 | push %es 50 | push %fs 51 | 52 | # 将出错码入栈 53 | pushl $0 54 | 55 | # 取原调用返回地址处堆栈指针位置,并压入堆栈 56 | lea 44(%esp),%edx 57 | pushl %edx 58 | 59 | # 此时已进入内核态,恢复内核代码数据段选择符 60 | movl $0x10,%edx 61 | mov %dx,%ds 62 | mov %dx,%es 63 | mov %dx,%fs 64 | 65 | # 调用 实际的处理函数,一开始压入栈中的那个 66 | call *%eax 67 | 68 | # 恢复堆栈,让堆栈指针重新指向寄存器 fs 入栈处 69 | addl $8,%esp 70 | 71 | # 恢复上下文,与上面压栈相对,后进先出 72 | pop %fs 73 | pop %es 74 | pop %ds 75 | popl %ebp 76 | popl %esi 77 | popl %edi 78 | popl %edx 79 | popl %ecx 80 | popl %ebx 81 | popl %eax 82 | 83 | # 中断返回 84 | iret 85 | 86 | # int 1 / debug 调试中断入口点 87 | debug: 88 | # do_debug C 函数指针入栈,以下同 89 | pushl $do_int3 90 | jmp no_error_code 91 | 92 | # int 2 / 非屏蔽中断调用入口点 93 | nmi: 94 | pushl $do_nmi 95 | jmp no_error_code 96 | 97 | # int 3 98 | int3: 99 | pushl $do_int3 100 | jmp no_error_code 101 | 102 | # int 4 / 溢出出错处理中断入口点 103 | overflow: 104 | pushl $do_overflow 105 | jmp no_error_code 106 | 107 | # int 5 / 边界检查出错中断入口点 108 | bounds: 109 | pushl $do_bounds 110 | jmp no_error_code 111 | 112 | # int 6 / 无效操作指令出错中断入口点 113 | invalid_op: 114 | pushl $do_invalid_op 115 | jmp no_error_code 116 | 117 | # int 9 / 协处理器段超出出错中断入口点 118 | coprocessor_segment_overrun: 119 | pushl $do_coprocessor_segment_overrun 120 | jmp no_error_code 121 | 122 | # int 15 – 保留 123 | reserved: 124 | pushl $do_reserved 125 | jmp no_error_code 126 | 127 | # int45 / ( = 0x20 + 13 ) 数学协处理器 (Coprocessor) 发出的中断 128 | # 当协处理器执行完一个操作时就会发出 IRQ13 中断信号,以通知 CPU 操作完成 129 | irq13: 130 | pushl %eax 131 | 132 | # 80387 在执行计算时,CPU 会等待其操作的完成 133 | xorb %al,%al 134 | 135 | # 通过写 0xF0 端口,本中断将消除 CPU 的 BUSY 延续信号 136 | # 并重新激活 80387 的处理器扩展请求引脚 PEREQ 137 | # 该操作主要是为了确保在继续执行 80387 的任何指令之前,响应本中断 138 | outb %al,$0xF0 139 | 140 | # 向 8259 主中断控制芯片发送 EOI(中断结束)信号。 141 | 142 | movb $0x20,%al 143 | outb %al,$0x20 144 | 145 | # 这两个跳转指令起延时作用 146 | jmp 1f 147 | 1: 148 | jmp 1f 149 | 1: 150 | # 再向 8259 从中断控制芯片发送EOI (中断结束) 信号 151 | outb %al,$0xA0 152 | popl %eax 153 | 154 | # coprocessor_error 原来在本文件中,现在已经放到 kernel/system_call.s 155 | jmp coprocessor_error 156 | 157 | # 以下中断在调用时会在中断返回地址之后,将出错号压入堆栈,因此返回时也需要将出错号弹出 158 | # int 8 / 双出错故障 159 | double_fault: 160 | # C 函数地址入栈 161 | pushl $do_double_fault 162 | 163 | error_code: 164 | # error code <-> %eax,eax 原来的值被保存在堆栈上 165 | xchgl %eax,4(%esp) 166 | 167 | # &function <-> %ebx,ebx 原来的值被保存在堆栈上 168 | xchgl %ebx,(%esp) 169 | 170 | # 保存上下文 171 | pushl %ecx 172 | pushl %edx 173 | pushl %edi 174 | pushl %esi 175 | pushl %ebp 176 | push %ds 177 | push %es 178 | push %fs 179 | 180 | # 将出错码入栈 181 | pushl %eax 182 | 183 | # 取原调用返回地址处堆栈指针位置,并压入堆栈 184 | lea 44(%esp),%eax # offset 185 | pushl %eax 186 | 187 | # 此时已进入内核态,恢复内核代码数据段选择符 188 | movl $0x10,%eax 189 | mov %ax,%ds 190 | mov %ax,%es 191 | mov %ax,%fs 192 | 193 | # 调用 实际的处理函数,一开始压入栈中的那个 194 | call *%ebx 195 | 196 | # 恢复堆栈,让堆栈指针重新指向寄存器 fs 入栈处 197 | addl $8,%esp 198 | 199 | # 恢复上下文 200 | pop %fs 201 | pop %es 202 | pop %ds 203 | popl %ebp 204 | popl %esi 205 | popl %edi 206 | popl %edx 207 | popl %ecx 208 | popl %ebx 209 | popl %eax 210 | 211 | # 中断返回 212 | iret 213 | 214 | # int 10 / 无效的任务状态段(TSS) 215 | invalid_TSS: 216 | pushl $do_invalid_TSS 217 | jmp error_code 218 | 219 | # int 11 / 段不存在 220 | segment_not_present: 221 | pushl $do_segment_not_present 222 | jmp error_code 223 | 224 | # int 12 / 堆栈段错误 225 | stack_segment: 226 | pushl $do_stack_segment 227 | jmp error_code 228 | 229 | # int 13 / 一般保护性出错 230 | general_protection: 231 | pushl $do_general_protection 232 | jmp error_code 233 | 234 | # int 7 / 设备不存在 device_not_available 在 kernel/system_call.s 235 | # int 14 / 页错误 page_fault 在 mm/page.s 236 | # int 16 / 协处理器错误 coprocessor_error 在 kernel/system_call.s 237 | # 时钟中断 int 0x20 timer_interrupt 在 kernel/system_call.s 238 | # 系统调用 int 0x80 system_call 在 kernel/system_call.s 239 | -------------------------------------------------------------------------------- /linux-0.11/kernel/blk_drv/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for the FREAX-kernel block device drivers. 3 | # 4 | # Note! Dependencies are done automagically by 'make dep', which also 5 | # removes any old dependencies. DON'T put your own dependencies here 6 | # unless it's something special (ie not a .c file). 7 | # 8 | 9 | include ../../Option.mk 10 | 11 | CFLAGS += -I../../include 12 | CPP += -I../../include 13 | 14 | .c.s: 15 | $(CC) $(CFLAGS) \ 16 | -S -o $*.s $< 17 | .s.o: 18 | $(AS) -o $*.o $< 19 | .c.o: 20 | $(CC) $(CFLAGS) \ 21 | -c -o $*.o $< 22 | 23 | OBJS = ll_rw_blk.o floppy.o hd.o ramdisk.o 24 | 25 | blk_drv.a: $(OBJS) 26 | $(AR) rcs blk_drv.a $(OBJS) 27 | sync 28 | 29 | clean: 30 | rm -f core *.o *.a tmp_make 31 | for i in *.c;do rm -f `basename $$i .c`.s;done 32 | 33 | dep: 34 | sed '/\#\#\# Dependencies/q' < Makefile > tmp_make 35 | (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ 36 | $(CPP) -M $$i;done) >> tmp_make 37 | cp tmp_make Makefile 38 | 39 | ### Dependencies: 40 | floppy.s floppy.o: floppy.c ../../include/linux/sched.h ../../include/linux/head.h \ 41 | ../../include/linux/fs.h ../../include/sys/types.h \ 42 | ../../include/linux/mm.h ../../include/signal.h \ 43 | ../../include/linux/kernel.h ../../include/linux/fdreg.h \ 44 | ../../include/asm/system.h ../../include/asm/io.h \ 45 | ../../include/asm/segment.h blk.h 46 | hd.s hd.o: hd.c ../../include/linux/config.h ../../include/linux/sched.h \ 47 | ../../include/linux/head.h ../../include/linux/fs.h \ 48 | ../../include/sys/types.h ../../include/linux/mm.h \ 49 | ../../include/signal.h ../../include/linux/kernel.h \ 50 | ../../include/linux/hdreg.h ../../include/asm/system.h \ 51 | ../../include/asm/io.h ../../include/asm/segment.h blk.h 52 | ll_rw_blk.s ll_rw_blk.o: ll_rw_blk.c ../../include/errno.h \ 53 | ../../include/linux/sched.h ../../include/linux/head.h \ 54 | ../../include/linux/fs.h ../../include/sys/types.h \ 55 | ../../include/linux/mm.h ../../include/signal.h \ 56 | ../../include/linux/kernel.h ../../include/asm/system.h blk.h 57 | ramdisk.s ramdisk.o: ramdisk.c ../../include/string.h ../../include/linux/config.h \ 58 | ../../include/linux/sched.h ../../include/linux/head.h \ 59 | ../../include/linux/fs.h ../../include/sys/types.h \ 60 | ../../include/linux/mm.h ../../include/signal.h \ 61 | ../../include/linux/kernel.h ../../include/asm/system.h \ 62 | ../../include/asm/segment.h ../../include/asm/memory.h blk.h 63 | -------------------------------------------------------------------------------- /linux-0.11/kernel/blk_drv/blk.h: -------------------------------------------------------------------------------- 1 | #ifndef _BLK_H 2 | #define _BLK_H 3 | 4 | // 块设备的数量 5 | #define NR_BLK_DEV 7 6 | 7 | // 下面定义的NR_REQUEST 是请求队列中所包含的项数 8 | // 注意,读操作仅使用这些项低端的2/3;读操作优先处理 9 | 10 | // 32 项好象是一个合理的数字:已经足够从电梯算法中获得好处 11 | // 但当缓冲区在队列中而锁住时又不显得是很大的数 12 | // 64 就看上去太大了(当大量的写/同步操作运行时很容易引起长时间的暂停) 13 | #define NR_REQUEST 32 14 | 15 | // OK,下面是 request 结构的一个扩展形式 16 | // 因而当实现以后,我们就可以在分页请求中使用同样的 request 结构 17 | // 在分页处理中,bh 是 NULL,而 waiting 则用于等待读/写的完成 18 | 19 | // 下面是请求队列中项的结构。其中如果dev=-1,则表示该项没有被使用 20 | struct request 21 | { 22 | int dev; // 使用的设备号 23 | int cmd; // 命令(READ 或 WRITE) 24 | int errors; //操作时产生的错误次数 25 | unsigned long sector; // 起始扇区。(1 块=2 扇区) 26 | unsigned long nr_sectors; // 读/写扇区数 27 | char *buffer; // 数据缓冲区 28 | struct task_struct *waiting; // 任务等待操作执行完成的地方 29 | struct buffer_head *bh; // 缓冲区头指针 30 | struct request *next; // 指向下一请求项 31 | }; 32 | 33 | /* 34 | * This is used in the elevator algorithm: Note that 35 | * reads always go before writes. This is natural: reads 36 | * are much more time-critical than writes. 37 | */ 38 | 39 | // 下面的定义用于电梯算法:注意读操作总是在写操作之前进行 40 | // 这是很自然的:读操作对时间的要求要比写操作严格得多 41 | 42 | #define IN_ORDER(s1, s2) \ 43 | ((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \ 44 | ((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \ 45 | (s1)->sector < (s2)->sector)))) 46 | 47 | // 块设备结构 48 | struct blk_dev_struct 49 | { 50 | // 请求操作的函数指针 51 | void (*request_fn)(void); 52 | 53 | // 请求信息结构 54 | struct request *current_request; 55 | }; 56 | 57 | // 块设备表(数组),每种块设备占用一项 58 | extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; 59 | 60 | // 请求队列数组 61 | extern struct request request[NR_REQUEST]; 62 | 63 | // 等待空闲请求的任务结构队列头指针 64 | extern struct task_struct *wait_for_request; 65 | 66 | // 在块设备驱动程序(如hd.c)要包含此头文件时 67 | // 必须先定义驱动程序对应设备的主设备号 68 | // 这样下面就能为包含本文件的驱动程序给出正确的宏定义 69 | #ifdef MAJOR_NR 70 | 71 | // 需要时加入条目,目前块设备仅支持硬盘和软盘(还有虚拟盘) 72 | 73 | // RAM 盘的主设备号是 1,根据这里的定义可以推理内存块主设备号也为 1 74 | #if (MAJOR_NR == 1) 75 | 76 | // RAM 盘(内存虚拟盘) 77 | 78 | // 设备名称ramdisk 79 | #define DEVICE_NAME "ramdisk" 80 | 81 | // 设备请求函数do_rd_request() 82 | #define DEVICE_REQUEST do_rd_request 83 | 84 | // 设备号(0--7) 85 | #define DEVICE_NR(device) ((device)&7) 86 | 87 | // 开启设备。虚拟盘无须开启和关闭 88 | #define DEVICE_ON(device) 89 | 90 | // 关闭设备 91 | #define DEVICE_OFF(device) 92 | 93 | // 软驱的主设备号是 2 94 | #elif (MAJOR_NR == 2) 95 | /* floppy */ 96 | 97 | // 设备名称 floppy 98 | #define DEVICE_NAME "floppy" 99 | 100 | // 设备中断处理程序 do_floppy() 101 | #define DEVICE_INTR do_floppy 102 | 103 | // 设备请求函数 do_fd_request() 104 | #define DEVICE_REQUEST do_fd_request 105 | 106 | // 设备号(0--3) 107 | #define DEVICE_NR(device) ((device)&3) 108 | 109 | // 开启设备函数 floppyon() 110 | #define DEVICE_ON(device) floppy_on(DEVICE_NR(device)) 111 | 112 | // 关闭设备函数 floppyoff() 113 | #define DEVICE_OFF(device) floppy_off(DEVICE_NR(device)) 114 | 115 | // 硬盘主设备号是 3 116 | #elif (MAJOR_NR == 3) 117 | /* harddisk */ 118 | 119 | // 硬盘名称harddisk 120 | #define DEVICE_NAME "harddisk" 121 | 122 | // 设备中断处理程序do_hd() 123 | #define DEVICE_INTR do_hd 124 | 125 | // 设备请求函数do_hd_request() 126 | #define DEVICE_REQUEST do_hd_request 127 | 128 | // 设备号(0--1)。每个硬盘可以有4 个分区 129 | #define DEVICE_NR(device) (MINOR(device) / 5) 130 | 131 | // 硬盘一直在工作,无须开启和关闭 132 | #define DEVICE_ON(device) 133 | #define DEVICE_OFF(device) 134 | 135 | #else 136 | // 未知块设备 137 | #error "unknown blk device" 138 | 139 | #endif 140 | 141 | // CURRENT 是指定主设备号的当前请求结构 142 | #define CURRENT (blk_dev[MAJOR_NR].current_request) 143 | 144 | // CURRENT_DEV 是 CURRENT 的设备号 145 | #define CURRENT_DEV DEVICE_NR(CURRENT->dev) 146 | 147 | // 下面申明两个宏定义为函数指针 148 | #ifdef DEVICE_INTR 149 | void (*DEVICE_INTR)(void) = NULL; 150 | #endif 151 | static void(DEVICE_REQUEST)(void); 152 | 153 | // / 释放锁定的缓冲区(块) 154 | static inline void unlock_buffer(struct buffer_head *bh) 155 | { 156 | // 如果指定的缓冲区 bh 并没有被上锁,则显示警告信息 157 | if (!bh->b_lock) 158 | printk(DEVICE_NAME ": free buffer being unlocked\n"); 159 | 160 | // 否则将该缓冲区解锁 161 | bh->b_lock = 0; 162 | 163 | // 唤醒等待该缓冲区的进程 164 | wake_up(&bh->b_wait); 165 | } 166 | 167 | // 结束请求处理 168 | // 首先关闭指定块设备,然后检查此次读写缓冲区是否有效 169 | // 如果有效则根据参数值设置缓冲区数据更新标志,并解锁该缓冲区 170 | // 如果更新标志参数值是 0,表示此次请求项的操作已失败 171 | // 因此显示相关块设备 IO 错误信息 172 | // 最后,唤醒等待该请求项的进程以及等待空闲请求项出现的进程 173 | // 释放并从请求链表中删除本请求项 174 | static inline void end_request(int uptodate) 175 | { 176 | DEVICE_OFF(CURRENT->dev); // 关闭设备 177 | 178 | if (CURRENT->bh) // CURRENT 为指定主设备号的当前请求结构 179 | { 180 | // 置更新标志 181 | CURRENT->bh->b_uptodate = uptodate; 182 | 183 | // 解锁缓冲区 184 | unlock_buffer(CURRENT->bh); 185 | } 186 | if (!uptodate) // 如果更新标志为 0 则显示设备错误信息 187 | { 188 | printk(DEVICE_NAME " I/O error\n\r"); 189 | printk("dev %04x, block %d\n\r", CURRENT->dev, 190 | CURRENT->bh->b_blocknr); 191 | } 192 | wake_up(&CURRENT->waiting); // 唤醒等待该请求项的进程 193 | wake_up(&wait_for_request); // 唤醒等待请求的进程 194 | CURRENT->dev = -1; // 释放该请求项 195 | 196 | // 从请求链表中删除该请求项,并且当前请求项指针指向下一个请求项 197 | CURRENT = CURRENT->next; 198 | } 199 | 200 | // 定义初始化请求宏 201 | #define INIT_REQUEST \ 202 | repeat: \ 203 | if (!CURRENT) /* 如果当前请求结构指针为null 则返回 */ \ 204 | return; /* 表示本设备目前已无需要处理的请求项 */ \ 205 | if (MAJOR(CURRENT->dev) != MAJOR_NR) /* 如果当前设备的主设备号不对则死机 */ \ 206 | panic(DEVICE_NAME ": request list destroyed"); \ 207 | if (CURRENT->bh) \ 208 | { \ 209 | if (!CURRENT->bh->b_lock) /* 如果在进行请求操作时缓冲区没锁定则死机 */ \ 210 | panic(DEVICE_NAME ": block not locked"); \ 211 | } 212 | 213 | #endif 214 | 215 | #endif 216 | -------------------------------------------------------------------------------- /linux-0.11/kernel/blk_drv/ll_rw_blk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/blk_dev/ll_rw.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // 该程序处理块设备的所有读/写操作 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "blk.h" 15 | 16 | // 请求结构中含有加载 nr 扇区数据到内存中的所有必须的信息 17 | struct request request[NR_REQUEST]; 18 | 19 | // 是用于在请求数组没有空闲项时进程的临时等待处 20 | struct task_struct *wait_for_request = NULL; 21 | 22 | // blk_dev_struct 块设备结构是: 23 | // do_request-address // 对应主设备号的请求处理程序指针 24 | // current-request // 该设备的下一个请求 25 | 26 | // 该数组使用主设备号作为索引 27 | // 实际内容将在各种块设备驱动程序初始化时填入 28 | // 例如,硬盘驱动程序进行初始化时 29 | // 第一条语句即用于设置 blk_dev[3] 的内容 30 | struct blk_dev_struct blk_dev[NR_BLK_DEV] = { 31 | {NULL, NULL}, // 0 - 无设备 32 | {NULL, NULL}, // 1 - 内存 33 | {NULL, NULL}, // 2 - 软驱设备 34 | {NULL, NULL}, // 3 - 硬盘设备 35 | {NULL, NULL}, // 4 - ttyx 设备 36 | {NULL, NULL}, // 5 - tty 设备 37 | {NULL, NULL} // 6 - lp 打印机设备 38 | }; 39 | 40 | // 锁定指定的缓冲区 bh 41 | // 如果指定的缓冲区已经被其它任务锁定,则使自己睡眠(不可中断地等待) 42 | // 直到被执行解锁缓冲区的任务明确地唤醒 43 | static inline void lock_buffer(struct buffer_head *bh) 44 | { 45 | cli(); // 关中断 46 | 47 | // 如果缓冲区已被锁定,则睡眠,直到缓冲区解锁 48 | while (bh->b_lock) 49 | sleep_on(&bh->b_wait); 50 | 51 | // 立刻锁定该缓冲区 52 | bh->b_lock = 1; 53 | sti(); // 开中断 54 | } 55 | 56 | // 释放(解锁)锁定的缓冲区 57 | static inline void unlock_buffer(struct buffer_head *bh) 58 | { 59 | // 如果该缓冲区并没有被锁定,则打印出错信息 60 | if (!bh->b_lock) 61 | printk("ll_rw_block.c: buffer not locked\n\r"); 62 | 63 | // 清锁定标志 64 | bh->b_lock = 0; 65 | 66 | // 唤醒等待该缓冲区的任务 67 | wake_up(&bh->b_wait); 68 | } 69 | 70 | // add-request() 向链表中加入一项请求 71 | // 它会关闭中断,这样就能安全地处理请求链表了 72 | 73 | // 向链表中加入请求项,参数 dev 指定块设备,req 是请求项结构信息指针 74 | static void add_request(struct blk_dev_struct *dev, struct request *req) 75 | { 76 | struct request *tmp; 77 | 78 | req->next = NULL; 79 | cli(); // 关中断 80 | if (req->bh) 81 | req->bh->b_dirt = 0; // 清缓冲区 脏 标志 82 | 83 | // 如果 dev 的当前请求(current_request)子段为空,则表示目前该设备没有请求项 84 | // 本次是第 1 个请求项,因此可将块设备当前请求指针直接指向该请求项,并立刻执行相应设备的请求函数 85 | if (!(tmp = dev->current_request)) 86 | { 87 | dev->current_request = req; 88 | 89 | // 开中断 90 | sti(); 91 | 92 | // 执行设备请求函数,对于硬盘是 do_hd_request() 93 | (dev->request_fn)(); 94 | return; 95 | } 96 | 97 | // 如果目前该设备已经有请求项在等待,则首先利用电梯算法搜索最佳插入位置 98 | // 然后将当前请求插入到请求链表中,电梯算法的作用是让磁盘磁头的移动距离最小,从而改善硬盘访问时间 99 | for (; tmp->next; tmp = tmp->next) 100 | if ((IN_ORDER(tmp, req) || 101 | !IN_ORDER(tmp, tmp->next)) && 102 | IN_ORDER(req, tmp->next)) 103 | break; 104 | req->next = tmp->next; 105 | tmp->next = req; 106 | sti(); 107 | } 108 | 109 | // 创建请求项并插入请求队列 110 | // 参数是:主设备号 major,命令 rw,存放数据的缓冲区头指针 bh 111 | static void make_request(int major, int rw, struct buffer_head *bh) 112 | { 113 | struct request *req; 114 | int rw_ahead; 115 | 116 | // WRITEA/READA 是一种特殊情况 - 它们并非必要,所以如果缓冲区已经上锁 117 | // 我们就不管它而退出,否则的话就执行一般的读/写操作 118 | 119 | // 这里'READ'和'WRITE'后面的'A'字符代表英文单词Ahead,表示提前预读/写数据块的意思 120 | // 对于命令是 READA/WRITEA 的情况,当指定的缓冲区正在使用,已被上锁时,就放弃预读/写请求 121 | // 否则就作为普通的 READ/WRITE 命令进行操作 122 | if ((rw_ahead = (rw == READA || rw == WRITEA))) 123 | { 124 | if (bh->b_lock) 125 | return; 126 | if (rw == READA) 127 | rw = READ; 128 | else 129 | rw = WRITE; 130 | } 131 | 132 | // 如果命令不是 READ 或 WRITE 则表示内核程序有错,显示出错信息并死机 133 | if (rw != READ && rw != WRITE) 134 | panic("Bad block dev command, must be R/W/RA/WA"); 135 | 136 | // 锁定缓冲区,如果缓冲区已经上锁,则当前任务(进程)就会睡眠,直到被明确地唤醒 137 | lock_buffer(bh); 138 | 139 | // 如果命令是写并且缓冲区数据不脏(没有被修改过) 140 | // 或者命令是读并且缓冲区数据是更新过的 141 | // 则不用添加这个请求,将缓冲区解锁并退出 142 | if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) 143 | { 144 | unlock_buffer(bh); 145 | return; 146 | } 147 | repeat: 148 | // 我们不能让队列中全都是写请求项:我们需要为读请求保留一些空间 149 | // 读操作是优先的,请求队列的后三分之一空间是为读准备的 150 | 151 | // 请求项是从请求数组末尾开始搜索空项填入的 152 | // 根据上述要求,对于读命令请求,可以直接从队列末尾开始操作 153 | // 而写请求则只能从队列 2/3 处向队列头处搜索空项填入 154 | if (rw == READ) 155 | // 对于读请求,将队列指针指向队列尾部 156 | req = request + NR_REQUEST; 157 | else 158 | // 对于写请求,队列指针指向队列 2/3 处 159 | req = request + ((NR_REQUEST * 2) / 3); 160 | 161 | // 搜索一个空请求项 162 | // 从后向前搜索,当请求结构 request 的 dev 字段值 =-1 时,表示该项未被占用 163 | while (--req >= request) 164 | if (req->dev < 0) 165 | break; 166 | 167 | // 如果没有找到空闲项,则让该次新请求睡眠:需检查是否提前读/写 168 | // 如果没有一项是空闲的(此时request 数组指针已经搜索越过头部) 169 | // 则查看此次请求是否是提前读/写(READA 或WRITEA) 170 | // 如果是则放弃此次请求 171 | // 否则让本次请求睡眠(等待请求队列腾出空项),过一会再来搜索请求队列 172 | if (req < request) 173 | { 174 | // 如果是提前读/写请求,则解锁缓冲区,退出 175 | if (rw_ahead) 176 | { 177 | unlock_buffer(bh); 178 | return; 179 | } 180 | 181 | // 否则让本次请求睡眠,过会再查看请求队列 182 | sleep_on(&wait_for_request); 183 | goto repeat; 184 | } 185 | 186 | // 向空闲请求项中填写请求信息,并将其加入队列中 187 | // 程序执行到这里表示已找到一个空闲请求项 188 | 189 | // 设备号 190 | req->dev = bh->b_dev; 191 | 192 | // 命令(READ/WRITE) 193 | req->cmd = rw; 194 | 195 | // 操作时产生的错误次数 196 | req->errors = 0; 197 | 198 | // 起始扇区,块号转换成扇区号 199 | req->sector = bh->b_blocknr << 1; 200 | 201 | // 读写扇区数 202 | req->nr_sectors = 2; 203 | 204 | // 数据缓冲区 205 | req->buffer = bh->b_data; 206 | 207 | // 任务等待操作执行完成的地方 208 | req->waiting = NULL; 209 | 210 | // 缓冲块头指针 211 | req->bh = bh; 212 | 213 | // 指向下一请求项 214 | req->next = NULL; 215 | 216 | // 将请求项加入队列中 217 | add_request(major + blk_dev, req); 218 | } 219 | 220 | // 低层读写数据块函数,是块设备与系统其它部分的接口函数 221 | // 该函数在 fs/buffer.c 中被调用 222 | // 主要功能是创建块设备读写请求项,并插入到指定块设备请求队列中 223 | // 实际的读写操作则是由设备的 request_fn() 函数完成 224 | // 对于硬盘操作,该函数是 do_hd_request(); 225 | // 对于软盘操作,该函数是 do_fd_request(); 226 | // 对于虚拟盘则是 do_rd_request(); 227 | // 另外,需要读/写块设备的信息已保存在缓冲块头结构中,如设备号、块号 228 | // 参数:rw – READ、READA、WRITE 或 WRITEA 命令;bh – 数据缓冲块头指针 229 | void ll_rw_block(int rw, struct buffer_head *bh) 230 | { 231 | // 主设备号(对于硬盘是3) 232 | unsigned int major; 233 | 234 | // 如果设备的主设备号不存在或者该设备的读写操作函数不存在,则显示出错信息,并返回 235 | if ((major = MAJOR(bh->b_dev)) >= NR_BLK_DEV || 236 | !(blk_dev[major].request_fn)) 237 | { 238 | printk("Trying to read nonexistent block-device\n\r"); 239 | return; 240 | } 241 | // 创建请求项并插入请求队列 242 | make_request(major, rw, bh); 243 | } 244 | 245 | 246 | // 块设备初始化函数,由初始化程序 main.c 调用 247 | // 初始化请求数组,将所有请求项置为空闲项(dev = -1),有 32 项(NR_REQUEST = 32) 248 | void blk_dev_init(void) 249 | { 250 | int i; 251 | 252 | for (i = 0; i < NR_REQUEST; i++) 253 | { 254 | request[i].dev = -1; 255 | request[i].next = NULL; 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /linux-0.11/kernel/blk_drv/ramdisk.c: -------------------------------------------------------------------------------- 1 | 2 | // 由 Theodore Ts'o 实现,1991-12-02 3 | 4 | // Theodore Ts'o (Ted Ts'o) 是 linux 社区中的著名人物 5 | // Linux 在世界范围内的流行也有他很大的功劳 6 | // 早在Linux 操作系统刚问世时,他就怀着极大的热情为 linux 的发展提供了 maillist 7 | // 并在北美洲地区最早设立了 linux 的 ftp 站点(tsx-11.mit.edu) 8 | // 而且至今仍然为广大 linux 用户提供服务 9 | // 他对 linux 作出的最大贡献之一是提出并实现了 ext2 文件系统 10 | // 该文件系统已成为 linux 世界中事实上的文件系统标准 11 | // 最近他又推出了 ext3 文件系统,大大提高了文件系统的稳定性和访问效率 12 | // 作为对他的推崇,第 97 期(2002 年5 月)的 linuxjournal 期刊将他作为了封面人物 13 | // 并对他进行了采访。目前,他为 IBM linux 技术中心工作 14 | // 并从事着有关 LSB (Linux Standard Base) 等方面的工作 15 | // (他的主页:http://thunk.org/tytso/) 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // RAM 盘主设备号是 1,主设备号必须在 blk.h 之前被定义 28 | #define MAJOR_NR 1 29 | #include "blk.h" 30 | 31 | // 虚拟盘在内存中的起始位置,初始化函数 rd_init() 中确定 32 | char *rd_start; 33 | 34 | // 虚拟盘所占内存大小(字节) 35 | int rd_length = 0; 36 | 37 | // 虚拟盘当前请求项操作函数 38 | // 程序结构与 do_hd_request() 类似 39 | // 在低级块设备接口函数 ll_rw_block() 建立了虚拟盘(rd)的请求项并添加到 rd 的链表中之后 40 | // 就会调用该函数对 rd 当前请求项进行处理 41 | // 该函数首先计算当前请求项中,指定的起始扇区对应虚拟盘所处内存的起始位置 addr 42 | // 和要求的扇区数对应的字节长度值 len 43 | // 然后根据请求项中的命令进行操作 44 | // 若是写命令 WRITE,就把请求项所指缓冲区中的数据直接复制到内存位置 addr 处 45 | // 若是读操作则反之,数据复制完成后即可直接调用 end_request() 对本次请求项作结束处理 46 | // 然后跳转到函数开始处再去处理下一个请求项 47 | void do_rd_request(void) 48 | { 49 | int len; 50 | char *addr; 51 | 52 | // 检测请求项的合法性,若已没有请求项则退出 53 | INIT_REQUEST; 54 | 55 | // 下面语句取得 ramdisk 的起始扇区对应的内存起始位置和内存长度 56 | // 其中 sector << 9 表示 sector * 512,CURRENT 定义为(blk_dev[MAJOR_NR].current_request) 57 | addr = rd_start + (CURRENT->sector << 9); 58 | len = CURRENT->nr_sectors << 9; 59 | 60 | // 如果子设备号不为 1 或者对应内存起始位置 > 虚拟盘末尾,则结束该请求,并跳转到 repeat 处 61 | // 标号 repeat 定义在宏 INIT_REQUEST 内,位于宏的开始处 62 | if ((MINOR(CURRENT->dev) != 1) || (addr + len > rd_start + rd_length)) 63 | { 64 | end_request(0); 65 | goto repeat; 66 | } 67 | 68 | // 如果是写命令(WRITE),则将请求项中缓冲区的内容复制到 addr 处,长度为 len 字节 69 | if (CURRENT->cmd == WRITE) 70 | { 71 | (void)memcpy(addr, 72 | CURRENT->buffer, 73 | len); 74 | } 75 | 76 | // 如果是读命令(READ),则将 addr 开始的内容复制到请求项中缓冲区中,长度为 len 字节 77 | else if (CURRENT->cmd == READ) 78 | { 79 | (void)memcpy(CURRENT->buffer, 80 | addr, 81 | len); 82 | } 83 | // 否则显示命令不存在,死机 84 | else 85 | panic("unknown ramdisk-command"); 86 | 87 | // 请求项成功后处理,置更新标志,并继续处理本设备的下一请求项 88 | end_request(1); 89 | goto repeat; 90 | } 91 | 92 | // 返回内存虚拟盘 ramdisk 所需的内存量 93 | 94 | // 虚拟盘初始化函数:确定虚拟盘在内存中的起始地址,长度,并对整个虚拟盘区清零 95 | long rd_init(long mem_start, int length) 96 | { 97 | int i; 98 | char *cp; 99 | 100 | // do_rd_request() 101 | blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; 102 | 103 | // 对于 16MB 系统,该值为 4MB 104 | rd_start = (char *)mem_start; 105 | rd_length = length; 106 | cp = rd_start; 107 | for (i = 0; i < length; i++) 108 | *cp++ = '\0'; 109 | return (length); 110 | } 111 | 112 | // 如果根文件系统设备(root device)是 ramdisk 的话,则尝试加载它 113 | // root device 原先是指向软盘的,我们将它改成指向 ramdisk 114 | 115 | // 尝试把根文件系统加载到虚拟盘中 116 | // 该函数将在内核设置函数 setup() 中被调用;另外,1 磁盘块 = 1024 字节 117 | void rd_load(void) 118 | { 119 | // 高速缓冲块头指针 120 | struct buffer_head *bh; 121 | 122 | // 文件超级块结构 123 | struct super_block s; 124 | 125 | /* 表示根文件系统映象文件在 boot 盘第 256 磁盘块开始处 */ 126 | int block = 256; /* Start at block 256 */ 127 | int i = 1; 128 | int nblocks; 129 | char *cp; /* Move pointer */ 130 | 131 | // 如果 ramdisk 的长度为零,则退出 132 | if (!rd_length) 133 | return; 134 | 135 | // 显示 ramdisk 的大小以及内存起始位置 136 | printk("Ram disk: %d bytes, starting at 0x%x\n", rd_length, 137 | (int)rd_start); 138 | 139 | // 如果此时根文件设备不是软盘,则退出 140 | if (MAJOR(ROOT_DEV) != 2) 141 | return; 142 | 143 | // 读软盘块 256+1,256,256+2。breada() 用于读取指定的数据块, 144 | // 并标出还需要读的块,然后返回含有数据块的缓冲区指针 145 | // 如果返回NULL,则表示数据块不可读 146 | // 这里 block+1 是指磁盘上的超级块 147 | bh = breada(ROOT_DEV, block + 1, block, block + 2, -1); 148 | if (!bh) 149 | { 150 | printk("Disk error while looking for ramdisk!\n"); 151 | return; 152 | } 153 | 154 | // 将 s 指向缓冲区中的磁盘超级块 (d_super_block 磁盘中超级块结构) 155 | *((struct d_super_block *)&s) = *((struct d_super_block *)bh->b_data); 156 | brelse(bh); 157 | 158 | // 如果超级块中魔数不对,则说明不是 minix 文件系统 159 | if (s.s_magic != SUPER_MAGIC) 160 | /* No ram disk image present, assume normal floppy boot */ 161 | // 磁盘中没有 ramdisk 映像文件,退出去执行通常的软盘引导 162 | return; 163 | 164 | // 块数 = 逻辑块数(区段数) * 2^(每区段块数的次方) 165 | // 如果数据块数大于内存中虚拟盘所能容纳的块数,则也不能加载 166 | // 显示出错信息并返回,否则显示加载数据块信息 167 | nblocks = s.s_nzones << s.s_log_zone_size; 168 | if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) 169 | { 170 | printk("Ram disk image too big! (%d blocks, %d avail)\n", 171 | nblocks, rd_length >> BLOCK_SIZE_BITS); 172 | return; 173 | } 174 | printk("Loading %d bytes into ram disk... 0000k", 175 | nblocks << BLOCK_SIZE_BITS); 176 | 177 | // cp 指向虚拟盘起始处,然后将磁盘上的根文件系统映象文件复制到虚拟盘上 178 | cp = rd_start; 179 | while (nblocks) 180 | { 181 | // 如果需读取的块数多于 3 快则采用超前预读方式读数据块 182 | if (nblocks > 2) 183 | bh = breada(ROOT_DEV, block, block + 1, block + 2, -1); 184 | 185 | // 否则就单块读取 186 | else 187 | bh = bread(ROOT_DEV, block); 188 | if (!bh) 189 | { 190 | printk("I/O error on block %d, aborting load\n", 191 | block); 192 | return; 193 | } 194 | 195 | // 将缓冲区中的数据复制到 cp 处 196 | (void)memcpy(cp, bh->b_data, BLOCK_SIZE); 197 | 198 | // 释放缓冲区 199 | brelse(bh); 200 | 201 | // 打印加载块计数值 202 | printk("\010\010\010\010\010%4dk", i); 203 | 204 | // 虚拟盘指针前移 205 | cp += BLOCK_SIZE; 206 | block++; 207 | nblocks--; 208 | i++; 209 | } 210 | printk("\010\010\010\010\010done \n"); 211 | 212 | // 修改 ROOT_DEV 使其指向虚拟盘 ramdisk 213 | ROOT_DEV = 0x0101; 214 | } 215 | -------------------------------------------------------------------------------- /linux-0.11/kernel/chr_drv/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for the FREAX-kernel character device drivers. 3 | # 4 | # Note! Dependencies are done automagically by 'make dep', which also 5 | # removes any old dependencies. DON'T put your own dependencies here 6 | # unless it's something special (ie not a .c file). 7 | # 8 | 9 | include ../../Option.mk 10 | 11 | CFLAGS += -I../../include 12 | CPP += -I../../include 13 | 14 | .c.s: 15 | $(CC) $(CFLAGS) \ 16 | -S -o $*.s $< 17 | .s.o: 18 | $(AS) -o $*.o $< 19 | .c.o: 20 | $(CC) $(CFLAGS) \ 21 | -c -o $*.o $< 22 | 23 | OBJS = tty_io.o console.o keyboard.o serial.o rs_io.o \ 24 | tty_ioctl.o 25 | 26 | chr_drv.a: $(OBJS) 27 | $(AR) rcs chr_drv.a $(OBJS) 28 | sync 29 | 30 | keyboard.s: keyboard.S ../../include/linux/config.h 31 | $(CPP) keyboard.S -o keyboard.s 32 | 33 | clean: 34 | rm -f core *.o *.a tmp_make keyboard.s 35 | for i in *.c;do rm -f `basename $$i .c`.s;done 36 | 37 | dep: 38 | sed '/\#\#\# Dependencies/q' < Makefile > tmp_make 39 | (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ 40 | $(CPP) -M $$i;done) >> tmp_make 41 | cp tmp_make Makefile 42 | 43 | ### Dependencies: 44 | console.s console.o: console.c ../../include/linux/sched.h \ 45 | ../../include/linux/head.h ../../include/linux/fs.h \ 46 | ../../include/sys/types.h ../../include/linux/mm.h \ 47 | ../../include/signal.h ../../include/linux/tty.h \ 48 | ../../include/termios.h ../../include/asm/io.h \ 49 | ../../include/asm/system.h 50 | serial.s serial.o: serial.c ../../include/linux/tty.h ../../include/termios.h \ 51 | ../../include/linux/sched.h ../../include/linux/head.h \ 52 | ../../include/linux/fs.h ../../include/sys/types.h \ 53 | ../../include/linux/mm.h ../../include/signal.h \ 54 | ../../include/asm/system.h ../../include/asm/io.h 55 | tty_io.s tty_io.o: tty_io.c ../../include/ctype.h ../../include/errno.h \ 56 | ../../include/signal.h ../../include/sys/types.h \ 57 | ../../include/linux/sched.h ../../include/linux/head.h \ 58 | ../../include/linux/fs.h ../../include/linux/mm.h \ 59 | ../../include/linux/tty.h ../../include/termios.h \ 60 | ../../include/asm/segment.h ../../include/asm/system.h 61 | tty_ioctl.s tty_ioctl.o: tty_ioctl.c ../../include/errno.h ../../include/termios.h \ 62 | ../../include/linux/sched.h ../../include/linux/head.h \ 63 | ../../include/linux/fs.h ../../include/sys/types.h \ 64 | ../../include/linux/mm.h ../../include/signal.h \ 65 | ../../include/linux/kernel.h ../../include/linux/tty.h \ 66 | ../../include/asm/io.h ../../include/asm/segment.h \ 67 | ../../include/asm/system.h 68 | -------------------------------------------------------------------------------- /linux-0.11/kernel/chr_drv/rs_io.s: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/rs_io.s 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | # 该程序模块实现 rs232 输入输出中断处理程序 8 | 9 | .text 10 | .globl rs1_interrupt,rs2_interrupt 11 | 12 | # size 是读写队列缓冲区的字节长度 13 | # 必须是 2 的次方并且需与 tty_io.c 中的值匹配! 14 | size = 1024 15 | 16 | # 以下这些是读写缓冲结构中的偏移量 17 | # 对应定义在 include/linux/tty.h 文件中 tty_queue 结构中各变量的偏移量 18 | 19 | # 串行端口号字段偏移 20 | rs_addr = 0 21 | 22 | # 缓冲区中头指针字段偏移 23 | head = 4 24 | 25 | # 缓冲区中尾指针字段偏移 26 | tail = 8 27 | 28 | # 等待该缓冲的进程字段偏移 29 | proc_list = 12 30 | 31 | # 缓冲区字段偏移 32 | buf = 16 33 | 34 | # 当写队列里还剩 256 个字符空间 (WAKEUP_CHARS) 时,我们就可以写 35 | startup = 256 /* chars left in write queue when we restart it */ 36 | 37 | # 这些是实际的中断程序 38 | # 程序首先检查中断的来源,然后执行相应的处理 39 | .align 2 40 | # 串行端口1 中断处理程序入口点 41 | rs1_interrupt: 42 | # tty 表中对应串口1 的读写缓冲指针的地址入栈 43 | pushl $table_list+8 44 | 45 | # 字符缓冲队列结构格式请参见 include/linux/tty.h 46 | jmp rs_int 47 | 48 | .align 2 49 | rs2_interrupt: 50 | # 串行端口2 中断处理程序入口点 51 | pushl $table_list+16 52 | rs_int: 53 | # 保存上下文 54 | pushl %edx 55 | pushl %ecx 56 | pushl %ebx 57 | pushl %eax 58 | push %es 59 | push %ds 60 | 61 | # 由于这是一个中断程序,我们不知道 ds 是否正确 62 | # 所以加载它们(让ds、es 指向内核数据段 63 | pushl $0x10 64 | pop %ds 65 | pushl $0x10 66 | pop %es 67 | 68 | # 将缓冲队列指针地址存入 edx 寄存器 69 | # 也即上面最先压入堆栈的地址 70 | movl 24(%esp),%edx 71 | 72 | # 取读缓冲队列结构指针(地址) -> edx 73 | # 对于串行终端,data 字段存放着串行端口地址(端口号) 74 | movl (%edx),%edx 75 | 76 | # 取串口1(或串口2)的端口号 -> edx 77 | movl rs_addr(%edx),%edx 78 | 79 | # edx 指向中断标识寄存器 80 | addl $2,%edx 81 | # 中断标识寄存器端口是 0x3fa(0x2fa) 82 | rep_int: 83 | 84 | # eax 清零 85 | xorl %eax,%eax 86 | 87 | # 取中断标识字节,用以判断中断来源(有4 种中断情况) 88 | inb %dx,%al 89 | 90 | # 首先判断有无待处理的中断(位0=1 无中断;=0 有中断) 91 | testb $1,%al 92 | 93 | # 若无待处理中断,则跳转至退出处理处 end 94 | jne end 95 | 96 | # 这不会发生,但是 …… 97 | cmpb $6,%al 98 | 99 | # al 值>6? 是则跳转至end(没有这种状态) 100 | ja end 101 | 102 | # 再取缓冲队列指针地址 -> ecx 103 | movl 24(%esp),%ecx 104 | 105 | # 将中断标识寄存器端口号 0x3fa(0x2fa) 入栈 106 | pushl %edx 107 | 108 | # 0x3f8(0x2f8) 109 | subl $2,%edx 110 | 111 | # 不乘4,位 0 已是 0 112 | call *jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */ 113 | 114 | # 上面语句是指,当有待处理中断时,al 中位 0=0,位 2-1 是中断类型 115 | # 因此相当于已经将中断类型乘了 2,这里再乘 2, 116 | # 获得跳转表对应各中断类型地址,并跳转到那里去作相应处理 117 | # 中断来源有 4 种: 118 | # modem 状态发生变化; 119 | # 要写(发送)字符; 120 | # 要读(接收)字符; 121 | # 线路状态发生变化 122 | # 要发送字符中断是通过设置发送保持寄存器标志实现的 123 | # 在 serial.c 程序中的 rs_write() 函数中 124 | # 当写缓冲队列中有数据时,就会修改中断允许寄存器内容 125 | # 添加上发送保持寄存器中断允许标志,从而在系统需要发送字符时引起串行中断发生 126 | 127 | # 弹出中断标识寄存器端口号 0x3fa(或0x2fa) 128 | popl %edx 129 | 130 | # 跳转,继续判断有无待处理中断并继续处理 131 | jmp rep_int 132 | end: 133 | # 向中断控制器发送结束中断指令 EOI 134 | movb $0x20,%al 135 | outb %al,$0x20 /* EOI */ 136 | 137 | # 恢复上下文 138 | pop %ds 139 | pop %es 140 | popl %eax 141 | popl %ebx 142 | popl %ecx 143 | popl %edx 144 | 145 | # 丢弃缓冲队列指针地址 146 | addl $4,%esp # jump over _table_list entry 147 | iret 148 | 149 | # 各中断类型处理程序地址跳转表,共有 4 种中断来源: 150 | # modem 状态变化中断 151 | # 写字符中断 152 | # 读字符中断 153 | # 线路状态有问题中断 154 | jmp_table: 155 | .long modem_status,write_char,read_char,line_status 156 | 157 | # 由于 modem 状态发生变化而引发此次中断 158 | # 通过读 modem 状态寄存器对其进行复位操作 159 | .align 2 160 | modem_status: 161 | # 通过读 modem 状态寄存器进行复位(0x3fe) 162 | addl $6,%edx 163 | inb %dx,%al 164 | ret 165 | 166 | # 由于线路状态发生变化而引起这次串行中断 167 | # 通过读线路状态寄存器对其进行复位操作 168 | .align 2 169 | line_status: 170 | # 通过读线路状态寄存器进行复位(0x3fd) 171 | addl $5,%edx 172 | inb %dx,%al 173 | ret 174 | 175 | # 由于串行设备(芯片)接收到字符而引起这次中断 176 | # 将接收到的字符放到读缓冲队列read_q 头指针(head)处 177 | # 并且让该指针前移一个字符位置 178 | # 若 head 指针已经到达缓冲区末端,则让其折返到缓冲区开始处 179 | # 最后调用 C 函数 do_tty_interrupt(),也即 copy_to_cooked() 180 | # 把读入的字符经过一定处理,放入规范模式缓冲队列(辅助缓冲队列 secondary)中 181 | .align 2 182 | read_char: 183 | # 读取字符 -> al 184 | inb %dx,%al 185 | 186 | # 当前串口缓冲队列指针地址 -> edx 187 | movl %ecx,%edx 188 | 189 | # 缓冲队列指针表首址 - 当前串口队列指针地址 -> edx 190 | subl $table_list,%edx 191 | 192 | # 差值/8,对于串口1 是 1,对于串口2 是 2 193 | shrl $3,%edx 194 | 195 | # 取读缓冲队列结构地址 -> ecx 196 | movl (%ecx),%ecx # read-queue 197 | 198 | # 取读队列中缓冲头指针 -> ebx 199 | movl head(%ecx),%ebx 200 | 201 | # 将字符放在缓冲区中头指针所指的位置 202 | movb %al,buf(%ecx,%ebx) 203 | 204 | # 将头指针前移一字节 205 | incl %ebx 206 | 207 | # 用缓冲区大小对头指针进行模操作,指针不能超过缓冲区大小 208 | andl $size-1,%ebx 209 | 210 | # 缓冲区头指针与尾指针比较 211 | cmpl tail(%ecx),%ebx 212 | 213 | # 若相等,表示缓冲区满,跳转到标号1 处 214 | je 1f 215 | 216 | # 保存修改过的头指针 217 | movl %ebx,head(%ecx) 218 | 1: 219 | # 将串口号压入堆栈(1- 串口1,2 - 串口2),作为参数 220 | pushl %edx 221 | 222 | # 调用 tty 中断处理 C 函数 223 | call do_tty_interrupt 224 | 225 | # 丢弃入栈参数,并返回 226 | addl $4,%esp 227 | ret 228 | 229 | # 由于设置了发送保持寄存器允许中断标志而引起此次中断 230 | # 说明对应串行终端的写字符缓冲队列中有字符需要发送 231 | # 于是计算出写队列中当前所含字符数 232 | # 若字符数已小于 256 个则唤醒等待写操作进程 233 | # 然后从写缓冲队列尾部取出一个字符发送,并调整和保存尾指针 234 | # 如果写缓冲队列已空,则跳转到 write_buffer_empty,处理写缓冲队列空的情况 235 | .align 2 236 | write_char: 237 | # 取写缓冲队列结构地址 -> ecx 238 | movl 4(%ecx),%ecx # write-queue 239 | 240 | # 取写队列头指针 -> ebx 241 | movl head(%ecx),%ebx 242 | 243 | # 头指针 - 尾指针 = 队列中字符数 244 | subl tail(%ecx),%ebx 245 | 246 | # 对指针取模运算 247 | andl $size-1,%ebx # nr chars in queue 248 | 249 | # 如果头指针 = 尾指针,说明写队列无字符,跳转处理 250 | je write_buffer_empty 251 | 252 | # 队列中字符数超过 256 个? 253 | cmpl $startup,%ebx 254 | 255 | # 超过,则跳转处理 256 | ja 1f 257 | 258 | # 唤醒等待的进程 259 | # 取等待该队列的进程的指针,并判断是否为空 260 | movl proc_list(%ecx),%ebx # wake up sleeping process 261 | 262 | # 有等待的进程吗? 263 | testl %ebx,%ebx # is there any? 264 | 265 | # 是空的,则向前跳转到标号1 处 266 | je 1f 267 | 268 | # 否则将进程置为可运行状态(唤醒进程)。。 269 | movl $0,(%ebx) 270 | 1: 271 | # 取尾指针 272 | movl tail(%ecx),%ebx 273 | 274 | # 从缓冲中尾指针处取一字符 -> al 275 | movb buf(%ecx,%ebx),%al 276 | 277 | # 向端口 0x3f8(0x2f8) 送出到保持寄存器中 278 | outb %al,%dx 279 | 280 | # 尾指针前移 281 | incl %ebx 282 | 283 | # 尾指针若到缓冲区末端,则折回 284 | andl $size-1,%ebx 285 | 286 | # 保存已修改过的尾指针 287 | movl %ebx,tail(%ecx) 288 | 289 | # 尾指针与头指针比较 290 | cmpl head(%ecx),%ebx 291 | 292 | # 若相等,表示队列已空,则跳转 293 | je write_buffer_empty 294 | ret 295 | 296 | # 处理写缓冲队列 write_q 已空的情况,若有等待写该串行终端的进程则唤醒之 297 | # 然后屏蔽发送保持寄存器空中断,不让发送保持寄存器空时产生中断 298 | .align 2 299 | write_buffer_empty: 300 | 301 | # 唤醒等待的进程 302 | # 取等待该队列的进程的指针,并判断是否为空 303 | movl proc_list(%ecx),%ebx # wake up sleeping process 304 | 305 | # 有等待的进程吗? 306 | testl %ebx,%ebx # is there any? 307 | 308 | # 无,则向前跳转到标号1 处 309 | je 1f 310 | 311 | # 否则将进程置为可运行状态(唤醒进程) 312 | movl $0,(%ebx) 313 | 1: 314 | # 指向端口 0x3f9(0x2f9) 315 | incl %edx 316 | 317 | # 读取中断允许寄存器 318 | inb %dx,%al 319 | 320 | # 稍作延迟。 321 | jmp 1f 322 | 1: 323 | jmp 1f 324 | 1: 325 | # 屏蔽发送保持寄存器空中断(位1) 326 | andb $0xd,%al 327 | 328 | # 写入 0x3f9(0x2f9) 329 | outb %al,%dx 330 | ret 331 | -------------------------------------------------------------------------------- /linux-0.11/kernel/chr_drv/serial.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/serial.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // serial.c 8 | // 该程序用于实现 rs232 的输入输出功能 9 | // void rs_write(struct tty_struct *queue); 10 | // void rs_init(void); 11 | // 以及与传输IO 有关系的所有中断处理程序 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // 当写队列中含有 WAKEUP_CHARS 个字符时,就开始发送 19 | #define WAKEUP_CHARS (TTY_BUF_SIZE / 4) 20 | 21 | // 串行口 1 的中断处理程序 22 | extern void rs1_interrupt(void); 23 | 24 | // 串行口 2 的中断处理程序 25 | extern void rs2_interrupt(void); 26 | 27 | // 初始化串行端口 28 | // port: 串口1 - 0x3F8,串口2 - 0x2F8 29 | static void init(int port) 30 | { 31 | // 设置线路控制寄存器的 DLAB 位(位7) 32 | outb_p(0x80, port + 3); /* set DLAB of line control reg */ 33 | 34 | // 发送波特率因子低字节,0x30->2400bps 35 | outb_p(0x30, port); /* LS of divisor (48 -> 2400 bps */ 36 | 37 | // 发送波特率因子高字节,0x00 38 | outb_p(0x00, port + 1); /* MS of divisor */ 39 | 40 | // 复位 DLAB 位,数据位为 8 位 41 | outb_p(0x03, port + 3); /* reset DLAB */ 42 | 43 | // 设置 DTR,RTS,辅助用户输出2 44 | outb_p(0x0b, port + 4); /* set DTR,RTS, OUT_2 */ 45 | 46 | // 除了写(写保持空)以外,允许所有中断源中断 47 | outb_p(0x0d, port + 1); /* enable all intrs but writes */ 48 | 49 | // 读数据口,以进行复位操作(?) 50 | (void)inb(port); /* read data port to reset things (?) */ 51 | } 52 | 53 | // 初始化串行中断程序和串行接口 54 | void rs_init(void) 55 | { 56 | // 设置串行口1 的中断门向量(硬件 IRQ4 信号) 57 | set_intr_gate(0x24, rs1_interrupt); 58 | 59 | // 设置串行口2 的中断门向量(硬件 IRQ3 信号) 60 | set_intr_gate(0x23, rs2_interrupt); 61 | 62 | // 初始化串行口1(.data 是端口号) 63 | init(tty_table[1].read_q.data); 64 | 65 | // 初始化串行口2 66 | init(tty_table[2].read_q.data); 67 | 68 | // 允许主 8259A 芯片的 IRQ3,IRQ4 中断信号请求 69 | outb(inb_p(0x21) & 0xE7, 0x21); 70 | } 71 | 72 | // 在tty_write() 已将数据放入输出(写)队列时会调用下面的子程序 73 | // 必须首先检查写队列是否为空,并相应设置中断寄存器 74 | 75 | // 串行数据发送输出 76 | // 实际上只是开启串行发送保持寄存器已空中断标志 77 | // 在 UART 将数据发送出去后允许发中断信号 78 | void rs_write(struct tty_struct *tty) 79 | { 80 | // 关中断 81 | cli(); 82 | 83 | // 如果写队列不空,则从0x3f9(或0x2f9) 首先读取中断允许寄存器内容 84 | // 添上发送保持寄存器中断允许标志(位1)后,再写回该寄存器 85 | // 这样就会让串行设备由于要写(发送)字符而引发中断 86 | if (!EMPTY(tty->write_q)) 87 | outb(inb_p(tty->write_q.data + 1) | 0x02, tty->write_q.data + 1); 88 | 89 | // 开中断 90 | sti(); 91 | } 92 | -------------------------------------------------------------------------------- /linux-0.11/kernel/math/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # FREAX 内核的 Makefile 文件 3 | 4 | # 注意!依赖关系是由'make dep'自动进行的,它也会自动去除原来的依赖信息 5 | # 不要把你自己的依赖关系信息放在这里,除非是特别文件的(也即不是一个.c 文件的信息) 6 | # (Linux 最初的名字叫FREAX,后来被 ftp.funet.fi 的管理员改成 Linux 这个名字) 7 | 8 | # 这里导入通用的编译工具以及编译选项 9 | include ../../Option.mk 10 | 11 | CFLAGS += -I../../include 12 | CPP += -I../../include 13 | 14 | .c.s: 15 | $(CC) $(CFLAGS) \ 16 | -S -o $*.s $< 17 | .s.o: 18 | $(AS) -o $*.o $< 19 | .c.o: 20 | $(CC) $(CFLAGS) \ 21 | -c -o $*.o $< 22 | 23 | # 定义目标文件变量 OBJS 24 | OBJS = math_emulate.o 25 | 26 | # 在有了先决条件 OBJS 后使用下面的命令连接成目标 math.a 库文件 27 | math.a: $(OBJS) 28 | $(AR) rcs math.a $(OBJS) 29 | sync 30 | 31 | # 下面的规则用于清理工作。当执行'make clean'时,就会执行命令,去除所有编译连接生成的文件。 32 | # 'rm'是文件删除命令,选项 -f 含义是忽略不存在的文件,并且不显示删除信息 33 | clean: 34 | rm -f core *.o *.a tmp_make 35 | for i in *.c;do rm -f `basename $$i .c`.s;done 36 | 37 | # 下面得目标或规则用于检查各文件之间的依赖关系。方法如下: 38 | # 使用字符串编辑程序 sed 对 Makefile 文件(这里即是自己)进行处理 39 | # 输出为删除 Makefile 文件中 '### Dependencies' 行后面的所有行 40 | # 并生成 tmp_make 临时文件;然后对 kernel/ 目录下的每一个 C 文件执行 gcc 预处理操作 41 | # -M 标志告诉预处理程序输出描述每个目标文件相关性的规则,并且这些规则符合 make 语法 42 | # 对于每一个源文件,预处理程序输出一个 make 规则, 43 | # 其结果形式是相应源程序文件的目标文件名加上其依赖关系 44 | # 该源文件中包含的所有头文件列表,把预处理结果都添加到临时文件 tmp_make 中 45 | # 然后将该临时文件复制成新的 Makefile 文件 46 | dep: 47 | sed '/\#\#\# Dependencies/q' < Makefile > tmp_make 48 | (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ 49 | $(CPP) -M $$i;done) >> tmp_make 50 | cp tmp_make Makefile 51 | 52 | ### Dependencies: 53 | -------------------------------------------------------------------------------- /linux-0.11/kernel/math/math_emulate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/math/math_emulate.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // 该目录里应该包含数学仿真代码,目前仅产生一个信号 8 | 9 | // 信号头文件 定义信号符号常量,信号结构以及信号操作函数原型 10 | #include 11 | 12 | // 调度程序头文件,定义了任务结构 task_struct、第 1 个初始任务的数据 13 | // 还有一些以宏的形式定义的有关描述符参数设置和获取的内联汇编函数程序 14 | #include 15 | 16 | // 内核头文件 含有一些内核常用函数的原形定义 17 | #include 18 | 19 | // 段操作头文件,定义了有关段寄存器操作的嵌入式汇编函数 20 | #include 21 | 22 | // 协处理器仿真函数 23 | // 中断处理程序调用的 C 函数,参见 kernel/math/system_call.s 24 | void math_emulate(long edi, long esi, long ebp, long sys_call_ret, 25 | long eax, long ebx, long ecx, long edx, 26 | unsigned short fs, unsigned short es, unsigned short ds, 27 | unsigned long eip, unsigned short cs, unsigned long eflags, 28 | unsigned short ss, unsigned long esp) 29 | { 30 | unsigned char first, second; 31 | 32 | // 0x0007 表示用户代码空间 33 | 34 | // 选择符 0x000F 表示在局部描述符表中描述符索引值=1,即代码空间 35 | // 如果段寄存器 cs 不等于 0x000F 36 | // 则表示 cs 一定是内核代码选择符,是在内核代码空间则出错 37 | // 显示此时的cs:eip 值,并显示信息 “内核中需要数学仿真”,然后进入死机状态 38 | if (cs != 0x000F) 39 | { 40 | printk("math_emulate: %04x:%08x\n\r", cs, eip); 41 | panic("Math emulation needed in kernel"); 42 | } 43 | 44 | // 取用户数据区堆栈数据 first 和 second 45 | // 显示这些数据,并给进程设置浮点异常信号 SIGFPE 46 | first = get_fs_byte((char *)((*&eip)++)); 47 | second = get_fs_byte((char *)((*&eip)++)); 48 | printk("%04x:%08x %02x %02x\n\r", cs, eip - 2, first, second); 49 | current->signal |= 1 << (SIGFPE - 1); 50 | } 51 | 52 | // 协处理器出错处理函数 53 | // 中断处理程序调用的 C 函数,参见 kernel/math/system_call.s 54 | void math_error(void) 55 | { 56 | // 协处理器指令,(以非等待形式) 清除所有异常标志、忙标志和状态字位 7。 57 | __asm__("fnclex"); 58 | if (last_task_used_math) 59 | last_task_used_math->signal |= 1 << (SIGFPE - 1); 60 | } 61 | -------------------------------------------------------------------------------- /linux-0.11/kernel/mktime.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/mktime.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // 时间头文件,定义了标准时间数据结构 tm 和一些处理时间函数原型 8 | #include 9 | 10 | // 这不是库函数,它仅供内核使用,因此我们不关心小于 1970 年的年份等,但假定一切均很正常 11 | // 同样,时区 TZ 问题也先忽略,我们只是尽可能简单地处理问题 12 | // 最好能找到一些公开的库函数(尽管我认为 minix 的时间函数是公开的) 13 | 14 | // 另外,我恨那个设置 1970 年开始的人,难道他们就不能选择从一个闰年开始? 15 | // 我恨格里高利历、罗马教皇、主教,我什么都不在乎。我是个脾气暴躁的人。 16 | 17 | // 格里高利历 是指 公元纪年法📅,1582 年,时任罗马教皇的格列高利十三世👴,予以批准颁行 18 | 19 | // 1 分钟的秒数 20 | #define MINUTE 60 21 | 22 | // 1 小时的秒数 23 | #define HOUR (60 * MINUTE) 24 | 25 | // 1 天的秒数 26 | #define DAY (24 * HOUR) 27 | 28 | // 1 年的秒数 29 | #define YEAR (365 * DAY) 30 | 31 | // 有趣的是我们考虑进了闰年 32 | // 下面以年为界限,定义了每个月开始时的秒数时间数组 33 | 34 | static int month[12] = { 35 | 0, 36 | DAY *(31), 37 | DAY *(31 + 29), 38 | DAY *(31 + 29 + 31), 39 | DAY *(31 + 29 + 31 + 30), 40 | DAY *(31 + 29 + 31 + 30 + 31), 41 | DAY *(31 + 29 + 31 + 30 + 31 + 30), 42 | DAY *(31 + 29 + 31 + 30 + 31 + 30 + 31), 43 | DAY *(31 + 29 + 31 + 30 + 31 + 30 + 31 + 31), 44 | DAY *(31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30), 45 | DAY *(31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31), 46 | DAY *(31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30)}; 47 | 48 | // 该函数计算从 1970 年1 月1 日 0 时起到开机时刻经过的秒数,作为开机时间 49 | long kernel_mktime(struct tm *tm) 50 | { 51 | long res; 52 | int year; 53 | if (tm->tm_year >= 70) 54 | year = tm->tm_year - 70; // 从 70 年到现在经过的年数(2 位表示方式),因此会有 2000 年问题。 55 | else 56 | /* Y2K bug fix by hellotigercn 20110803 */ 57 | // 来自 https://github.com/yuan-xy/Linux-0.11 58 | year = tm->tm_year + 100 - 70; 59 | 60 | // 为了获得正确的闰年数,这里需要这样一个魔幻偏值 (y+1) 61 | 62 | // 这些年经过的秒数时间 + 每个闰年时多 1 天的秒数时间,在加上当年到当月时的秒数 63 | res = YEAR * year + DAY * ((year + 1) / 4); 64 | res += month[tm->tm_mon]; 65 | 66 | // 以及 (y+2)。如果 (y+2) 不是闰年,那么我们就必须进行调整 (减去一天的秒数时间) 67 | if (tm->tm_mon > 1 && ((year + 2) % 4)) 68 | res -= DAY; 69 | 70 | // 再加上本月过去的天数的秒数时间 71 | res += DAY * (tm->tm_mday - 1); 72 | 73 | // 再加上当天过去的小时数的秒数时间 74 | res += HOUR * tm->tm_hour; 75 | 76 | // 再加上 1 小时内过去的分钟数的秒数时间 77 | res += MINUTE * tm->tm_min; 78 | 79 | // 再加上 1 分钟内已过的秒数 80 | res += tm->tm_sec; 81 | 82 | // 即等于从 1970 年以来经过的秒数时间 83 | return res; 84 | } 85 | 86 | /* 87 | 闰年的基本计算方法是: 88 | 如果 y 能被 4 除尽且不能被 100 除尽,或者能被 400 除尽,则 y 是闰年 89 | 90 | 这里 400 年很奇怪,为什么是这样呢? 91 | 92 | 一回归年大概是 365.2422 天 93 | 94 | 所以如果一年 365 天的话,这样四年就少了 0.2422 * 4 = 0.9688 天 95 | 于是四年要加一天,但是又每四年多出了 1 - 0.9688 = 0.0312 天 96 | 97 | 但是如果持续加下去的话,400 年也就是要加 100 天 98 | 这样多加了 0.0312 * 100 = 3.12 天 99 | 于是在 400 年中就会有缺少三个闰年弥补误差 100 | 这样 400 年后还是差 0.12 天,不过下一次补误差就需要至少 3200 年,于是就忽略了。 101 | */ 102 | -------------------------------------------------------------------------------- /linux-0.11/kernel/panic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/panic.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // 该函数在整个内核中使用(包括在 头文件 *.h, 内存管理程序 mm 和文件系统 fs 中) 8 | // 用以指出主要的出错问题,然后进入死循环 9 | 10 | #define PANIC 11 | 12 | // 内核头文件,含有一些内核常用函数的原形定义 13 | #include 14 | 15 | // 调度程序头文件,定义了任务结构 task_struct、初始任务 0 的数据 16 | // 还有一些有关描述符参数设置和获取的内联汇编函数宏语句 17 | #include 18 | 19 | // 实际上是整型 int fs/buffer.c 20 | void sys_sync(void); 21 | 22 | // 该函数用来显示内核中出现的重大错误信息 23 | // 并运行文件系统同步函数,然后进入死循环 / 死机 24 | // 如果当前进程是任务 0 的话,还说明是交换任务出错 25 | // 并且还没有运行文件系统同步函数 26 | volatile void panic(const char *s) 27 | { 28 | printk("Kernel panic: %s\n\r", s); 29 | if (current == task[0]) 30 | printk("In swapper task - not syncing\n\r"); 31 | else 32 | sys_sync(); 33 | for (;;) 34 | ; 35 | } 36 | -------------------------------------------------------------------------------- /linux-0.11/kernel/printk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/printk.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // 当处于内核模式时,我们不能使用 printf,因为寄存器 fs 指向其它不感兴趣的地方 8 | // 自己编制一个 printf 并在使用前保存 fs,一切就解决了 9 | 10 | // 标准参数头文件。以宏的形式定义变量参数列表 11 | // 主要说明了一个类型 (va_list) 和三个宏 (va_start, va_arg 和va_end) 12 | // 用于 vsprintf、vprintf、vfprintf 函数 13 | // 获取 printf 结构字符串中的可变参数 14 | #include 15 | 16 | // 标准定义头文件。定义了 NULL, offsetof(TYPE, MEMBER) 17 | #include 18 | 19 | // 内核头文件,含有一些内核常用函数的原形定义 20 | #include 21 | 22 | // 输出缓冲区 23 | static char buf[1024]; 24 | 25 | // 下面该函数 vsprintf() 在 linux/kernel/vsprintf.c 26 | extern int vsprintf(char *buf, const char *fmt, va_list args); 27 | 28 | // 内核使用的显示函数 29 | int printk(const char *fmt, ...) 30 | { 31 | // va_list 实际上是一个字符指针类型 32 | va_list args; 33 | int i; 34 | 35 | // 参数处理开始函数。在 stdarg.h 36 | va_start(args, fmt); 37 | 38 | // 使用格式串 fmt 将参数列表 args 输出到 buf 中 39 | // 返回值 i 等于输出字符串的长度 40 | i = vsprintf(buf, fmt, args); 41 | 42 | // 参数处理结束函数 43 | va_end(args); 44 | 45 | __asm__("push %%fs\n\t" // 保存 fs 46 | "push %%ds\n\t" // 压入 ds 47 | "pop %%fs\n\t" // 弹出 ds 到 fs,也即 令 fs = ds 48 | "pushl %0\n\t" // 将字符串长度压入堆栈(这三个入栈是调用参数) 49 | "pushl $buf\n\t" // 将 buf 的地址压入堆栈 50 | "pushl $0\n\t" // 将数值 0 压入堆栈,是通道号 channel 51 | "call tty_write\n\t" // 调用 tty_write 函数 52 | "addl $8,%%esp\n\t" // 调用完成恢复栈 53 | "popl %0\n\t" // 弹出字符串长度值,作为返回值 54 | "pop %%fs" // 恢复原fs 寄存器 55 | : // 没有输出参数 56 | : "r"(i) // 输入参数为 i 57 | : "ax", "cx", "dx"); // 通知编译器 ax, cx, dx 可能已经改变 58 | 59 | //返回输出字符串长度 60 | return i; 61 | } 62 | -------------------------------------------------------------------------------- /linux-0.11/kernel/signal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/signal.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // 调度程序头文件,定义了任务结构 task_struct、初始任务 0 的数据, 8 | // 还有一些有关 描述符参数设置 和 获取的内联汇编函数宏语句 9 | #include 10 | 11 | // 内核头文件,含有一些内核常用函数的原形定义 12 | #include 13 | 14 | // 段操作头文件,定义了有关段寄存器操作的内联汇编函数 15 | #include 16 | 17 | // 信号头文件,定义信号符号常量,信号结构以及信号操作函数原型 18 | #include 19 | 20 | // 前面的限定符 volatile 要求编译器不要对其进行优化 21 | volatile void do_exit(int error_code); 22 | 23 | // 获取当前任务信号屏蔽位图 24 | int sys_sgetmask() 25 | { 26 | return current->blocked; 27 | } 28 | 29 | // 设置新的信号屏蔽位图,SIGKILL 不能被屏蔽,返回值是原信号屏蔽位图 30 | int sys_ssetmask(int newmask) 31 | { 32 | int old = current->blocked; 33 | 34 | current->blocked = newmask & ~(1 << (SIGKILL - 1)); 35 | return old; 36 | } 37 | 38 | // 复制 sigaction 数据到 fs 数据段 to 处 39 | static inline void save_old(char *from, char *to) 40 | { 41 | int i; 42 | 43 | // 验证 to 处的内存是否足够 44 | verify_area(to, sizeof(struct sigaction)); 45 | for (i = 0; i < sizeof(struct sigaction); i++) 46 | { 47 | // 复制到 fs 段,一般是用户数据段 48 | put_fs_byte(*from, to); 49 | from++; 50 | to++; 51 | } 52 | } 53 | 54 | // 把 sigaction 数据从 fs 数据段 from 位置复制到to 处 55 | static inline void get_new(char *from, char *to) 56 | { 57 | int i; 58 | 59 | for (i = 0; i < sizeof(struct sigaction); i++) 60 | *(to++) = get_fs_byte(from++); 61 | } 62 | 63 | // signal()系统调用,类似于 sigaction() 64 | // 为指定的信号安装新的信号句柄(信号处理程序)。 65 | // 信号句柄可以是用户指定的函数,也可以是 SIG_DFL(默认句柄)或 SIG_IGN(忽略)。 66 | // 参数 67 | // signum --指定的信号; 68 | // handler -- 指定的句柄; 69 | // restorer –恢复函数指针,该函数由 Libc 库提供, 70 | // 用于在信号处理程序结束后,恢复系统调用返回时几个寄存器的原有值, 71 | // 以及系统调用的返回值, 72 | // 就好象系统调用没有执行过信号处理程序而直接返回到用户程序一样 73 | // 函数返回原信号句柄 74 | int sys_signal(int signum, long handler, long restorer) 75 | { 76 | struct sigaction tmp; 77 | 78 | // 信号值要在(1-32)范围内,并且不得是 SIGKILL 79 | if (signum < 1 || signum > 32 || signum == SIGKILL) 80 | return -1; 81 | 82 | // 指定的信号处理句柄 83 | tmp.sa_handler = (void (*)(int))handler; 84 | 85 | // 执行时的信号屏蔽码 86 | tmp.sa_mask = 0; 87 | 88 | // 该句柄只使用 1 次后就恢复到默认值 89 | // 并允许信号在自己的处理句柄中收到 90 | tmp.sa_flags = SA_ONESHOT | SA_NOMASK; 91 | 92 | // 保存恢复处理函数指针 93 | tmp.sa_restorer = (void (*)(void))restorer; 94 | handler = (long)current->sigaction[signum - 1].sa_handler; 95 | current->sigaction[signum - 1] = tmp; 96 | return handler; 97 | } 98 | 99 | // sigaction() 系统调用 100 | // 改变进程在收到一个信号时的操作,signum 是除了 SIGKILL 以外的任何信号 101 | // [如果新操作(action)不为空] 则新操作被安装 102 | // 如果oldaction 指针不为空,则原操作被保留到 oldaction 103 | // 成功则返回 0,否则返回 -1 104 | int sys_sigaction(int signum, const struct sigaction *action, 105 | struct sigaction *oldaction) 106 | { 107 | struct sigaction tmp; 108 | 109 | // 信号值要在(1-32)范围内,并且信号 SIGKILL 的处理句柄不能被改变 110 | if (signum < 1 || signum > 32 || signum == SIGKILL) 111 | return -1; 112 | 113 | // 在信号的 sigaction 结构中设置新的操作 114 | tmp = current->sigaction[signum - 1]; 115 | get_new((char *)action, 116 | (char *)(signum - 1 + current->sigaction)); 117 | 118 | // 如果 oldaction 指针不为空的话,则将原操作指针保存到 oldaction 所指的位置 119 | if (oldaction) 120 | save_old((char *)&tmp, (char *)oldaction); 121 | 122 | // 如果允许信号在自己的信号句柄中收到,则令屏蔽码为 0,否则设置屏蔽本信号 123 | if (current->sigaction[signum - 1].sa_flags & SA_NOMASK) 124 | current->sigaction[signum - 1].sa_mask = 0; 125 | else 126 | current->sigaction[signum - 1].sa_mask |= (1 << (signum - 1)); 127 | return 0; 128 | } 129 | 130 | // 系统调用中断处理程序中真正的信号处理程序 kernel/system_call.s 131 | // 该段代码的主要作用是,将信号的处理句柄插入到用户程序堆栈中 132 | // 并在本系统调用结束返回后立刻执行信号句柄程序,然后继续执行用户的程序 133 | void do_signal(long signr, long eax, long ebx, long ecx, long edx, 134 | long fs, long es, long ds, 135 | long eip, long cs, long eflags, 136 | unsigned long *esp, long ss) 137 | { 138 | unsigned long sa_handler; 139 | long old_eip = eip; 140 | struct sigaction *sa = current->sigaction + signr - 1; 141 | int longs; 142 | unsigned long *tmp_esp; 143 | 144 | sa_handler = (unsigned long)sa->sa_handler; 145 | 146 | // 如果句柄为 SIG_IGN(忽略),则返回; 147 | // 如果句柄为 SIG_DFL(默认处理),则如果信号是 SIGCHLD 则返回,否则终止进程的执行 148 | if (sa_handler == 1) 149 | return; 150 | if (!sa_handler) 151 | { 152 | if (signr == SIGCHLD) 153 | return; 154 | else 155 | // 为什么以信号位图为参数?不为什么!?😊 156 | // 这里应该是 do_exit(1<<(signr)) 157 | do_exit(1 << (signr - 1)); 158 | } 159 | 160 | // 如果该信号句柄只需使用一次,则将该句柄置空 (该信号句柄已经保存在 sa_handler 指针中) 161 | if (sa->sa_flags & SA_ONESHOT) 162 | sa->sa_handler = NULL; 163 | 164 | // 下面这段代码将信号处理句柄插入到用户堆栈中 165 | // 同时也将 sa_restorer, signr, 进程屏蔽码 (如果 SA_NOMASK 没置位), eax, ecx, edx 作为参数, 166 | // 以及原调用系统调用的程序返回指针,及标志寄存器值压入堆栈 167 | // 因此在本次系统调用中断 (0x80) 返回用户程序时会首先执行用户的信号句柄程序 168 | // 然后再继续执行用户程序 169 | // 将用户调用系统调用的代码指针 eip 指向该信号处理句柄 170 | *(&eip) = sa_handler; 171 | 172 | // 如果允许信号自己的处理句柄收到信号自己,则也需要将进程的阻塞码压入堆栈 173 | // 注意,这里 longs 的结果应该选择 (7*4):(8*4),因为堆栈是以 4 字节为单位操作的 174 | longs = (sa->sa_flags & SA_NOMASK) ? 7 : 8; 175 | 176 | // 将原调用程序的用户堆栈指针向下扩展7(或8)个长字(用来存放调用信号句柄的参数等) 177 | // 并检查内存使用情况(例如如果内存超界则分配新页等) 178 | *(&esp) -= longs; 179 | verify_area(esp, longs * 4); 180 | 181 | // 在用户堆栈中从下到上存放 sa_restorer, 信号 signr, 屏蔽码 blocked(如果 SA_NOMASK 置位), 182 | // eax, ecx, edx, eflags 和用户程序原代码指针 183 | tmp_esp = esp; 184 | put_fs_long((long)sa->sa_restorer, tmp_esp++); 185 | put_fs_long(signr, tmp_esp++); 186 | if (!(sa->sa_flags & SA_NOMASK)) 187 | put_fs_long(current->blocked, tmp_esp++); 188 | put_fs_long(eax, tmp_esp++); 189 | put_fs_long(ecx, tmp_esp++); 190 | put_fs_long(edx, tmp_esp++); 191 | put_fs_long(eflags, tmp_esp++); 192 | put_fs_long(old_eip, tmp_esp++); 193 | 194 | // 进程阻塞码(屏蔽码)添上 sa_mask 中的码位 195 | current->blocked |= sa->sa_mask; 196 | } 197 | -------------------------------------------------------------------------------- /linux-0.11/kernel/sys.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/kernel/sys.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // 错误号头文件。包含系统中各种出错号 8 | #include 9 | 10 | // 调度程序头文件,定义了任务结构 task_struct、初始任务 0 的数据, 11 | // 还有一些有关描述符参数设置和获取的内联汇编函数宏语句 12 | #include 13 | 14 | // tty 头文件,定义了有关 tty_io,串行通信方面的参数、常数 15 | #include 16 | 17 | // 内核头文件 含有一些内核常用函数的原形定义 18 | #include 19 | 20 | // 段操作头文件 定义了有关段寄存器操作的嵌入式汇编函数 21 | #include 22 | 23 | // 定义了进程中运行时间的结构 tms 以及 times() 函数原型 24 | #include 25 | 26 | // 系统名称结构头文件 27 | #include 28 | 29 | // 返回日期和时间 30 | int sys_ftime() 31 | { 32 | return -ENOSYS; 33 | } 34 | 35 | int sys_break() 36 | { 37 | return -ENOSYS; 38 | } 39 | 40 | // 用于当前进程对子进程进行调试 41 | int sys_ptrace() 42 | { 43 | return -ENOSYS; 44 | } 45 | 46 | // 改变并打印终端行设置 47 | int sys_stty() 48 | { 49 | return -ENOSYS; 50 | } 51 | 52 | // 取终端行设置信息 53 | int sys_gtty() 54 | { 55 | return -ENOSYS; 56 | } 57 | 58 | // 修改文件名 59 | int sys_rename() 60 | { 61 | return -ENOSYS; 62 | } 63 | 64 | int sys_prof() 65 | { 66 | return -ENOSYS; 67 | } 68 | 69 | // 设置当前任务的实际以及/或者有效组ID(gid) 70 | // 如果任务没有超级用户特权,那么只能互换其实际组 ID 和有效组 ID 71 | // 如果任务具有超级用户特权,就能任意设置有效的和实际的组 ID 72 | // 保留的gid(saved gid)被设置成与有效 gid 同值 73 | int sys_setregid(int rgid, int egid) 74 | { 75 | if (rgid > 0) 76 | { 77 | if ((current->gid == rgid) || 78 | suser()) 79 | current->gid = rgid; 80 | else 81 | return (-EPERM); 82 | } 83 | if (egid > 0) 84 | { 85 | if ((current->gid == egid) || 86 | (current->egid == egid) || 87 | (current->sgid == egid) || 88 | suser()) 89 | current->egid = egid; 90 | else 91 | return (-EPERM); 92 | } 93 | return 0; 94 | } 95 | 96 | // 设置进程组号(gid),如果任务没有超级用户特权 97 | // 它可以使用 setgid() 将其有效 gid(effective gid) 98 | // 设置为成其保留 gid(saved gid) 或其实际 gid(real gid) 99 | // 如果任务有超级用户特权,则实际 gid、有效 gid 和保留 gid 100 | // 都被设置成参数指定的 gid 101 | int sys_setgid(int gid) 102 | { 103 | return (sys_setregid(gid, gid)); 104 | } 105 | 106 | // 打开或关闭进程计帐功能 107 | int sys_acct() 108 | { 109 | return -ENOSYS; 110 | } 111 | 112 | // 映射任意物理内存到进程的虚拟地址空间 113 | int sys_phys() 114 | { 115 | return -ENOSYS; 116 | } 117 | 118 | int sys_lock() 119 | { 120 | return -ENOSYS; 121 | } 122 | 123 | int sys_mpx() 124 | { 125 | return -ENOSYS; 126 | } 127 | 128 | int sys_ulimit() 129 | { 130 | return -ENOSYS; 131 | } 132 | 133 | // 返回从 1970 年1 月1 日00:00:00 GMT 开始计时的时间值(秒) 134 | // 如果 tloc 不为 null,则时间值也存储在那里 135 | int sys_time(long *tloc) 136 | { 137 | int i; 138 | 139 | i = CURRENT_TIME; 140 | if (tloc) 141 | { 142 | // 验证内存容量是否够 143 | verify_area(tloc, 4); 144 | 145 | // 也放入用户数据段 tloc 处 146 | put_fs_long(i, (unsigned long *)tloc); 147 | } 148 | return i; 149 | } 150 | 151 | // 无特权的用户可以见实际用户标识符(real uid) 152 | // 改成有效用户标识符(effective uid),反之亦然 153 | 154 | // 设置任务的实际以及/或者有效用户ID(uid) 155 | // 如果任务没有超级用户特权,那么只能互换其实际用户 ID 和有效用户 ID 156 | // 如果任务具有超级用户特权,就能任意设置有效的和实际的用户 ID 157 | // 保留的 uid (saved uid) 被设置成与有效 uid 同值 158 | int sys_setreuid(int ruid, int euid) 159 | { 160 | int old_ruid = current->uid; 161 | 162 | if (ruid > 0) 163 | { 164 | if ((current->euid == ruid) || 165 | (old_ruid == ruid) || 166 | suser()) 167 | current->uid = ruid; 168 | else 169 | return (-EPERM); 170 | } 171 | if (euid > 0) 172 | { 173 | if ((old_ruid == euid) || 174 | (current->euid == euid) || 175 | suser()) 176 | current->euid = euid; 177 | else 178 | { 179 | current->uid = old_ruid; 180 | return (-EPERM); 181 | } 182 | } 183 | return 0; 184 | } 185 | 186 | // 设置任务用户号(uid) 如果任务没有超级用户特权, 187 | // 它可以使用 setuid() 将其有效 uid(effective uid) 188 | // 设置成其保留 uid(saved uid) 或其实际 uid(real uid) 189 | // 如果任务有超级用户特权,则实际uid、有效uid 和保留uid 都被设置成参数指定的 uid 190 | int sys_setuid(int uid) 191 | { 192 | return (sys_setreuid(uid, uid)); 193 | } 194 | 195 | // 设置系统时间和日期,参数 tptr 是从 1970 年 1 月 1 日 00:00:00 GMT 开始计时的时间值(秒) 196 | // 调用进程必须具有超级用户权限 197 | int sys_stime(long *tptr) 198 | { 199 | if (!suser()) 200 | return -EPERM; 201 | startup_time = get_fs_long((unsigned long *)tptr) - jiffies / HZ; 202 | return 0; 203 | } 204 | 205 | // 获取当前任务时间。tms 结构中包括用户时间、系统时间、子进程用户时间、子进程系统时间 206 | int sys_times(struct tms *tbuf) 207 | { 208 | if (tbuf) 209 | { 210 | verify_area(tbuf, sizeof *tbuf); 211 | put_fs_long(current->utime, (unsigned long *)&tbuf->tms_utime); 212 | put_fs_long(current->stime, (unsigned long *)&tbuf->tms_stime); 213 | put_fs_long(current->cutime, (unsigned long *)&tbuf->tms_cutime); 214 | put_fs_long(current->cstime, (unsigned long *)&tbuf->tms_cstime); 215 | } 216 | return jiffies; 217 | } 218 | 219 | // 当参数 end_data_seg 数值合理,并且系统确实有足够的内存, 220 | // 而且进程没有超越其最大数据段大小时, 221 | // 该函数设置数据段末尾为 end_data_seg 指定的值, 222 | // 该值必须大于代码结尾并且要小于堆栈结尾 16KB 223 | // 返回值是数据段的新结尾值(如果返回值与要求值不同,则表明有错发生) 224 | // 该函数并不被用户直接调用,而由 libc 库函数进行包装,并且返回值也不一样 225 | int sys_brk(unsigned long end_data_seg) 226 | { 227 | // 如果参数>代码结尾,并且小于堆栈 - 16KB 228 | if (end_data_seg >= current->end_code && 229 | end_data_seg < current->start_stack - 16384) 230 | // 则设置新数据段结尾值 231 | current->brk = end_data_seg; 232 | 233 | // 返回进程当前的数据段结尾值 234 | return current->brk; 235 | } 236 | 237 | // 下面代码需要某些严格的检查,我只是没有胃口来做这些 238 | // 我也不完全明白 sessions/pgrp 等,还是让了解它们的人来做吧 239 | 240 | // 设置进程的进程组 ID 为pgid 241 | // 如果参数 pid=0,则使用当前进程号 242 | // 如果 pgid 为 0,则使用参数 pid 指定的进程的组 ID 作为 pgid 243 | // 如果该函数用于将进程从一个进程组移到另一个进程组 244 | // 则这两个进程组必须属于同一个会话 (session) 245 | // 在这种情况下,参数 pgid 指定了要加入的现有进程组 ID, 246 | // 此时该组的会话 ID 必须与将要加入进程的相同 247 | int sys_setpgid(int pid, int pgid) 248 | { 249 | int i; 250 | 251 | // 如果参数 pid=0,则使用当前进程号 252 | if (!pid) 253 | pid = current->pid; 254 | 255 | // 如果 pgid 为 0,则使用当前进程 pid 作为 pgid 256 | if (!pgid) 257 | // [??这里与POSIX 的描述有出入] 258 | pgid = current->pid; 259 | 260 | // 扫描任务数组,查找指定进程号的任务 261 | for (i = 0; i < NR_TASKS; i++) 262 | if (task[i] && task[i]->pid == pid) 263 | { 264 | // 如果该任务已经是首领,则返回出错 265 | if (task[i]->leader) 266 | return -EPERM; 267 | 268 | // 如果该任务的会话 ID 与当前进程的不同,则出错返回 269 | if (task[i]->session != current->session) 270 | return -EPERM; 271 | 272 | // 设置该任务的 pgrp 273 | task[i]->pgrp = pgid; 274 | return 0; 275 | } 276 | return -ESRCH; 277 | } 278 | 279 | // 返回当前进程的组号,与 getpgid(0) 等同 280 | int sys_getpgrp(void) 281 | { 282 | return current->pgrp; 283 | } 284 | 285 | // 创建一个会话(session)(即设置其leader=1) 286 | // 并且设置其会话号=其组号=其进程号 287 | // setsid -- SET Session ID 288 | int sys_setsid(void) 289 | { 290 | // 如果当前进程已是会话首领并且不是超级用户,则返回出错 291 | if (current->leader && !suser()) 292 | return -EPERM; 293 | 294 | // 设置当前进程为新会话首领 295 | current->leader = 1; 296 | 297 | // 设置本进程session = pid 298 | current->session = current->pgrp = current->pid; 299 | 300 | // 表示当前进程没有控制终端 301 | current->tty = -1; 302 | 303 | // 返回会话ID 304 | return current->pgrp; 305 | } 306 | 307 | // 获取系统信息。其中 utsname 结构包含 5 个字段 308 | // 分别是: 309 | // 本版本操作系统的名称、 310 | // 网络节点名称、 311 | // 当前发行级别、 312 | // 版本级别、 313 | // 硬件类型名称 314 | int sys_uname(struct utsname *name) 315 | { 316 | static struct utsname thisname = { 317 | // 这里给出了结构中的信息,这种编码肯定会改变 318 | "linux .0", "nodename", "release ", "version ", "machine "}; 319 | int i; 320 | 321 | // 如果存放信息的缓冲区指针为空,则返回出错 322 | if (!name) 323 | return -ERROR; 324 | 325 | // 验证缓冲区大小是否超限(超出已分配的内存等) 326 | verify_area(name, sizeof *name); 327 | 328 | // 将 utsname 中的信息逐字节复制到用户缓冲区中 329 | for (i = 0; i < sizeof *name; i++) 330 | put_fs_byte(((char *)&thisname)[i], i + (char *)name); 331 | return 0; 332 | } 333 | 334 | // 设置当前进程创建文件属性屏蔽码为 mask & 0777,并返回原屏蔽码 335 | int sys_umask(int mask) 336 | { 337 | int old = current->umask; 338 | 339 | current->umask = mask & 0777; 340 | return (old); 341 | } 342 | -------------------------------------------------------------------------------- /linux-0.11/lib/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for some libs needed in the kernel. 3 | # 4 | # Note! Dependencies are done automagically by 'make dep', which also 5 | # removes any old dependencies. DON'T put your own dependencies here 6 | # unless it's something special (ie not a .c file). 7 | # 8 | 9 | include ../Option.mk 10 | 11 | CFLAGS += -I../include 12 | CPP += -I../include 13 | 14 | .c.s: 15 | $(CC) $(CFLAGS) \ 16 | -S -o $*.s $< 17 | .s.o: 18 | $(AS) -o $*.o $< 19 | .c.o: 20 | $(CC) $(CFLAGS) \ 21 | -c -o $*.o $< 22 | 23 | OBJS = ctype.o _exit.o open.o close.o errno.o write.o dup.o setsid.o \ 24 | execve.o wait.o string.o malloc.o 25 | 26 | lib.a: $(OBJS) 27 | $(AR) rcs lib.a $(OBJS) 28 | sync 29 | 30 | clean: 31 | rm -f core *.o *.a tmp_make 32 | for i in *.c;do rm -f `basename $$i .c`.s;done 33 | 34 | dep: 35 | sed '/\#\#\# Dependencies/q' < Makefile > tmp_make 36 | (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ 37 | $(CPP) -M $$i;done) >> tmp_make 38 | cp tmp_make Makefile 39 | 40 | ### Dependencies: 41 | _exit.s _exit.o : _exit.c ../include/unistd.h ../include/sys/stat.h \ 42 | ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ 43 | ../include/utime.h 44 | close.s close.o : close.c ../include/unistd.h ../include/sys/stat.h \ 45 | ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ 46 | ../include/utime.h 47 | ctype.s ctype.o : ctype.c ../include/ctype.h 48 | dup.s dup.o : dup.c ../include/unistd.h ../include/sys/stat.h \ 49 | ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ 50 | ../include/utime.h 51 | errno.s errno.o : errno.c 52 | execve.s execve.o : execve.c ../include/unistd.h ../include/sys/stat.h \ 53 | ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ 54 | ../include/utime.h 55 | malloc.s malloc.o : malloc.c ../include/linux/kernel.h ../include/linux/mm.h \ 56 | ../include/asm/system.h 57 | open.s open.o : open.c ../include/unistd.h ../include/sys/stat.h \ 58 | ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ 59 | ../include/utime.h ../include/stdarg.h 60 | setsid.s setsid.o : setsid.c ../include/unistd.h ../include/sys/stat.h \ 61 | ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ 62 | ../include/utime.h 63 | string.s string.o : string.c ../include/string.h 64 | wait.s wait.o : wait.c ../include/unistd.h ../include/sys/stat.h \ 65 | ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ 66 | ../include/utime.h ../include/sys/wait.h 67 | write.s write.o : write.c ../include/unistd.h ../include/sys/stat.h \ 68 | ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ 69 | ../include/utime.h 70 | -------------------------------------------------------------------------------- /linux-0.11/lib/_exit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/_exit.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #define __LIBRARY__ 8 | 9 | // Linux 标准头文件,定义了各种符号常数和类型,并申明了各种函数 10 | // 如定义了 __LIBRARY__,则还含系统调用号和内嵌汇编 syscall0() 等 11 | #include 12 | 13 | // 内核使用的程序(退出)终止函数 14 | // 直接调用系统中断 int 0x80,功能号 __NR_exit 15 | // 参数:exit_code - 退出码。 16 | void _exit(int exit_code) 17 | { 18 | __asm__ __volatile__("int $0x80" ::"a"(__NR_exit), "b"(exit_code)); 19 | } 20 | -------------------------------------------------------------------------------- /linux-0.11/lib/close.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/close.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #define __LIBRARY__ 8 | #include 9 | 10 | // 关闭文件函数。 11 | // 下面该调用宏函数对应:int close(int fd) 12 | // 直接调用了系统中断int 0x80,参数是 __NR_close 13 | // 其中 fd 是文件描述符 14 | _syscall1(int, close, int, fd) 15 | -------------------------------------------------------------------------------- /linux-0.11/lib/ctype.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/ctype.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // 字符类型头文件,定义了一些有关字符类型判断和转换的宏 8 | #include 9 | 10 | // 一个临时字符变量,供 ctype.h 文件中转换字符宏函数使用 11 | // 字符特性数组(表),定义了各个字符对应的属性 12 | // 这些属性类型(如 _C 等)在 ctype.h 中定义 13 | // 用于判断字符是 控制字符(_C)、大写字符(_U)、小写字符(_L) 等所属类型 14 | char _ctmp; 15 | unsigned char _ctype[] = { 16 | 0x00, /* EOF */ 17 | _C, _C, _C, _C, _C, _C, _C, _C, /* 0-7 */ 18 | _C, _C | _S, _C | _S, _C | _S, _C | _S, _C | _S, _C, _C, /* 8-15 */ 19 | _C, _C, _C, _C, _C, _C, _C, _C, /* 16-23 */ 20 | _C, _C, _C, _C, _C, _C, _C, _C, /* 24-31 */ 21 | _S | _SP, _P, _P, _P, _P, _P, _P, _P, /* 32-39 */ 22 | _P, _P, _P, _P, _P, _P, _P, _P, /* 40-47 */ 23 | _D, _D, _D, _D, _D, _D, _D, _D, /* 48-55 */ 24 | _D, _D, _P, _P, _P, _P, _P, _P, /* 56-63 */ 25 | _P, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U | _X, _U, /* 64-71 */ 26 | _U, _U, _U, _U, _U, _U, _U, _U, /* 72-79 */ 27 | _U, _U, _U, _U, _U, _U, _U, _U, /* 80-87 */ 28 | _U, _U, _U, _P, _P, _P, _P, _P, /* 88-95 */ 29 | _P, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L | _X, _L, /* 96-103 */ 30 | _L, _L, _L, _L, _L, _L, _L, _L, /* 104-111 */ 31 | _L, _L, _L, _L, _L, _L, _L, _L, /* 112-119 */ 32 | _L, _L, _L, _P, _P, _P, _P, _C, /* 120-127 */ 33 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128-143 */ 34 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144-159 */ 35 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 160-175 */ 36 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 176-191 */ 37 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 192-207 */ 38 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 208-223 */ 39 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 224-239 */ 40 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 240-255 */ 41 | -------------------------------------------------------------------------------- /linux-0.11/lib/dup.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/dup.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #define __LIBRARY__ 8 | #include 9 | 10 | // 复制文件描述符函数 11 | // 下面该调用宏函数对应:int dup(int fd) 12 | // 直接调用了系统中断 int 0x80,参数是 __NR_dup 13 | // 其中 fd 是文件描述符 14 | _syscall1(int, dup, int, fd) 15 | -------------------------------------------------------------------------------- /linux-0.11/lib/errno.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/errno.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | // 该变量用于在函数调用失败时存放出错号 8 | int errno; 9 | -------------------------------------------------------------------------------- /linux-0.11/lib/execve.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/execve.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #define __LIBRARY__ 8 | #include 9 | 10 | // 加载并执行子进程(其它程序)函数 11 | // 下面该调用宏函数对应: 12 | // int execve(const char * file, char ** argv, char ** envp) 13 | // 参数: 14 | // file - 被执行程序文件名; 15 | // argv - 命令行参数指针数组; 16 | // envp - 环境变量指针数组; 17 | // 直接调用了系统中断 int 0x80 18 | // 参数是 __NR_execve 19 | _syscall3(int, execve, const char *, file, char **, argv, char **, envp) 20 | -------------------------------------------------------------------------------- /linux-0.11/lib/open.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/open.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #define __LIBRARY__ 8 | #include 9 | #include 10 | 11 | // 打开文件函数。 12 | // 打开并有可能创建一个文件 13 | // 参数:filename - 文件名;flag - 文件打开标志;... 14 | // 返回:文件描述符,若出错则置出错码,并返回 -1 15 | int open(const char *filename, int flag, ...) 16 | { 17 | register int res; 18 | va_list arg; 19 | // 利用 va_start() 宏函数,取得 flag 后面参数的指针 20 | // 然后调用系统中断 int 0x80,功能 open 进行文件打开操作 21 | // %0 - eax(返回的描述符或出错码); 22 | // %1 - eax(系统中断调用功能号__NR_open); 23 | // %2 - ebx(文件名filename); 24 | // %3 - ecx(打开文件标志flag); 25 | // %4 - edx(后随参数文件属性mode)。 26 | va_start(arg, flag); 27 | __asm__("int $0x80" 28 | : "=a"(res) 29 | : "0"(__NR_open), "b"(filename), "c"(flag), 30 | "d"(va_arg(arg, int))); 31 | 32 | // 系统中断调用返回值大于或等于 0,表示是一个文件描述符,则直接返回之 33 | if (res >= 0) 34 | return res; 35 | 36 | // 否则说明返回值小于 0,则代表一个出错码,设置该出错码并返回 -1 37 | errno = -res; 38 | return -1; 39 | } 40 | -------------------------------------------------------------------------------- /linux-0.11/lib/setsid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/setsid.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #define __LIBRARY__ 8 | #include 9 | 10 | // 创建一个会话并设置进程组号 11 | // 下面系统调用宏对应于函数:pid_t setsid() 12 | // 返回:调用进程的会话标识符(session ID) 13 | _syscall0(pid_t, setsid) 14 | -------------------------------------------------------------------------------- /linux-0.11/lib/wait.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/wait.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #define __LIBRARY__ 8 | #include 9 | #include 10 | 11 | // 等待进程终止系统调用函数 12 | // 该下面宏结构对应于函数: 13 | // pid_t waitpid(pid_t pid, int * wait_stat, int options) 14 | // 参数:pid -> 等待被终止进程的进程 id,或者是用于指定特殊情况的其它特定数值; 15 | // wait_stat -> 用于存放状态信息; 16 | // options -> WNOHANG 或 WUNTRACED 或是 0 17 | _syscall3(pid_t, waitpid, pid_t, pid, int *, wait_stat, int, options) 18 | 19 | // wait() 系统调用,直接调用 waitpid() 函数 20 | pid_t wait(int *wait_stat) 21 | { 22 | return waitpid(-1, wait_stat, 0); 23 | } 24 | -------------------------------------------------------------------------------- /linux-0.11/lib/write.c: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/lib/write.c 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | #define __LIBRARY__ 8 | #include 9 | 10 | // 写文件系统调用函数 11 | // 该宏结构对应于函数: 12 | // int write(int fd, const char * buf, off_t count) 13 | // 参数: 14 | // fd - 文件描述符; 15 | // buf - 写缓冲区指针; 16 | // count - 写字节数 17 | // 返回: 18 | // 成功时返回写入的字节数(0 表示写入0 字节); 19 | // 出错时将返回-1,并且设置了出错号 20 | _syscall3(int, write, int, fd, const char *, buf, off_t, count) 21 | -------------------------------------------------------------------------------- /linux-0.11/mm/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../Option.mk 3 | 4 | LDFLAGS += -r 5 | CFLAGS += -I../include 6 | CPP += -I../include 7 | 8 | .c.o: 9 | $(CC) $(CFLAGS) \ 10 | -c -o $*.o $< 11 | .s.o: 12 | $(AS) -o $*.o $< 13 | .c.s: 14 | $(CC) $(CFLAGS) \ 15 | -S -o $*.s $< 16 | 17 | OBJS = memory.o page.o 18 | 19 | all: mm.o 20 | 21 | mm.o: $(OBJS) 22 | $(LD) $(LDFLAGS) -o mm.o $(OBJS) 23 | 24 | clean: 25 | rm -f core *.o *.a tmp_make 26 | for i in *.c;do rm -f `basename $$i .c`.s;done 27 | 28 | dep: 29 | sed '/\#\#\# Dependencies/q' < Makefile > tmp_make 30 | (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make 31 | cp tmp_make Makefile 32 | 33 | ### Dependencies: 34 | memory.o: memory.c ../include/signal.h ../include/sys/types.h \ 35 | ../include/asm/system.h ../include/linux/sched.h \ 36 | ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ 37 | ../include/linux/kernel.h 38 | -------------------------------------------------------------------------------- /linux-0.11/mm/page.s: -------------------------------------------------------------------------------- 1 | /* 2 | * linux/mm/page.s 3 | * 4 | * (C) 1991 Linus Torvalds 5 | */ 6 | 7 | # page.s 程序包含底层页异常处理代码。实际的工作在 memory.c 中完成 8 | 9 | .globl page_fault 10 | 11 | page_fault: 12 | # 取出错码到 eax,保存上下文 13 | xchgl %eax,(%esp) 14 | pushl %ecx 15 | pushl %edx 16 | push %ds 17 | push %es 18 | push %fs 19 | 20 | # 置内核数据段选择符 21 | movl $0x10,%edx 22 | mov %dx,%ds 23 | mov %dx,%es 24 | mov %dx,%fs 25 | 26 | # 取引起页面异常的线性地址 27 | movl %cr2,%edx 28 | 29 | # 将该线性地址和出错码压入堆栈,作为调用函数的参数 30 | pushl %edx 31 | pushl %eax 32 | 33 | # 测试标志P,如果不是缺页引起的异常则跳转 34 | testl $1,%eax 35 | jne 1f 36 | 37 | # 调用缺页处理函数 38 | call do_no_page 39 | jmp 2f 40 | 1: 41 | # 调用写保护处理函数 42 | call do_wp_page 43 | 2: 44 | # 丢弃压入栈的两个参数 45 | addl $8,%esp 46 | 47 | # 恢复上下文 48 | pop %fs 49 | pop %es 50 | pop %ds 51 | popl %edx 52 | popl %ecx 53 | popl %eax 54 | iret 55 | -------------------------------------------------------------------------------- /tests/bochsrc: -------------------------------------------------------------------------------- 1 | # configuration file generated by Bochs 2 | plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true 3 | config_interface: textconfig 4 | display_library: x, options="gui_debug" 5 | memory: host=32, guest=32 6 | romimage: file="/usr/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none 7 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 8 | boot: a 9 | floppy_bootsig_check: disabled=0 10 | floppya: 1_44="boot.bin", status=inserted 11 | # no floppyb 12 | ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 13 | ata0-master: type=none 14 | ata0-slave: type=none 15 | ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15 16 | ata1-master: type=none 17 | ata1-slave: type=none 18 | ata2: enabled=false 19 | ata3: enabled=false 20 | optromimage1: file=none 21 | optromimage2: file=none 22 | optromimage3: file=none 23 | optromimage4: file=none 24 | optramimage1: file=none 25 | optramimage2: file=none 26 | optramimage3: file=none 27 | optramimage4: file=none 28 | pci: enabled=1, chipset=i440fx 29 | vga: extension=vbe, update_freq=5, realtime=1 30 | cpu: count=1:1:1, ips=4000000, quantum=16, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 31 | cpuid: level=6, stepping=3, model=3, family=6, vendor_string="AuthenticAMD", brand_string="AMD Athlon(tm) processor" 32 | cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true 33 | cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, avx_f16c=false 34 | cpuid: avx_fma=false, bmi=0, xop=false, fma4=false, tbm=false, x86_64=true, 1g_pages=false 35 | cpuid: pcid=false, fsgsbase=false, smep=false, smap=false, mwait=true 36 | print_timestamps: enabled=0 37 | debugger_log: - 38 | magic_break: enabled=1 39 | port_e9_hack: enabled=0 40 | private_colormap: enabled=0 41 | clock: sync=none, time0=local, rtc_sync=0 42 | # no cmosimage 43 | log: - 44 | logprefix: %t%e%d 45 | debug: action=ignore 46 | info: action=report 47 | error: action=report 48 | panic: action=ask 49 | keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none 50 | mouse: type=ps2, enabled=false, toggle=ctrl+mbutton 51 | speaker: enabled=true, mode=system 52 | parport1: enabled=true, file=none 53 | parport2: enabled=false 54 | com1: enabled=true, mode=null 55 | com2: enabled=false 56 | com3: enabled=false 57 | com4: enabled=false 58 | -------------------------------------------------------------------------------- /tests/boot.asm: -------------------------------------------------------------------------------- 1 | org 0x7c00 2 | 3 | xchg bx, bx 4 | 5 | mov ah, 3; 获取光标位置 6 | mov bh, 0; 第 0 页 7 | int 0x10 8 | 9 | xchg bx, bx 10 | ; es: bp 表示字符串的位置 11 | mov ax, 0 12 | mov es, ax 13 | mov bp, message 14 | 15 | mov ah, 0x13; 表示打印字符串 16 | mov al, 1; 表示移动光标 17 | mov cx, message.end - message 18 | mov bh, 0 19 | mov bl, 7 20 | int 0x10 21 | 22 | xchg bx, bx 23 | 24 | read_mbr: 25 | ; 读取主引导扇区 26 | 27 | ; 缓冲区位 es:bx = 0x10000 28 | mov ax, 0x1000 29 | mov es, ax 30 | mov bx, 0 31 | 32 | mov ah, 2; 表示读磁盘 33 | mov al, 1; 读取一个扇区 34 | mov ch, 0; 磁道号 0 35 | mov cl, 1; 第一个扇区,注意扇区号从 1 开始 36 | mov dh, 0; 第 0 个磁头(盘面) 37 | mov dl, 0; 驱动器号 0 38 | int 0x13 39 | jnc .success 40 | .error: 41 | ; 读取失败 42 | jmp .error 43 | .success: 44 | ; 此时读取成功 45 | xchg bx, bx 46 | 47 | mov word es:[508], 0x1122 48 | mov bx, 0 49 | 50 | mov ah, 3; 表示写磁盘 51 | mov al, 1; 写一个扇区 52 | mov ch, 0; 磁道号 0 53 | mov cl, 1; 第一个扇区,注意扇区号从 1 开始 54 | mov dh, 0; 第 0 个磁头(盘面) 55 | mov dl, 0; 驱动器号 0 56 | int 0x13 57 | 58 | xchg bx, bx; 59 | 60 | halt: 61 | jmp halt 62 | 63 | message: 64 | db "hello world", 10, 13, 0 65 | .end 66 | 67 | times 510 - ($ - $$) db 0 68 | dw 0xaa55 69 | -------------------------------------------------------------------------------- /tests/makefile: -------------------------------------------------------------------------------- 1 | 2 | boot.bin: boot.asm 3 | nasm -f bin $< -o $@ 4 | 5 | .PHONY: bochs 6 | bochs: boot.bin 7 | bochs -q -unlock -f bochsrc 8 | --------------------------------------------------------------------------------