├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── README.md ├── bochs ├── PKGBUILD ├── bochsrc ├── bochsrc.gdb └── bochsrc.grub ├── docs ├── 01 系统引导 │ ├── 001 概述.md │ ├── 002 配置开发环境.md │ ├── 003 主引导扇区.md │ ├── 004 在 U 盘启动.md │ ├── 005 实模式 print.md │ ├── 006 硬盘读写.md │ ├── 007 内核加载器.md │ ├── 008 内存检测.md │ ├── 009 保护模式和全局描述符.md │ ├── 010 进入内核.md │ ├── 055 multiboot2 头.md │ ├── 056 multiboot2 引导.md │ └── images │ │ ├── 80286-segment descriptor.jpg │ │ ├── 80386-segment descriptor.jpg │ │ ├── harddisk_1.jpeg │ │ ├── harddisk_2.jpg │ │ ├── harddisk_3.jpg │ │ ├── harddisk_4.jpg │ │ ├── harddisk_5.jpg │ │ ├── harddisk_6.jpg │ │ └── harddisk_7.jpg ├── 02 二进制基础 │ ├── 011 ELF 文件格式简介.md │ ├── 012 bootloader 补充说明.md │ ├── 013 编译和链接.md │ ├── 014 基础数据类型.md │ ├── 015 bochs-gdb.md │ ├── 016 qemu + vmware.md │ ├── 017 gcc 汇编分析.md │ ├── 018 堆栈和函数.md │ ├── 019 局部变量和参数传递.md │ └── images │ │ └── compile_1.drawio.svg ├── 03 字符输出 │ ├── 020 输入和输出.md │ ├── 021 字符串处理.md │ ├── 022 基础显卡驱动.md │ ├── 023 可变参数原理.md │ ├── 024 printk.md │ ├── 025 断言 assert.md │ ├── 026 调试工具.md │ ├── 112 串口输出日志.md │ └── images │ │ ├── ansi_01.drawio.svg │ │ └── console_01.drawio.svg ├── 04 中断和时钟 │ ├── 027 内核全局描述符表.md │ ├── 028 任务及上下文.md │ ├── 029 中断函数.md │ ├── 030 中断描述符.md │ ├── 031 异常.md │ ├── 032 外中断原理.md │ ├── 033 外中断控制器.md │ ├── 034 中断上下文.md │ ├── 035 计数器和时钟.md │ ├── 036 蜂鸣器.md │ ├── 037 时间.md │ ├── 038 实时时钟.md │ ├── 045 外中断控制.md │ └── images │ │ ├── 80486.jpg │ │ ├── 8259a-block.jpg │ │ ├── 8259a.drawio.svg │ │ ├── eflags.drawio.svg │ │ ├── gp_error_code.drawio.svg │ │ ├── instruction_01.drawio.svg │ │ ├── intel_8253_mode.jpg │ │ ├── interrupt_01.drawio.svg │ │ ├── interrupt_context.drawio.svg │ │ ├── speaker_01.jpg │ │ ├── speaker_02.jpg │ │ └── task_01.drawio.svg ├── 05 内存管理 │ ├── 039 内存管理初步.md │ ├── 040 物理内存管理.md │ ├── 041 内存映射原理.md │ ├── 042 内核内存映射.md │ ├── 044 内核虚拟内存管理.md │ ├── 065 内核堆内存管理.md │ ├── 066 用户内存映射.md │ ├── 067 进程用户态栈.md │ ├── 119 内核内存保护.md │ ├── 121 内核内存保护配置.md │ ├── 130 一些优化.md │ └── images │ │ ├── memory_cr0.drawio.svg │ │ ├── memory_cr3.drawio.svg │ │ ├── memory_map_01.drawio.svg │ │ ├── memory_map_02.drawio.svg │ │ ├── memory_map_03.drawio.svg │ │ ├── memory_map_04.drawio.svg │ │ ├── memory_map_05.drawio.svg │ │ ├── memory_map_06.drawio.svg │ │ ├── memory_map_136.drawio.svg │ │ ├── memory_map_176.drawio.svg │ │ ├── memory_paging_01.drawio.svg │ │ ├── memory_paging_02.drawio.svg │ │ ├── memory_pde.drawio.svg │ │ ├── memory_pte.drawio.svg │ │ └── page_fault.jpg ├── 06 数据结构 │ ├── 043 数据结构位图.md │ ├── 049 数据结构链表.md │ ├── 061 数据结构循环队列.md │ └── images │ │ └── list.drawio.svg ├── 07 任务管理 │ ├── 046 创建内核线程.md │ ├── 050 任务阻塞和就绪.md │ ├── 051 基础任务.md │ ├── 052 任务睡眠和唤醒.md │ ├── 053 互斥和信号量.md │ ├── 054 锁.md │ ├── 062 任务状态段简介.md │ ├── 063 进入用户模式.md │ ├── 069 任务 ID.md │ ├── 123 错误处理机制.md │ ├── 124 定时器.md │ ├── 125 任务会话.md │ ├── 127 信号.md │ ├── 128 闹钟.md │ └── images │ │ ├── Edsger_Wybe_Dijkstra.jpg │ │ ├── Privilege_Level.drawio.svg │ │ ├── interrupt_context.drawio.svg │ │ ├── setup_task.drawio.svg │ │ ├── signal.drawio.svg │ │ ├── task_pcb_01.drawio.svg │ │ ├── tss.jpg │ │ └── tss_descriptor.jpg ├── 08 系统调用 │ ├── 047 系统调用.md │ ├── 048 系统调用 yield.md │ ├── 068 系统调用 brk.md │ ├── 070 系统调用 fork.md │ ├── 071 系统调用 exit.md │ ├── 072 系统调用 waitpid.md │ ├── 073 系统调用 time.md │ ├── 107 系统调用 mmap,munmap.md │ └── images │ │ └── memory_map.drawio.svg ├── 09 设备驱动 │ ├── 058 键盘中断.md │ ├── 059 键盘驱动.md │ ├── 060 键盘 LED 灯.md │ ├── 074 硬盘同步 PIO.md │ ├── 075 硬盘异步 PIO.md │ ├── 076 识别硬盘.md │ ├── 077 硬盘分区.md │ ├── 078 虚拟设备.md │ ├── 079 块设备请求.md │ ├── 080 磁盘调度电梯算法.md │ ├── 111 串口设备驱动.md │ ├── 122 CSI 控制序列.md │ ├── 126 TTY 设备.md │ ├── 131 CPU 功能检测.md │ ├── 132 FPU 浮点运算单元.md │ ├── 134 ISA 总线.md │ ├── 135 声霸卡驱动.md │ ├── 136 软盘驱动.md │ ├── 137 PCI 总线.md │ ├── 139 磁盘驱动优化.md │ ├── 140 IDE 硬盘 UDMA.md │ ├── 144 e1000 网卡驱动.md │ ├── 177 ATAPI 光盘驱动.md │ └── images │ │ ├── CAN_Connecteur.svg.png │ │ ├── DB-25_male.svg.png │ │ ├── Floppy_disk_2009_G1.jpg │ │ ├── IBM_Model_F_AT.png │ │ ├── IBM_Model_F_XT.png │ │ ├── IBM_PS_2.jpeg │ │ ├── PCI_Command_Register.jpg │ │ ├── PCI_Configuration_Space.jpg │ │ ├── PCI_Status_Register.jpg │ │ ├── PCI_System_Block.jpg │ │ ├── PCI_und_PCIe_Slots.jpg │ │ ├── elevator.drawio.svg │ │ ├── isa_card.jpg │ │ ├── isa_slot.jpg │ │ ├── keyboard_scancode.svg │ │ ├── oldschool.jpg │ │ ├── partition.drawio.svg │ │ └── ps2_controller.drawio.svg ├── 10 用户程序 │ ├── 064 printf.md │ ├── 100 系统交互 osh.md │ ├── 108 ELF 文件解析.md │ ├── 109 ELF 符号解析.md │ ├── 110 程序加载和执行.md │ ├── 113 C 运行时环境.md │ ├── 114 参数和环境变量.md │ ├── 117 管道序列.md │ ├── 118 init 进程.md │ ├── 120 小结.md │ ├── 133 基础浮点运算.md │ └── images │ │ ├── argv_layout.drawio.svg │ │ ├── elf_layout.jpg │ │ ├── memory_map.drawio.svg │ │ ├── memory_map2.drawio.svg │ │ └── memory_map_boot.drawio.svg ├── 11 文件系统 │ ├── 081 哈希表和高速缓冲.md │ ├── 082 创建文件系统.md │ ├── 083 文件系统简介.md │ ├── 084 根超级块.md │ ├── 085 文件系统位图操作.md │ ├── 086 文件系统 inode.md │ ├── 087 文件系统状态.md │ ├── 088 文件系统目录操作.md │ ├── 089 文件系统 namei.md │ ├── 090 读写 inode.md │ ├── 091 截断 inode.md │ ├── 092 系统调用 mkdir,rmdir.md │ ├── 093 系统调用 link,unlink.md │ ├── 094 打开 inode.md │ ├── 095 文件初始化.md │ ├── 096 系统调用 open,close.md │ ├── 097 系统调用 read,write.md │ ├── 098 系统调用 lseek.md │ ├── 099 系统调用 getcwd,chdir.md │ ├── 101 系统调用 stat,fstat.md │ ├── 102 系统调用 mknod.md │ ├── 103 系统调用 mount,umount.md │ ├── 104 格式化文件系统.md │ ├── 105 虚拟磁盘.md │ ├── 106 标准输入输出.md │ ├── 115 输入输出重定向.md │ ├── 116 管道.md │ ├── 155 虚拟文件系统.md │ ├── 178 ISO9660 文件系统.md │ └── images │ │ ├── block.drawio.svg │ │ ├── block1.drawio.svg │ │ ├── buffer_map.drawio.svg │ │ ├── hashmap.drawio.svg │ │ ├── inode.drawio.svg │ │ ├── list.drawio.svg │ │ └── memory_map.drawio.svg ├── 12 调试工具 │ ├── 129 异常与调试.md │ └── 999 WSL2 开发环境.md ├── 13 系统优化 │ ├── 138 系统优化.md │ ├── 176 高速缓冲优化.md │ ├── 179 版本 1.0.0.md │ └── 180 小结 v2.md ├── 14 网络协议 │ ├── 141 通信的基本原理.md │ ├── 142 以太网协议.md │ ├── 143 基础网络配置.md │ ├── 145 数据包高速缓冲.md │ ├── 146 网络协议简介.md │ ├── 147 CRC 校验和.md │ ├── 148 虚拟网络设备.md │ ├── 149 以太网协议实现.md │ ├── 150 ARP 协议.md │ ├── 151 ARP 缓存.md │ ├── 152 IP 协议.md │ ├── 153 ICMP 协议.md │ ├── 154 IP 校验和.md │ ├── 156 套接字.md │ ├── 157 数据包套接字.md │ ├── 158 原始套接字.md │ ├── 159 ping.md │ ├── 160 localhost 环回地址.md │ ├── 161 UDP 协议.md │ ├── 162 TCP 协议简介.md │ ├── 163 TCP 客户端连接.md │ ├── 164 TCP 选项和重置.md │ ├── 165 TCP 发送和接收.md │ ├── 166 TCP 定时器.md │ ├── 167 TCP 断开连接.md │ ├── 168 TCP 服务端连接.md │ ├── 169 TCP 窗口管理.md │ ├── 170 TCP 超时重传.md │ ├── 171 TCP 拥塞控制.md │ ├── 172 TCP 总结.md │ ├── 173 网络配置.md │ ├── 174 DHCP 协议.md │ ├── 175 DNS 域名解析.md │ └── images │ │ ├── ARPANET_imp.jpg │ │ ├── Arpanet_1977.png │ │ ├── Bob_Kahn.jpg │ │ ├── Bob_Metcalfejpg.jpg │ │ ├── FTP_cable3.jpg │ │ ├── Fiber_optic_illuminated.jpg │ │ ├── Kleinrock.jpg │ │ ├── RG-59.jpg │ │ ├── SteveCrocker.jpg │ │ ├── Vint_Cerf.jpg │ │ ├── arp.drawio.svg │ │ ├── bootp.drawio.svg │ │ ├── dhcp_option.drawio.svg │ │ ├── dns.drawio.svg │ │ ├── dns_answer.drawio.svg │ │ ├── dns_example.drawio.svg │ │ ├── dns_query.drawio.svg │ │ ├── e1000.drawio.svg │ │ ├── ethernet_farme.svg │ │ ├── ethernet_farme1.svg │ │ ├── ethernet_signal.jpg │ │ ├── handshake.drawio.svg │ │ ├── hub.jpg │ │ ├── icmp.drawio.svg │ │ ├── ip.drawio.svg │ │ ├── ip_addr.drawio.svg │ │ ├── layers.drawio.svg │ │ ├── model.drawio.svg │ │ ├── net_arp.drawio.svg │ │ ├── net_ethernet.drawio.svg │ │ ├── net_icmp.drawio.svg │ │ ├── net_ip.drawio.svg │ │ ├── net_tcp.drawio.svg │ │ ├── net_udp.drawio.svg │ │ ├── netif.drawio.svg │ │ ├── pbuf.drawio.svg │ │ ├── switch.jpg │ │ ├── tcp.drawio.svg │ │ ├── tcp_close.drawio.svg │ │ ├── tcp_congestion.drawio.svg │ │ ├── tcp_recv.drawio.svg │ │ ├── tcp_send.drawio.svg │ │ ├── tcp_state.drawio.svg │ │ ├── tcp_state2.drawio.svg │ │ ├── tcp_state3.drawio.svg │ │ ├── topology.drawio.png │ │ └── udp.drawio.svg └── others │ ├── images │ └── snapshot.png │ ├── 参考文献.md │ └── 问题及答案 (Question and Answer).md ├── notes ├── 01 系统引导 │ └── 002 配置开发环境.md ├── 11 文件系统 │ └── 081 哈希表和高速缓冲.md └── README.md ├── onix.code-workspace ├── src ├── boot │ ├── boot.asm │ └── loader.asm ├── builtin │ ├── alarm.c │ ├── cat.c │ ├── client.c │ ├── count.c │ ├── dup.c │ ├── echo.c │ ├── env.c │ ├── err.c │ ├── float.c │ ├── httpd.c │ ├── ifconfig.c │ ├── init.c │ ├── kill.c │ ├── ls.c │ ├── osh.c │ ├── ping.c │ ├── pkt.c │ ├── player.c │ ├── server.c │ ├── tcp_client.c │ ├── tcp_nagle.c │ ├── tcp_server.c │ ├── tcp_surge.c │ └── uname.c ├── fs │ ├── dev.c │ ├── file.c │ ├── fsyscall.c │ ├── inode.c │ ├── iso9660 │ │ └── iso9660.c │ ├── minix │ │ ├── minix.c │ │ └── minix.h │ ├── namei.c │ ├── pipe │ │ └── pipe.c │ └── super.c ├── include │ └── onix │ │ ├── arena.h │ │ ├── assert.h │ │ ├── bitmap.h │ │ ├── buffer.h │ │ ├── console.h │ │ ├── cpu.h │ │ ├── debug.h │ │ ├── device.h │ │ ├── errno.h │ │ ├── fifo.h │ │ ├── fpu.h │ │ ├── fs.h │ │ ├── global.h │ │ ├── ide.h │ │ ├── interrupt.h │ │ ├── io.h │ │ ├── isa.h │ │ ├── list.h │ │ ├── math.h │ │ ├── memory.h │ │ ├── mio.h │ │ ├── multiboot2.h │ │ ├── mutex.h │ │ ├── net.h │ │ ├── net │ │ ├── addr.h │ │ ├── arp.h │ │ ├── chksum.h │ │ ├── dhcp.h │ │ ├── eth.h │ │ ├── icmp.h │ │ ├── ip.h │ │ ├── netif.h │ │ ├── pbuf.h │ │ ├── pkt.h │ │ ├── port.h │ │ ├── raw.h │ │ ├── socket.h │ │ ├── tcp.h │ │ ├── types.h │ │ └── udp.h │ │ ├── onix.h │ │ ├── pci.h │ │ ├── printk.h │ │ ├── rtc.h │ │ ├── sb16.h │ │ ├── signal.h │ │ ├── stat.h │ │ ├── stdarg.h │ │ ├── stdio.h │ │ ├── stdlib.h │ │ ├── string.h │ │ ├── syscall.h │ │ ├── task.h │ │ ├── time.h │ │ ├── timer.h │ │ ├── tty.h │ │ ├── types.h │ │ └── uname.h ├── kernel │ ├── alarm.c │ ├── arena.c │ ├── assert.c │ ├── buffer.c │ ├── clock.c │ ├── console.c │ ├── cpu.c │ ├── debug.c │ ├── device.c │ ├── e1000.c │ ├── execve.c │ ├── floppy.c │ ├── fpu.c │ ├── gate.c │ ├── global.c │ ├── handler.asm │ ├── ide.c │ ├── idle.c │ ├── init.c │ ├── interrupt.c │ ├── io.asm │ ├── isa.c │ ├── keyboard.c │ ├── main.c │ ├── memory.c │ ├── mio.c │ ├── mutex.c │ ├── onix.c │ ├── pci.c │ ├── printk.c │ ├── ramdisk.c │ ├── rtc.c │ ├── sb16.c │ ├── schedule.asm │ ├── serial.c │ ├── signal.c │ ├── start.asm │ ├── system.c │ ├── task.c │ ├── test.c │ ├── time.c │ ├── timer.c │ ├── tty.c │ └── uname.c ├── lib │ ├── assert.c │ ├── bitmap.c │ ├── crt.asm │ ├── crt1.c │ ├── fifo.c │ ├── list.c │ ├── math.c │ ├── printf.c │ ├── restorer.asm │ ├── stdlib.c │ ├── strerror.c │ ├── string.c │ ├── syscall.c │ ├── time.c │ └── vsprintf.c ├── makefile ├── net │ ├── addr.c │ ├── arp.c │ ├── chksum.c │ ├── dhcp.c │ ├── eth.c │ ├── icmp.c │ ├── iovec.c │ ├── ip.c │ ├── loopif.c │ ├── netif.c │ ├── pbuf.c │ ├── pkt.c │ ├── port.c │ ├── raw.c │ ├── resolv.c │ ├── socket.c │ ├── tcp.c │ ├── tcp_input.c │ ├── tcp_option.c │ ├── tcp_output.c │ ├── tcp_pcb.c │ ├── tcp_timer.c │ └── udp.c └── utils │ ├── cdrom.mk │ ├── cmd.mk │ ├── deer.mp3 │ ├── generate.py │ ├── grub.cfg │ ├── image.mk │ ├── master.sfdisk │ ├── net.mk │ ├── network.conf │ ├── resolv.conf │ └── slave.sfdisk └── tests ├── basic ├── call.asm ├── hello.c ├── hello.s ├── makefile ├── params.c ├── params.s ├── test_syscall.asm ├── types.c └── variables.c └── net ├── arp.ipynb ├── fcs.ipynb ├── icmp.ipynb ├── ip.ipynb ├── net.ipynb ├── net.py ├── pkt.ipynb ├── raw.ipynb ├── tcp.ipynb ├── tcp_client.py ├── tcp_close.py ├── tcp_nagle.py ├── tcp_server.py ├── tcp_surge.py └── udp_server.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.img 3 | *.ini 4 | *.bin 5 | *.map 6 | *.o 7 | *.out 8 | *.zst 9 | *.gz 10 | *.lock 11 | *.vmdk 12 | *.log 13 | *.pyc 14 | 15 | build 16 | bochs/src 17 | bochs/pkg 18 | __pycache__ 19 | .vscode/settings.json 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Steven 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bochs/PKGBUILD: -------------------------------------------------------------------------------- 1 | # 用于调试 C 程序 以下内容是从 Archlinux AUR 包和 bochs 包抠出来的,专门用于 32 位系统的调试, 2 | # 默认的是 64 位操作系统所以会有一些问题 3 | # 安装使用 makepkg -si 4 | 5 | pkgname=bochs-gdb 6 | pkgver=2.7 7 | pkgrel=1 8 | pkgdesc="A portable x86 PC emulation software package with gdbstub" 9 | arch=('x86_64') 10 | url="http://bochs.sourceforge.net/" 11 | license=('LGPL') 12 | depends=('gcc-libs' 'libxrandr' 'libxpm' 'gtk2') 13 | source=("http://downloads.sourceforge.net/sourceforge/bochs/bochs-$pkgver.tar.gz") 14 | sha256sums=('a010ab1bfdc72ac5a08d2e2412cd471c0febd66af1d9349bc0d796879de5b17a') 15 | 16 | prepare() { 17 | cd "$srcdir/bochs-$pkgver" 18 | # 4.X kernel is basically 3.20 19 | sed -i 's/2\.6\*|3\.\*)/2.6*|3.*|4.*)/' configure* 20 | } 21 | 22 | build() { 23 | cd "$srcdir/bochs-$pkgver" 24 | 25 | ./configure \ 26 | --prefix=/usr \ 27 | --without-wx \ 28 | --with-x11 \ 29 | --with-x \ 30 | --with-term \ 31 | --disable-docbook \ 32 | --enable-cpu-level=6 \ 33 | --enable-fpu \ 34 | --enable-3dnow \ 35 | --enable-disasm \ 36 | --enable-long-phy-address \ 37 | --enable-disasm \ 38 | --enable-pcidev \ 39 | --enable-usb \ 40 | --enable-all-optimizations \ 41 | --enable-gdb-stub \ 42 | --with-nogui \ 43 | # --with-sdl \ 44 | # --enable-plugins \ 45 | # --enable-smp \ 46 | # --enable-x86-debugger \ 47 | # --enable-debugger \ 48 | # --enable-x86-64 \ 49 | # --enable-avx \ 50 | # --enable-evex \ 51 | sed -i 's/^LIBS = /LIBS = -lpthread/g' Makefile 52 | make -j 1 53 | } 54 | 55 | package() { 56 | cd "$srcdir/bochs-$pkgver" 57 | make DESTDIR="$pkgdir" install 58 | install -Dm644 .bochsrc "$pkgdir/etc/bochsrc-sample.txt" 59 | 60 | cd "$pkgdir/usr/bin/" 61 | mv bochs bochs-gdb 62 | rm -rf bochs-gdb-a20 63 | rm bximage 64 | cd "$pkgdir/usr/" 65 | rm -rfv share 66 | cd "$pkgdir" 67 | rm -rfv etc 68 | } 69 | -------------------------------------------------------------------------------- /docs/01 系统引导/001 概述.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 3 | ## 目标 4 | 5 | - 系统引导 6 | - 硬件及驱动: 7 | - CPU 8 | - 显示器 9 | - 键盘 10 | - 硬盘 11 | - 时钟 12 | - 任务调度:进程,线程 13 | - 内存管理 14 | - 文件系统:最复杂 15 | - 系统调用 16 | - shell 17 | 18 | ## 前驱要求 19 | 20 | - C 语言 21 | - x86 汇编语言 22 | - 环境配置: 23 | - VMWare 24 | - Archlinux 25 | - bochs 26 | - bochs-gdb 27 | - qemu 28 | - vscode - remote 29 | 30 | ## 口头禅 31 | 32 | 这个 - 那个 …… 33 | 34 | ## Onix 35 | -------------------------------------------------------------------------------- /docs/01 系统引导/003 主引导扇区.md: -------------------------------------------------------------------------------- 1 | # 主引导扇区 2 | 3 | ## BIOS 4 | 5 | Basic Input Output System 6 | 7 | BIOS 在加电自检将主引导扇区读 0x7c00 位置,并跳转到这里执行。 8 | 9 | int 0x10; BIOS 系统调用,显示器相关的功能 10 | 11 | ## 实模式 12 | 13 | 8086模式,16位,保护模式 14 | 15 | - Real Mode 16 | - Protected Mode 17 | 18 | ```s 19 | ; 0xb8000 文本显示器的内存区域 20 | mov ax, 0xb800 21 | mov ds, ax 22 | mov byte [0], 'H' 23 | ``` 24 | ## 实模式的寻址方式 25 | 26 | > 有效地址 = 段地址 * 16 + 偏移地址 27 | 28 | EA = 0xb800 * 0x10 + 0 = 0xb8000 29 | 30 | EA (Effective Address) 31 | 32 | 16 bit - 1M - 20 bit 33 | 34 | 20 - 16 = 4 35 | 36 | 段地址 << 4 37 | 38 | 32 bit - 4G 39 | 40 | ## 主引导扇区的结构 41 | 42 | - 代码:446B 43 | - 硬盘分区表:64B = 4 * 16B 44 | - 魔数:0xaa55 - 0x55 0xaa 45 | 46 | ## 主要功能 47 | 48 | 读取内核加载器,并执行 49 | 50 | ## 参考文献 51 | 52 | - IBM PS 2 and PC BIOS Interface Technical Reference 53 | -------------------------------------------------------------------------------- /docs/01 系统引导/004 在 U 盘启动.md: -------------------------------------------------------------------------------- 1 | # 在 U 盘启动 2 | 3 | ## 答疑 4 | 5 | 1. 运行操作系统的模拟器 qemu 更好用 6 | 1. bochs 7 | 2. 参考书 8 | 1. x86 汇编语言 9 | 2. 操作系统真象还原 10 | 3. 现在 CPU 实模式也是这样 11 | 12 | ## 在 U 盘启动 13 | 14 | 需要一个可以格式化的 U 盘 15 | 16 | 格式化 U 盘 17 | 18 | sudo fdisk /dev/sdb 19 | 20 | All space for primary partitions is in use. 21 | 22 | d 删除分区 23 | -------------------------------------------------------------------------------- /docs/01 系统引导/005 实模式 print.md: -------------------------------------------------------------------------------- 1 | # 实模式 print 2 | 3 | - ah: 0x0e 4 | - al: 字符 5 | - int 0x10 6 | 7 | xchg bx, bx; bochs 魔术断点 8 | 9 | ```s 10 | mov si, booting 11 | call print 12 | 13 | ; 阻塞 14 | jmp $ 15 | 16 | print: 17 | mov ah, 0x0e 18 | .next: 19 | mov al, [si] 20 | cmp al, 0 21 | jz .done 22 | int 0x10 23 | inc si 24 | jmp .next 25 | .done: 26 | ret 27 | 28 | booting: 29 | db "Booting Onix...", 10, 13, 0; \n\r 30 | ``` -------------------------------------------------------------------------------- /docs/01 系统引导/007 内核加载器.md: -------------------------------------------------------------------------------- 1 | # 内核加载器 2 | 3 | - 写内核加载器 loader 4 | - 将 loader 写入硬盘 5 | - 在主引导扇区读入 6 | - 检测正确性 7 | - 跳转到 loader 执行 8 | 9 | ## 实模式的内存布局 10 | 11 | | 起始地址 | 结束地址 | 大小 | 用途 | 12 | | --------- | --------- | -------- | ------------------ | 13 | | `0x000` | `0x3FF` | 1KB | 中断向量表 | 14 | | `0x400` | `0x4FF` | 256B | BIOS 数据区 | 15 | | `0x500` | `0x7BFF` | 29.75 KB | 可用区域 | 16 | | `0x7C00` | `0x7DFF` | 512B | MBR 加载区域 | 17 | | `0x7E00` | `0x9FBFF` | 607.6KB | 可用区域 | 18 | | `0x9FC00` | `0x9FFFF` | 1KB | 扩展 BIOS 数据区 | 19 | | `0xA0000` | `0xAFFFF` | 64KB | 用于彩色显示适配器 | 20 | | `0xB0000` | `0xB7FFF` | 32KB | 用于黑白显示适配器 | 21 | | `0xB8000` | `0xBFFFF` | 32KB | 用于文本显示适配器 | 22 | | `0xC0000` | `0xC7FFF` | 32KB | 显示适配器 BIOS | 23 | | `0xC8000` | `0xEFFFF` | 160KB | 映射内存 | 24 | | `0xF0000` | `0xFFFEF` | 64KB-16B | 系统 BIOS | 25 | | `0xFFFF0` | `0xFFFFF` | 16B | 系统 BIOS 入口地址 | 26 | 27 | -------------------------------------------------------------------------------- /docs/01 系统引导/010 进入内核.md: -------------------------------------------------------------------------------- 1 | # 进入内核 2 | 3 | ## 整理项目目录 4 | 5 | - boot 6 | - kernel 7 | 8 | ## 编写简单内核 9 | 10 | ```s 11 | [bits 32] 12 | 13 | global _start 14 | _start: 15 | mov byte [0xb8000], 'K'; 表示进入了内核 16 | jmp $; 阻塞 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/01 系统引导/055 multiboot2 头.md: -------------------------------------------------------------------------------- 1 | # multiboot2 头 2 | 3 | 要支持 multiboot2,内核必须添加一个 multiboot 头,而且必须再内核开始的 32768(0x8000) 字节,而且必须 64 字节对齐; 4 | 5 | | 偏移 | 类型 | 名称 | 备注 | 6 | | ----- | ---- | ------------------- | ---- | 7 | | 0 | u32 | 魔数 (magic) | 必须 | 8 | | 4 | u32 | 架构 (architecture) | 必须 | 9 | | 8 | u32 | 头部长度 (header_length) | 必须 | 10 | | 12 | u32 | 校验和 (checksum) | 必须 | 11 | | 16-XX | | 标记 (tags) | 必须 | 12 | 13 | - `magic` = 0xE85250D6 14 | - `architecture`: 15 | - 0:32 位保护模式 16 | - `checksum`:与 `magic`, `architecture`, `header_length` 相加必须为 `0` 17 | 18 | ## 参考文献 19 | 20 | - 21 | - 22 | - 23 | - 24 | - 25 | - 26 | - 27 | - 28 | 29 | -------------------------------------------------------------------------------- /docs/01 系统引导/056 multiboot2 引导.md: -------------------------------------------------------------------------------- 1 | # multiboot2 引导 2 | 3 | ## i386 状态 4 | 5 | - EAX:魔数 `0x36d76289` 6 | - EBX:包含 bootloader 存储 multiboot2 信息结构体的,32 位 物理地址 7 | - CS:32 位 可读可执行的代码段,尺寸 4G 8 | - DS/ES/FS/GS/SS:32 位可读写的数据段,尺寸 4G 9 | - A20 线:启用 10 | - CR0:PG = 0, PE = 1,其他未定义 11 | - EFLAGS:VM = 0, IF = 0, 其他未定义 12 | - ESP:内核必须尽早切换栈顶地址 13 | - GDTR:内核必须尽早使用自己的全局描述符表 14 | - IDTR:内核必须在设置好自己的中断描述符表之前关闭中断 15 | 16 | Virtual 8086 Mode 17 | 18 | ## 参考文献 19 | 20 | - -------------------------------------------------------------------------------- /docs/01 系统引导/images/80286-segment descriptor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/01 系统引导/images/80286-segment descriptor.jpg -------------------------------------------------------------------------------- /docs/01 系统引导/images/80386-segment descriptor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/01 系统引导/images/80386-segment descriptor.jpg -------------------------------------------------------------------------------- /docs/01 系统引导/images/harddisk_1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/01 系统引导/images/harddisk_1.jpeg -------------------------------------------------------------------------------- /docs/01 系统引导/images/harddisk_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/01 系统引导/images/harddisk_2.jpg -------------------------------------------------------------------------------- /docs/01 系统引导/images/harddisk_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/01 系统引导/images/harddisk_3.jpg -------------------------------------------------------------------------------- /docs/01 系统引导/images/harddisk_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/01 系统引导/images/harddisk_4.jpg -------------------------------------------------------------------------------- /docs/01 系统引导/images/harddisk_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/01 系统引导/images/harddisk_5.jpg -------------------------------------------------------------------------------- /docs/01 系统引导/images/harddisk_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/01 系统引导/images/harddisk_6.jpg -------------------------------------------------------------------------------- /docs/01 系统引导/images/harddisk_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/01 系统引导/images/harddisk_7.jpg -------------------------------------------------------------------------------- /docs/02 二进制基础/011 ELF 文件格式简介.md: -------------------------------------------------------------------------------- 1 | # ELF 文件格式简介 2 | 3 | Executable and Linking Format / 可执行和链接的格式 4 | 5 | 1. 可执行程序 / python / bash / gcc 6 | 2. 可重定位文件 / gcc -c `.o` / 静态库 ar `.a` 7 | 3. 共享的目标文件 / 动态链接库 `.so` 8 | 9 | ## 可执行程序 10 | 11 | 1. 代码 .text 段 section ELF / segment CPU 12 | 2. 数据: 13 | 1. .data section / 已经初始化过的数据 14 | 2. .bss 未初始化过的数据 Block Started by Symbol 15 | 16 | ## 程序分析 17 | 18 | ```c++ 19 | #include 20 | 21 | int main() 22 | { 23 | printf("hello world!!!\n"); 24 | return 0; 25 | } 26 | ``` 27 | 28 | 编译成为 32 位的程序 29 | 30 | gcc -m32 -static hello.c -o hello 31 | 32 | --- 33 | 34 | readelf -e hello 35 | 36 | ## 参考文献 37 | 38 | - 39 | -------------------------------------------------------------------------------- /docs/02 二进制基础/012 bootloader 补充说明.md: -------------------------------------------------------------------------------- 1 | # bootloader 补充说明 2 | 3 | - boot.asm 4 | - loader.asm 5 | 6 | bootloader 的主要功能就是从 BIOS 加载内核,并且提供内核需要的参数。 7 | 8 | ## bochs-x 9 | 10 | 11 | 12 | sudo apt install bochs-x 13 | 14 | ---- 15 | 16 | bximage -hd -mode="flat" -size=60 -q master.img 17 | 18 | ## 0xaa55 19 | 20 | 魔数,就是用来检测错误; 21 | 22 | - A 1010 23 | - 5 0101 24 | - 1010101001010101 25 | 26 | ## 0x7c00 27 | 28 | - IBM PC 5150 29 | - DOS 1.0 30 | 31 | 32K = 0x8000 32 | 33 | 栈:一般会放在内存的最后,因为栈时向下增长的; 34 | 35 | 32k - 1k = 0x7c00 36 | 37 | ## grub 38 | 39 | - multiboot / 多系统启动 40 | 41 | ## 参考文献 42 | 43 | - 44 | - 45 | -------------------------------------------------------------------------------- /docs/02 二进制基础/014 基础数据类型.md: -------------------------------------------------------------------------------- 1 | # 基础数据类型 2 | 3 | ```c++ 4 | #define EOF -1 // END OF FILE 5 | 6 | #define NULL 0 // 空指针 7 | 8 | #define bool _Bool 9 | #define true 1 10 | #define false 0 11 | 12 | #define _packed __attribute__((packed)) // 用于定义特殊的结构体 13 | 14 | typedef unsigned int size_t; 15 | 16 | typedef char int8; 17 | typedef short int16; 18 | typedef int int32; 19 | typedef long long int64; 20 | 21 | typedef unsigned char u8; 22 | typedef unsigned short u16; 23 | typedef unsigned int u32; 24 | typedef unsigned long long u64; 25 | ``` 26 | 27 | ## 参考文献 28 | 29 | - https://code.visualstudio.com/docs/editor/variables-reference 30 | -------------------------------------------------------------------------------- /docs/02 二进制基础/016 qemu + vmware.md: -------------------------------------------------------------------------------- 1 | # qemu + vmware 2 | 3 | ## qemu 4 | 5 | 安装 qemu 6 | 7 | sudo pacman -S qemu 8 | sudo pacman -S qemu-arch-extra 9 | 10 | ## vmware 11 | 12 | 转换硬盘格式 13 | 14 | qemu-img convert -pO vmdk $< $@ 15 | 16 | ## samba 17 | 18 | ## 参考文献 19 | 20 | - 21 | -------------------------------------------------------------------------------- /docs/02 二进制基础/017 gcc 汇编分析.md: -------------------------------------------------------------------------------- 1 | # gcc 汇编分析 2 | 3 | ## CFI 4 | 5 | Call Frame Information / 调用栈帧信息 6 | 7 | 一种 DWARF 的信息,用于调试,获得调用异常; 8 | 9 | -fno-asynchronous-unwind-tables 10 | 11 | ## PIC 12 | 13 | Position Independent Code / 位置无关的代码 14 | 15 | call __x86.get_pc_thunk.ax 16 | 17 | 获取调用时 `eip` 的值,CPU 指令指针寄存器 extended instruction pointer 18 | 19 | mov eax, eip; 错误的指令 20 | 21 | 得到 `_GLOBAL_OFFSET_TABLE_`,里面存储了 符号的地址信息 22 | 23 | -fno-pic 24 | 25 | ## ident 26 | 27 | GCC 的版本信息 28 | 29 | ## 栈对齐 30 | 31 | -16 = 0 - 16 = 0x00000000 - 0x10 = 0xfffffff0 32 | 33 | 将栈对齐到 16 字节; 34 | 35 | 字节对齐的话,访问内存更加高效,使用更少的时钟周期; 36 | 37 | -mpreferred-stack-boundary=2 38 | 39 | ## 栈帧 40 | 41 | pushl %ebp 42 | movl %esp, %ebp 43 | 44 | leave 45 | movl %ebp, %esp 46 | popl $ebp 47 | 48 | -fomit-frame-pointer 49 | 50 | ## 解析代码 51 | 52 | ```s 53 | .file "hello.c" # 文件名 54 | 55 | .text # 代码段 56 | .globl message # 将 message 导出 57 | 58 | .data # 数据段 59 | .align 4 # 对齐到四个字节 60 | 61 | .type message, @object 62 | .size message, 16 63 | message: 64 | .string "hello world!!!\n" 65 | 66 | .globl buf 67 | .bss # bss 段 68 | .align 32 69 | .type buf, @object 70 | .size buf, 1024 71 | buf: 72 | .zero 1024 73 | 74 | .text 75 | .globl main 76 | .type main, @function 77 | main: 78 | 79 | pushl $message # message 的地址压入栈中 80 | call printf # 调用 printf 81 | addl $4, %esp # 恢复栈 82 | movl $0, %eax # 函数返回值存储在 eax 寄存器中; 83 | ret # 函数调用返回 84 | .size main, .-main 85 | .section .note.GNU-stack,"",@progbits # 标记栈不可运行 86 | ``` 87 | 88 | ## 参考文献 89 | 90 | - 91 | - 92 | -------------------------------------------------------------------------------- /docs/02 二进制基础/018 堆栈和函数.md: -------------------------------------------------------------------------------- 1 | # 堆栈和函数 2 | 3 | ## 堆栈 4 | 5 | 栈:是一个很重要的数据结构,特征:后进先出 6 | 7 | - 堆栈是一块内存区域; 8 | - 栈顶指针是在 `ss:esp` 寄存器中,栈底是在高地址,向下增长 9 | 10 | - `push` 入栈 11 | - `pop` 出栈 12 | - `pusha` 将 8 个基础寄存器压入栈中 13 | - `popa` 将 7 个基础寄存器弹出,与 `pusha` 相对,会忽略 esp 14 | 15 | ## 函数 16 | 17 | - `call`: 会将 `call` 返回的下一条指令地址压入栈中 18 | - `ret`: 将栈顶弹出到 eip 19 | - `call` 和 `ret` 无关 20 | -------------------------------------------------------------------------------- /docs/02 二进制基础/019 局部变量和参数传递.md: -------------------------------------------------------------------------------- 1 | # 局部变量和参数传递 2 | 3 | ```c++ 4 | int add(int x, int y) 5 | { 6 | int z = x + y; 7 | return z; 8 | } 9 | 10 | int main() 11 | { 12 | int a = 5; 13 | int b = 3; 14 | int c = add(a, b); 15 | return 0; 16 | } 17 | ``` 18 | 19 | ```s 20 | .file "params.c" 21 | .text 22 | .globl add 23 | .type add, @function 24 | add: 25 | pushl %ebp 26 | movl %esp, %ebp 27 | 28 | subl $4, %esp # 一个局部变量 29 | movl 8(%ebp), %edx # a 30 | movl 12(%ebp), %eax # b 31 | addl %edx, %eax # eax += edx 32 | movl %eax, -4(%ebp) # z = x + y; 33 | movl -4(%ebp), %eax # eax = z; 34 | 35 | leave 36 | ret 37 | .size add, .-add 38 | .globl main 39 | .type main, @function 40 | main: 41 | pushl %ebp 42 | movl %esp, %ebp # 保存栈帧 43 | 44 | subl $12, %esp # 保存 12 个字节,有三个局部变量 45 | movl $5, -12(%ebp) # a 46 | movl $3, -8(%ebp) # b 47 | 48 | pushl -8(%ebp) # b 49 | pushl -12(%ebp) # a 50 | call add # 调用 51 | addl $8, %esp # 恢复栈 52 | 53 | movl %eax, -4(%ebp) # c = add(a, b); 54 | movl $0, %eax # 返回值存储在 eax 寄存器中 55 | 56 | leave # 恢复栈帧 57 | ret # 函数返回 58 | .size main, .-main 59 | .section .note.GNU-stack,"",@progbits 60 | ``` 61 | 62 | ## 栈帧 63 | 64 | 保存函数局部变量的信息,可以用于回溯调用函数; 65 | 66 | ## 栈保护 67 | 68 | canary 69 | 70 | ## 寄存器传递参数 71 | 72 | `printf` // 支持多参数 73 | -------------------------------------------------------------------------------- /docs/03 字符输出/020 输入和输出.md: -------------------------------------------------------------------------------- 1 | # 输入和输出 2 | 3 | CPU 外部设备寄存器的编号就是端口号,65536 个 4 | 5 | - CRT 地址寄存器 0x3D4 6 | - CRT 数据寄存器 0x3D5 7 | - CRT 光标位置 - 高位 0xE 8 | - CRT 光标位置 - 低位 0xF 9 | 10 | ## 参考文献 11 | 12 | - 13 | - 14 | - 15 | -------------------------------------------------------------------------------- /docs/03 字符输出/021 字符串处理.md: -------------------------------------------------------------------------------- 1 | # 字符串处理 2 | 3 | `#include ` 4 | 5 | ```c++ 6 | char *strcpy(char *dest, const char *src); 7 | char *strcat(char *dest, const char *src); 8 | size_t strlen(const char *str); 9 | int strcmp(const char *lhs, const char *rhs); 10 | char *strchr(const char *str, int ch); 11 | char *strrchr(const char *str, int ch); 12 | 13 | int memcmp(const void *lhs, const void *rhs, size_t count); 14 | void *memset(void *dest, int ch, size_t count); 15 | void *memcpy(void *dest, const void *src, size_t count); 16 | void *memchr(const void *ptr, int ch, size_t count); 17 | ``` 18 | 19 | ## 参考文献 20 | 21 | - 22 | -------------------------------------------------------------------------------- /docs/03 字符输出/023 可变参数原理.md: -------------------------------------------------------------------------------- 1 | # 可变参数原理 2 | 3 | ```c++ 4 | int printf(const char* format, ...); 5 | ``` 6 | 7 | - `va_list`:保存可变参数指针 8 | - `va_start`:启用可变参数 9 | - `va_arg`:获取下一个参数 10 | - `va_end`:结束可变参数 11 | 12 | # 参考文献 13 | 14 | - 15 | -------------------------------------------------------------------------------- /docs/03 字符输出/025 断言 assert.md: -------------------------------------------------------------------------------- 1 | # 断言 assert 2 | 3 | 用于确定程序的运行状态,防止错误蔓延!!! 4 | 5 | 并且提供尽可能多的出错信息,以供排错。 6 | 7 | ## 参考文献 8 | 9 | - 10 | -------------------------------------------------------------------------------- /docs/03 字符输出/026 调试工具.md: -------------------------------------------------------------------------------- 1 | # 调试工具 2 | 3 | ```c++ 4 | asm volatile("xchgw %bx, %bx") 5 | DEBUGK(); 6 | ``` 7 | 8 | ## 参考文献 9 | 10 | - 11 | - 12 | -------------------------------------------------------------------------------- /docs/03 字符输出/112 串口输出日志.md: -------------------------------------------------------------------------------- 1 | # 串口输出日志 2 | 3 | 有了 shell 之后,直接将日志输出到控制台,开始有点乱了,可以将日志通过宏去掉,或者将日志输出到串口。 4 | 5 | 另外,bochs [^bochs][^serial] 好像对串口的支持不太好,目前不知道是为什么所以直接禁了。 6 | 7 | ## TTY 8 | 9 | teletypewriter - 电传打字机 10 | 11 | ## 参考 12 | 13 | [^bochs]: 14 | [^serial]: 15 | -------------------------------------------------------------------------------- /docs/04 中断和时钟/027 内核全局描述符表.md: -------------------------------------------------------------------------------- 1 | # 内核全局描述符表 2 | 3 | ```c++ 4 | descriptor_t gdt[GDT_SIZE]; // 内核全局描述符表 5 | pointer_t gdt_ptr; // 内核全局描述符表指针 6 | ``` 7 | 8 | ```s 9 | lgdt [gdt_ptr]; 加载 gdt 10 | sgdt [gdt_ptr]; 保存 gdt 11 | ``` 12 | -------------------------------------------------------------------------------- /docs/04 中断和时钟/028 任务及上下文.md: -------------------------------------------------------------------------------- 1 | # 任务及上下文 2 | 3 | ## 任务 4 | 5 | 任务就是进程或者线程,协程,就是一个执行流; 6 | 7 | - 程序入口地址 8 | - 堆栈 - 内核栈 9 | - 寄存器信息 10 | 11 | ## ABI 调用约定 12 | 13 | Application Binary Interface 14 | 15 | System V ABI 16 | 17 | 调用方保存: 18 | 19 | - eax 20 | - ecx 21 | - edx 22 | 23 | 实现方保存,调用完成后寄存器值不变: 24 | 25 | - ebx 26 | - esi 27 | - edi 28 | - ebp 29 | - esp 30 | 31 | ## 内存分页 32 | 33 | 4G / 4K = 1M 34 | 35 | ## 任务内存分布 36 | 37 | ![](./images/task_01.drawio.svg) 38 | 39 | ## 参考文献 40 | 41 | - 42 | - 43 | - 44 | -------------------------------------------------------------------------------- /docs/04 中断和时钟/029 中断函数.md: -------------------------------------------------------------------------------- 1 | # 中断函数 2 | 3 | ## 简介 4 | 5 | - 内中断 6 | - 软中断: 7 | - 系统调用:读写文件 8 | - 异常: 9 | - 除零 10 | - 指令错误 11 | - 缺页错误 12 | - 外中断: 13 | - 时钟中断 14 | - 键盘中断 15 | - 硬盘中断: 16 | - 同步端口IO 17 | - 异步端口IO 18 | - DMA Direct Memory Access 19 | 20 | ![](./images/interrupt_01.drawio.svg) 21 | 22 | ## 中断函数 23 | 24 | * call / ret 25 | * eip 26 | * int(interrupt) / iret (interrupt return) 27 | - eip 28 | - cs 29 | - eflags 30 | 31 | ## 中断向量表 实模式 32 | 33 | 中断向量就是中断函数的指针 34 | 35 | > `0x000` ~ `0x3ff` 36 | 37 | 4 个字节表示一个中断向量,总共有 256 个中断函数指针 38 | 39 | 段地址 << 4 + 偏移地址 40 | 41 | * 偏移地址 (ip) 42 | * 段地址 (cs) 43 | 44 | int nr; 中断函数编号 0 ~ 255 45 | 46 | invoke # 调用,引发,触发 47 | 48 | ## 内存分段 49 | 50 | 16bit 能访问的64K内存, 51 | 52 | 32bit 访问所有的 4G 内存,也就是可以不分段,所以分段的概念就延续了下来; 53 | 54 | > 奥卡姆剃刀原则!!! 55 | 56 | ## 实模式中断代码 57 | 58 | ```s 59 | [org 0x7c00] 60 | 61 | ; 设置屏幕模式为文本模式,清除屏幕 62 | mov ax, 3 63 | int 0x10 64 | 65 | ; 初始化段寄存器 66 | mov ax, 0 67 | mov ds, ax 68 | mov es, ax 69 | mov ss, ax 70 | mov sp, 0x7c00 71 | 72 | xchg bx, bx 73 | ; call interrupt ; 普通调用 74 | 75 | mov word [0 * 4], interrupt 76 | mov word [0 * 4 + 2], 0 77 | 78 | ; int 0x80; linux 系统调用号 invoke 79 | 80 | mov dx, 0 81 | mov ax, 1 82 | mov bx, 0 83 | 84 | xchg bx, bx 85 | 86 | div bx ; dx : ax / bx 87 | 88 | 89 | ; 阻塞 90 | jmp $ 91 | 92 | interrupt: 93 | mov si, string 94 | call print 95 | xchg bx, bx 96 | ; ret ; 普通返回 97 | iret ; 中断返回 98 | 99 | print: 100 | mov ah, 0x0e 101 | .next: 102 | mov al, [si] 103 | cmp al, 0 104 | jz .done 105 | int 0x10 106 | inc si 107 | jmp .next 108 | .done: 109 | ret 110 | 111 | string: 112 | db ".", 0 113 | 114 | ; 填充 0 115 | times 510 - ($ - $$) db 0 116 | 117 | ; 主引导扇区的最后两个字节必须是 0x55 0xaa 118 | ; dw 0xaa55 119 | db 0x55, 0xaa 120 | ``` -------------------------------------------------------------------------------- /docs/04 中断和时钟/034 中断上下文.md: -------------------------------------------------------------------------------- 1 | # 中断上下文 2 | 3 | ## 目录 4 | 5 | - 中断上下文 6 | - 异常信息演示 7 | 8 | ## 中断上下文 9 | 10 | ```c++ 11 | // 用于省略函数的栈帧 12 | #define _ofp __attribute__((optimize("omit-frame-pointer"))) 13 | ``` 14 | 15 | ![](./images/interrupt_context.drawio.svg) 16 | 17 | ## 参考文献 18 | 19 | - 20 | - 21 | -------------------------------------------------------------------------------- /docs/04 中断和时钟/036 蜂鸣器.md: -------------------------------------------------------------------------------- 1 | # 蜂鸣器 2 | 3 | `\a\n` 4 | 5 | ## 扬声器 6 | 7 | ![](./images/speaker_01.jpg) 8 | 9 | 音频是录音设备在特定的时刻记录当时空气的张力值。 10 | 11 | ![](./images/speaker_02.jpg) 12 | 13 | - 位数:音频张力量化位数,采样的精度。 14 | - 采样率:每秒钟的采样数量,通常为 44100 Hz,或者 48000 Hz,人耳能听到的频率为 20 ~ 20000 Hz,而成年人一般只能听到 20 ~ 15000 Hz,所以根据 **奈奎斯特采样定律** 44100 Hz 的音频完全可以满足人耳的需要。采样率表示了,录音设备每秒采集数据的次数,也就是 bit 位数,每秒采集相应次数的数值,用来记录一秒内声音张力的变化; 15 | - 声道:声轨的数量,一般位为单声道或者立体声; 16 | - 码率(比特率):每秒播放的字节数,可以估计出缓冲区大小,也就是 位数 * 采样率,一些编码方式可能有压缩,所以码率一般不是恒定的值; 17 | 18 | ---- 19 | 20 | PC Speaker 是 PC 兼容机中最原始的声音设备,特点是独特的蜂鸣效果,所以有时也被称为蜂鸣器; 21 | 22 | 扬声器有两种状态,输入和输出,状态可以通过键盘控制器中的端口号 `0x61` 设置,该寄存器结构如下: 23 | 24 | | 位 | 描述 | 25 | | --- | --------------- | 26 | | 0 | 计数器 2 门有效 | 27 | | 1 | 扬声器数据有效 | 28 | | 2 | 通道校验有效 | 29 | | 3 | 奇偶校验有效 | 30 | | 4 | 保留 | 31 | | 5 | 保留 | 32 | | 6 | 通道错误 | 33 | | 7 | 奇偶错误 | 34 | 35 | 需要将 0 ~ 1 位置为 1,然后计数器 2 设置成 方波模式,就可以播放方波的声音。 36 | 37 | ## 440 Hz 38 | 39 | `A4 = 440Hz` 第一国际高度; 40 | 41 | - 小提琴:GDAE 42 | - 吉他:EADGBE 43 | - 二胡:DA 44 | - 琵琶:ADEA 45 | 46 | ## qemu 音频驱动 47 | 48 | - ALSA 49 | - coreaudio 50 | - dsound 51 | - oss 52 | - PulseAudio 53 | - SDL 54 | - spice 55 | - wav 56 | 57 | ## 参考文献 58 | 59 | - 60 | - 61 | - 62 | - 63 | - 64 | - 65 | - 66 | -------------------------------------------------------------------------------- /docs/04 中断和时钟/045 外中断控制.md: -------------------------------------------------------------------------------- 1 | # 外中断控制 2 | 3 | ## EFLAGS 4 | 5 | ![](../04%20中断和时钟/images/eflags.drawio.svg) 6 | 7 | ## 核心代码 8 | 9 | ```c++ 10 | bool interrupt_disable(); // 清除 IF 位,返回设置之前的值 11 | bool get_interrupt_state(); // 获得 IF 位 12 | void set_interrupt_state(bool state); // 设置 IF 位 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/04 中断和时钟/images/80486.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/04 中断和时钟/images/80486.jpg -------------------------------------------------------------------------------- /docs/04 中断和时钟/images/8259a-block.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/04 中断和时钟/images/8259a-block.jpg -------------------------------------------------------------------------------- /docs/04 中断和时钟/images/intel_8253_mode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/04 中断和时钟/images/intel_8253_mode.jpg -------------------------------------------------------------------------------- /docs/04 中断和时钟/images/speaker_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/04 中断和时钟/images/speaker_01.jpg -------------------------------------------------------------------------------- /docs/04 中断和时钟/images/speaker_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/04 中断和时钟/images/speaker_02.jpg -------------------------------------------------------------------------------- /docs/05 内存管理/039 内存管理初步.md: -------------------------------------------------------------------------------- 1 | # 内存管理初步 2 | 3 | 从 loader 的内存检测结果获取可用内存区域,操作形式尽可能兼容 grub multiboot 4 | 5 | 以下是目前的内存分布图: 6 | 7 | ![](./images/memory_map_01.drawio.svg) 8 | 9 | - 386/486 内存分页是以 4K = 4096B = 0x1000B 为单位; 10 | - 奔腾处理器引入了以 4M 为单位的页; 11 | 12 | ## 参考文献 13 | 14 | - 15 | - 16 | -------------------------------------------------------------------------------- /docs/05 内存管理/040 物理内存管理.md: -------------------------------------------------------------------------------- 1 | # 物理内存管理 2 | 3 | ## 目录 4 | 5 | - makefile 补充说明 6 | - 物理内存管理 7 | 8 | --- 9 | 10 | 从可用内存开始的位置分配一些页用于管理物理内存 11 | 12 | 每个物理页使用一个字节表示引用数量,意味着一个物理页最多被引用 255 次 13 | 14 | ```c++ 15 | // 分配一页物理内存 16 | static u32 get_page() 17 | 18 | // 释放一页物理内存 19 | static void put_page(u32 addr) 20 | ``` 21 | 22 | ## 参考文献 23 | 24 | - 赵炯 - 《Linux 内核完全注释》 25 | -------------------------------------------------------------------------------- /docs/05 内存管理/042 内核内存映射.md: -------------------------------------------------------------------------------- 1 | # 内核内存映射 2 | 3 | ## 目录 4 | 5 | - 内存映射 6 | - bochs 页映射调试 7 | 8 | --- 9 | 10 | ```c++ 11 | // 将最后一个页表指向页目录自己,方便修改 12 | // 不过,这样会浪费掉最后 4M 的线性地址空间,只能用来管理页表; 13 | page_entry_t *entry = &pde[1023]; 14 | entry_init(entry, IDX(KERNEL_PAGE_DIR)); 15 | ``` 16 | 17 | 将前 8M 的内存映射到,自已原先的位置,供内核使用; 18 | 19 | ![](./images/memory_map_02.drawio.svg) 20 | 21 | 映射完成之后的页面分布: 22 | 23 | ![](./images/memory_paging_02.drawio.svg) 24 | 25 | ## 刷新快表 26 | 27 | - `mov cr3, eax` 28 | - `invlpg` 29 | 30 | ```c++ 31 | // 刷新虚拟地址 vaddr 的 快表 TLB 32 | static void flush_tlb(u32 vaddr) 33 | { 34 | asm volatile("invlpg (%0)" ::"r"(vaddr) 35 | : "memory"); 36 | } 37 | ``` 38 | 39 | ---- 40 | 41 | ## 参考文献 42 | 43 | - 郑刚 - 《操作系统真象还原》,人民邮电出版社 44 | - 45 | - 46 | -------------------------------------------------------------------------------- /docs/05 内存管理/044 内核虚拟内存管理.md: -------------------------------------------------------------------------------- 1 | # 内核虚拟内存管理 2 | 3 | ## 内存布局 4 | 5 | ![](./images/memory_map_03.drawio.svg) 6 | 7 | ## 核心代码 8 | 9 | ```c++ 10 | // 分配 count 个连续的内核页 11 | u32 alloc_kpage(u32 count); 12 | 13 | // 释放 count 个连续的内核页 14 | void free_kpage(u32 vaddr, u32 count); 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/05 内存管理/065 内核堆内存管理.md: -------------------------------------------------------------------------------- 1 | # 内核堆内存管理 2 | 3 | ```c++ 4 | void *kmalloc(size_t size); 5 | void kfree(void *ptr); 6 | ``` 7 | 8 | ## 内存块描述符 9 | 10 | ```c++ 11 | typedef struct arena_descriptor_t 12 | { 13 | u32 total_block; 14 | u32 block_size; 15 | list_t free_list; 16 | } arena_descriptor_t; 17 | ``` 18 | 19 | ## arena 20 | 21 | ```c++ 22 | typedef struct arena_t 23 | { 24 | arena_descriptor_t *desc; 25 | u32 count; 26 | u32 large; 27 | u32 magic; 28 | } arena_t; 29 | ``` 30 | 31 | ## 参考文献 32 | 33 | - [郑刚 / 操作系统真象还原 / 人民邮电出版社 / 2016](https://book.douban.com/subject/26745156/) 34 | 35 | -------------------------------------------------------------------------------- /docs/05 内存管理/066 用户内存映射.md: -------------------------------------------------------------------------------- 1 | # 用户内存映射 2 | 3 | ```c++ 4 | // 将 vaddr 映射物理内存 5 | void link_page(u32 vaddr); 6 | 7 | // 去掉 vaddr 对应的物理内存映射 8 | void unlink_page(u32 vaddr); 9 | ``` 10 | 11 | ![](./images/memory_map_04.drawio.svg) 12 | 13 | 程序运行的局部性原理! 14 | -------------------------------------------------------------------------------- /docs/05 内存管理/067 进程用户态栈.md: -------------------------------------------------------------------------------- 1 | # 进程用户态栈 2 | 3 | ![](./image/../images/memory_map_05.drawio.svg) 4 | 5 | ## 缺页异常 6 | 7 | ![](./images/page_fault.jpg) 8 | 9 | ## 参考文献 10 | 11 | 1. Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3 Chapter 6 Interrupt and Exception Handling -------------------------------------------------------------------------------- /docs/05 内存管理/119 内核内存保护.md: -------------------------------------------------------------------------------- 1 | # 内核内存保护 2 | 3 | ## 现有的问题 4 | 5 | 现在用户程序也可以访问内核的内存,这样极不安全,所以需要一种机制来防止用户程序,无意或有意的访问和修改内核内存。 6 | 7 | ## 解决方法 8 | 9 | 将程序映射参数 user 置为 0,表示该页只能由内核访问。 10 | 11 | 除此以外,由于用到了平坦模型,所以内核必须检测用户程序系统调用传入的 内存空间,一定在用户的内存空间,否则也需要报错。 12 | 13 | ## 相关内容 14 | 15 | - [内存映射原理](../05%20内存管理/041%20内存映射原理.md) 16 | - [内核内存映射](../05%20内存管理/042%20内核内存映射.md) 17 | -------------------------------------------------------------------------------- /docs/05 内存管理/121 内核内存保护配置.md: -------------------------------------------------------------------------------- 1 | # 内核内存保护配置 2 | 3 | ## 回顾 4 | 5 | - 有问题,欢迎 PR 和 issue 6 | 7 | ## 配置 8 | 9 | 在 [内存内核保护](./119%20%E5%86%85%E6%A0%B8%E5%86%85%E5%AD%98%E4%BF%9D%E6%8A%A4.md) 里,我们将用户态的内核内存页的访问权限设置 `.user` 置为了 0,这样可以保证用户态进程访问内核内存时产生一个缺页异常,然后杀死错误进程。 10 | 11 | 但是,这样就一个缺点就是不便于调试,于是,这里将 `.user` 属性设置为可配置的参数。以便可以容易的调试。 12 | 13 | 功能修改: 14 | 15 | - 添加 `ONIX_DEBUG` 宏,判断是否是调试状态,然后分别对不同的状态进行处理。 16 | -------------------------------------------------------------------------------- /docs/05 内存管理/130 一些优化.md: -------------------------------------------------------------------------------- 1 | # 一些优化 2 | 3 | ## 更新初始化的方式 4 | 5 | 将需要中断功能的设备初始化放到 init 进程中,此使系统已经支持中断功能。一些设备初始化时必须要中断(比如软盘),使得后续功能尽可能一致。 6 | 7 | ## 内核扩容 8 | 9 | 将内核大小上线从 100K 调整到 127K,便于添加新功能,动机来自后期改动时产生的错误,意味着内核的大小将会超过 100K。 10 | 11 | ## 页表拷贝机制 12 | 13 | 实现页表和页目录的写时拷贝,提高内存的利用率。 14 | 15 | 由于之前没有找到更好的解决办法,原先的机制是 `fork` 时直接拷贝页表,这不会出问题,但是如果子进程和父进程之间的部分页表自始至终都没有发生变化的话,将会浪费很多内存。于是找到了这里的解决办法。 16 | 17 | 那就是将页表也实现写时复制,这样一方面会节约内存,另一方面会提升 fork 的效率。 18 | 19 | ## 输入输出双字 20 | 21 | 支持 32位 的输入和输出。 22 | 23 | ```c++ 24 | extern u32 inl(u16 port); // 输入一个双字 25 | extern void outl(u16 port, u32 value); // 输出一个双字 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/05 内存管理/images/page_fault.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/05 内存管理/images/page_fault.jpg -------------------------------------------------------------------------------- /docs/06 数据结构/043 数据结构位图.md: -------------------------------------------------------------------------------- 1 | # 数据结构位图 2 | 3 | 为了节约内存的使用,我们可以使用位图来标记一些二值 (0/1) 的信息; 4 | 5 | 尽可能简单,避免复杂的计算; 6 | 7 | ```c++ 8 | typedef struct bitmap_t 9 | { 10 | u8 *bits; // 位图缓冲区 11 | u32 length; // 位图缓冲区长度 12 | u32 offset; // 位图开始的偏移 13 | } bitmap_t; 14 | 15 | // 初始化位图 16 | void bitmap_init(bitmap_t *map, char *bits, u32 length, u32 offset); 17 | 18 | // 构造位图 19 | void bitmap_make(bitmap_t *map, char *bits, u32 length, u32 offset); 20 | 21 | // 测试位图的某一位是否为 1 22 | bool bitmap_test(bitmap_t *map, u32 index); 23 | 24 | // 设置位图某位的值 25 | void bitmap_set(bitmap_t *map, u32 index, bool value); 26 | 27 | // 从位图中得到连续的 count 位 28 | int bitmap_scan(bitmap_t *map, u32 count); 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/06 数据结构/049 数据结构链表.md: -------------------------------------------------------------------------------- 1 | # 数据结构链表 2 | 3 | ```c++ 4 | #define element_offset(type, member) (u32)(&((type *)0)->member) 5 | #define element_entry(type, member, ptr) (type *)((u32)ptr - element_offset(type, member)) 6 | ``` 7 | 8 | ![](./images/list.drawio.svg) 9 | -------------------------------------------------------------------------------- /docs/06 数据结构/061 数据结构循环队列.md: -------------------------------------------------------------------------------- 1 | # 数据结构循环队列 2 | 3 | ```c++ 4 | typedef struct fifo_t 5 | { 6 | char *buf; 7 | u32 length; 8 | u32 head; 9 | u32 tail; 10 | } fifo_t; 11 | 12 | void fifo_init(fifo_t *fifo, char *buf, u32 length); 13 | bool fifo_full(fifo_t *fifo); 14 | bool fifo_empty(fifo_t *fifo); 15 | char fifo_get(fifo_t *fifo); 16 | void fifo_put(fifo_t *fifo, char byte); 17 | ``` 18 | 19 | queue / fifo First in First out 20 | 21 | 生产者 - 消费者 22 | -------------------------------------------------------------------------------- /docs/07 任务管理/046 创建内核线程.md: -------------------------------------------------------------------------------- 1 | # 创建内核线程 2 | 3 | ## 进程控制块 4 | 5 | ![](./images/task_pcb_01.drawio.svg) 6 | 7 | ## 内核线程调度 8 | 9 | ![](./images/setup_task.drawio.svg) 10 | 11 | ## ROP 12 | 13 | Return Oreiented Programming 面向返回编程 14 | 15 | iret 返回到用户态 16 | -------------------------------------------------------------------------------- /docs/07 任务管理/050 任务阻塞和就绪.md: -------------------------------------------------------------------------------- 1 | # 任务阻塞和就绪 2 | 3 | ```c++ 4 | void task_block(task_t *task, list_t *blist, task_state_t state); 5 | void task_unblock(task_t *task); 6 | ``` 7 | 8 | 如果所有任务都阻塞,怎么办? 9 | -------------------------------------------------------------------------------- /docs/07 任务管理/051 基础任务.md: -------------------------------------------------------------------------------- 1 | # 基础任务 2 | 3 | ## 空闲进程 idle 4 | 5 | 用于在所有进程都阻塞的情况下,调度执行,一般进程 id 为 0 6 | 7 | 主要工作为: 8 | 9 | 1. 开中断 10 | 2. 关闭 CPU,等待外中断 11 | 3. yield 调度执行其他程序 12 | 13 | ## 初始化进程 init 14 | 15 | 用于执行初始化操作,进入到用户态等工作,进程 id 为 1 16 | -------------------------------------------------------------------------------- /docs/07 任务管理/052 任务睡眠和唤醒.md: -------------------------------------------------------------------------------- 1 | # 任务睡眠和唤醒 2 | 3 | ```c++ 4 | void task_sleep(u32 ms); 5 | void task_wakeup(); 6 | ``` 7 | -------------------------------------------------------------------------------- /docs/07 任务管理/054 锁.md: -------------------------------------------------------------------------------- 1 | # 锁 2 | 3 | ## 互斥锁 4 | 5 | ```c++ 6 | typedef struct lock_t 7 | { 8 | struct task_t *holder; // 持有者 9 | mutex_t mutex; // 互斥量 10 | u32 repeat; // 重入次数 11 | } lock_t; 12 | 13 | void lock_init(lock_t *lock); // 锁初始化 14 | void lock_acquire(lock_t *lock); // 加锁 15 | void lock_release(lock_t *lock); // 解锁 16 | ``` 17 | 18 | ## 自旋锁 19 | 20 | 自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被已经持有(即所谓的争用)的自旋锁,那么该线程就会一直进行忙循环一旋转一等待锁重新可用。要是锁未被争用,请求锁的执行线程便能立刻得到它,继续执行。在任意时间,自旋锁都可以防止多于一个的执行线程同时进入临界区。同一个锁可以用在多个位置,例如,对于给定数据的所有访问都可以得到保护和同步。 21 | 22 | 使用 CAS (Compare And Swap) 原语实现,IA32 中是 `cmpxchg` 指令,但在多处理机系统中 `cmpxchg` 指令本身不是原子的,还需要加 `lock` 来锁定内存总线实现原子操作。 23 | 24 | ## 读写锁 25 | 26 | 有时,锁的用途可以明确地分为读取和写入两个场景。并且绝大多数是读的情况,由于读并不影响数据内容,所以如果直接加锁就会影响性能,那么可以将读和写区别开来,这种形式的锁就是读写锁; 27 | 28 | 当对某个数据结构的操作可以像这样被划分为读/写 或者 消费者/生产者 两种类别时,类似读/写锁这样的机制就很有帮助了。这种自旋锁为读和写分别提供了不同的锁。一个或多个读任务可以并发地持有读者锁:相反,用于写的锁最多只能被一个写任务持有,而且此时不能有并发的读操作。有时把读/写锁叫做共享排斥锁,或者 并发/排斥锁,因为这种锁以共享(对读者而言)和排斥(对写者而言)的形式获得使用。 29 | 30 | 但在实现的时候需要注意,有可能会发生读者过多而饿死写着的情况。如果写的情况比较多就不应该使用这种锁。 31 | 32 | ## 参考文献 33 | 34 | 1. 35 | 2. [[美] Robert Love / Linux内核设计与实现 / 机械工业出版社 / 2011](https://book.douban.com/subject/6097773/) 36 | 3. 37 | -------------------------------------------------------------------------------- /docs/07 任务管理/063 进入用户模式.md: -------------------------------------------------------------------------------- 1 | # 进入用户模式 2 | 3 | ## 任务特权级环 4 | 5 | ![](./images/Privilege_Level.drawio.svg) 6 | 7 | ## 任务状态段 8 | 9 | - ss0 10 | - esp0 11 | 12 | ## 中断门处理过程 13 | 14 | 如果处理程序运行在低特权级,那么栈切换就会发生: 15 | 16 | - 内核特权级的 栈段选择子 和 栈顶指针 将会从当前的 TSS 段中获得,在内核栈中将会压入用户态的 栈段选择子 和 栈顶指针; 17 | - 保存当前的状态 eflags, cs, eip 到内核栈 18 | - 如果存在错误码的话,压入错误码 19 | 20 | 如果处理器运行在相同的特权级,那么相同特权级的中断代码将被执行: 21 | 22 | - 保存当前的状态 eflags, cs, eip 到内核栈 23 | - 如果存在错误码的话,压入错误码 24 | 25 | ## 进入用户模式 26 | 27 | - 内核栈 Return Oriented Programming 28 | - 用户栈 29 | - 中断返回 30 | 31 | ![](../04%20%E4%B8%AD%E6%96%AD%E5%92%8C%E6%97%B6%E9%92%9F/images/interrupt_context.drawio.svg) 32 | 33 | ![](./images/interrupt_context.drawio.svg) 34 | 35 | ## 参考文献 36 | 37 | 1. 38 | 2. Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3 Chapter 6 Interrupt and Exception Handling 39 | 3. [郑刚 / 操作系统真象还原 / 人民邮电出版社 / 2016](https://book.douban.com/subject/26745156/) 40 | -------------------------------------------------------------------------------- /docs/07 任务管理/069 任务 ID.md: -------------------------------------------------------------------------------- 1 | # 任务 ID 2 | 3 | ```c++ 4 | pid_t getpid(); // 获取进程 ID 5 | pid_t getppid(); // 获取父进程 ID 6 | pid_t fork(); // 复制进程 7 | ``` 8 | -------------------------------------------------------------------------------- /docs/07 任务管理/123 错误处理机制.md: -------------------------------------------------------------------------------- 1 | # 错误处理 2 | 3 | ## 内核堆内存缓存 4 | 5 | 缓存一定数量的内核堆内存页,避免频繁申请和释放。 6 | 7 | ## 定义错误 8 | 9 | ```c++ 10 | #define TIMELESS -1 // 无限时间 11 | 12 | #define ERROR 99 // 一般错误 13 | #define EOK 0 // 没错 14 | #define EPERM 1 // 操作没有许可 15 | #define ENOENT 2 // 文件或目录不存在 16 | #define ESRCH 3 // 指定的进程不存在 17 | #define EINTR 4 // 中断的函数调用 18 | #define EIO 5 // 输入输出错误 19 | #define ENXIO 6 // 指定设备或地址不存在 20 | #define E2BIG 7 // 参数列表太长 21 | #define ENOEXEC 8 // 执行程序格式错误 22 | #define EBADF 9 // 文件描述符错误 23 | #define ECHILD 10 // 子进程不存在 24 | #define EAGAIN 11 // 资源暂时不可用 25 | #define ENOMEM 12 // 内存不足 26 | #define EACCES 13 // 没有许可权限 27 | #define EFAULT 14 // 地址错 28 | #define ENOTBLK 15 // 不是块设备文件 29 | #define EBUSY 16 // 资源正忙 30 | #define EEXIST 17 // 文件已存在 31 | #define EXDEV 18 // 非法连接 32 | #define ENODEV 19 // 设备不存在 33 | #define ENOTDIR 20 // 不是目录文件 34 | #define EISDIR 21 // 是目录文件 35 | #define EINVAL 22 // 参数无效 36 | #define ENFILE 23 // 系统打开文件数太多 37 | #define EMFILE 24 // 打开文件数太多 38 | #define ENOTTY 25 // 不恰当的 IO 控制操作(没有 tty 终端) 39 | #define ETXTBSY 26 // 不再使用 40 | #define EFBIG 27 // 文件太大 41 | #define ENOSPC 28 // 设备已满(设备已经没有空间) 42 | #define ESPIPE 29 // 无效的文件指针重定位 43 | #define EROFS 30 // 文件系统只读 44 | #define EMLINK 31 // 连接太多 45 | #define EPIPE 32 // 管道错 46 | #define EDOM 33 // 域(domain)出错 47 | #define ERANGE 34 // 结果太大 48 | #define EDEADLK 35 // 避免资源死锁 49 | #define ENAMETOOLONG 36 // 文件名太长 50 | #define ENOLCK 37 // 没有锁定可用 51 | #define ENOSYS 38 // 功能还没有实现 52 | #define ENOTEMPTY 39 // 目录不空 53 | #define ETIME 62 // 超时 54 | ``` 55 | 56 | ## 阻塞和就绪 57 | 58 | 添加超时和就绪原因(正常,超时,异常) 59 | -------------------------------------------------------------------------------- /docs/07 任务管理/124 定时器.md: -------------------------------------------------------------------------------- 1 | # 定时器 2 | 3 | 加入内核定时器机制,使得任务可以在一定时间之后执行特定的功能。比如: 4 | 5 | - 任务睡眠 6 | - 蜂鸣器超时 7 | - 阻塞超时 8 | -------------------------------------------------------------------------------- /docs/07 任务管理/125 任务会话.md: -------------------------------------------------------------------------------- 1 | # 任务会话 2 | 3 | ## 任务会话 4 | 5 | - 一个会话包含多个进程组 6 | - 一个进程组包含多个进程 7 | - 一个进程包含多个线程 8 | 9 | 会话通常与 shell 联系在一起,每一次打开 shell 产生一个会话。 10 | 11 | 组通常与管道联系在一起,多个由管道连接的进程属于一个组。 12 | 13 | 会话可以有一个控制 tty。一个会话中最多只能有一个进程组是前台进程组。Linux 中 `jobs`, `fg`, `bg`, `CTRL + Z` 可以用来控制进程。 14 | 15 | 使用 `ps j` 可以查看进程的相关属性。 16 | 17 | ## 相关系统调用 18 | 19 | ```c++ 20 | pid_t setsid(void); 21 | ``` 22 | 23 | - 如果调用进程不是进程组首领,`setsid()` 函数将创建一个新会话; 24 | - 返回时,调用进程应该是这个新会话的会话首领,也是一个新进程组的进程组首领,并且应该没有控制终端。 25 | - 调用进程的进程组 ID 必须与调用进程的进程 ID 相等; 26 | - 调用进程必须是新进程组中唯一的进程,也是新会话中唯一的进程; 27 | 28 | ---- 29 | 30 | ```c++ 31 | pid_t setpgrp(); 32 | ``` 33 | 34 | - 如果调用进程不是会话首领,`setpgrp()` 将调用进程的进程组 ID 设置为调用进程 ID,如果 `setpgrp()` 创建了一个新会话,此新会话没有控制终端; 35 | - 当调用进程是会话首领时,`setpgrp()` 函数不起作用; 36 | 37 | --- 38 | 39 | ```c++ 40 | int setpgid(pid_t pid, pid_t pgid); 41 | ``` 42 | 43 | - `setpgid()` 函数应该加入一个现有的进程组,或者在调用进程的会话中创建一个新的进程组; 44 | - 会话领导者的进程组 ID 不能改变; 45 | - 进程号与 `pid` 匹配的进程的进程组 ID 将被设置为 `pgid`; 46 | - 特殊情况下,pid 为 0,表示调用进程应使用的进程号; 47 | - 如果 pgid 为 0,则表示指定进程应使用的进程 ID; 48 | 49 | --- 50 | 51 | ```c++ 52 | pid_t getpgrp(void); 53 | ``` 54 | 55 | - 返回调用进程的进程组 ID 56 | 57 | --- 58 | 59 | ## 参考 60 | 61 | - 62 | - 63 | - 64 | -------------------------------------------------------------------------------- /docs/07 任务管理/127 信号.md: -------------------------------------------------------------------------------- 1 | # 信号 2 | 3 | ## 信号简介 4 | 5 | 在 UNIX 系统中,信号是一种 **软件中断** 处理机制。信号机制提供了一种处理异步事件的方法。例如, 6 | 7 | 1. 用户在终端键盘上键入 `CTRL+C` 组合键来终止一个程序的执行。该操作就会产生一个 `SIGINT` (SIGnal INTerrupt) 信号,并被发送到当前前台执行的进程中; 8 | 2. 当进程设置了一个报警时钟,到期时,系统就会向进程发送一个 `SIGALRM` 信号; 9 | 3. 当发生硬件异常时,系统也会向正在执行的进程发送相应的信号; 10 | 4. 另外,一个进程也可以向另一个进程发送信号,使用 `kill()` 函数向同组的子进程发送终止执行信号。 11 | 12 | 通常使用一个 32 位无符号长整数,中的比特位表示各种不同信号(一种位图结构),因此最多可表示 32 个不同的信号。 13 | 14 | ## 信号处理方式 15 | 16 | 对于进程来说,当收到一个信号时,可以由三种不同的处理或操作方式。 17 | 1. 忽略该信号: 18 | 大多数信号都可以被进程忽略。但有两个信号忽略不掉:`SIGKILL` 和 `SIGSTOP`。不能被忽略掉的原因是能为超级用户提供一个确定的方法来终止或停止指定的任何进程。另外,若忽略掉某些硬件异常而产生的信号(如除0错误),则进程的行为或状态就可能变得不可知了。 19 | 2. 捕获该信号: 20 | 为了进行该操作,我们必须首先告诉内核在指定的信号发生时调用我们自定义的信号处理函数。在该处理函数中,我们可以做任何操作,当然也可以什么不做,起到忽略该信号的同样作用。自定义信号处理函数来捕获信号的一个例子是:如果我们在程序执行过程中创建了一些临时文件,那么我们就可以定义一个函数来捕获 `SIGTERM`(终止执行)信号,并在该函数中做一些清理临时文件的工作。`SIGTERM` 信号是 `kill` 命令发送的默认信号。 21 | 3. 执行默认操作: 22 | 内核为每种信号都提供一种默认操作。通常这些默认操作就是终止进程的执行。 23 | 24 | ## 信号处理流程 25 | 26 | ```c++ 27 | // 信号处理函数 28 | void sig_handler(int signr) 29 | { 30 | // 为处理下一次信号发生 31 | // 重新设置自己的处理函数 32 | signal(SIGINT, sig_handler); 33 | // 信号处理的内容 34 | } 35 | 36 | int main () 37 | { 38 | // 主程序中设置自己的信号处理函数 39 | signal(SIGINT, sig_handler); 40 | // 主程序其他内容 41 | } 42 | ``` 43 | 44 | ![](./images/signal.drawio.svg) 45 | 46 | 47 | ## 参考 48 | 49 | - 赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005 -------------------------------------------------------------------------------- /docs/07 任务管理/128 闹钟.md: -------------------------------------------------------------------------------- 1 | # 闹钟 2 | 3 | 利用信号 `SIGALRM` [^alarm] 实现闹钟。使得程序可以在特定的时间之后执行某函数。 4 | 5 | ```c++ 6 | // 设置闹钟 7 | int alarm(int sec); 8 | ``` 9 | 10 | ## 参考 11 | 12 | [^alarm]: 13 | -------------------------------------------------------------------------------- /docs/07 任务管理/images/Edsger_Wybe_Dijkstra.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/07 任务管理/images/Edsger_Wybe_Dijkstra.jpg -------------------------------------------------------------------------------- /docs/07 任务管理/images/tss.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/07 任务管理/images/tss.jpg -------------------------------------------------------------------------------- /docs/07 任务管理/images/tss_descriptor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/07 任务管理/images/tss_descriptor.jpg -------------------------------------------------------------------------------- /docs/08 系统调用/047 系统调用.md: -------------------------------------------------------------------------------- 1 | # 系统调用 2 | 3 | ## 目录 4 | 5 | - 程序调度细节 6 | - 进程资源竞争 7 | - 系统调用 8 | 9 | ## 系统调用 10 | 11 | 系统调用是用户进程与内核沟通的方式。可以将 CPU 从用户态转向内核态; 12 | 13 | 用中断门来实现系统调用,与 linux 兼容,使用 `0x80` 号中断函数; 14 | 15 | 其中 linux 32 位: 16 | 17 | - eax 存储系统调用号 18 | - ebx 存储第一个参数 19 | - ecx 存储第二个参数 20 | - edx 存储第三个参数 21 | - esi 存储第四个参数 22 | - edi 存储第五个参数 23 | - ebp 存储第六个参数 24 | - eax 存储返回值 25 | 26 | 系统调用的过程 27 | 28 | ```s 29 | mov eax, 系统调用号 30 | mov ebx, 第一个参数 31 | mov ecx, 第二个参数 32 | mov edx, 第三个参数 33 | int 0x80; 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/08 系统调用/048 系统调用 yield.md: -------------------------------------------------------------------------------- 1 | # 系统调用 yield 2 | 3 | yield() 系统调用,进程主动交出执行权,调度执行其他进程。 4 | -------------------------------------------------------------------------------- /docs/08 系统调用/068 系统调用 brk.md: -------------------------------------------------------------------------------- 1 | # 系统调用 brk 2 | 3 | break 4 | 5 | ```c++ 6 | int brk(void *addr); 7 | malloc(); 8 | free(); 9 | ``` 10 | 11 | man 2 brk 12 | 13 | ![](../05%20内存管理/images/memory_map_06.drawio.svg) 14 | 15 | -------------------------------------------------------------------------------- /docs/08 系统调用/070 系统调用 fork.md: -------------------------------------------------------------------------------- 1 | # 系统调用 fork 2 | 3 | ```c++ 4 | pid_t fork(void); // 创建子进程 5 | ``` 6 | 7 | - 子进程返回值为 0 8 | - 父进程返回值为子进程 id 9 | -------------------------------------------------------------------------------- /docs/08 系统调用/071 系统调用 exit.md: -------------------------------------------------------------------------------- 1 | # 系统调用 exit 2 | 3 | ```c++ 4 | noreturn void exit(int status); // 终止当前进程 5 | waitpid(); // 获取子进程的退出状态 6 | ``` 7 | -------------------------------------------------------------------------------- /docs/08 系统调用/072 系统调用 waitpid.md: -------------------------------------------------------------------------------- 1 | # 系统调用 waitpid 2 | 3 | ```c++ 4 | pid_t waitpid(pid_t pid, int32 *status); // 等待进程状态变化 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/08 系统调用/073 系统调用 time.md: -------------------------------------------------------------------------------- 1 | # 系统调用 time 2 | 3 | ```c++ 4 | time_t time(); // 获取当前时间戳 从 1970-01-01 00:00:00 开始的秒数 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/08 系统调用/107 系统调用 mmap,munmap.md: -------------------------------------------------------------------------------- 1 | # 系统调用 mmap,munmap 2 | 3 | 目前的内存布局: 4 | 5 | ![](./images/memory_map.drawio.svg) 6 | 7 | 实现系统调用,以调整文件映射内存。 8 | 9 | ```c++ 10 | void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 11 | int munmap(void *addr, size_t length); 12 | ``` 13 | 14 | Linux 系统 `mmap` 支持的参数比较多,可以参考相关文档,这里只实现 **共享内存** 和 私有内存。 15 | 16 | ## 参考 17 | 18 | - 19 | - 20 | -------------------------------------------------------------------------------- /docs/09 设备驱动/058 键盘中断.md: -------------------------------------------------------------------------------- 1 | # 键盘中断 2 | 3 | - 0x21 键盘中断向量; 4 | 5 | | 端口 | 操作类型 | 用途 | 6 | | ---- | -------- | ---------- | 7 | | 0x60 | 读/写 | 数据端口 | 8 | | 0x64 | 读 | 状态寄存器 | 9 | | 0x64 | 写 | 控制寄存器 | 10 | 11 | ![](../04%20%E4%B8%AD%E6%96%AD%E5%92%8C%E6%97%B6%E9%92%9F/images/8259a.drawio.svg) 12 | 13 | ## 参考文献 14 | 15 | - 16 | - 17 | - 18 | -------------------------------------------------------------------------------- /docs/09 设备驱动/060 键盘 LED 灯.md: -------------------------------------------------------------------------------- 1 | # 键盘 LED 灯 2 | 3 | PS/2 键盘接受很多种类型的命令,命令有一个字节,一些命令有数据必须在命令字节发送之后再发送。键盘通过一个 ACK(0xFA) (表示命令已收到) 或者 Resend(0xFE) (表示前一个命令有错误);在发送命令之间需要等待缓冲区为空; 4 | 5 | | 位 | 表示 | 6 | | --- | -------- | 7 | | 0 | 滚动锁定 | 8 | | 1 | 数字锁定 | 9 | | 2 | 大写锁定 | 10 | 11 | ## 参考文献 12 | 13 | 1. 14 | 2. 15 | 3. [赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005](https://book.douban.com/subject/1231236/) 16 | 17 | -------------------------------------------------------------------------------- /docs/09 设备驱动/076 识别硬盘.md: -------------------------------------------------------------------------------- 1 | # 识别硬盘 2 | 3 | ## 标准和非标准检测 4 | 5 | 目前所有的 BIOS 都标准化了 IDENTIFY 命令的使用,以检测所有类型的 ATA 总线设备的存在 PATA, PATAPI, SATAPI, SATA。 6 | 7 | 还有另外两种不推荐使用的非标准技术。第一个是选择一个设备(然后做 400ns 延迟),然后读取设备的状态寄存器。对于没有 “休眠” 的ATA 设备,将始终设置 RDY 位。这应该是可检测的,只要您已经测试了浮点数(所有位总是设置)。如果没有设备,那么状态值将为 0。这种方法不适用于检测 ATAPI 设备,它们的 RDY 位总是清除的(直到它们得到第一个 PACKET 命令)。 8 | 9 | 另一种方法是使用执行设备诊断命令(0x90)。它应该在错误寄存器(主总线上的 0x1F1 )中设置位来显示总线上主设备和从设备的存在。 10 | 11 | ## IDENTIFY 命令 12 | 13 | > 以下内容来自 osdev.org,经过测试可能不太准确; 14 | 15 | 要使用 IDENTIFY 命令: 16 | 17 | 1. 向 驱动器选择 IO 端口发送命令: 18 | 1. 主驱动器发送 0xA0,端口 0x1F6 19 | 2. 从驱动器发送 0xB0,端口 0x176 20 | 2. 将 扇区数量、LBA 等 IO 端口设置为 0 (端口 0x1F2 到 0x1F5) (这步可能不需要) 21 | 3. 将 IDENTIFY 命令(0xEC) 发送到命令 IO 端口(0x1F7) 22 | 4. 再次读取状态端口(0x1F7)。如果读的值为 0,则表示驱动器不存在 23 | 5. 对于任何其他值:轮询状态端口(0x1F7),直到第7位(BSY, value = 0x80)清除 24 | 6. 由于一些 ATAPI 驱动器不符合规范,此时您需要检查 LBAmid 和 LBAhi 端口(0x1F4和0x1F5),以查看它们是否非零。如果是这样,驱动器不是 ATA,您应该停止轮询。否则,继续轮询其中一个状态端口,直到第 3 位(DRQ,值=8)置位,或直到第 0 位(ERR,值=1)置位 25 | 7. 如果清除了 ERR,就可以从数据端口(0x1F0)读取数据了。读取 256 个 16 位值,并存储它们 26 | 27 | ## 复位驱动器/软件复位 28 | 29 | 对于非 ATAPI 驱动器,驱动器在发生重大错误后复位驱动器的唯一方法是在总线上做 **软件复位** 30 | 31 | 在适当的控制寄存器中为总线设置位 2 (SRST,值=4)。这将重置总线上的两个 ATA 设备,然后,你得自己再清除一遍。总线上的主驱动器被自动选择; 32 | 33 | ATAPI 驱动器在它们的 LBA_LOW 和 LBA_HIGH IO 端口上设置值,但不应该重置或甚至终止它们的当前命令。 34 | 35 | ## 参考文献 36 | 37 | - 38 | - Information Technology - AT Attachment - 8 ATA/ATAPI Command Set (ATA8-ACS) 39 | - 40 | - 41 | -------------------------------------------------------------------------------- /docs/09 设备驱动/078 虚拟设备.md: -------------------------------------------------------------------------------- 1 | # 虚拟设备 2 | 3 | 对硬件设备进行一层抽象,使得读写更加的统一,方便以后的操作。 4 | 5 | ```c++ 6 | // 安装设备 7 | dev_t device_install( 8 | int type, int subtype, 9 | void *ptr, char *name, dev_t parent, 10 | void *ioctl, void *read, void *write); 11 | 12 | // 根据子类型查找设备 13 | device_t *device_find(int type, idx_t idx); 14 | 15 | // 根据设备号查找设备 16 | device_t *device_get(dev_t dev); 17 | 18 | // 控制设备 19 | int device_ioctl(dev_t dev, int cmd, void *args, int flags); 20 | 21 | // 读设备 22 | int device_read(dev_t dev, void *buf, size_t count, idx_t idx, int flags); 23 | 24 | // 写设备 25 | int device_write(dev_t dev, void *buf, size_t count, idx_t idx, int flags); 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/09 设备驱动/079 块设备请求.md: -------------------------------------------------------------------------------- 1 | # 块设备请求 2 | 3 | 块设备 (如硬盘,软盘) 的读写以扇区(512B) 为单位,操作比较耗时,需要寻道,寻道时需要旋转磁头臂。所以需要一种策略来完成磁盘的访问。 4 | 5 | ```c++ 6 | // 块设备请求 7 | void device_request(dev_t dev, void *buf, u8 count, idx_t idx, int flags, u32 type); 8 | ``` 9 | 10 | ## 参考文献 11 | 12 | 1. [赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005](https://book.douban.com/subject/1231236/) 13 | -------------------------------------------------------------------------------- /docs/09 设备驱动/080 磁盘调度电梯算法.md: -------------------------------------------------------------------------------- 1 | # 磁盘调度电梯算法 2 | 3 | 由于磁盘性能的主要瓶颈在磁盘的寻道时间,也就是磁头臂的移动时间,所以要尽可能避免磁头臂的移动。电梯算法的作用是让磁头的综合移动距离最小,从而改善磁盘访问时间。 4 | 5 | ![](../01%20系统引导/images/harddisk_1.jpeg) 6 | 7 | ![](../01%20系统引导/images/harddisk_7.jpg) 8 | 9 | ![](./images/elevator.drawio.svg) 10 | 11 | ## LBA 和 CHS 12 | 13 | - LBA (Logical Block Addressing):逻辑块寻址,逻辑上认为磁盘的扇区编号从 0 开始依次递增,处理起来更方便; 14 | - Sector: 扇区,磁盘最小的单位,多个扇区够称一个磁道 15 | - Head: 磁头,用于读写盘面,一个磁盘可能有多个磁头,一个磁道读写完成,就换另一个磁头; 16 | - Cylinder:柱面,或者也可以认为是磁道 (Track),同一个位置的所有磁道共同构成了柱面;当所有磁道都读写完时,就需要切换磁道,也就产生了寻道的问题。因此柱面是磁盘读写最大的单位。 17 | 18 | 下面是 LBA 和 CHS 的转换公式: 19 | 20 | - CYL = LBA / (HPC * SPT) 21 | 22 | - HEAD = (LBA % (HPC * SPT)) / SPT 23 | 24 | - SECT = (LBA % (HPC * SPT)) % SPT + 1 25 | 26 | - LBA = ( ( CYL * HPC + HEAD ) * SPT ) + SECT - 1 27 | 28 | 其中: 29 | 30 | - CYL 表示柱面 (Cylinder) 31 | - HEAD 表示磁头 (Head) 32 | - SECT 表示扇区 (Sector) 33 | - LBA 表示逻辑块地址 (Logical Block Addressing) 34 | - HPC 表示柱面磁头数 (Head Per Cylinder) 35 | - SPT 表示磁道扇区数 (Sector Per Track) 36 | 37 | ## 参考文献 38 | 39 | 1. [赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005](https://book.douban.com/subject/1231236/) 40 | 2. 41 | -------------------------------------------------------------------------------- /docs/09 设备驱动/126 TTY 设备.md: -------------------------------------------------------------------------------- 1 | # TTY 设备 2 | 3 | 根据 [^tty_demystified] 中的描述。 4 | 5 | ![](./images/oldschool.jpg) 6 | 7 | (TeleTYpewriter, TTY) 原指电传打字机,电传打字机曾经在世界各地以大型网络方式连接起来,称为电传,用于传输商业电报,但彼时电传打字机尚未连接到任何计算机。 8 | 9 | 随着科技的发展与进步,很多电传打字机的模型和功能略有不同,于是产生了一个软件兼容层,来处理不同打字机之间的通信协议。现在物理的电传打字机已经绝迹,除非你参观电子设备的博物馆。不过 TTY 这种兼容层设备在 UNIX 世界保留了下来。 10 | 11 | 可以认为 TTY 电传打字机设备是一种中间的兼容层,用以处理不同字符设备之间的通信。这些设备可能是控制台、键盘、串口等。 12 | 13 | 除此以外 TTY 通常还和进程的会话和任务相关。 14 | 15 | 可以使用 `stty -a` 查看 TTY 设备的配置信息。 16 | 17 | ## 参考 18 | 19 | [^tty_demystified]: 20 | -------------------------------------------------------------------------------- /docs/09 设备驱动/131 CPU 功能检测.md: -------------------------------------------------------------------------------- 1 | # CPU 功能检测 2 | 3 | ## CPUID 4 | 5 | `CPUID` [^osdev] 指令可以用于获取很多 CPU 的信息,比如供应商(Vendor) 字符串,和模型数,内部缓存大小,以及很多其他有趣的东西。 6 | 7 | EFLAGS 位图: 8 | 9 | ![](../04%20中断和时钟/images/eflags.drawio.svg) 10 | 11 | ## 功能检测 12 | 13 | 在执行 `CPUID` 指令之前,首先应该检测处理器是否支持该指令,如果 EFLAGS 的 ID 位可修改,那么表示处理器支持 `CPUID` 指令。 14 | 15 | ## 基础用法 16 | 17 | 执行 `CPUID` 指令前,将参数 `0` 传入 `EAX`,不同的参数将返回不同的信息。 18 | 19 | ### 供应商 ID 20 | 21 | 当 `EAX=0` 时,CPUID 将返回供应商 ID 字符串到 `EBX`, `EDX` 和 `ECX`,将它们写入内存将得到长度 12 的字符串。这个字符串表示了供应商的 ID。下面是典型的供应商 ID: 22 | 23 | ```c++ 24 | #define CPUID_VENDOR_AMD "AuthenticAMD" 25 | #define CPUID_VENDOR_INTEL "GenuineIntel" 26 | ``` 27 | 28 | `EAX` 中返回最大的输入数; 29 | 30 | ### CPU 功能 31 | 32 | 使用参数 `EAX` = 1 调用 CPUID,返回一个位图存储在 `EDX` 和 `ECX` 中 [^info]。注意不同品牌的 CPU 可能有不同的含义。 33 | 34 | ## 参考 35 | 36 | [^osdev]: https://wiki.osdev.org/CPUID 37 | [^info]: Intel® 64 and IA-32 Architectures Software Developer's Manual 38 | -------------------------------------------------------------------------------- /docs/09 设备驱动/139 磁盘驱动优化.md: -------------------------------------------------------------------------------- 1 | # 磁盘驱动优化 2 | 3 | ## 磁盘类型检测 4 | 5 | 来自 ATA/ATAPI 规范的草案副本(T13 1410D rev 3b, 365页),对 qemu 有效。 6 | 7 | 要检测 ATA 磁盘是非包设备还是包设备,可以使用存储在 `sector count` 和 `LBA Low,Mid,High` 寄存器中的签名 (对于 ATA 控制器,寄存器 0x1F2-0x1F5)。 8 | 9 | 如果这些寄存器是 0x01, 0x01, 0x00, 0x00,那么连接的设备是非包设备,`IDENTIFY DEVICE(0xEC)` 功能应该有效。如果它们是 0x01, 0x01, 0x14, 0xEB,那么该设备是一个包设备,并且应该使用 `IDENTIFY PACKET DEVICE(0xA1)` 来检测设备。 10 | 11 | 当设备上电、复位或接收 `EXECUTE DEVICE DIAGNOSTIC` 命令时,该签名被设置/重置 (对于数据包设备,`IDENTIFY DEVICE` 也会重置签名)。 12 | 13 | ## 磁盘错误处理 14 | 15 | 将磁盘错误,逐步向上传递; 16 | 17 | ## 参考 18 | 19 | - 20 | - 21 | -------------------------------------------------------------------------------- /docs/09 设备驱动/images/CAN_Connecteur.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/CAN_Connecteur.svg.png -------------------------------------------------------------------------------- /docs/09 设备驱动/images/DB-25_male.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/DB-25_male.svg.png -------------------------------------------------------------------------------- /docs/09 设备驱动/images/Floppy_disk_2009_G1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/Floppy_disk_2009_G1.jpg -------------------------------------------------------------------------------- /docs/09 设备驱动/images/IBM_Model_F_AT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/IBM_Model_F_AT.png -------------------------------------------------------------------------------- /docs/09 设备驱动/images/IBM_Model_F_XT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/IBM_Model_F_XT.png -------------------------------------------------------------------------------- /docs/09 设备驱动/images/IBM_PS_2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/IBM_PS_2.jpeg -------------------------------------------------------------------------------- /docs/09 设备驱动/images/PCI_Command_Register.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/PCI_Command_Register.jpg -------------------------------------------------------------------------------- /docs/09 设备驱动/images/PCI_Configuration_Space.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/PCI_Configuration_Space.jpg -------------------------------------------------------------------------------- /docs/09 设备驱动/images/PCI_Status_Register.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/PCI_Status_Register.jpg -------------------------------------------------------------------------------- /docs/09 设备驱动/images/PCI_System_Block.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/PCI_System_Block.jpg -------------------------------------------------------------------------------- /docs/09 设备驱动/images/PCI_und_PCIe_Slots.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/PCI_und_PCIe_Slots.jpg -------------------------------------------------------------------------------- /docs/09 设备驱动/images/isa_card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/isa_card.jpg -------------------------------------------------------------------------------- /docs/09 设备驱动/images/isa_slot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/isa_slot.jpg -------------------------------------------------------------------------------- /docs/09 设备驱动/images/oldschool.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/09 设备驱动/images/oldschool.jpg -------------------------------------------------------------------------------- /docs/10 用户程序/064 printf.md: -------------------------------------------------------------------------------- 1 | # printf 2 | 3 | ## 系统调用 write 4 | 5 | ```c++ 6 | int32 write(fd_t fd, char *buf, u32 len); 7 | ``` 8 | 9 | - kmalloc 10 | - kfree 11 | -------------------------------------------------------------------------------- /docs/10 用户程序/100 系统交互 osh.md: -------------------------------------------------------------------------------- 1 | # 系统交互 osh 2 | 3 | 实现一些简单的 shell 命令: 4 | 5 | - ls 6 | - cd 7 | - pwd 8 | - mkdir 9 | - rmdir 10 | - rm 11 | - cat 12 | - clear 13 | - exit 14 | 15 | 以及一些系统调用: 16 | 17 | ```c++ 18 | // 读取目录 19 | int readdir(fd_t fd, void *dir, int count); 20 | ``` 21 | 22 | ## 参考 23 | 24 | - 史蒂文斯 (w.richard Stevens) & 拉戈 (stephen A.rago) - UNIX 环境高级编程(第三版) 25 | - 26 | -------------------------------------------------------------------------------- /docs/10 用户程序/110 程序加载和执行.md: -------------------------------------------------------------------------------- 1 | # 程序加载和执行 2 | 3 | 根据 ELF [^elf] 文件格式,将代码和数据加载到用户内存,并执行。 4 | 5 | 进程内存布局如下: 6 | 7 | ![](./images/memory_map.drawio.svg) 8 | 9 | ## 相关内容 10 | 11 | - 异常 12 | - 外中断 13 | - 系统调用(软中断) 14 | 15 | ## 参考 16 | 17 | [^elf]: 18 | -------------------------------------------------------------------------------- /docs/10 用户程序/113 C 运行时环境.md: -------------------------------------------------------------------------------- 1 | # C 运行时环境 2 | 3 | ISO C 标准定义了 `main` 函数 [^main] 可以定义为没有参数的函数,或者带有两个参数 `argc` 和 `argv` 的函数,表示命令行参数的数量和字符指针: 4 | 5 | ```c++ 6 | int main (int argc, char *argv[]) 7 | ``` 8 | 9 | 而对于 UNIX 系统,`main` 函数的定义有第三种方式 [^unix_main]: 10 | 11 | ```c++ 12 | int main (int argc, char *argv[], char *envp[]) 13 | ``` 14 | 15 | 其中,第三个参数 `envp` 表示系统环境变量的字符指针数组,环境变量字符串是形如 `NAME=value` 这样以 `=` 连接的字符串,比如 `SHELL=/bin/sh`。 16 | 17 | 在执行 `main` 函数之前,`libc` C 函数库需要首先为程序做初始化,比如初始化堆内存,初始化标准输入输出文件,程序参数和环境变量等, 18 | 19 | 在 main 函数结束之后,还需要做一些收尾的工作,比如执行 `atexit` [^atexit] 注册的函数,和调用 `exit` 系统调用,让程序退出。 20 | 21 | 所以 ld 默认的入口地址在 `_start`,而不是 `main`。 22 | 23 | C RunTime 24 | 25 | ## 参考 26 | 27 | [^main]: 28 | [^unix_main]: 29 | [^atexit]: 30 | 31 | -------------------------------------------------------------------------------- /docs/10 用户程序/114 参数和环境变量.md: -------------------------------------------------------------------------------- 1 | # 参数和环境变量 2 | 3 | 调用 `execve` 系统调用时,操作系统会把参数 `argv` 和环境变量 `envp` 拷贝到用户栈顶,然后跳转到程序入口地址。 4 | 5 | 参数和环境变量的内存布局如下图所示: 6 | 7 | ![](./images/argv_layout.drawio.svg) 8 | 9 | ## 一些基础命令 10 | 11 | - echo: 将命令行的参数,输出到标准输出; 12 | - cat: 打印文件内容 13 | - ls: 显示目录内容 14 | 15 | ## 参考 16 | 17 | - 18 | - 19 | -------------------------------------------------------------------------------- /docs/10 用户程序/117 管道序列.md: -------------------------------------------------------------------------------- 1 | # 管道序列 2 | 3 | shell 中可以用 `|` 来表示管道 [^pipe],格式如下: 4 | 5 | command1 | command2 6 | 7 | 表示将 `command1` 的输出重定向到 `command2` 的输入。 8 | 9 | ## 参考 10 | 11 | [^pipe]: 12 | -------------------------------------------------------------------------------- /docs/10 用户程序/118 init 进程.md: -------------------------------------------------------------------------------- 1 | # init 进程 2 | 3 | 调整 `osh` 和 `init` 两个程序,使之完全运行在用户内存空间; 4 | 5 | 为了方便调试,osh 和 init 是直接编译到内核当中的,这是不对的,现在已经可以执行程序了,于是需要将程序独立出来,作为一个用户程序来执行。 6 | -------------------------------------------------------------------------------- /docs/10 用户程序/133 基础浮点运算.md: -------------------------------------------------------------------------------- 1 | # 基础浮点运算 2 | 3 | 为了避免 gcc 生成 SSE 指令,可以限定处理器类型为 奔腾处理器 [^options]: 4 | 5 | ```makefile 6 | CFLAGS+= -march=pentium # pentium 处理器 7 | ``` 8 | 9 | ## 基础浮点函数 [^manual] 10 | 11 | 设 $x=ST0, y=ST1$ 12 | 13 | | 指令 | 描述 | 14 | | ------- | ---------------------------- | 15 | | F2XM1 | $x = 2^x - 1$ | 16 | | FABS | $x = \|x\|$ | 17 | | FCHS | $x = -x$ | 18 | | FCOS | $x = \cos(x)$ | 19 | | FPATAN | $y = \arctan({y \over x})$ | 20 | | FPREM | $x = fmodf(x, y)$ [^fmodf] | 21 | | FPREM1 | $x = reminderf(x, y)$ [^remainder] | 22 | | FPTAN | $y = \tan(x), x = 1$ | 23 | | FRNDINT | $x = \lfloor x \rfloor$ | 24 | | FSCALE | $x = x \cdot 2^{[y]}$ | 25 | | FSIN | $x = \sin(x)$ | 26 | | FSINCOS | $x = \cos(x), y =\sin(x)$ | 27 | | FSQRT | $x = \sqrt{x}$ | 28 | | FYL2X | $y = y \cdot \log_2(x)$ | 29 | | FYL2XP1 | $y = y \cdot \log_2(x + 1)$ | 30 | 31 | ## 参考 32 | 33 | [^options]: 34 | [^manual]: 35 | [^fmodf]: 36 | [^remainder]: 37 | -------------------------------------------------------------------------------- /docs/10 用户程序/images/elf_layout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/10 用户程序/images/elf_layout.jpg -------------------------------------------------------------------------------- /docs/11 文件系统/081 哈希表和高速缓冲.md: -------------------------------------------------------------------------------- 1 | # 哈希表和高速缓冲 2 | 3 | ## 哈希表概述 4 | 5 | 哈希表:散列表,以关键字和数据 (key - value) 的形式直接进行访问的数据结构。 6 | 7 | - 哈希函数:需要将 key 转换成整型,并且不同的 key 之间尽可能减少冲突; 8 | - 冲突的解决方法: 9 | - 开放地址法 10 | - 拉链法 11 | - 平均时间复杂度 $O(1)$ 12 | - 装载因子 load factor 13 | - DDoS 攻击 14 | 15 | ## 高速缓冲 16 | 17 | 一般来说,性能不同的两个系统之间应该有一个缓冲区; 18 | 19 | 文件系统以块为单位访问磁盘,块一般是 $2^n, n \in \mathbb{N}$ 个扇区。其中 4K 比较常见;这里我们使用 1K,也就是 2 个扇区作为一块。 20 | 21 | 高速缓冲将块存储在哈希表中,以降低对磁盘的访问频率,提高性能。 22 | 23 | 另外还有一个空闲块链表; 24 | 25 | ## 内存布局 26 | 27 | ![](./images/memory_map.drawio.svg) 28 | 29 | --- 30 | 31 | ## 高速缓冲布局 32 | 33 | ![](./images/buffer_map.drawio.svg) 34 | 35 | ## 参考 36 | 37 | 1. [赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005](https://book.douban.com/subject/1231236/) 38 | 2. [Maurice J.Bach / UNIX操作系统设计 / 机械工业出版社 / 2000](https://book.douban.com/subject/1035710/) 39 | -------------------------------------------------------------------------------- /docs/11 文件系统/082 创建文件系统.md: -------------------------------------------------------------------------------- 1 | # 创建文件系统 2 | 3 | ## 高速缓冲补充 4 | 5 | - 模运算 6 | - 高速缓冲过程 7 | - 缓存: 8 | - memcache / redis 9 | - 缓冲: 10 | - 消息队列 / rabbitMQ / kafka 11 | 12 | ### 缓冲内存 13 | 14 | ![](./images/buffer_map.drawio.svg) 15 | 16 | --- 17 | 18 | ### 哈希表 19 | 20 | ![](./images/hashmap.drawio.svg) 21 | 22 | --- 23 | 24 | ### 空闲链表 25 | 26 | ![](./images/list.drawio.svg) 27 | 28 | ## 文件系统 29 | 30 | 为简单起见,本操作系统使用 minix 第一版文件系统。主要的好处是,可以抄 linux 0.11 的代码。 31 | 32 | ## 参考 33 | 34 | - man mkfs.minix 35 | - man losetup 36 | -------------------------------------------------------------------------------- /docs/11 文件系统/083 文件系统简介.md: -------------------------------------------------------------------------------- 1 | # 文件系统简介 2 | 3 | 一般常见的文件系统分为两种类型: 4 | 5 | - 文件分配表 (File Allocation Table FAT) 6 | - 索引表 7 | 8 | minux 文件系统使用索引表结构; 9 | 10 | ## 块 11 | 12 | ![](./images/block.drawio.svg) 13 | 14 | ## inode 15 | 16 | ```c++ 17 | typedef struct inode_desc_t 18 | { 19 | u16 mode; // 文件类型和属性(rwx 位) 20 | u16 uid; // 用户id(文件拥有者标识符) 21 | u32 size; // 文件大小(字节数) 22 | u32 mtime; // 修改时间戳 这个时间戳应该用 UTC 时间,不然有瑕疵 23 | u8 gid; // 组id(文件拥有者所在的组) 24 | u8 nlinks; // 链接数(多少个文件目录项指向该i 节点) 25 | u16 zone[9]; // 直接 (0-6)、间接(7)或双重间接 (8) 逻辑块号 26 | } inode_desc_t; 27 | ``` 28 | 29 | **勘误:**:文件系统块索引数量应该是 512 个,而不是 64 个 30 | 31 | ![](./images/inode.drawio.svg) 32 | 33 | ## 超级块 34 | 35 | ```c++ 36 | typedef struct super_desc_t 37 | { 38 | u16 inodes; // 节点数 39 | u16 zones; // 逻辑块数 40 | u16 imap_blocks; // i 节点位图所占用的数据块数 41 | u16 zmap_blocks; // 逻辑块位图所占用的数据块数 42 | u16 firstdatazone; // 第一个数据逻辑块号 43 | u16 log_zone_size; // log2(每逻辑块数据块数) 44 | u32 max_size; // 文件最大长度 45 | u16 magic; // 文件系统魔数 46 | } super_desc_t; 47 | ``` 48 | ![](./images/block1.drawio.svg) 49 | 50 | ## 目录 51 | 52 | 如果inode 文件 是目录,那么文件块的内容就是下面这种数据结构。 53 | 54 | ```c++ 55 | // 文件目录项结构 56 | typedef struct dentry_t 57 | { 58 | u16 nr; // i 节点 59 | char name[14]; // 文件名 60 | } dentry_t; 61 | ``` 62 | 63 | ## 参考 64 | 65 | 1. [赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005](https://book.douban.com/subject/1231236/) 66 | 2. 67 | -------------------------------------------------------------------------------- /docs/11 文件系统/084 根超级块.md: -------------------------------------------------------------------------------- 1 | # 根超级块 2 | 3 | 创建超级块表,以及读取根超级块; 4 | 5 | 使用已经创建好的文件系统,假设 Linux 的文件系统是稳定的(这个假设极其合理),方便排错; 6 | 7 | ```c++ 8 | super_block_t *get_super(dev_t dev); // 获得 dev 对应的超级块 9 | super_block_t *read_super(dev_t dev); // 读取 dev 对应的超级块 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/11 文件系统/085 文件系统位图操作.md: -------------------------------------------------------------------------------- 1 | # 文件系统位图操作 2 | 3 | 主要完成以下四个函数: 4 | 5 | ```c++ 6 | idx_t balloc(dev_t dev); // 分配一个文件块 7 | void bfree(dev_t dev, idx_t idx); // 释放一个文件块 8 | idx_t ialloc(dev_t dev); // 分配一个文件系统 inode 9 | void ifree(dev_t dev, idx_t idx); // 释放一个文件系统 inode 10 | ``` 11 | 12 | > 一大禁忌:禁止 if 嵌套!提升代码可读性的最好方式! 13 | 14 | - 尽早返回 15 | -------------------------------------------------------------------------------- /docs/11 文件系统/086 文件系统 inode.md: -------------------------------------------------------------------------------- 1 | # 文件系统 inode 2 | 3 | 主要完成以下几个函数: 4 | 5 | ```c++ 6 | inode_t *iget(dev_t dev, idx_t nr); // 获得设备 dev 的 nr inode 7 | void iput(inode_t *inode); // 释放 inode 8 | 9 | // 获取 inode 第 block 块的索引值 10 | // 如果不存在 且 create 为 true,则创建 11 | idx_t bmap(inode_t *inode, idx_t block, bool create); 12 | ``` 13 | -------------------------------------------------------------------------------- /docs/11 文件系统/087 文件系统状态.md: -------------------------------------------------------------------------------- 1 | # 文件系统状态 2 | 3 | 定义文件访问权限,以及一些相关的宏。 4 | 5 | ```console 6 | drwxr-xr-x 4 steven root 4.0K Oct 14 00:30 bochs 7 | ``` 8 | 9 | | 条目 | 说明 | 10 | | ------------ | -------------- | 11 | | d | 表示文件类型 | 12 | | rwx | 文件所有者权限 | 13 | | r-x | 组用户权限 | 14 | | r-x | 其他人权限 | 15 | | 4 | 硬链接数 | 16 | | steven | 用户名 | 17 | | root | 组名 | 18 | | 4.0K | 文件大小 | 19 | | Oct 14 00:30 | 最后修改时间 | 20 | | bochs | 文件名 | 21 | 22 | ## 文件类型 23 | 24 | - `-` 表示一般文件 25 | - `d` 表示目录文件 26 | - `l` 表示符号链接,或软连接,用于使用一个不同的文件名来引用另一个文件,符号链接可以跨越文件系统,而链接到另一个文件系统的文件上。删除一个符号链接并不影响被链接的文件。此外还有硬链接,硬链接无法跨越文件系统。链接数表示硬连接的数量。 27 | - `p` 命名管道 28 | - `c` 字符设备 29 | - `b` 块设备 30 | - `s` 套接字 31 | 32 | minux 的文件信息存储在 `inode.mode` 字段中,总共有 16 位,其中: 33 | 34 | - 高 4 位用于表示文件类型 35 | - 中 3 位用于表示特殊标志 36 | - 低 9 位用于表示文件权限 37 | 38 | ## 参考 39 | 40 | 1. [赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005](https://book.douban.com/subject/1231236/) 41 | 2. 42 | -------------------------------------------------------------------------------- /docs/11 文件系统/088 文件系统目录操作.md: -------------------------------------------------------------------------------- 1 | # 文件系统目录操作 2 | 3 | 主要完成以下几个函数: 4 | 5 | ```c++ 6 | // 判断文件名是否相等 7 | bool match_name(const char *name, const char *entry_name, char **next); 8 | 9 | // 获取 dir 目录下的 name 目录 所在的 dentry_t 和 buffer_t 10 | buffer_t *find_entry(inode_t **dir, const char *name, char **next, dentry_t **result); 11 | 12 | // 在 dir 目录中添加 name 目录项 13 | buffer_t *add_entry(inode_t *dir, const char *name, dentry_t **result); 14 | ``` 15 | -------------------------------------------------------------------------------- /docs/11 文件系统/089 文件系统 namei.md: -------------------------------------------------------------------------------- 1 | # 文件系统 namei 2 | 3 | 主要完成以下函数: 4 | 5 | ```c++ 6 | // 获取 pathname 对应的父目录 inode 7 | static inode_t *named(char *pathname, char **next); 8 | 9 | // 获取 pathname 对应的 inode 10 | static inode_t *namei(char *pathname); 11 | ``` 12 | -------------------------------------------------------------------------------- /docs/11 文件系统/090 读写 inode.md: -------------------------------------------------------------------------------- 1 | # 读写 inode 2 | 3 | 主要完成以下函数: 4 | 5 | ```c++ 6 | // 从 inode 的 offset 处,读 len 个字节到 buf 7 | int inode_read(inode_t *inode, char *buf, u32 len, off_t offset) 8 | 9 | // 从 inode 的 offset 处,将 buf 的 len 个字节写入磁盘 10 | int inode_write(inode_t *inode, char *buf, u32 len, off_t offset) 11 | ``` 12 | -------------------------------------------------------------------------------- /docs/11 文件系统/091 截断 inode.md: -------------------------------------------------------------------------------- 1 | # 截断 inode 2 | 3 | 主要完成以下函数: 4 | 5 | ```c++ 6 | // 释放 inode 所有文件块 7 | void inode_truncate(inode_t *inode); 8 | ``` -------------------------------------------------------------------------------- /docs/11 文件系统/092 系统调用 mkdir,rmdir.md: -------------------------------------------------------------------------------- 1 | # 系统调用 mkdir,rmdir 2 | 3 | 完成以下系统调用: 4 | 5 | ```c++ 6 | // 创建目录 7 | int mkdir(char *pathname, int mode); 8 | 9 | // 删除目录 10 | int rmdir(char *pathname); 11 | ``` -------------------------------------------------------------------------------- /docs/11 文件系统/093 系统调用 link,unlink.md: -------------------------------------------------------------------------------- 1 | # 系统调用 link,unlink 2 | 3 | 完成以下系统调用: 4 | 5 | ```c++ 6 | // 创建文件硬链接 7 | int link(char *oldname, char *newname); 8 | 9 | // 删除文件 10 | int unlink(char *filename); 11 | ``` 12 | 13 | `goto` 破坏了程序的结构化,Djikstra,程序里禁止使用 `goto` 14 | 15 | C 语言没有异常机制: 16 | 17 | try catch finally 18 | 19 | 我们希望,目录是一种树形结构,没有环,最次,有向无环图。 20 | -------------------------------------------------------------------------------- /docs/11 文件系统/094 打开 inode.md: -------------------------------------------------------------------------------- 1 | # 打开 inode 2 | 3 | 主要完成以下函数: 4 | 5 | ```c++ 6 | // 打开文件,返回 inode,用于系统调用 open 7 | inode_t *inode_open(char *pathname, int flag, int mode); 8 | ``` -------------------------------------------------------------------------------- /docs/11 文件系统/095 文件初始化.md: -------------------------------------------------------------------------------- 1 | # 文件初始化 2 | 3 | - 定义文件数据结构 4 | - 创建全局文件表 5 | - 创建进程文件表 6 | -------------------------------------------------------------------------------- /docs/11 文件系统/096 系统调用 open,close.md: -------------------------------------------------------------------------------- 1 | # 系统调用 open,close 2 | 3 | 完成以下系统调用: 4 | 5 | ```c++ 6 | // 打开文件 7 | fd_t open(char *filename, int flags, int mode); 8 | // 创建普通文件 9 | fd_t creat(char *filename, int mode); 10 | // 关闭文件 11 | void close(fd_t fd); 12 | ``` 13 | -------------------------------------------------------------------------------- /docs/11 文件系统/097 系统调用 read,write.md: -------------------------------------------------------------------------------- 1 | # 系统调用 read,write 2 | 3 | 完成以下系统调用: 4 | 5 | ```c++ 6 | // 读文件 7 | int read(fd_t fd, char *buf, int len); 8 | // 写文件 9 | int write(fd_t fd, char *buf, int len); 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/11 文件系统/098 系统调用 lseek.md: -------------------------------------------------------------------------------- 1 | # 系统调用 lseek 2 | 3 | 完成以下系统调用: 4 | 5 | ```c++ 6 | // 设置文件偏移量 7 | int lseek(fd_t fd, off_t offset, int whence); 8 | ``` 9 | 10 | ## 参考 11 | 12 | - 史蒂文斯 (w.richard Stevens) & 拉戈 (stephen A.rago) - UNIX 环境高级编程(第三版) 13 | -------------------------------------------------------------------------------- /docs/11 文件系统/099 系统调用 getcwd,chdir.md: -------------------------------------------------------------------------------- 1 | # 系统调用 getcwd,chdir 2 | 3 | 完成以下系统调用: 4 | 5 | ```c++ 6 | // 获取当前路径 7 | char *getcwd(char *buf, size_t size); 8 | // 切换当前目录 9 | int chdir(char *pathname); 10 | // 切换根目录 11 | int chroot(char *pathname); 12 | ``` 13 | -------------------------------------------------------------------------------- /docs/11 文件系统/101 系统调用 stat,fstat.md: -------------------------------------------------------------------------------- 1 | # 系统调用 stat,fstat 2 | 3 | 完成以下系统调用: 4 | 5 | ```c++ 6 | // 获取文件状态 7 | int stat(char *filename, stat_t *statbuf); 8 | int fstat(fd_t fd, stat_t *statbuf); 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/11 文件系统/102 系统调用 mknod.md: -------------------------------------------------------------------------------- 1 | # 系统调用 mknod 2 | 3 | 完成以下系统调用: 4 | 5 | ```c++ 6 | // 创建设备文件 7 | int mknod(char *filename, int mode, int dev); 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/11 文件系统/103 系统调用 mount,umount.md: -------------------------------------------------------------------------------- 1 | # 系统调用 mount,umount 2 | 3 | 完成以下系统调用: 4 | 5 | ```c++ 6 | // 挂载设备 7 | int mount(char *devname, char *dirname, int flags); 8 | // 卸载设备 9 | int umount(char *target); 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/11 文件系统/104 格式化文件系统.md: -------------------------------------------------------------------------------- 1 | # 格式化文件系统 2 | 3 | 在 [文件系统简介](./083%20%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E7%AE%80%E4%BB%8B.md) 里,我们知道 minux 系统总共有一下几个组成部分: 4 | 5 | ![](./images/block1.drawio.svg) 6 | 7 | - 文件块:用于存储文件内容 8 | - inode 块:用于存储 inode,inode 中是一个文件或目录的信息 9 | - 块位图:用于表示哪些文件块被占用 10 | - inode 位图:用于表示哪些 inode 被占用 11 | - 超级块:用于描述以上四部分的位置,超级块位于第 1 块 12 | - 引导块:其中有主引导扇区,是第 0 块 13 | -------------------------------------------------------------------------------- /docs/11 文件系统/105 虚拟磁盘.md: -------------------------------------------------------------------------------- 1 | # 虚拟磁盘 2 | 3 | 用内存模拟磁盘,以创建临时的文件系统,比如 /dev,linux 中的文件系统可以使用 `df` 命令查看。 4 | 5 | 目前的内存布局: 6 | 7 | ![](./images/memory_map.drawio.svg) 8 | -------------------------------------------------------------------------------- /docs/11 文件系统/106 标准输入输出.md: -------------------------------------------------------------------------------- 1 | # 标准输入输出 2 | 3 | 创建标准输入输出文件 [^stdio],以及处理字符设备和块设备的读写。 4 | 5 | - C 标准文件: 6 | - struct FILE *stdin 7 | - stdout 8 | - stderr 9 | - 文件描述符: 10 | - STDIN_FILENO 11 | - STDOUT_FILENO 12 | - STDERR_FILENO 13 | 14 | ## 参考 15 | 16 | [^stdio]: https://man7.org/linux/man-pages/man3/stdin.3.html 17 | -------------------------------------------------------------------------------- /docs/11 文件系统/115 输入输出重定向.md: -------------------------------------------------------------------------------- 1 | # 输入输出重定向 2 | 3 | ## 系统调用 4 | 5 | 系统调用 `dup` [^dup],用于复制文件描述符; 6 | 7 | ```c++ 8 | // 复制文件描述符 9 | fd_t dup(fd_t oldfd); 10 | fd_t dup2(fd_t oldfd, fd_t newfd); 11 | ``` 12 | 13 | ## 重定向 14 | 15 | shell 输入输出重定向 [^redirect] 有很多类型,一般常见以下几种,用于操作标准输入输出的文件; 16 | 17 | 输入重定向: 18 | 19 | ```console 20 | cmd < filename 21 | ``` 22 | 23 | 输出重定向: 24 | 25 | ```console 26 | cmd > filename 27 | ``` 28 | 29 | 输出追加重定向: 30 | 31 | ```console 32 | cmd >> filename 33 | ``` 34 | 35 | ## 参考 36 | 37 | [^dup]: 38 | [^redirect]: 39 | -------------------------------------------------------------------------------- /docs/11 文件系统/116 管道.md: -------------------------------------------------------------------------------- 1 | # 管道 2 | 3 | 管道是一种进程间通信的方式,`pipe` 系统调用可以返回两个文件描述符,其中前一个用于读,后一个用于写,那么读进程就可以读取写进程写入的内容。 4 | 5 | 管道只能在具有公共祖先的两个进程之间使用。通常,一个管道由一个进程创建,在进程调用 `fork` 之后,这个管道就能在父进程和子进程之间使用了。 6 | 7 | 除此以外,还有一种管道 FIFO,有时被称为**命名管道**。 8 | 9 | ```c++ 10 | // 创建管道 11 | int pipe(fd_t pipefd[2]); 12 | ``` 13 | 14 | ## 参考 15 | 16 | 1. [赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005](https://book.douban.com/subject/1231236/) 17 | 2. 史蒂文斯 (w.richard Stevens) & 拉戈 (stephen A.rago) - UNIX 环境高级编程(第三版) 18 | -------------------------------------------------------------------------------- /docs/11 文件系统/155 虚拟文件系统.md: -------------------------------------------------------------------------------- 1 | # 虚拟文件系统 2 | 3 | 定义和重构虚拟文件系统: 4 | 5 | ```c++ 6 | typedef struct fs_op_t 7 | { 8 | int (*mkfs)(dev_t dev, int args); 9 | 10 | int (*super)(dev_t dev, super_t *super); 11 | 12 | int (*open)(inode_t *dir, char *name, int flags, int mode, inode_t **result); 13 | void (*close)(inode_t *inode); 14 | 15 | int (*read)(inode_t *inode, char *data, int len, off_t offset); 16 | int (*write)(inode_t *inode, char *data, int len, off_t offset); 17 | int (*truncate)(inode_t *inode); 18 | 19 | int (*stat)(inode_t *inode, stat_t *stat); 20 | int (*permission)(inode_t *inode, int mask); 21 | 22 | int (*namei)(inode_t *dir, char *name, char **next, inode_t **result); 23 | int (*mkdir)(inode_t *dir, char *name, int mode); 24 | int (*rmdir)(inode_t *dir, char *name); 25 | int (*link)(inode_t *odir, char *oldname, inode_t *ndir, char *newname); 26 | int (*unlink)(inode_t *dir, char *name); 27 | int (*mknod)(inode_t *dir, char *name, int mode, int dev); 28 | int (*readdir)(inode_t *inode, dentry_t *entry, size_t count, off_t offset); 29 | } fs_op_t; 30 | ``` 31 | 32 | ## 参考 33 | 34 | - 35 | -------------------------------------------------------------------------------- /docs/12 调试工具/129 异常与调试.md: -------------------------------------------------------------------------------- 1 | # 异常与调试 2 | 3 | 如果你的代码是自己手敲的,首先建议与原代码比较一下,不然很可能敲错,浪费时间去调试,因为调试确实很费时间,不过调试这种踩坑的过程确实可以让人成长,就看你自己的抉择了。 4 | 5 | 这里说明一些常见的错误,及调试的方式,希望对你有帮助。 6 | 7 | ## 异常 8 | 9 | ### CPU Reset 10 | 11 | 异常处理的时候,如果中断描述符配置的不对,就会引发 CPU 重置,易见于 bochs。 12 | 13 | ### 出错位置 14 | 15 | 如果,中断描述符配置正确,异常的时候,打印出相关的异常信息,并阻塞,此使可以利用 eip 的信息来找到出错的位置。 16 | 17 | ## VMWare 调试内核 18 | 19 | VMWare 的行为有时与 qemu 不一致,这一般是内核写得不够通用,需要对特定的情况做更一般的处理。 20 | 21 | 使用 gdb stub [^vmware_gdb] 就可以对 VMWare 一样对内核单步调试。 22 | 23 | ```ini 24 | debugStub.listen.guest32 = "TRUE" 25 | debugStub.listen.guest32.remote = "TRUE" 26 | debugStub.port.guest32 = "1234" 27 | monitor.debugOnStartGuest32 = "TRUE" 28 | debugStub.hideBreakpoints = "TRUE" 29 | ``` 30 | 31 | 另外如果是 64 位系统,则可以如下配置,不过目前没有引入; 32 | 33 | ```ini 34 | debugStub.listen.guest64 = "TRUE" 35 | debugStub.listen.guest64.remote = "TRUE" 36 | debugStub.port.guest64 = "1234" 37 | monitor.debugOnStartGuest32 = "TRUE" 38 | debugStub.hideBreakpoints = "TRUE" 39 | ``` 40 | 41 | ## 参考 42 | 43 | [^vmware_gdb]: -------------------------------------------------------------------------------- /docs/12 调试工具/999 WSL2 开发环境.md: -------------------------------------------------------------------------------- 1 | # WSL2 开发环境 2 | 3 | ## minix 文件系统 4 | 5 | mount 的时候 minix 文件系统未知,通过查证,需要重新编译 Linux,然后开启 minix 文件系统支持。 6 | 7 | 需要安装一些包: 8 | 9 | sudo pacman -S bc 10 | sudo pacman -S pahole 11 | 12 | --- 13 | 14 | 从下面的地址下载的源码。 15 | 16 | > 17 | 18 | --- 19 | 20 | 修改文件 `Microsoft/config-wsl`,添加下面一行,以支持 minix 文件系统: 21 | 22 | CONFIG_MINIX_FS=y 23 | 24 | 我还改了: 25 | 26 | CONFIG_CC_VERSION_TEXT="gcc (GCC) 13.2.1 20230801" 27 | 28 | --- 29 | 30 | 然后: 31 | 32 | make KCONFIG_CONFIG=Microsoft/config-wsl -j16 33 | 34 | 35 | 后面 -j16 表示 16 个任务同时编译,可以根据自己的 CPU 数量来设置,等待,编译完成。。。 36 | 37 | --- 38 | 39 | 然后将编译好的文件 `arch/x86/boot/bzImage` 复制到用户目录 `%UserProfile%`, 该目录一般为 `C:/Users/xxx/`,当然也可以放在其他位置,自行决定;也有人直接替换了 `C:\Windows\System32\lxss\tools\kernel`,然后无需下面的配置文件,不过直觉告诉我,这种方式不推荐。 40 | 41 | 在用户目录添加或修改配置文件 `.wslconfig` 42 | 43 | ``` 44 | [wsl2] 45 | kernel=C:\\Users\\xxx\\bzImage 46 | ``` 47 | 48 | 然后重启 `wsl2` 49 | 50 | wsl --shutdown 51 | 52 | 重新进入 `wsl`,输入命令 `uname -r` 得到: 53 | 54 | 5.15.123.1-microsoft-standard-WSL2 55 | 56 | ## 网络 NAT 模式 57 | 58 | 这部分内容来自 [@znyin](https://github.com/znyinyyniu),经过了优化。 59 | 60 | ## 参考 61 | 62 | - 63 | - 64 | -------------------------------------------------------------------------------- /docs/13 系统优化/138 系统优化.md: -------------------------------------------------------------------------------- 1 | # 系统优化 2 | 3 | ## 映射物理内存 4 | 5 | ```cpp 6 | // 映射物理内存页 7 | void map_page(u32 vaddr, u32 paddr); 8 | // 映射物理内存区域 9 | void map_area(u32 paddr, u32 size); 10 | ``` 11 | 12 | ## 内核扩容 13 | 14 | 将内核容量扩容到 500KB; 15 | 16 | ## 兼容 bochs 串口 17 | 18 | 经过调试,发现了之前 bochs 串口的错误,感觉应该是 bochs 本身模拟的问题,不过由于 bochs 时钟的问题,也发现了一些正常机器发现不了的问题,比如,**不应该在外中断处理函数中阻塞进程,应该尽可能迅速返回**,不过这要引入 **中断下半部** 的概念,目前先苟着,又不是不能用。 19 | -------------------------------------------------------------------------------- /docs/13 系统优化/176 高速缓冲优化.md: -------------------------------------------------------------------------------- 1 | # 高速缓冲优化 2 | 3 | 为了支持不同的文件系统 比如 ISO9660,需要 2K 的扇区大小,实现不同大小的高速缓冲,包括 1K,2K,4K; 4 | 5 | ## 内核校验和 6 | 7 | 为了确保内核在开始执行时是正确的,特别设计了 CRC32 校验其完整性,由于 grub 的原因,将内核移动到 `0x20000` 开始的位置; 8 | 9 | ![](../05%20内存管理/images/memory_map_176.drawio.svg) 10 | 11 | 由于需要修改 `kernel.bin` 的内容,写入校验和,需要安装 python 依赖: 12 | 13 | pip install pyelftools 14 | 15 | 或者: 16 | 17 | sudo pacman -S python-pyelftools 18 | -------------------------------------------------------------------------------- /docs/13 系统优化/179 版本 1.0.0.md: -------------------------------------------------------------------------------- 1 | # 版本 1.0.0 2 | 3 | 哈哈!版本 1.0.0 发布!!!,发布 `onix_1.0.0.iso` 文件; 4 | 5 | ![](../others/images/snapshot.png) 6 | 7 | ## uname 系统调用 8 | 9 | 用于获取简单的系统信息; 10 | 11 | ## 配置方式 12 | 13 | ## qemu 14 | 15 | 需要提前配置 `tap0` 设备,用于网络; 16 | 17 | ```sh 18 | qemu-system-i386 -m 32M -audiodev pa,id=snd -machine pcspk-audiodev=snd -device sb16,audiodev=snd -rtc base=localtime -chardev stdio,mux=on,id=com1 -serial chardev:com1 -netdev tap,id=eth0,ifname=tap0,script=no,downscript=no -device e1000,netdev=eth0 -drive file=onix_1.0.0.iso,media=cdrom,if=ide -boot d 19 | ``` 20 | 21 | ## vmware 22 | 23 | 修改虚拟机 `.vmx` 文件,添加如下内容,另外可能需要添加一个串口设备: 24 | 25 | ```sh 26 | # 声霸卡 27 | sound.present = "TRUE" 28 | sound.virtualDev = "sb16" 29 | sound.opl3.enabled = "TRUE" 30 | sound.autodetect = "TRUE" 31 | sound.baseAddr = "0x220" 32 | sound.dma16 = "5" 33 | sound.dma8 = "1" 34 | sound.irq = "5" 35 | 36 | # e1000 网卡 37 | ethernet0.virtualDev = "e1000" 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/13 系统优化/180 小结 v2.md: -------------------------------------------------------------------------------- 1 | # 小结 v2 2 | 3 | 和 [120 小结](../10%20用户程序/120%20小结.md) 一样,事情必须以某种形式恰当地结束,不然总会有千丝万缕的联系,难以继续前行。所以应该为目标设定一个 **截止日期(Deadline)**,这一点很重要。 4 | 5 | ## 回顾 6 | 7 | 通过大半年的努力,终于实现了 2023 年的主要目标 —— 网络协议栈。除此以外,还有一些很多重要的功能和优化: 8 | 9 | - [x] 错误处理 10 | - [ ] IO的时候尽可能保留原始信息 11 | - [x] 信号机制 12 | - [ ] 键盘快捷键 13 | - [x] CTRL+C 14 | - [x] 声霸卡驱动 15 | - [x] 软盘驱动 16 | - [x] PCI 总线枚举 17 | - [x] 磁盘 UDMA 18 | - [x] e1000 网卡驱动 19 | - [x] ATAPI 驱动 20 | - [x] ISO9660 文件系统 21 | - [x] 网络协议: 22 | - [x] Ethernet(802.3) 23 | - [ ] SLIP: Serial Line Internet Protocol 24 | - [ ] WiFi(802.11) 25 | - [x] ARP 26 | - [x] IP 27 | - [ ] 路由和转发 28 | - [ ] NAT(Network Address Translation) 29 | - [x] ICMP 30 | - [x] UDP 31 | - [x] TCP 32 | - [x] DHCP 33 | - [x] DNS 34 | - [x] HTTP 35 | 36 | 其中很多实现的相当简单,没有考虑很多复杂的情况,所以,应该会有很多的 bug,如果发现后面再慢慢改吧。 37 | 38 | 实际上以上很多的内容去年就已经实现了,但是要整理讲出来还是有不少的难度。需要考虑很多开发过程中不会考虑的问题,主要的问题是:记录过程中存在的问题,以及解决的方法。比如 TCP 协议实现的过程一波三折,当然现在我依然不够满意,不过精力有限,就这样吧。 39 | 40 | ## 下一步的计划 41 | 42 | 另外,还有很多功能没有实现,比如: 43 | 44 | - BIOS 优化 45 | - UEFI 引导 46 | - 64 位支持 47 | - 动态链接与重定位 48 | - 图形界面 49 | - USB(XHCI) 驱动 50 | - SATA(AHCI) 驱动 51 | - NVME 驱动 52 | -------------------------------------------------------------------------------- /docs/14 网络协议/145 数据包高速缓冲.md: -------------------------------------------------------------------------------- 1 | # 数据包高速缓冲 2 | 3 | 这是一种缓存机制,我们应该尽可能避免调用 `memcpy` 函数。 4 | 5 | ![](./images/pbuf.drawio.svg) 6 | 7 | - MTU(Maximum Transmission Unit) [^mtu] [^rfc894]:1500 8 | 9 | [^mtu]: 10 | [^rfc894]: 11 | 12 | ## e1000 收发机制 13 | 14 | ![](./images/e1000.drawio.svg) 15 | 16 | ## 参考 17 | 18 | - 19 | - 20 | -------------------------------------------------------------------------------- /docs/14 网络协议/148 虚拟网络设备.md: -------------------------------------------------------------------------------- 1 | # 虚拟网络设备 2 | 3 | 添加虚拟网络设备,以及内核线程用于网络的收包和发包。 4 | 5 | ![网络系统结构图](./images/netif.drawio.svg) 6 | 7 | ## 参考 8 | 9 | - 10 | - 11 | -------------------------------------------------------------------------------- /docs/14 网络协议/149 以太网协议实现.md: -------------------------------------------------------------------------------- 1 | # 以太网协议实现 2 | 3 | ## 网络协议栈 4 | 5 | ![](./images/net_ethernet.drawio.svg) 6 | 7 | ## MAC 地址 8 | 9 | ![](./images/ethernet_farme1.svg) 10 | 11 | MAC Address(Media Access Control Address),MAC 地址就是在媒体接入层上使用的地址,也叫物理地址,其被固化在网卡的 ROM 中。MAC 地址长度为 6 字节,48 位;该地址由 IEEE 管理,可以通过 IEEE 购买注册 MAC 地址 [^regauth];其前 3 个字节表示组织唯一标志符 (Organizationally Unique Identifier,即 OUI),由 IEEE 的注册管理机构给不同厂家分配的代码,以区分不同的厂家,后 3 个字节由厂家自行分配,称为扩展标识符 (Company ID CID)。同一个厂家生产的网卡中 MAC 地址后 24 位是不同的。 12 | 13 | ## Length/Type 14 | 15 | 如果值 <= 1500,则描述数据包的长度,若值 >= 1536(0x0600) 则表示类型,中间的空洞未定义; 16 | 17 | 具体值参考 [^802],其中比较重要的有: 18 | 19 | - 0x0800:IPv4 协议 20 | - 0x0806:ARP 协议 21 | - 0x86DD:IPv6 协议 22 | 23 | ## 参考 24 | 25 | - IEEE Std 802.3™-2015 (Revision of IEEE Std 802.3-2012), IEEE Standard for Ethernet 26 | - [LwIP 应用开发实战指南](https://doc.embedfire.com/products/link/zh/latest/tutorial/ebf_lwip_tutorial.html) 27 | 28 | [^regauth]: https://standards.ieee.org/products-programs/regauth/ 29 | [^802]: https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml#ieee-802-numbers-1 30 | -------------------------------------------------------------------------------- /docs/14 网络协议/150 ARP 协议.md: -------------------------------------------------------------------------------- 1 | # ARP 协议 2 | 3 | ARP(Address Resolution Protocol) 协议定义在 RFC826 [^RFC826],用于解析局域网中 IP 地址对应的 MAC 地址。 4 | 5 | 另外,还有 RARP(Reverse Address Resolution Protocol) 协议,定义在 RFC903 [^RFC903],用于解析 MAC 地址对应的 IP 地址; 6 | 7 | ## ARP 报文 8 | 9 | ![ARP 报文](./images/arp.drawio.svg) 10 | 11 | ```c++ 12 | typedef struct arp_t 13 | { 14 | u16 hwtype; // 硬件类型 15 | u16 proto; // 协议类型 16 | u8 hwlen; // 硬件地址长度 17 | u8 protolen; // 协议地址长度 18 | u16 opcode; // 操作类型 19 | eth_addr_t hwsrc; // 源 MAC 地址 20 | ip_addr_t ipsrc; // 源 IP 地址 21 | eth_addr_t hwdst; // 目的 MAC 地址 22 | ip_addr_t ipdst; // 目的 IP 地址 23 | } _packed arp_t; 24 | ``` 25 | 26 | 其中: 27 | 28 | - 硬件类型 `1` 表示以太网 29 | - 协议类型 `0x0800` 表示 IPv4 30 | - 操作类型: 31 | - 1:请求 32 | - 2:回复 33 | 34 | ## 网络协议栈 35 | 36 | ![](./image/../images/net_arp.drawio.svg) 37 | 38 | ARP 协议放在 **网络层** 不太合适,更多的认为它是 **数据链路层** 的协议,这是有争议的,但这种分类意义不大,它更像是网络层和数据链路层的胶水,使两层能够联系起来。 39 | 40 | ## 参考 41 | 42 | [^RFC826]: 43 | [^RFC903]: 44 | -------------------------------------------------------------------------------- /docs/14 网络协议/151 ARP 缓存.md: -------------------------------------------------------------------------------- 1 | # ARP 缓存 2 | 3 | 缓存 IP -> MAC 地址的映射关系; 4 | 5 | ## 子网掩码 6 | 7 | Classless Inter-Domain Routing(CIDR) 无类别域间路由,定义在 RFC4632 [^rfc4632]; 8 | 9 | 互联网的机器都有一个唯一的 IP 地址,但是 IP 地址被分为两个部分,前面的部分称为 网络部分,后面的部分称为主机部分,这两部分通过子网掩码标记,子网掩码的 32 位分成了两部分,高位全为 1,低位全为 0; 10 | 11 | 比如对于常见的子网掩码:`255.255.255.0`,其二进制为 `11111111_11111111_11111111_00000000`,表示如果两个 IP 地址的前三个字节(高 24 位)如果相等,则两 IP 在同一个局域网中,发包时可以直接发送。比如:`192.168.111.11` 和 `192.168.111.22`;由于子网掩码的前面(高位)全为 1,所以很多时候子网掩码可以表示成 1 的数量,与 IP 地址合并在一起表示成 `192.168.111.11/24`。 12 | 13 | ## 参考 14 | 15 | - 16 | 17 | [^rfc4632]: 18 | -------------------------------------------------------------------------------- /docs/14 网络协议/153 ICMP 协议.md: -------------------------------------------------------------------------------- 1 | # ICMP 协议 2 | 3 | ICMP (Internet Control Message Protocol) Internet 控制报文协议,定义在 RFC792 [^rfc792],用于在主机之间传递控制消息,比如测试网络连通性,主机是否可达,路由是否可用等,虽然没有传输信息,但是确实网络中很重要的协议。 4 | 5 | ## ICMP 数据报 6 | 7 | ![](./images/icmp.drawio.svg) 8 | 9 | 不同类型的数据报格式有所不同,具体参考 RFC792; 10 | 11 | ## ICMP 类型 12 | 13 | - 0:Echo Replay,查询应答 14 | - 3:Destination Unreachable Message,目标不可达 15 | - 8:Echo,查询 16 | 17 | ## 协议栈 18 | 19 | ![](./images/net_icmp.drawio.svg) 20 | 21 | ## 参考 22 | 23 | - 24 | [^rfc792]: 25 | -------------------------------------------------------------------------------- /docs/14 网络协议/156 套接字.md: -------------------------------------------------------------------------------- 1 | # 套接字 2 | 3 | 1983,伯克利套接字(Berkeley sockets) [^sockets] 最初随着 4.2BSD(Berkeley Software Distribution) UNIX 操作系统一同发布,作为一种编程接口。然而,直到 1989 年,加州大学伯克利分校才从 AT&T 公司专有的 UNIX 的许可限制中发布了操作系统和网络库的版本。所有现代操作系统都实现了伯克利套接字接口的一个版本。它成为在因特网上运行的应用程序的标准接口。BSD 套接字 API 是用 C 语言编写的。大多数其他编程语言都提供类似的接口,通常以基于 C API 的包装器库的形式编写。 4 | 5 | ## 系统调用 6 | 7 | ```c++ 8 | int socket(int domain, int type, int protocol); 9 | int listen(int fd, int backlog); 10 | int accept(int fd, sockaddr_t *addr, int *addrlen); 11 | int bind(int fd, const sockaddr_t *name, int namelen); 12 | int connect(int fd, const sockaddr_t *name, int namelen); 13 | int shutdown(int fd, int how); 14 | 15 | int getpeername(int fd, sockaddr_t *name, int *namelen); 16 | int getsockname(int fd, sockaddr_t *name, int *namelen); 17 | int getsockopt(int fd, int level, int optname, void *optval, int *optlen); 18 | int setsockopt(int fd, int level, int optname, const void *optval, int optlen); 19 | 20 | int recv(int fd, void *data, int size, u32 flags); 21 | int recvfrom(int fd, void *data, int size, u32 flags, sockaddr_t *from, int *fromlen); 22 | int recvmsg(int fd, msghdr_t *msg, u32 flags); 23 | 24 | int send(int fd, const void *data, int size, u32 flags); 25 | int sendto(int fd, const void *data, int size, u32 flags, const sockaddr_t *to, int tolen); 26 | int sendmsg(int fd, msghdr_t *msg, u32 flags); 27 | ``` 28 | 29 | ## 参考 30 | 31 | [^sockets]: 32 | -------------------------------------------------------------------------------- /docs/14 网络协议/157 数据包套接字.md: -------------------------------------------------------------------------------- 1 | # 数据包套接字 2 | 3 | 数据包套接字 用于在设备驱动程序 (OSI第2层) 级别接收或发送原始数据包。它们允许用户在物理层之上的用户空间中实现协议模块。 4 | 5 | ```c++ 6 | socket(AF_PACKET, SOCK_RAW, 0); 7 | ``` 8 | 9 | ## 参考 10 | 11 | [^packet]: 12 | 13 | - 14 | -------------------------------------------------------------------------------- /docs/14 网络协议/158 原始套接字.md: -------------------------------------------------------------------------------- 1 | # 原始套接字 2 | 3 | ## 超时设置 4 | 5 | `setsockopt` [^setsockopt] 可以用来设置 socket 选项; 6 | 7 | ```c++ 8 | int setsockopt( 9 | int socket, int level, int option_name, 10 | const void *option_value, socklen_t option_len); 11 | ``` 12 | 13 | ## 原始套接字 14 | 15 | 原始套接字 [^raw] 允许在用户空间中实现新的 IPv4 协议。原始套接字 接收或发送 不包括 链路层报头的原始数据报。 16 | 17 | ```c++ 18 | socket(AF_INET, SOCK_RAW, PROTO_IP); 19 | ``` 20 | 21 | ## 参考 22 | 23 | [^setsockopt]: 24 | [^raw]: 25 | -------------------------------------------------------------------------------- /docs/14 网络协议/159 ping.md: -------------------------------------------------------------------------------- 1 | # ping 2 | 3 | ping [^ping] 程序是 Mike Muuss 于 1983 年 12 月在弹道研究实验室(现在的美国陆军研究实验室)工作时编写的。David Mills 关于使用 ICMP 回声包进行 IP 网络诊断和测量的评论促使 Muuss 创建了解决网络问题的实用程序。 4 | 5 | 作者以声纳发出的声音来命名它 [^story],因为它的方法类似于声纳的回声定位。 6 | 7 | PING 的后缩略语 Packet InterNet Groper 已经使用了 30 多年,虽然 Muuss 说,从他的角度来看,PING 并不是一个缩写,但他承认 Mills 扩展了这个名字。 8 | 9 | ## 参考 10 | 11 | [^ping]: 12 | [^story]: 13 | -------------------------------------------------------------------------------- /docs/14 网络协议/160 localhost 环回地址.md: -------------------------------------------------------------------------------- 1 | # localhost 环回地址 2 | 3 | 环回地址 [^loopback] 为 `127.0.0.1/8`,表示本机地址;`/8` 意思是说 `127.x.x.x`,都是本机地址,和 `127.0.0.1` 是一样的,当然,这个设计很傻,毫无疑问,浪费了很多地址。 4 | 5 | `localhost` 是一个主机名,用来表示本机,一般 `localhost` 会被域名解析为 `127.0.0.1`; 6 | 7 | ## 参考 8 | 9 | [^loopback]: 10 | -------------------------------------------------------------------------------- /docs/14 网络协议/161 UDP 协议.md: -------------------------------------------------------------------------------- 1 | # UDP 协议 2 | 3 | UDP(User Datagram Protocol) 用户数据报协议定义在 RFC768 [^rfc768],目的是以最小的代价发送数据。 4 | 5 | ## UDP 报文 6 | 7 | ![](./images/udp.drawio.svg) 8 | 9 | ## 协议栈 10 | 11 | ![](./images/net_udp.drawio.svg) 12 | 13 | ## 参考 14 | 15 | [^rfc768]: 16 | -------------------------------------------------------------------------------- /docs/14 网络协议/162 TCP 协议简介.md: -------------------------------------------------------------------------------- 1 | # TCP 协议简介 2 | 3 | TCP(Transmission Control Protocol) 传输控制协议,最新的定义在 RFC9293[^rfc9293]; 4 | 5 | TCP 协议为了在不可靠的 IP 协议上提供可靠的字节流而设计的协议,它有很多机制: 6 | 7 | - 连接机制 8 | - 确认重传 9 | - 滑动窗口 10 | - 拥塞控制 11 | 12 | ## TCP 报文 13 | 14 | ![](./images/tcp.drawio.svg) 15 | 16 | 说明: 17 | 18 | - 头长度:TCP 头部长度,单位 4 字节,数据长度 = IP 数据长度 - TCP 头长度; 19 | - 紧急指针:用于紧急数据; 20 | - 选项:MSS(Maximum Segment Size),最大分段大小,一般为 1460 (IP_MTU(1500) - IP_HDR(20) - TCP_HDR(20)) 21 | 22 | 标志位的说明: 23 | 24 | - **F**IN: Finish,没有更多的数据发送,序列号用于终止连接 25 | - **S**YN: Synchronize,序列号用于同步建立连接 26 | - **R**ST: Reset,连接重置 27 | - **P**SH: Push,提交功能 28 | - **A**CK: Acknowledgement,确认号有效 29 | - **U**RG: Urgent,紧急指针有效 30 | 31 | 下面两个是后加的 [^rfc3168],用于显式拥塞控制: 32 | 33 | - **E**CE: ECN(Explicit Congestion Notification)-Echo,显式拥塞响应 34 | - **C**WR: Congestion Window Reduced,拥塞窗口减少 35 | 36 | ## 协议栈 37 | 38 | ![](./images/net_tcp.drawio.svg) 39 | 40 | ## 参考 41 | 42 | [^rfc9293]: 43 | [^rfc3168]: 44 | -------------------------------------------------------------------------------- /docs/14 网络协议/163 TCP 客户端连接.md: -------------------------------------------------------------------------------- 1 | # TCP 客户端连接 2 | 3 | ## 三次握手 4 | 5 | ![](./images/handshake.drawio.svg) 6 | 7 | ## TCP 状态转移图 8 | 9 | ![](./images/tcp_state.drawio.svg) 10 | 11 | ## 初始序列号 12 | 13 | 关于 ISN(Initial Sequence Number) **初始序列号** 的选择在 RFC9293 [^isn] 中有讨论,主要注意的地方有两点: 14 | 15 | - 尽力防止连接断开恢复之后,旧连接的数据包,影响到新连接; 16 | - 防止攻击者恶意攻击; 17 | 18 | ## 其他 19 | 20 | 由于需要测试的过程中需要重复使用服务端连接,需要做一些配置 [^timewait],其中的原理需要等到 TCP 连接断开后再做解释; 21 | 22 | ## 参考 23 | 24 | [^timewait]: 25 | [^isn]: 26 | -------------------------------------------------------------------------------- /docs/14 网络协议/165 TCP 发送和接收.md: -------------------------------------------------------------------------------- 1 | # TCP 发送和接收 2 | 3 | ## 发送 4 | 5 | ![](./images/tcp_send.drawio.svg) 6 | 7 | ## 接收 8 | 9 | ![](./images/tcp_recv.drawio.svg) 10 | 11 | ## 参考 12 | 13 | - 14 | -------------------------------------------------------------------------------- /docs/14 网络协议/166 TCP 定时器.md: -------------------------------------------------------------------------------- 1 | # TCP 定时器 2 | 3 | TCP 为每条连接建立了七个定时器。按照它们在一条连接生存期内出现的次序,简要介绍如下: 4 | 5 | 1. 连接建立(connection establishment)定时器:在发送 SYN 报文段建立一条新连接时启动。如果没有在 75 秒内收到响应,连接建立将中止; 6 | 7 | 2. 重传(retransmission) 定时器:在 TCP 发送数据时设定。如果定时器已超时而对端的确认还未到达,TCP 将重传数据。重传定时器的值(即 TCP 等待对端确认的时间)是动态计算的,取决于 TCP 为该连接测量的往返时间和该报文段已被重传的次数; 8 | 9 | 3. 延迟 ACK(delayed ACK) 定时器:在 TCP 收到必须被确认但不需要马上发出确认的数据时设定,TCP 等待 200ms 后发送确认响应。如果,在这 200ms 内,有数据要在该连接上发送,延迟的 `ACK` 响应就可随着数据一起发送回对端,称为捎带确认; 10 | 11 | 4. 持续(persist) 定时器:在连接对端通告接收窗口为 0, 阻止 TCP 继续发送数据时设定。由于连接对端发送的窗口通告不可靠(只有数据才会被确认, ACK 不会被确认),允许 TCP 继续发送数据的后续窗口更新有可能丢失。因此,如果 TCP 有数据要发送,但对端通告接收窗口为 0, 则持续定时器启动,超时后向对端发送 1 字节的数据,判定对端接收窗口是否已打开。与重传定时器类似,持续定时器的值也是动态计算的,取决于连接的往返时间,在 5 秒到 60 秒之间取值; 12 | 13 | 5. 保活(keepalive) 定时器:在应用进程选取了插口的 `SO_KEEPALVE` 选项时生效。如果连接的连续空闲时间超过 2 小时,保活定时器超时,向对端发送连接探测报文段,强迫对端响应。如果收到了期待的响应, TCP 可确定对端主机工作正常,在该连接再次空闲超过 2 小时之前,TCP 不会再进行保活测试。如果收到的是其他响应,TCP 可确定对端主机已重启。如果连续若干次保活测试都未收到响应,TCP 就假定对端主机已崩溃,尽管它无法区分是主机故障(例如,系统崩溃而尚未重启),还是连接故障(例如,中间的路由器发生故障或电话线断了); 14 | 15 | 6. `FIN_WAIT2` 定时器:当某个连接从 `FIN_WAIT1` 状态变迁到 `FIN_WAIT2` 状态,并且不能再接收任何新数据时(意味着应用进程调用了 `close`, 而非 `shutdown`,没有利用 TCP 的半关闭功能),`FIN_WAIT2` 定时器启动,设为 10 分钟。定时器超时后,重新设为 75 秒,第二次超时后连接被关闭。加入这个定时器的目的是为了避免如果对端一直不发送 `FIN`,某个连接会永远滞留在 `FIN_WAIT2` 状态; 16 | 17 | 7. `TIME_WAIT` 定时器, 一般也称为 2MSL 定时器。2MSL 指两倍的 MSL, 最大报文段生存时间。当连接转移到 `TIME_WAIT` 状态,即连接主动关闭时,定时器启动;连接进入 `TIME_WAIT` 状态时,定时器设定为 1 分钟,超时后,TCP 控制块 和 PCB 被删除,端口号可重新使用; 18 | 19 | TCP包括两个定时器函数: 20 | 21 | - 一个函数每 200 ms 调用一次(快速定时器); 22 | - 一个函数每 500 ms 调用一次(慢速定时器); 23 | 24 | 延迟 ACK 定时器与其他 6 个定时器有所不同:如果某个连接上设定了延迟 ACK 定时器,那么下一次 200 ms 定时器超时后,延迟的 ACK 必须被发送 ACK 的延迟时间必须在 0-200 ms 之间。其他的定时器每 500ms 递减一次,计数器减为 0 时,就触发相应的动作。 25 | 26 | ## 参考 27 | 28 | - TCP/IP 详解 卷2:实现 29 | -------------------------------------------------------------------------------- /docs/14 网络协议/167 TCP 断开连接.md: -------------------------------------------------------------------------------- 1 | # TCP 断开连接 2 | 3 | ## 四次挥手 4 | 5 | ![](./images/tcp_close.drawio.svg) 6 | 7 | ## 状态转移图 8 | 9 | ![](./images/tcp_state2.drawio.svg) 10 | -------------------------------------------------------------------------------- /docs/14 网络协议/168 TCP 服务端连接.md: -------------------------------------------------------------------------------- 1 | # TCP 服务端连接 2 | 3 | ## 状态转移图 4 | 5 | ![](./images/tcp_state3.drawio.svg) 6 | -------------------------------------------------------------------------------- /docs/14 网络协议/169 TCP 窗口管理.md: -------------------------------------------------------------------------------- 1 | # TCP 窗口管理 2 | 3 | ## 滑动窗口 4 | 5 | ![](./images/tcp_send.drawio.svg) 6 | 7 | ![](./images/tcp_recv.drawio.svg) 8 | 9 | ## 两种机制 10 | 11 | - Nagle 算法:发送端有很多小块数据,直接发送会产生很多,那么可以缓存到一起发送; 12 | - 糊涂窗口综合征:接收端接收窗口很小,而发送端有很多数据发送的时候,会产生很多数据包; 13 | 14 | ## 持续计时器 15 | 16 | 在连接对端通告接收窗口为 0, 阻止 TCP 继续发送数据时设定。由于连接对端发送的窗口通告不可靠(只有数据才会被确认, ACK 不会被确认),允许 TCP 继续发送数据的后续窗口更新有可能丢失。因此,如果 TCP 有数据要发送,但对端通告接收窗口为 0, 则持续定时器启动,超时后向对端发送 1 字节的数据,判定对端接收窗口是否已打开。与重传定时器类似,持续定时器的值也是动态计算的,取决于连接的往返时间,在 5 秒到 60 秒之间取值; 17 | 18 | ## 保活计时器 19 | 20 | 在应用进程选取了插口的 `SO_KEEPALVE` 选项时生效。如果连接的连续空闲时间超过 2 小时,保活定时器超时,向对端发送连接探测报文段,强迫对端响应。如果收到了期待的响应, TCP 可确定对端主机工作正常,在该连接再次空闲超过 2 小时之前,TCP 不会再进行保活测试。如果收到的是其他响应,TCP 可确定对端主机已重启。如果连续若干次保活测试都未收到响应,TCP 就假定对端主机已崩溃,尽管它无法区分是主机故障(例如,系统崩溃而尚未重启),还是连接故障(例如,中间的路由器发生故障或电话线断了); 21 | 22 | ## 参考 23 | 24 | - TCP/IP 详解 卷2:实现 25 | -------------------------------------------------------------------------------- /docs/14 网络协议/170 TCP 超时重传.md: -------------------------------------------------------------------------------- 1 | # TCP 超时重传 2 | 3 | ## 重传超时 4 | 5 | TCP 超时和重传的基础是怎样根据给定连接的 往返时间 RTT(Round Trip Time) 设置 RTO (Retransmission TimeOut)重传超时,以取的对特定连接比较合理的重传时间。 6 | 7 | ### 关键术语 8 | 9 | - RTT: Round Trip Time 往返时间 10 | - SRTT: Smoothed RTT 平滑的往返时间,(加权平均值) 11 | - RTO: Retransmission TimeOut 重传超时 12 | - RTTVAR: RTT 的残差(估计值) 13 | 14 | ### 经典方法 15 | 16 | $$ 17 | \text{SRTT} = \alpha(\text{SRTT}) + (1 - \alpha)(\text{RTT}) 18 | $$ 19 | 20 | 每次有新的 RTT 时,需要更新 SRTT,其中 $\alpha \in [0.8, 0.9]$,这种方法叫做指数加权移动平均 (Exponentially Weighted Moving Average EWMA),或者 低通滤波器(Low-Pass Filter),实现起来比较简单; 21 | 22 | RFC793 推荐如下公式计算 RTO: 23 | 24 | $$ 25 | \text{RTO} = \min(\text{ubound}, \max(\text{lbound}, \beta(\text{SRTT}))) 26 | $$ 27 | 28 | - lbound:RTO 下界,最小值; 29 | - ubound:RTO 上界,最大值; 30 | - $\beta$:时延离散因子,推荐值 1.3 ~ 2.0 (可以理解为 RTT 的方差); 31 | 32 | ### 标准方法 33 | 34 | 标准方法定义在 RFC6298; 35 | 36 | $$ 37 | \begin{aligned} 38 | \text{SRTT} &= (1 - g)(\text{SRTT}) + g(\text{RTT}) \\ 39 | \text{RTTVAR} &= (1 - h)(\text{RTTVAR}) + h(|\text{RTT} - \text{SRTT}|) \\ 40 | \text{RTO} &= \text{SRTT} + 4(\text{RTTVAR}) 41 | \end{aligned} 42 | $$ 43 | 44 | 经过简单的计算,可以的到如下的公式,更容易用于程序设计; 45 | 46 | $$ 47 | \begin{aligned} 48 | E &= \text{RTT} - \text{SRTT} \\ 49 | \text{SRTT} &= \text{SRTT} + gE \\ 50 | \text{RTTVAR} &= \text{RTTVAR} + h(|E| - \text{RTTVAR}) \\ 51 | \text{RTO} &= \text{SRTT} + 4(\text{RTTVAR}) 52 | \end{aligned} 53 | $$ 54 | 55 | 其中 $\displaystyle g = {1 \over 8}$,$\displaystyle h = {1 \over 4}$; 56 | 57 | ## 指数退避 58 | 59 | 每次重传间隔时间加倍称为二进制指数退避(binary exponential backoff); 60 | 61 | ## 参考 62 | 63 | - TCP/IP 详解 卷1:协议 64 | - TCP/IP 详解 卷2:实现 65 | -------------------------------------------------------------------------------- /docs/14 网络协议/171 TCP 拥塞控制.md: -------------------------------------------------------------------------------- 1 | # TCP 拥塞控制 2 | 3 | ![](./images/tcp_congestion.drawio.svg) 4 | 5 | 路由器(网关)因无法处理高速率到达的流量而被迫丢弃数据包的现象称为 **拥塞**; 6 | 7 | 为了缓解这一情况,TCP 的通信双方实行 **拥塞控制机制**; 8 | 9 | 针对丢包的情况,TCP 采取的机制是重传,包括 **超时重传** 和 **快速重传**; 10 | 11 | ## 拥塞检测 12 | 13 | 当拥塞状况出现(或将要出现)时,我们可以减缓 TCP 发送端的发送速率;但没有一个明确的信号告知拥塞状况已发生。只能来推断拥塞发生,通常是看是否有 **丢包发生**; 14 | 15 | 一般通过观察收到重复的 ACK 来检测丢包发生,当收到乱序数据包时,接收方会向发送方发送 ACK,以明确想要得到的数据; 16 | 17 | ## 慢启动 18 | 19 | 在传输初始阶段,由于未知网络传输能力,需要缓慢探测可用传输资源,防止短时间内大量数据注入导致拥塞。慢启动算法正是针对这一问题而设计。在数据传输之初或者重传计时器检测到丢包后,需要执行慢启动。 20 | 21 | 慢启动的目的是,使 TCP 在用拥塞避免探寻更多可用带宽之前得到 cwnd 值,以及帮助 TCP 建立 ACK 时钟。 22 | 23 | 每个好的 ACK 到来时 cwnd 的值加一个 mss,直到出现丢包,确定慢启动阈值; 24 | 25 | $$ 26 | \text{cwnd}_{t+1} = \text{cwnd}_{t} + \text{mss} 27 | $$ 28 | 29 | ## 拥塞避免 30 | 31 | 一旦 cwnd 超过慢启动阈值,意味着可能有更多的资源用于传输,于是进入线性增长阶段,每收到一个好的 ACK,cwnd 做如下更新: 32 | 33 | $$ 34 | \text{cwnd}_{t+1} = \text{cwnd}_{t} + \text{mss} \times \text{mss} / \text{cwnd}_{t} 35 | $$ 36 | 37 | ## 快速重传和快速恢复 38 | 39 | 快速重传:当丢包开始出现时,不一定是网络出现了拥塞,不用等到重传定时器超时,此时立刻重传数据包,可能就会恢复正常; 40 | 41 | 快速恢复:发生丢包时,需要重新慢启动,但是在快速重传之后,会将拥塞窗口置为线性增加阈值,然后重新线性增加; 42 | 43 | ## 参考 44 | 45 | - TCP/IP 详解 卷1:协议 46 | - TCP/IP 详解 卷2:实现 47 | -------------------------------------------------------------------------------- /docs/14 网络协议/173 网络配置.md: -------------------------------------------------------------------------------- 1 | # 网络配置 2 | 3 | 简单的网络地址配置功能,用于配置: 4 | 5 | - IP 地址 6 | - 子网掩码 7 | - 默认网关 8 | - MAC 地址 9 | 10 | 以及实现了简单的命令:`ifconfig` 11 | 12 | ## 参考 13 | 14 | - 15 | -------------------------------------------------------------------------------- /docs/14 网络协议/175 DNS 域名解析.md: -------------------------------------------------------------------------------- 1 | # DNS 域名解析 2 | 3 | 到目前为止,我们使用 IP 地址来识别主机。对大众来说,这些地址太烦琐而难以使用和记忆,因此互联网支持使用主机名称(hostname) 来识别包括客户机和服务器在内的主机。为了使用如 TCP/IP 等协议,主机名称通过称为 **名称解析** 的过程转换成 IP 地址。在互联网中存在不同形式 4 | 的名称解析,但是最普遍、最重要的一种是采用分布式数据库系统,即人们熟知的域名系统(DNS Domain Name System)。 5 | 6 | DNS 使用 UDP 或 TCP 协议,端口号为 53,一般先使用 UDP 查询,如果出错,再使用 TCP 协议; 7 | 8 | ## DNS 协议 9 | 10 | DNS协议定义在 RFC1035 [^rfc1035]; 11 | 12 | ![](./images/dns.drawio.svg) 13 | 14 | ### 名称和标签 15 | 16 | 假设查询的域名为 `www.example.com`: 17 | 18 | ![](./images/dns_example.drawio.svg) 19 | 20 | ### 查询的格式 21 | 22 | ![](./images/dns_query.drawio.svg) 23 | 24 | ### 回答的格式 25 | 26 | ![](./images/dns_answer.drawio.svg) 27 | 28 | ### 资源记录类型 29 | 30 | - A: IPv4 地址; 31 | - AAAA: IPv6 地址; 32 | - CNAME: (Canonical NAME) 规范名称,用于只想另一个域名; 33 | 34 | ## 参考 35 | 36 | - TCP/IP详解 卷1:协议 37 | - DNS 与 BIND 38 | 39 | [^rfc1035]: 40 | -------------------------------------------------------------------------------- /docs/14 网络协议/images/ARPANET_imp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/ARPANET_imp.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/Arpanet_1977.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/Arpanet_1977.png -------------------------------------------------------------------------------- /docs/14 网络协议/images/Bob_Kahn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/Bob_Kahn.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/Bob_Metcalfejpg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/Bob_Metcalfejpg.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/FTP_cable3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/FTP_cable3.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/Fiber_optic_illuminated.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/Fiber_optic_illuminated.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/Kleinrock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/Kleinrock.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/RG-59.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/RG-59.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/SteveCrocker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/SteveCrocker.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/Vint_Cerf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/Vint_Cerf.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/ethernet_signal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/ethernet_signal.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/hub.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/hub.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/switch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/switch.jpg -------------------------------------------------------------------------------- /docs/14 网络协议/images/topology.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/14 网络协议/images/topology.drawio.png -------------------------------------------------------------------------------- /docs/others/images/snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/docs/others/images/snapshot.png -------------------------------------------------------------------------------- /docs/others/参考文献.md: -------------------------------------------------------------------------------- 1 | # 参考文献 2 | 3 | > 参考持续更新中 ...... 4 | 5 | ## 相关网页 6 | 7 | 1. 8 | 9 | 这里有你想要的几乎一切!!! 10 | 11 | ## 参考项目 12 | 13 | 1. 14 | 2. 15 | 3. 16 | 17 | ## 参考书目 18 | 19 | 1. [郑刚 / 操作系统真象还原 / 人民邮电出版社 / 2016](https://book.douban.com/subject/26745156/) 20 | 21 | 2. [赵炯 / Linux内核完全注释 / 机械工业出版社 / 2005](https://book.douban.com/subject/1231236/) 22 | 23 | 3. [李忠 / 王晓波 / 余洁 / x86汇编语言 / 电子工业出版社 / 2013](https://book.douban.com/subject/20492528/) 24 | 25 | 4. [Richard Blum / 汇编语言程序设计 / 机械工业出版社 / 2006](https://book.douban.com/subject/1446250/) 26 | 27 | 5. [于渊 / Orange'S:一个操作系统的实现 / 电子工业出版社 / 2009](https://book.douban.com/subject/3735649/) 28 | 29 | 6. [[日] 川合秀实 / 30天自制操作系统 / 人民邮电出版社 / 2012](https://book.douban.com/subject/11530329/) 30 | 31 | 7. [俞甲子 / 石凡 / 潘爱民 / 程序员的自我修养 / 电子工业出版社 / 2009](https://book.douban.com/subject/3652388/) 32 | 33 | 8. [王爽 / 汇编语言(第2版) / 清华大学出版社 / 2008](https://book.douban.com/subject/3037562/) 34 | 35 | 9. [Maurice J.Bach / UNIX操作系统设计 / 机械工业出版社 / 2000](https://book.douban.com/subject/1035710/) 36 | 37 | 10. [[美] Robert Love / Linux内核设计与实现 / 机械工业出版社 / 2011](https://book.douban.com/subject/6097773/) 38 | 39 | 11. [John R. Levine / Linkers and Loaders / Morgan Kaufmann / 1999](https://book.douban.com/subject/1436811/) 40 | 41 | 12. [Stephen G. Kochan / Programming in C / Sams Publishing / 2004](https://book.douban.com/subject/2250163/) 42 | 43 | 13. [[美]瑞安 奥尼尔 / Linux二进制分析 / 人民邮电出版社 / 2017](https://book.douban.com/subject/27592738/) 44 | 45 | 14. [Paul A. Carter / PC Assembly Language / Amazon Digital Services LLC / 2016](https://book.douban.com/subject/26892163/) 46 | 47 | 15. [Robert Mecklenburg / Managing Projects with GNU Make / O'Reilly Media / 2004](https://book.douban.com/subject/1850994/) 48 | -------------------------------------------------------------------------------- /docs/others/问题及答案 (Question and Answer).md: -------------------------------------------------------------------------------- 1 | # 问题及答案 2 | 3 | ## gcc -m32 链接错误 4 | 5 | 添加 `-m32` 指定 32 位模式时,可能会发生 `/usr/bin/ld: skipping incompatible ... *.so` 的错误,这时可能需要安装 32 位动态链接库: 6 | 7 | sudo pacman -S lib32-gcc-libs 8 | 9 | ----- 10 | 11 | ## 无法调试汇编代码 12 | 13 | 根据大家反馈的描述,可能存在的问题: 14 | 15 | 1. vscode 调试配置 `launch.json` 中调试目录 `cwd` 配置的不对,请尝试配置到 `${workspaceFolder}/src`,或者与 `makefile` 位置相同; 16 | 17 | 2. `nasm` 版本不对,参考相关版本 `nasm >= 2.15.05`,主要问题是 `-g` 生成的 DWARF 信息版本不对; 18 | 19 | 3. `vscode` 设置需要勾选 **Debug:Allow Breakpoints Everywhere**; 20 | 21 | -------------------------------------------------------------------------------- /notes/01 系统引导/002 配置开发环境.md: -------------------------------------------------------------------------------- 1 | # 配置开发环境 2 | 3 | ## 视频地址 4 | 5 | 6 | 7 | ## 汇编插件 ASM Code Lens 8 | 9 | 这是一个常见的问题:视频中的汇编插件是:ASM Code Lens [^asm]; 10 | 11 | ## bochs 12 | 13 | bochs [^bochs] 由于流行度过低,应该是没人维护了,从 Community Repository 中移除,最后有人重新上传到了 AUR [^aur],导致所以无法使用 `pacman` 来安装包。 14 | 15 | 这种情况下,可以使用 makepkg 来安装,点击下面的链接,进入 bochs AUR 页面: 16 | 17 | 18 | 19 | 然后点击 Download Shapshot 下载包 `bochs.tar.gz` 到某目录; 20 | 21 | 然后执行 `tar -xvf bochs.tar.gz` 解包 22 | 23 | 然后进入 bochs 目录,执行如下命令,安装 bochs 24 | 25 | makepkg -si 26 | 27 | > 注:由于 bochs-gdb 本身就在 aur 中,视频 015 内容应该不受影响; 28 | 29 | 30 | ## 参考 31 | 32 | [^bochs]: 33 | [^aur]: 34 | [^asm]: 35 | -------------------------------------------------------------------------------- /notes/11 文件系统/081 哈希表和高速缓冲.md: -------------------------------------------------------------------------------- 1 | # 哈希表和高速缓冲 2 | 3 | ## 视频地址 4 | 5 | 6 | 7 | ## 相关问题 8 | 9 | - Hash 时取模一定要模质数吗? [^hash_question] 10 | 11 | ## 参考 12 | 13 | [^hash_question]: 14 | 15 | -------------------------------------------------------------------------------- /notes/README.md: -------------------------------------------------------------------------------- 1 | # 学习笔记 2 | 3 | 操作系统系列视频,是我的一个爱好,单纯就是觉得好玩,另外看起来还有点用,所以分享出来,希望能够帮助到更多的人,之后收到了很多不错的反馈,比如有人利用视频内容和面试官对线找到了工作,还有人想要做课程设计,或者毕业设计,用到了其中的代码,我觉得都挺好的,随便用。 4 | 5 | 一个重要的问题是:视频录制出来就不可修改了。它记录了我在录制视频那一刻的所有对与错,尽管我尽可能的避免错误,但是,总会有错误的,有一些重要的错误,我会在后面的视频更正。还有一些我没遇到的小错误。这些错误绝大多数在配置环境方面。 6 | 7 | > 如果你还没有看对应的视频,那么可能应该先看看对应的笔记; 8 | 9 | 如果你也遇到了问题,并且解决了,你可以将你的理解写到笔记里,提交 Pull Request;或者,应用程序的版本或者环境在未来的某个时刻发生了变化,导致原先的命令无法执行等,希望你能帮助大家,完善这些问题,也可以如笔记。大家一起让这个操作系统变得更好一点。 10 | -------------------------------------------------------------------------------- /onix.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } -------------------------------------------------------------------------------- /src/builtin/alarm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static int signal_handler(int sig) 6 | { 7 | printf("pid %d when %d signal %d happened \a\n", getpid(), time(), sig); 8 | signal(SIGALRM, (int)signal_handler); 9 | alarm(2); 10 | } 11 | 12 | int main(int argc, char const *argv[]) 13 | { 14 | signal(SIGALRM, (int)signal_handler); 15 | alarm(2); 16 | while (true) 17 | { 18 | printf("pid %d when %d sleeping 1 second\n", getpid(), time()); 19 | sleep(1000); 20 | } 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /src/builtin/cat.c: -------------------------------------------------------------------------------- 1 | #ifdef ONIX 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #else 9 | #include 10 | #include 11 | #include 12 | #endif 13 | 14 | #define BUFLEN 1024 15 | 16 | char buf[BUFLEN]; 17 | 18 | int main(int argc, char const *argv[]) 19 | { 20 | if (argc < 2) 21 | { 22 | return EOF; 23 | } 24 | 25 | int fd = open((char *)argv[1], O_RDONLY, 0); 26 | if (fd < EOK) 27 | { 28 | printf("file %s not exists.\n", argv[1]); 29 | return fd; 30 | } 31 | 32 | while (1) 33 | { 34 | int len = read(fd, buf, BUFLEN); 35 | if (len < 0) 36 | { 37 | break; 38 | } 39 | write(1, buf, len); 40 | } 41 | close(fd); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /src/builtin/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define BUFLEN 2048 9 | 10 | static char tx_buf[BUFLEN]; 11 | static char rx_buf[BUFLEN]; 12 | 13 | int main(int argc, char const *argv[]) 14 | { 15 | int fd = socket(AF_INET, SOCK_DGRAM, PROTO_UDP); 16 | 17 | int ret; 18 | sockaddr_in_t addr; 19 | inet_aton("0.0.0.0", addr.addr); 20 | addr.family = AF_INET; 21 | addr.port = htons(6666); 22 | 23 | ret = bind(fd, (sockaddr_t *)&addr, sizeof(sockaddr_in_t)); 24 | 25 | printf("socket bind %d\n", ret); 26 | 27 | inet_aton("192.168.111.11", addr.addr); 28 | addr.family = AF_INET; 29 | addr.port = htons(7777); 30 | 31 | ret = connect(fd, (sockaddr_t *)&addr, sizeof(sockaddr_in_t)); 32 | printf("socket connect %d\n", ret); 33 | 34 | u16 length = sprintf(tx_buf, "hello udp server %d\n", time()); 35 | ret = send(fd, tx_buf, length, 0); 36 | printf("socket send %d\n", ret); 37 | 38 | printf("receiving\n"); 39 | ret = recv(fd, rx_buf, sizeof(rx_buf), 0); 40 | printf("socket recv %d bytes\n", ret); 41 | rx_buf[ret] = 0; 42 | printf("msg: %s\n", rx_buf); 43 | 44 | close(fd); 45 | return EOK; 46 | } 47 | -------------------------------------------------------------------------------- /src/builtin/count.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char const *argv[]) 5 | { 6 | int counter = 1; 7 | while (counter) 8 | { 9 | printf("hello onix %d\a\n", counter++); 10 | sleep(1000); 11 | } 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /src/builtin/dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char const *argv[]) 7 | { 8 | char ch; 9 | while (true) 10 | { 11 | int n = read(STDIN_FILENO, &ch, 1); 12 | if (n < EOK) 13 | { 14 | break; 15 | } 16 | if (ch == '\n') 17 | { 18 | write(STDOUT_FILENO, &ch, 1); 19 | break; 20 | } 21 | write(STDOUT_FILENO, &ch, 1); 22 | write(STDOUT_FILENO, &ch, 1); 23 | } 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/builtin/echo.c: -------------------------------------------------------------------------------- 1 | #ifdef ONIX 2 | #include 3 | #include 4 | #include 5 | #include 6 | #else 7 | #include 8 | #include 9 | #endif 10 | 11 | int main(int argc, char const *argv[]) 12 | { 13 | for (size_t i = 1; i < argc; i++) 14 | { 15 | printf(argv[i]); 16 | if (i < argc - 1) 17 | { 18 | printf(" "); 19 | } 20 | } 21 | printf("\n"); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /src/builtin/env.c: -------------------------------------------------------------------------------- 1 | #ifdef ONIX 2 | #include 3 | #include 4 | #include 5 | #include 6 | #else 7 | #include 8 | #include 9 | #endif 10 | 11 | int main(int argc, char const *argv[], char const *envp[]) 12 | { 13 | for (size_t i = 0; i < argc; i++) 14 | { 15 | printf("%s\n", argv[i]); 16 | } 17 | 18 | for (size_t i = 0; 1; i++) 19 | { 20 | if (!envp[i]) 21 | break; 22 | int len = strlen(envp[i]); 23 | if (len >= 1022) 24 | continue; 25 | printf("%s\n", envp[i]); 26 | } 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /src/builtin/err.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | char *video = (char *)0xB8000; 6 | printf("char in 0x%X is %c\n", video, *video); 7 | *video = 'E'; 8 | printf("changed to E\n"); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /src/builtin/float.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char const *argv[]) 6 | { 7 | double x = 3.000325; 8 | printf("x = %f\n", x); 9 | printf("sin(%f) = %f\n", x, sin(x)); 10 | printf("cos(%f) = %f\n", x, cos(x)); 11 | printf("tan(%f) = %f\n", x, tan(x)); 12 | printf("sqrt(%f) = %f\n", x, sqrt(x)); 13 | printf("log2(%f) = %f\n", x, log2(x)); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/builtin/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | if (getpid() != 1) 8 | { 9 | printf("init already running...\n"); 10 | return 0; 11 | } 12 | 13 | while (true) 14 | { 15 | u32 status; 16 | pid_t pid = fork(); 17 | if (pid) 18 | { 19 | pid_t child = waitpid(pid, &status); 20 | printf("wait pid %d status %d %d\n", child, status, time()); 21 | } 22 | else 23 | { 24 | int err = execve("/bin/osh.out", NULL, NULL); 25 | printf("execve /bin/osh.out error %d\n", err); 26 | exit(err); 27 | } 28 | } 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /src/builtin/kill.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | if (argc < 2) 8 | { 9 | return -1; 10 | } 11 | return kill(atoi(argv[1]), SIGTERM); 12 | } -------------------------------------------------------------------------------- /src/builtin/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define BUFLEN 0x1000 9 | 10 | static char buf[BUFLEN]; 11 | 12 | int main(int argc, char const *argv[]) 13 | { 14 | int fd = socket(AF_INET, SOCK_RAW, PROTO_IP); 15 | 16 | msghdr_t msg; 17 | iovec_t iov; 18 | 19 | msg.name = NULL; 20 | msg.namelen = 0; 21 | 22 | msg.iov = &iov; 23 | msg.iovlen = 1; 24 | 25 | iov.base = buf; 26 | iov.size = sizeof(buf); 27 | 28 | printf("receiving...\n"); 29 | int ret = recvmsg(fd, &msg, 0); 30 | printf("recvmsg %d\n", ret); 31 | 32 | ip_t *ip = (ip_t *)iov.base; 33 | 34 | printf("recv ip %r -> %r : %s\n", ip->src, ip->dst, ip->payload); 35 | 36 | ip_addr_t addr; 37 | ip_addr_copy(addr, ip->dst); 38 | ip_addr_copy(ip->dst, ip->src); 39 | ip_addr_copy(ip->src, addr); 40 | 41 | memcpy(ip->payload, "this is ack message", 19); 42 | iov.size = sizeof(ip_t) + 19; 43 | 44 | sendmsg(fd, &msg, 0); 45 | 46 | close(fd); 47 | return EOK; 48 | } 49 | -------------------------------------------------------------------------------- /src/builtin/tcp_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define BUFLEN 2048 9 | 10 | static char tx_buf[BUFLEN]; 11 | static char rx_buf[BUFLEN]; 12 | 13 | int main(int argc, char const *argv[]) 14 | { 15 | sockaddr_in_t addr; 16 | 17 | int fd = socket(AF_INET, SOCK_STREAM, PROTO_TCP); 18 | if (fd < EOK) 19 | { 20 | printf("create client socket failure...\n"); 21 | return fd; 22 | } 23 | 24 | inet_aton("172.16.16.11", addr.addr); 25 | addr.family = AF_INET; 26 | addr.port = htons((u16)time()); 27 | 28 | int ret = bind(fd, (sockaddr_t *)&addr, sizeof(sockaddr_in_t)); 29 | printf("socket bind %d\n", ret); 30 | if (ret < EOK) 31 | goto rollback; 32 | 33 | int val = 1; 34 | ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, 4); 35 | printf("socket keepalive %d\n", ret); 36 | if (ret < EOK) 37 | goto rollback; 38 | 39 | ret = setsockopt(fd, SOL_SOCKET, SO_TCP_NODELAY, &val, 4); 40 | printf("socket nodelay %d\n", ret); 41 | if (ret < EOK) 42 | goto rollback; 43 | 44 | inet_aton("172.16.16.1", addr.addr); 45 | addr.family = AF_INET; 46 | addr.port = htons(7777); 47 | ret = connect(fd, (sockaddr_t *)&addr, sizeof(sockaddr_in_t)); 48 | printf("socket connect %d\n", ret); 49 | 50 | sleep(1000); 51 | 52 | int len = sprintf(tx_buf, "hello tcp server %d", time()); 53 | send(fd, tx_buf, len, 0); 54 | 55 | ret = recv(fd, rx_buf, BUFLEN, 0); 56 | if (ret > 0) 57 | { 58 | rx_buf[ret] = 0; 59 | printf("received %d bytes: %s.\n", ret, rx_buf); 60 | } 61 | 62 | rollback: 63 | if (fd > 0) 64 | close(fd); 65 | } 66 | -------------------------------------------------------------------------------- /src/builtin/tcp_nagle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define BUFLEN 2048 9 | 10 | static char tx_buf[BUFLEN]; 11 | static char rx_buf[BUFLEN]; 12 | 13 | int main(int argc, char const *argv[]) 14 | { 15 | sockaddr_in_t addr; 16 | 17 | int fd = socket(AF_INET, SOCK_STREAM, PROTO_TCP); 18 | if (fd < EOK) 19 | { 20 | printf("create client socket failure...\n"); 21 | return fd; 22 | } 23 | 24 | inet_aton("192.168.111.33", addr.addr); 25 | addr.family = AF_INET; 26 | addr.port = htons((u16)time()); 27 | 28 | int ret = bind(fd, (sockaddr_t *)&addr, sizeof(sockaddr_in_t)); 29 | printf("socket bind %d\n", ret); 30 | if (ret < EOK) 31 | goto rollback; 32 | 33 | int val = 1; 34 | ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, 4); 35 | printf("socket keepalive %d\n", ret); 36 | if (ret < EOK) 37 | goto rollback; 38 | 39 | val = 1; 40 | ret = setsockopt(fd, SOL_SOCKET, SO_TCP_NODELAY, &val, 4); 41 | printf("socket nodelay %d\n", ret); 42 | if (ret < EOK) 43 | goto rollback; 44 | 45 | inet_aton("192.168.111.1", addr.addr); 46 | addr.family = AF_INET; 47 | addr.port = htons(7777); 48 | ret = connect(fd, (sockaddr_t *)&addr, sizeof(sockaddr_in_t)); 49 | printf("socket connect %d\n", ret); 50 | 51 | // 测试 nagle 52 | int count = 0; 53 | while (true) 54 | { 55 | count++; 56 | tx_buf[0] = (time() % 26) + 'A'; 57 | ret = send(fd, tx_buf, 1, 0); 58 | printf("message sent %d/%d...\n", ret, count); 59 | if (ret < EOK) 60 | goto rollback; 61 | } 62 | 63 | rollback: 64 | if (fd > 0) 65 | close(fd); 66 | } 67 | -------------------------------------------------------------------------------- /src/builtin/tcp_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define BUFLEN 2048 9 | 10 | static char tx_buf[BUFLEN]; 11 | static char rx_buf[BUFLEN]; 12 | 13 | int main(int argc, char const *argv[]) 14 | { 15 | sockaddr_in_t addr; 16 | fd_t client = -1; 17 | fd_t server = -1; 18 | 19 | server = socket(AF_INET, SOCK_STREAM, PROTO_TCP); 20 | if (server < EOK) 21 | { 22 | printf("create server socket failure...\n"); 23 | return server; 24 | } 25 | 26 | inet_aton("0.0.0.0", addr.addr); 27 | addr.family = AF_INET; 28 | addr.port = htons(6666); 29 | 30 | int ret = bind(server, (sockaddr_t *)&addr, sizeof(sockaddr_in_t)); 31 | printf("socket bind %d\n", ret); 32 | if (ret < 0) 33 | goto rollback; 34 | 35 | ret = listen(server, 1); 36 | printf("socket listen %d\n", ret); 37 | if (ret < 0) 38 | goto rollback; 39 | 40 | printf("waiting for client...\n"); 41 | client = accept(server, (sockaddr_t *)&addr, 0); 42 | if (client < 0) 43 | { 44 | printf("accept failure...\n"); 45 | goto rollback; 46 | } 47 | printf("socket acccept %d\n", client); 48 | 49 | int opt=1; 50 | ret=setsockopt(client,SOL_SOCKET,SO_TCP_NODELAY,&opt,4); 51 | if (ret < 0) 52 | { 53 | printf("set nodelay error.\n"); 54 | goto rollback; 55 | } 56 | 57 | int len = sprintf(tx_buf, "hello tcp client %d", time()); 58 | send(client, tx_buf, len, 0); 59 | 60 | ret = recv(client, rx_buf, BUFLEN, 0); 61 | if (ret > 0) 62 | { 63 | rx_buf[ret] = 0; 64 | printf("received %d bytes: %s.\n", ret, rx_buf); 65 | } 66 | sleep(1000); 67 | 68 | rollback: 69 | if (client > 0) 70 | close(client); 71 | if (server > 0) 72 | close(server); 73 | } 74 | -------------------------------------------------------------------------------- /src/builtin/uname.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char const *argv[], char const *envp[]) 8 | { 9 | utsname_t name; 10 | int ret = uname(&name); 11 | if (ret < 0) 12 | { 13 | printf(strerror(ret)); 14 | return ret; 15 | } 16 | printf("%s_%s\n", name.sysname, name.version); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /src/include/onix/arena.h: -------------------------------------------------------------------------------- 1 | /* (C) Copyright 2022 Steven; 2 | * @author: Steven kangweibaby@163.com 3 | * @date: 2022-06-28 4 | */ 5 | 6 | #ifndef ONIX_ARENA_H 7 | #define ONIX_ARENA_H 8 | 9 | #include 10 | #include 11 | 12 | #define DESC_COUNT 7 13 | 14 | typedef list_node_t block_t; // 内存块 15 | 16 | // 内存描述符 17 | typedef struct arena_descriptor_t 18 | { 19 | u32 total_block; // 一页内存分成了多少块 20 | u32 block_size; // 块大小 21 | int page_count; // 空闲页数量 22 | list_t free_list; // 空闲列表 23 | } arena_descriptor_t; 24 | 25 | // 一页或多页内存 26 | typedef struct arena_t 27 | { 28 | arena_descriptor_t *desc; // 该 arena 的描述符 29 | u32 count; // 当前剩余多少块 或 页数 30 | u32 large; // 表示是不是超过 1024 字节 31 | u32 magic; // 魔数 32 | } arena_t; 33 | 34 | void *kmalloc(size_t size); 35 | void kfree(void *ptr); 36 | 37 | #endif -------------------------------------------------------------------------------- /src/include/onix/assert.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_ASSERT_H 2 | #define ONIX_ASSERT_H 3 | 4 | void assertion_failure(char *exp, char *file, char *base, int line); 5 | 6 | #define assert(exp) \ 7 | if (exp) \ 8 | ; \ 9 | else \ 10 | assertion_failure(#exp, __FILE__, __BASE_FILE__, __LINE__) 11 | 12 | void panic(const char *fmt, ...); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/include/onix/bitmap.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_BITMAP_H 2 | #define ONIX_BITMAP_H 3 | 4 | #include 5 | 6 | typedef struct bitmap_t 7 | { 8 | u8 *bits; // 位图缓冲区 9 | u32 length; // 位图缓冲区长度 10 | u32 offset; // 位图开始的偏移 11 | } bitmap_t; 12 | 13 | // 初始化位图 14 | void bitmap_init(bitmap_t *map, char *bits, u32 length, u32 offset); 15 | 16 | // 构造位图 17 | void bitmap_make(bitmap_t *map, char *bits, u32 length, u32 offset); 18 | 19 | // 测试位图的某一位是否为 1 20 | bool bitmap_test(bitmap_t *map, u32 index); 21 | 22 | // 设置位图某位的值 23 | void bitmap_set(bitmap_t *map, u32 index, bool value); 24 | 25 | // 从位图中得到连续的 count 位 26 | int bitmap_scan(bitmap_t *map, u32 count); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/include/onix/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_BUFFER_H 2 | #define ONIX_BUFFER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define HASH_COUNT 31 // 应该是个素数 9 | #define MAX_BUF_COUNT 4096 // 最大缓冲数量 10 | 11 | typedef struct bdesc_t 12 | { 13 | u32 count; // 缓存数量 14 | u32 size; // 缓存大小 15 | 16 | list_t free_list; // 已经申请未使用的块 17 | list_t idle_list; // 缓存链表,被释放的块 18 | list_t wait_list; // 等待进程链表 19 | list_t hash_table[HASH_COUNT]; // 缓存哈希表 20 | } bdesc_t; 21 | 22 | typedef struct buffer_t 23 | { 24 | char *data; // 数据区 25 | bdesc_t *desc; // 描述符指针 26 | dev_t dev; // 设备号 27 | idx_t block; // 块号 28 | int count; // 引用计数 29 | list_node_t hnode; // 哈希表拉链节点 30 | list_node_t rnode; // 缓冲节点 31 | lock_t lock; // 锁 32 | bool dirty; // 是否与磁盘不一致 33 | bool valid; // 是否有效 34 | } buffer_t; 35 | 36 | buffer_t *bread(dev_t dev, idx_t block, size_t size); 37 | err_t bwrite(buffer_t *buf); 38 | err_t brelse(buffer_t *buf); 39 | err_t bdirty(buffer_t *buf, bool dirty); 40 | 41 | #endif -------------------------------------------------------------------------------- /src/include/onix/console.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_CONSOLE_H 2 | #define ONIX_CONSOLE_H 3 | 4 | #include 5 | 6 | void console_init(); 7 | void console_clear(); 8 | 9 | #endif -------------------------------------------------------------------------------- /src/include/onix/debug.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ONIX_DEBUG_H 3 | #define ONIX_DEBUG_H 4 | 5 | void debugk(char *file, int line, const char *fmt, ...); 6 | 7 | #define BMB asm volatile("xchgw %bx, %bx") // bochs magic breakpoint 8 | #define DEBUGK(fmt, args...) debugk(__BASE_FILE__, __LINE__, fmt, ##args) 9 | 10 | // #define LOGK(fmt, args...) DEBUGK(fmt, ##args) 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/include/onix/fifo.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_FIFO_H 2 | #define ONIX_FIFO_H 3 | 4 | #include 5 | 6 | typedef struct fifo_t 7 | { 8 | char *buf; 9 | u32 length; 10 | u32 head; 11 | u32 tail; 12 | } fifo_t; 13 | 14 | void fifo_init(fifo_t *fifo, char *buf, u32 length); 15 | bool fifo_full(fifo_t *fifo); 16 | bool fifo_empty(fifo_t *fifo); 17 | char fifo_get(fifo_t *fifo); 18 | void fifo_put(fifo_t *fifo, char byte); 19 | 20 | #endif -------------------------------------------------------------------------------- /src/include/onix/fpu.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_FPU_H 2 | #define ONIX_FPU_H 3 | 4 | #include 5 | #include 6 | 7 | enum 8 | { 9 | CR0_PE = 1 << 0, // Protection Enable 启用保护模式 10 | CR0_MP = 1 << 1, // Monitor Coprocessor 11 | CR0_EM = 1 << 2, // Emulation 启用模拟,表示没有 FPU 12 | CR0_TS = 1 << 3, // Task Switch 任务切换,延迟保存浮点环境 13 | CR0_ET = 1 << 4, // Extension Type 保留 14 | CR0_NE = 1 << 5, // Numeric Error 启用内部浮点错误报告 15 | CR0_WP = 1 << 16, // Write Protect 写保护(禁止超级用户写入只读页)帮助写时复制 16 | CR0_AM = 1 << 18, // Alignment Mask 对齐掩码 17 | CR0_NW = 1 << 29, // Not Write-Through 不是直写 18 | CR0_CD = 1 << 30, // Cache Disable 禁用内存缓冲 19 | CR0_PG = 1 << 31, // Paging 启用分页 20 | }; 21 | 22 | // Intel® 64 and IA-32 Architectures Software Developer's Manual 23 | // Figure 8-9. Protected Mode x87 FPU State Image in Memory, 32-Bit Format 24 | 25 | typedef struct fpu_t 26 | { 27 | u16 control; 28 | u16 RESERVED; 29 | u16 status; 30 | u16 RESERVED; 31 | u16 tag; 32 | u16 RESERVED; 33 | u32 fip0; 34 | u32 fop0; 35 | u32 fdp0; 36 | u32 fdp1; 37 | u8 regs[80]; 38 | } _packed fpu_t; 39 | 40 | bool fpu_check(); 41 | void fpu_disable(task_t *task); 42 | void fpu_enable(task_t *task); 43 | 44 | #endif -------------------------------------------------------------------------------- /src/include/onix/io.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_IO_H 2 | #define ONIX_IO_H 3 | 4 | #include 5 | 6 | extern u8 inb(u16 port); // 输入一个字节 7 | extern u16 inw(u16 port); // 输入一个字 8 | extern u32 inl(u16 port); // 输入一个双字 9 | 10 | extern void outb(u16 port, u8 value); // 输出一个字节 11 | extern void outw(u16 port, u16 value); // 输出一个字 12 | extern void outl(u16 port, u32 value); // 输出一个双字 13 | 14 | #endif -------------------------------------------------------------------------------- /src/include/onix/isa.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_ISA_H 2 | #define ONIX_ISA_H 3 | 4 | #include 5 | 6 | #define DMA_MODE_CHECK 0x00 // 自检模式 7 | #define DMA_MODE_READ 0x04 // 外部设备读出(写内存) 8 | #define DMA_MODE_WRITE 0x08 // 写入外部设备(读内存) 9 | #define DMA_MODE_AUTO 0x10 // 自动模式 10 | #define DMA_MODE_DOWN 0x20 // 从高地址向低地址访问内存 11 | #define DMA_MODE_DEMAND 0x00 // 按需传输; 12 | #define DMA_MODE_SINGLE 0x40 // 单次 DMA 传输; 13 | #define DMA_MODE_BLOCK 0x80 // 块 DMA 传输; 14 | #define DMA_MODE_CASCADE 0xC0 // 级联模式(用于级联另一个 DMA 控制器); 15 | 16 | void isa_dma_mask(u8 channel, bool mask); 17 | void isa_dma_addr(u8 channel, void *addr); 18 | void isa_dma_size(u8 channel, u32 size); 19 | void isa_dma_reset(u8 channel); 20 | void isa_dma_mode(u8 channel, u8 mode); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/include/onix/list.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_LIST_H 2 | #define ONIX_LIST_H 3 | 4 | #include 5 | 6 | #define element_offset(type, member) (u32)(&((type *)0)->member) 7 | #define element_entry(type, member, ptr) (type *)((u32)ptr - element_offset(type, member)) 8 | #define element_node_offset(type, node, key) ((int)(&((type *)0)->key) - (int)(&((type *)0)->node)) 9 | #define element_node_key(node, offset) *(int *)((int)node + offset) 10 | 11 | // 链表结点 12 | typedef struct list_node_t 13 | { 14 | struct list_node_t *prev; // 下一个结点 15 | struct list_node_t *next; // 前一个结点 16 | } list_node_t; 17 | 18 | // 链表 19 | typedef struct list_t 20 | { 21 | list_node_t head; // 头结点 22 | list_node_t tail; // 尾结点 23 | } list_t; 24 | 25 | // 初始化链表 26 | void list_init(list_t *list); 27 | 28 | // 在 anchor 结点前插入结点 node 29 | void list_insert_before(list_node_t *anchor, list_node_t *node); 30 | 31 | // 在 anchor 结点后插入结点 node 32 | void list_insert_after(list_node_t *anchor, list_node_t *node); 33 | 34 | // 插入到头结点后 35 | void list_push(list_t *list, list_node_t *node); 36 | 37 | // 移除头结点后的结点 38 | list_node_t *list_pop(list_t *list); 39 | 40 | // 插入到尾结点前 41 | void list_pushback(list_t *list, list_node_t *node); 42 | 43 | // 移除尾结点前的结点 44 | list_node_t *list_popback(list_t *list); 45 | 46 | // 查找链表中结点是否存在 47 | bool list_search(list_t *list, list_node_t *node); 48 | 49 | // 从链表中删除结点 50 | void list_remove(list_node_t *node); 51 | 52 | // 判断链表是否为空 53 | bool list_empty(list_t *list); 54 | 55 | // 获得链表长度 56 | u32 list_size(list_t *list); 57 | 58 | // 链表插入排序 59 | void list_insert_sort(list_t *list, list_node_t *node, int offset); 60 | 61 | #endif -------------------------------------------------------------------------------- /src/include/onix/math.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_MATH_H 2 | #define ONIX_MATH_H 3 | 4 | double sin(double x); 5 | double cos(double x); 6 | double tan(double x); 7 | double sqrt(double x); 8 | double log2(double x); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/include/onix/mio.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_MIO_H 2 | #define ONIX_MIO_H 3 | 4 | #include 5 | 6 | // 映射内存 IO 7 | 8 | // 内存输入字节 8bit 9 | u8 minb(u32 addr); 10 | // 内存输入字 16bit 11 | u16 minw(u32 addr); 12 | // 内存输入双字 32bit 13 | u32 minl(u32 addr); 14 | 15 | 16 | // 内存输出字节 17 | void moutb(u32 addr, u8 value); 18 | // 内存输出字 19 | void moutw(u32 addr, u16 value); 20 | // 内存输出双字 21 | void moutl(u32 addr, u32 value); 22 | 23 | #endif -------------------------------------------------------------------------------- /src/include/onix/multiboot2.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_MULTIBOOT2 2 | #define ONIX_MULTIBOOT2 3 | 4 | #include 5 | 6 | // 进入内核时 eax 寄存器的值 7 | #define MULTIBOOT2_MAGIC 0x36d76289 8 | 9 | #define MULTIBOOT_TAG_TYPE_END 0 10 | #define MULTIBOOT_TAG_TYPE_MMAP 6 11 | 12 | #define MULTIBOOT_MEMORY_AVAILABLE 1 13 | #define MULTIBOOT_MEMORY_RESERVED 2 14 | #define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 15 | #define MULTIBOOT_MEMORY_NVS 4 16 | #define MULTIBOOT_MEMORY_BADRAM 5 17 | 18 | // multiboot tag 19 | typedef struct multi_tag_t 20 | { 21 | u32 type; 22 | u32 size; 23 | } multi_tag_t; 24 | 25 | // multiboot mmap entry 26 | typedef struct multi_mmap_entry_t 27 | { 28 | u64 addr; 29 | u64 len; 30 | u32 type; 31 | u32 zero; 32 | } multi_mmap_entry_t; 33 | 34 | // multiboot mmap tag 35 | typedef struct multi_tag_mmap_t 36 | { 37 | u32 type; 38 | u32 size; 39 | u32 entry_size; 40 | u32 entry_version; 41 | multi_mmap_entry_t entries[0]; 42 | } multi_tag_mmap_t; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/include/onix/mutex.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_MUTEX_H 2 | #define ONIX_MUTEX_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct mutex_t 8 | { 9 | bool value; // 信号量 10 | list_t waiters; // 等待队列 11 | } mutex_t; 12 | 13 | void mutex_init(mutex_t *mutex); // 初始化互斥量 14 | void mutex_lock(mutex_t *mutex); // 尝试持有互斥量 15 | void mutex_unlock(mutex_t *mutex); // 释放互斥量 16 | 17 | typedef struct lock_t 18 | { 19 | struct task_t *holder; // 持有者 20 | mutex_t mutex; // 互斥量 21 | u32 repeat; // 重入次数 22 | } lock_t; 23 | 24 | void lock_init(lock_t *lock); // 锁初始化 25 | void lock_acquire(lock_t *lock); // 加锁 26 | void lock_release(lock_t *lock); // 解锁 27 | 28 | #endif -------------------------------------------------------------------------------- /src/include/onix/net.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_H 2 | #define ONIX_NET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #endif -------------------------------------------------------------------------------- /src/include/onix/net/addr.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_ADDR_H 2 | #define ONIX_NET_ADDR_H 3 | 4 | #include 5 | 6 | // MAC 地址拷贝 7 | void eth_addr_copy(eth_addr_t dst, eth_addr_t src); 8 | 9 | // 判断地址是否全为 0 10 | bool eth_addr_isany(eth_addr_t addr); 11 | 12 | // 比较两 mac 地址是否相等 13 | bool eth_addr_cmp(eth_addr_t addr1, eth_addr_t addr2); 14 | 15 | // IP 地址拷贝 16 | void ip_addr_copy(ip_addr_t dst, ip_addr_t src); 17 | 18 | // 字符串转换 IP 地址 19 | err_t inet_aton(const char *str, ip_addr_t addr); 20 | 21 | // 比较两 ip 地址是否相等 22 | bool ip_addr_cmp(ip_addr_t addr1, ip_addr_t addr2); 23 | 24 | // 比较两地址是否在同一子网 25 | bool ip_addr_maskcmp(ip_addr_t addr1, ip_addr_t addr2, ip_addr_t mask); 26 | 27 | // 判断地址是否是广播地址 28 | bool ip_addr_isbroadcast(ip_addr_t addr, ip_addr_t mask); 29 | 30 | // 判断地址是否全为 0 31 | bool ip_addr_isany(ip_addr_t addr); 32 | 33 | // 判断地址是否为多播地址 34 | bool ip_addr_ismulticast(ip_addr_t addr); 35 | 36 | // 判断 IP 地址是不是自己 37 | bool ip_addr_isown(ip_addr_t addr); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/include/onix/net/arp.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_ARP_H 2 | #define ONIX_NET_ARP_H 3 | 4 | #include 5 | #include 6 | 7 | enum 8 | { 9 | ARP_OP_REQUEST = 1, // ARP 请求 10 | ARP_OP_REPLY = 2, // ARP 回复 11 | }; 12 | 13 | #define ARP_HARDWARE_ETH 1 // ARP 以太网 14 | #define ARP_HARDWARE_ETH_LEN 6 // ARP 以太网地址长度 15 | 16 | #define ARP_PROTOCOL_IP 0x0800 // ARP 协议 IP 17 | #define ARP_PROTOCOL_IP_LEN 4 // ARP IP 地址长度 4 18 | 19 | #define ARP_ENTRY_TIMEOUT 600 // ARP 缓冲失效时间秒 20 | #define ARP_RETRY 5 // ARP 请求重试次数 21 | #define ARP_DELAY 2 // ARP 请求延迟秒 22 | #define ARP_REFRESH_DELAY 1000 // ARP 刷新间隔毫秒 23 | 24 | typedef struct arp_t 25 | { 26 | u16 hwtype; // 硬件类型 27 | u16 proto; // 协议类型 28 | u8 hwlen; // 硬件地址长度 29 | u8 protolen; // 协议地址长度 30 | u16 opcode; // 操作类型 31 | eth_addr_t hwsrc; // 源 MAC 地址 32 | ip_addr_t ipsrc; // 源 IP 地址 33 | eth_addr_t hwdst; // 目的 MAC 地址 34 | ip_addr_t ipdst; // 目的 IP 地址 35 | } _packed arp_t; 36 | 37 | err_t arp_input(netif_t *netif, pbuf_t *pbuf); 38 | err_t arp_eth_output(netif_t *netif, pbuf_t *pbuf, ip_addr_t addr, u16 type, u32 len); 39 | 40 | #endif -------------------------------------------------------------------------------- /src/include/onix/net/chksum.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_CHKSUM_H 2 | #define ONIX_NET_CHKSUM_H 3 | 4 | #include 5 | 6 | u32 eth_fcs(void *data, int len); 7 | u16 ip_chksum(void *data, int len); 8 | u16 inet_chksum(void *data, u16 len, ip_addr_t dst, ip_addr_t src, u16 proto); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/include/onix/net/dhcp.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_DHCP_H 2 | #define ONIX_NET_DHCP_H 3 | 4 | #include 5 | 6 | void dhcp_start(netif_t *netif); 7 | void dhcp_stop(netif_t *netif); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/include/onix/net/eth.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_ETH_H 2 | #define ONIX_NET_ETH_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define ETH_FCS_LEN 4 9 | 10 | #define ETH_BROADCAST "\xff\xff\xff\xff\xff\xff" 11 | #define ETH_ANY "\x00\x00\x00\x00\x00\x00" 12 | 13 | enum 14 | { 15 | ETH_TYPE_IP = 0x0800, // IPV4 协议 16 | ETH_TYPE_ARP = 0x0806, // ARP 协议 17 | ETH_TYPE_IPV6 = 0x86DD, // IPV6 协议 18 | }; 19 | 20 | // 以太网帧 21 | typedef struct eth_t 22 | { 23 | eth_addr_t dst; // 目标地址 24 | eth_addr_t src; // 源地址 25 | u16 type; // 类型 26 | union 27 | { 28 | u8 payload[0]; // 载荷 29 | arp_t arp[0]; // arp 包 30 | ip_t ip[0]; // ip 包 31 | }; 32 | 33 | } _packed eth_t; 34 | 35 | err_t eth_input(netif_t *netif, pbuf_t *pbuf); 36 | err_t eth_output(netif_t *netif, pbuf_t *pbuf, eth_addr_t dst, u16 type, u32 len); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/include/onix/net/icmp.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_ICMP_H 2 | #define ONIX_NET_ICMP_H 3 | 4 | #include 5 | 6 | enum 7 | { 8 | ICMP_ER = 0, // Echo reply 9 | ICMP_DUR = 3, // Destination unreachable 10 | ICMP_SQ = 4, // Source quench 11 | ICMP_RD = 5, // Redirect 12 | ICMP_ECHO = 8, // Echo 13 | ICMP_TE = 11, // Time exceeded 14 | ICMP_PP = 12, // Parameter problem 15 | ICMP_TS = 13, // Timestamp 16 | ICMP_TSR = 14, // Timestamp reply 17 | ICMP_IRQ = 15, // Information request 18 | ICMP_IR = 16, // Information reply 19 | }; 20 | 21 | typedef struct icmp_echo_t 22 | { 23 | u8 type; // 类型 24 | u8 code; // 状态码 25 | u16 chksum; // 校验和 26 | u16 id; // 标识 27 | u16 seq; // 序号 28 | u8 payload[0]; // 可能是特殊的字符串 29 | } _packed icmp_echo_t; 30 | 31 | typedef struct icmp_t 32 | { 33 | u8 type; // 类型 34 | u8 code; // 状态码 35 | u16 chksum; // 校验和 36 | u32 RESERVED; // 保留 37 | u8 payload[0]; // 载荷 38 | } _packed icmp_t; 39 | 40 | err_t icmp_input(netif_t *netif, pbuf_t *pbuf); 41 | err_t icmp_echo(netif_t *netif, pbuf_t *pbuf, ip_addr_t dst); 42 | 43 | #endif -------------------------------------------------------------------------------- /src/include/onix/net/ip.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_IP_H 2 | #define ONIX_NET_IP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define IP_VERSION_4 4 11 | #define IP_TTL 64 12 | 13 | #define IP_ANY "\x00\x00\x00\x00" 14 | #define IP_BROADCAST "\xFF\xFF\xFF\xFF" 15 | 16 | enum 17 | { 18 | IP_PROTOCOL_NONE = 0, 19 | IP_PROTOCOL_ICMP = 1, 20 | IP_PROTOCOL_TCP = 6, 21 | IP_PROTOCOL_UDP = 17, 22 | IP_PROTOCOL_TEST = 254, 23 | }; 24 | 25 | #define IP_FLAG_NOFRAG 0b10 26 | #define IP_FLAG_NOLAST 0b100 27 | 28 | typedef struct ip_t 29 | { 30 | u8 header : 4; // 头长度 31 | u8 version : 4; // 版本号 32 | u8 tos; // type of service 服务类型 33 | u16 length; // 数据包长度 34 | 35 | // 以下用于分片 36 | u16 id; // 标识,每发送一个分片该值加 1 37 | u8 offset0 : 5; // 分片偏移量高 5 位,以 8字节 为单位 38 | u8 flags : 3; // 标志位,1:保留,2:不分片,4:不是最后一个分片 39 | u8 offset1; // 分片偏移量低 8 位,以 8字节 为单位 40 | 41 | u8 ttl; // 生存时间 Time To Live,每经过一个路由器该值减一,到 0 则丢弃 42 | u8 proto; // 上层协议,1:ICMP 6:TCP 17:UDP 43 | u16 chksum; // 校验和 44 | ip_addr_t src; // 源 IP 地址 45 | ip_addr_t dst; // 目的 IP 地址 46 | 47 | union 48 | { 49 | u8 payload[0]; // 载荷 50 | icmp_t icmp[0]; // ICMP 协议 51 | icmp_echo_t echo[0]; // ICMP ECHO 协议 52 | udp_t udp[0]; // UDP 协议 53 | tcp_t tcp[0]; // TCP 协议 54 | }; 55 | 56 | } _packed ip_t; 57 | 58 | err_t ip_input(netif_t *netif, pbuf_t *pbuf); 59 | err_t ip_output(netif_t *netif, pbuf_t *pbuf, ip_addr_t dst, u8 proto, u16 len); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/include/onix/net/pbuf.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_PBUF_H 2 | #define ONIX_NET_PBUF_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct pbuf_t 9 | { 10 | list_node_t node; // 列表节点 11 | size_t length; // 载荷长度 12 | u32 count; // 引用计数 13 | 14 | list_node_t tcpnode; // TCP 缓冲节点 15 | u8 *data; // TCP 数据指针 16 | size_t total; // TCP 总长度 头 + 数据长度 17 | size_t size; // TCP 数据长度 18 | u32 seqno; // TCP 序列号 19 | 20 | union 21 | { 22 | u8 payload[0]; // 载荷 23 | eth_t eth[0]; // 以太网帧 24 | }; 25 | } pbuf_t; 26 | 27 | pbuf_t *pbuf_get(); 28 | void pbuf_put(pbuf_t *pbuf); 29 | 30 | #endif -------------------------------------------------------------------------------- /src/include/onix/net/pkt.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_PKT_H 2 | #define ONIX_NET_PKT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct pkt_pcb_t 9 | { 10 | list_node_t node; 11 | 12 | eth_addr_t laddr; 13 | eth_addr_t raddr; 14 | netif_t *netif; 15 | 16 | int protocol; 17 | 18 | list_t rx_pbuf_list; 19 | struct task_t *rx_waiter; 20 | } pkt_pcb_t; 21 | 22 | err_t pkt_input(netif_t *netif, pbuf_t *pbuf); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/include/onix/net/port.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_PORT_H 2 | #define ONIX_NET_PORT_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct port_map_t 8 | { 9 | bitmap_t map; 10 | u32 buf; 11 | } port_map_t; 12 | 13 | // 获取端口号 14 | err_t port_get(port_map_t *map, u16 port); 15 | 16 | // 释放端口号 17 | void port_put(port_map_t *map, u16 port); 18 | 19 | // 初始化端口号位图 20 | void port_init(port_map_t *map); 21 | 22 | #endif -------------------------------------------------------------------------------- /src/include/onix/net/raw.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef ONIX_NET_RAW_H 4 | #define ONIX_NET_RAW_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define RAW_TTL 255 11 | 12 | typedef struct raw_pcb_t 13 | { 14 | list_node_t node; 15 | ip_addr_t laddr; 16 | ip_addr_t raddr; 17 | u16 protocol; 18 | 19 | list_t rx_pbuf_list; 20 | struct task_t *rx_waiter; 21 | } raw_pcb_t; 22 | 23 | err_t raw_input(netif_t *netif, pbuf_t *pbuf); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/include/onix/net/types.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_TYPES_H 2 | #define ONIX_NET_TYPES_H 3 | 4 | #include 5 | 6 | // 主机字节序到网络字节序 7 | #define htonl(l) ((((l)&0xFF) << 24) | (((l)&0xFF00) << 8) | (((l)&0xFF0000) >> 8) | (((l)&0xFF000000) >> 24)) 8 | #define htons(s) ((((s)&0xFF) << 8) | (((s)&0xFF00) >> 8)) 9 | 10 | // 网络字节序到主机字节序 11 | #define ntohl(l) htonl((l)) 12 | #define ntohs(s) htons((s)) 13 | 14 | #define NET_TIMEOUT 5000 15 | 16 | #define ETH_MTU 1518 17 | #define IP_MTU 1500 18 | 19 | #define ETH_ADDR_LEN 6 20 | #define IP_ADDR_LEN 4 21 | 22 | typedef u8 eth_addr_t[ETH_ADDR_LEN]; // MAC 地址 23 | typedef u8 ip_addr_t[IP_ADDR_LEN]; // IPV4 地址 24 | 25 | typedef struct netif_t netif_t; 26 | typedef struct pbuf_t pbuf_t; 27 | 28 | #endif -------------------------------------------------------------------------------- /src/include/onix/net/udp.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_NET_UDP_H 2 | #define ONIX_NET_UDP_H 3 | 4 | #include 5 | #include 6 | 7 | enum 8 | { 9 | UDP_FLAG_NOCHKSUM = 0x01, 10 | UDP_FLAG_BROADCAST = 0x02, 11 | UDP_FLAG_CONNECTED = 0x04 12 | }; 13 | 14 | typedef struct udp_pcb_t udp_pcb_t; 15 | 16 | typedef struct udp_t 17 | { 18 | u16 sport; // 源端口号 19 | u16 dport; // 目的端口号 20 | u16 length; // 长度 21 | u16 chksum; // 校验和 22 | u8 payload[0]; // 载荷 23 | } _packed udp_t; 24 | 25 | typedef struct udp_pcb_t 26 | { 27 | list_node_t node; // 链表结点 28 | 29 | ip_addr_t laddr; // 本地地址 30 | ip_addr_t raddr; // 远程地址 31 | u16 lport; // 本地端口号 32 | u16 rport; // 远程端口号 33 | 34 | u32 flags; // 状态 35 | 36 | list_t rx_pbuf_list; // 接收缓冲队列 37 | struct task_t *rx_waiter; // 等待进程 38 | } udp_pcb_t; 39 | 40 | int udp_input(netif_t *netif, pbuf_t *pbuf); 41 | 42 | #endif -------------------------------------------------------------------------------- /src/include/onix/onix.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_H 2 | #define ONIX_H 3 | 4 | // 内核魔数,用于校验错误 5 | #define ONIX_MAGIC 0x20220205 6 | 7 | #ifndef ONIX_VERSION 8 | #define ONIX_VERSION "1.0.0" 9 | #endif 10 | 11 | #endif -------------------------------------------------------------------------------- /src/include/onix/printk.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_PRINTK_H 2 | #define ONIX_PRINTK_H 3 | 4 | int printk(const char *fmt, ...); 5 | 6 | #endif -------------------------------------------------------------------------------- /src/include/onix/rtc.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_RTC_H 2 | #define ONIX_RTC_H 3 | 4 | void set_alarm(u32 secs); 5 | u8 cmos_read(u8 addr); 6 | void cmos_write(u8 addr, u8 value); 7 | 8 | #endif -------------------------------------------------------------------------------- /src/include/onix/sb16.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_SB16_H 2 | #define ONIX_SB16_H 3 | 4 | typedef enum sb16_cmd_t 5 | { 6 | SB16_CMD_ON = 1, // 声卡开启 7 | SB16_CMD_OFF, // 声卡关闭 8 | SB16_CMD_MONO8, // 八位单声道模式 9 | SB16_CMD_STEREO16, // 16位立体声模式 10 | SB16_CMD_VOLUME, // 设置音量 11 | } sb16_cmd_t; 12 | 13 | #endif -------------------------------------------------------------------------------- /src/include/onix/signal.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_SIGNAL_H 2 | #define ONIX_SIGNAL_H 3 | 4 | #include 5 | 6 | enum SIGNAL 7 | { 8 | SIGHUP = 1, // 挂断控制终端或进程 9 | SIGINT, // 来自键盘的中断 10 | SIGQUIT, // 来自键盘的退出 11 | SIGILL, // 非法指令 12 | SIGTRAP, // 跟踪断点 13 | SIGABRT, // 异常结束 14 | SIGIOT = 6, // 异常结束 15 | SIGUNUSED, // 没有使用 16 | SIGFPE, // 协处理器出错 17 | SIGKILL = 9, // 强迫进程终止 18 | SIGUSR1, // 用户信号 1,进程可使用 19 | SIGSEGV, // 无效内存引用 20 | SIGUSR2, // 用户信号 2,进程可使用 21 | SIGPIPE, // 管道写出错,无读者 22 | SIGALRM, // 实时定时器报警 23 | SIGTERM = 15, // 进程终止 24 | SIGSTKFLT, // 栈出错(协处理器) 25 | SIGCHLD, // 子进程停止或被终止 26 | SIGCONT, // 恢复进程继续执行 27 | SIGSTOP, // 停止进程的执行 28 | SIGTSTP, // tty 发出停止进程,可忽略 29 | SIGTTIN, // 后台进程请求输入 30 | SIGTTOU = 22, // 后台进程请求输出 31 | }; 32 | 33 | #define MINSIG 1 34 | #define MAXSIG 31 35 | 36 | #define SIGMASK(sig) (1 << (sig - 1)) 37 | 38 | // 不阻止在指定的信号处理程序中再收到该信号 39 | #define SIG_NOMASK 0x40000000 40 | 41 | // 信号句柄一旦被调用过就恢复到默认处理句柄 42 | #define SIG_ONESHOT 0x80000000 43 | 44 | #define SIG_DFL ((void (*)(int))0) // 默认的信号处理程序(信号句柄) 45 | #define SIG_IGN ((void (*)(int))1) // 忽略信号的处理程序 46 | 47 | typedef u32 sigset_t; 48 | 49 | // 信号处理结构 50 | typedef struct sigaction_t 51 | { 52 | void (*handler)(int); // 信号处理函数 53 | sigset_t mask; // 信号屏蔽码 54 | u32 flags; 55 | void (*restorer)(void); // 恢复函数指针 56 | } sigaction_t; 57 | 58 | // 获取信号屏蔽码 59 | int sgetmask(); 60 | // 设置信号屏蔽码 61 | int ssetmask(int newmask); 62 | // 注册信号处理函数 63 | int signal(int sig, int handler); 64 | // 注册信号处理函数,并且返回旧信号处理函数 65 | int sigaction(int sig, sigaction_t *action, sigaction_t *oldaction); 66 | 67 | #endif -------------------------------------------------------------------------------- /src/include/onix/stdarg.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_STDARG_H 2 | #define ONIX_STDARG_H 3 | 4 | typedef char *va_list; 5 | 6 | #define stack_size(t) (sizeof(t) <= sizeof(char *) ? sizeof(char *) : sizeof(t)) 7 | #define va_start(ap, v) (ap = (va_list)&v + sizeof(char *)) 8 | #define va_arg(ap, t) (*(t *)((ap += stack_size(t)) - stack_size(t))) 9 | #define va_end(ap) (ap = (va_list)0) 10 | 11 | #endif -------------------------------------------------------------------------------- /src/include/onix/stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_STDIO_H 2 | #define ONIX_STDIO_H 3 | 4 | #include 5 | 6 | int vsprintf(char *buf, const char *fmt, va_list args); 7 | int sprintf(char *buf, const char *fmt, ...); 8 | int printf(const char *fmt, ...); 9 | 10 | #endif -------------------------------------------------------------------------------- /src/include/onix/stdlib.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_STDLIB_H 2 | #define ONIX_STDLIB_H 3 | 4 | #include 5 | 6 | #define MAX(a, b) (a < b ? b : a) 7 | #define MIN(a, b) (a < b ? a : b) 8 | #define ABS(a) (a < 0 ? -a : a) 9 | 10 | void delay(u32 count); 11 | void hang(); 12 | 13 | char toupper(char ch); 14 | char tolower(char ch); 15 | 16 | u8 bcd_to_bin(u8 value); 17 | u8 bin_to_bcd(u8 value); 18 | 19 | u32 div_round_up(u32 num, u32 size); 20 | 21 | bool isdigit(int c); 22 | 23 | int atoi(const char *str); 24 | 25 | #endif -------------------------------------------------------------------------------- /src/include/onix/string.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_STRING_H 2 | #define ONIX_STRING_H 3 | 4 | #include 5 | 6 | char *strcpy(char *dest, const char *src); 7 | char *strncpy(char *dest, const char *src, size_t count); 8 | char *strcat(char *dest, const char *src); 9 | size_t strlen(const char *str); 10 | size_t strnlen(const char *str, size_t maxlen); 11 | int strcmp(const char *lhs, const char *rhs); 12 | char *strchr(const char *str, int ch); 13 | char *strrchr(const char *str, int ch); 14 | char *strsep(const char *str); 15 | char *strrsep(const char *str); 16 | 17 | int memcmp(const void *lhs, const void *rhs, size_t count); 18 | void *memset(void *dest, int ch, size_t count); 19 | void *memcpy(void *dest, const void *src, size_t count); 20 | void *memchr(const void *ptr, int ch, size_t count); 21 | 22 | #endif -------------------------------------------------------------------------------- /src/include/onix/time.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_TIME_H 2 | #define ONIX_TIME_H 3 | 4 | #include 5 | 6 | typedef struct tm 7 | { 8 | int tm_sec; // 秒数 [0,59] 9 | int tm_min; // 分钟数 [0,59] 10 | int tm_hour; // 小时数 [0,59] 11 | int tm_mday; // 1 个月的天数 [0,31] 12 | int tm_mon; // 1 年中月份 [0,11] 13 | int tm_year; // 从 1900 年开始的年数 14 | int tm_wday; // 1 星期中的某天 [0,6] (星期天 =0) 15 | int tm_yday; // 1 年中的某天 [0,365] 16 | int tm_isdst; // 夏令时标志 17 | } tm; 18 | 19 | void time_read_bcd(tm *time); 20 | void time_read(tm *time); 21 | time_t mktime(tm *time); 22 | void localtime(time_t stamp, tm *time); 23 | 24 | #endif -------------------------------------------------------------------------------- /src/include/onix/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_TIMER_H 2 | #define ONIX_TIMER_H 3 | 4 | #include 5 | #include 6 | 7 | typedef void *handler_t; 8 | 9 | typedef struct timer_t 10 | { 11 | list_node_t node; // 链表节点 12 | struct task_t *task; // 相关任务 13 | u32 expires; // 超时时间 14 | void (*handler)(struct timer_t *); // 超时处理函数 15 | void *arg; // 参数 16 | bool active; // 激活状态 17 | } timer_t; 18 | 19 | // 添加定时器 20 | timer_t *timer_add(u32 expire_ms, handler_t handler, void *arg); 21 | // 释放定时器 22 | void timer_put(timer_t *timer); 23 | // 唤醒定时器 24 | void timer_wakeup(); 25 | // 移除 task 相关的全部定时器 26 | void timer_remove(struct task_t *task); 27 | // 更新定时器超时 28 | void timer_update(timer_t *timer, u32 expire_ms); 29 | // 获取超时时间片 30 | int timer_expire_jiffies(u32 expire_ms); 31 | // 判断是否超时 32 | bool timer_is_expires(u32 expires); 33 | 34 | #endif -------------------------------------------------------------------------------- /src/include/onix/tty.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_TTY_H 2 | 3 | #include 4 | 5 | typedef struct tty_t 6 | { 7 | dev_t rdev; // 读设备号 8 | dev_t wdev; // 写设备号 9 | pid_t pgid; // 进程组 10 | } tty_t; 11 | 12 | // ioctl 设置进程组命令 13 | #define TIOCSPGRP 0x5410 14 | 15 | #endif -------------------------------------------------------------------------------- /src/include/onix/types.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_TYPES_H 2 | #define ONIX_TYPES_H 3 | 4 | #include 5 | #include 6 | 7 | #define EOF -EEOF // END OF FILE 8 | 9 | #define NULL ((void *)0) // 空指针 10 | 11 | #define EOS '\0' // 字符串结尾 12 | 13 | #define CONCAT(x, y) x##y 14 | #define RESERVED_TOKEN(x, y) CONCAT(x, y) 15 | #define RESERVED RESERVED_TOKEN(reserved, __LINE__) 16 | 17 | #ifndef __cplusplus 18 | #define bool _Bool 19 | #define true 1 20 | #define false 0 21 | #endif 22 | 23 | // 标记弱符号 24 | #define weak __attribute__((__weak__)) 25 | 26 | // 标记不会返回的函数 27 | #define noreturn __attribute__((__noreturn__)) 28 | 29 | // 用于定义特殊的结构体 30 | #define _packed __attribute__((packed)) 31 | 32 | // 用于省略函数的栈帧 33 | #define _ofp __attribute__((optimize("omit-frame-pointer"))) 34 | 35 | #define _inline __attribute__((always_inline)) inline 36 | 37 | typedef unsigned int size_t; 38 | 39 | typedef char int8; 40 | typedef short int16; 41 | typedef int int32; 42 | typedef long long int64; 43 | 44 | typedef unsigned char u8; 45 | typedef unsigned short u16; 46 | typedef unsigned int u32; 47 | typedef unsigned long long u64; 48 | 49 | typedef int32 pid_t; 50 | typedef int32 dev_t; 51 | 52 | typedef u32 time_t; 53 | typedef u32 idx_t; 54 | 55 | typedef u16 mode_t; // 文件权限 56 | 57 | typedef int32 fd_t; 58 | typedef enum std_fd_t 59 | { 60 | STDIN_FILENO, 61 | STDOUT_FILENO, 62 | STDERR_FILENO, 63 | } std_fd_t; 64 | 65 | typedef int32 off_t; // 文件偏移 66 | 67 | typedef int err_t; // 错误类型 68 | 69 | #endif -------------------------------------------------------------------------------- /src/include/onix/uname.h: -------------------------------------------------------------------------------- 1 | #ifndef ONIX_UNAME_H 2 | #define ONIX_UNAME_H 3 | 4 | #include 5 | 6 | typedef struct utsname_t 7 | { 8 | char sysname[9]; // 本版本操作系统的名称 9 | char nodename[9]; // 与实现相关的网络中节点名称 10 | char release[9]; // 本实现的当前发行级别 11 | char version[9]; // 本次发行的版本级别 12 | char machine[9]; // 系统运行的硬件类型名称 13 | } utsname_t; 14 | 15 | #endif -------------------------------------------------------------------------------- /src/kernel/alarm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern int sys_kill(); 5 | 6 | static void task_alarm(timer_t *timer) 7 | { 8 | timer->task->alarm = NULL; 9 | sys_kill(timer->task->pid, SIGALRM); 10 | } 11 | 12 | int sys_alarm(int sec) 13 | { 14 | task_t *task = running_task(); 15 | if (task->alarm) 16 | { 17 | timer_put(task->alarm); 18 | } 19 | task->alarm = timer_add(sec * 1000, task_alarm, 0); 20 | } -------------------------------------------------------------------------------- /src/kernel/assert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static u8 buf[1024]; 8 | 9 | // 强制阻塞 10 | static void spin(char *name) 11 | { 12 | printk("spinning in %s ...\n", name); 13 | while (true) 14 | ; 15 | } 16 | 17 | void assertion_failure(char *exp, char *file, char *base, int line) 18 | { 19 | printk( 20 | "\n--> assert(%s) failed!!!\n" 21 | "--> file: %s \n" 22 | "--> base: %s \n" 23 | "--> line: %d \n", 24 | exp, file, base, line); 25 | 26 | spin("assertion_failure()"); 27 | 28 | // 不可能走到这里,否则出错; 29 | asm volatile("ud2"); 30 | } 31 | 32 | void panic(const char *fmt, ...) 33 | { 34 | va_list args; 35 | va_start(args, fmt); 36 | int i = vsprintf(buf, fmt, args); 37 | va_end(args); 38 | 39 | printk("!!! panic !!!\n--> %s \n", buf); 40 | spin("panic()"); 41 | 42 | // 不可能走到这里,否则出错; 43 | asm volatile("ud2"); 44 | } -------------------------------------------------------------------------------- /src/kernel/clock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define PIT_CHAN0_REG 0X40 9 | #define PIT_CHAN2_REG 0X42 10 | #define PIT_CTRL_REG 0X43 11 | 12 | #define HZ 100 13 | #define OSCILLATOR 1193182 14 | #define CLOCK_COUNTER (OSCILLATOR / HZ) 15 | #define JIFFY (1000 / HZ) 16 | 17 | #define SPEAKER_REG 0x61 18 | #define BEEP_HZ 440 19 | #define BEEP_COUNTER (OSCILLATOR / BEEP_HZ) 20 | #define BEEP_MS 100 21 | 22 | u32 volatile jiffies = 0; 23 | u32 jiffy = JIFFY; 24 | 25 | bool volatile beeping = 0; 26 | 27 | void start_beep() 28 | { 29 | if (!beeping) 30 | { 31 | outb(SPEAKER_REG, inb(SPEAKER_REG) | 0b11); 32 | beeping = true; 33 | 34 | task_sleep(BEEP_MS); 35 | 36 | outb(SPEAKER_REG, inb(SPEAKER_REG) & 0xfc); 37 | beeping = false; 38 | } 39 | } 40 | 41 | void clock_handler(int vector) 42 | { 43 | assert(vector == 0x20); 44 | send_eoi(vector); // 发送中断处理结束 45 | 46 | jiffies++; 47 | // DEBUGK("clock jiffies %d ...\n", jiffies); 48 | 49 | timer_wakeup(); 50 | 51 | task_t *task = running_task(); 52 | assert(task->magic == ONIX_MAGIC); 53 | 54 | task->jiffies = jiffies; 55 | task->ticks--; 56 | if (!task->ticks) 57 | { 58 | schedule(); 59 | } 60 | } 61 | 62 | extern u32 startup_time; 63 | 64 | time_t sys_time() 65 | { 66 | return startup_time + (jiffies * JIFFY) / 1000; 67 | } 68 | 69 | void pit_init() 70 | { 71 | // 配置计数器 0 时钟 72 | outb(PIT_CTRL_REG, 0b00110100); 73 | outb(PIT_CHAN0_REG, CLOCK_COUNTER & 0xff); 74 | outb(PIT_CHAN0_REG, (CLOCK_COUNTER >> 8) & 0xff); 75 | 76 | // 配置计数器 2 蜂鸣器 77 | outb(PIT_CTRL_REG, 0b10110110); 78 | outb(PIT_CHAN2_REG, (u8)BEEP_COUNTER); 79 | outb(PIT_CHAN2_REG, (u8)(BEEP_COUNTER >> 8)); 80 | } 81 | 82 | void clock_init() 83 | { 84 | pit_init(); 85 | set_interrupt_handler(IRQ_CLOCK, clock_handler); 86 | set_interrupt_mask(IRQ_CLOCK, true); 87 | } 88 | -------------------------------------------------------------------------------- /src/kernel/cpu.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // 检测是否支持 cpuid 指令 4 | bool cpu_check_cpuid() 5 | { 6 | bool ret; 7 | asm volatile( 8 | "pushfl \n" // 保存 eflags 9 | 10 | "pushfl \n" // 得到 eflags 11 | "xorl $0x00200000, (%%esp)\n" // 反转 ID 位 12 | "popfl\n" // 写入 eflags 13 | 14 | "pushfl\n" // 得到 eflags 15 | "popl %%eax\n" // 写入 eax 16 | "xorl (%%esp), %%eax\n" // 将写入的值与原值比较 17 | "andl $0x00200000, %%eax\n" // 得到 ID 位 18 | "shrl $21, %%eax\n" // 右移 21 位,得到是否支持 19 | 20 | "popfl\n" // 恢复 eflags 21 | : "=a"(ret)); 22 | return ret; 23 | } 24 | 25 | // 得到供应商 ID 字符串 26 | void cpu_vendor_id(cpu_vendor_t *item) 27 | { 28 | asm volatile( 29 | "cpuid \n" 30 | : "=a"(*((u32 *)item + 0)), 31 | "=b"(*((u32 *)item + 1)), 32 | "=d"(*((u32 *)item + 2)), 33 | "=c"(*((u32 *)item + 3)) 34 | : "a"(0)); 35 | item->info[12] = 0; 36 | } 37 | 38 | void cpu_version(cpu_version_t *item) 39 | { 40 | asm volatile( 41 | "cpuid \n" 42 | : "=a"(*((u32 *)item + 0)), 43 | "=b"(*((u32 *)item + 1)), 44 | "=c"(*((u32 *)item + 2)), 45 | "=d"(*((u32 *)item + 3)) 46 | : "a"(1)); 47 | } 48 | -------------------------------------------------------------------------------- /src/kernel/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static char buf[1024]; 8 | 9 | void debugk(char *file, int line, const char *fmt, ...) 10 | { 11 | device_t *device = device_find(DEV_SERIAL, 0); 12 | if (!device) 13 | { 14 | device = device_find(DEV_CONSOLE, 0); 15 | } 16 | 17 | int i = sprintf(buf, "[%s] [%d] ", file, line); 18 | device_write(device->dev, buf, i, 0, 0); 19 | 20 | va_list args; 21 | va_start(args, fmt); 22 | i = vsprintf(buf, fmt, args); 23 | va_end(args); 24 | 25 | device_write(device->dev, buf, i, 0, 0); 26 | } -------------------------------------------------------------------------------- /src/kernel/idle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // #include 6 | 7 | #define LOGK(fmt, args...) DEBUGK(fmt, ##args) 8 | 9 | void idle_thread() 10 | { 11 | set_interrupt_state(true); 12 | u32 counter = 0; 13 | while (true) 14 | { 15 | // LOGK("idle task.... %d\n", counter++); 16 | // BMB; 17 | asm volatile( 18 | "sti\n" // 开中断 19 | "hlt\n" // 关闭 CPU,进入暂停状态,等待外中断的到来 20 | ); 21 | yield(); // 放弃执行权,调度执行其他任务 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/kernel/io.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | section .text; 代码段 4 | 5 | global inb ; 将 inb 导出 6 | inb: 7 | push ebp; 8 | mov ebp, esp ; 保存帧 9 | 10 | xor eax, eax ; 将 eax 清空 11 | mov edx, [ebp + 8]; port 12 | in al, dx; 将端口号 dx 的 8 bit 输入到 al 13 | 14 | jmp $+2 ; 一点点延迟 15 | jmp $+2 ; 一点点延迟 16 | jmp $+2 ; 一点点延迟 17 | 18 | leave ; 恢复栈帧 19 | ret 20 | 21 | global outb 22 | outb: 23 | push ebp; 24 | mov ebp, esp ; 保存帧 25 | 26 | mov edx, [ebp + 8]; port 27 | mov eax, [ebp + 12]; value 28 | out dx, al; 将 al 中的 8 bit 输入出到 端口号 dx 29 | 30 | jmp $+2 ; 一点点延迟 31 | jmp $+2 ; 一点点延迟 32 | jmp $+2 ; 一点点延迟 33 | 34 | leave ; 恢复栈帧 35 | ret 36 | 37 | global inw 38 | inw: 39 | push ebp; 40 | mov ebp, esp ; 保存帧 41 | 42 | xor eax, eax ; 将 eax 清空 43 | mov edx, [ebp + 8]; port 44 | in ax, dx; 将端口号 dx 的 16 bit 输入到 ax 45 | 46 | jmp $+2 ; 一点点延迟 47 | jmp $+2 ; 一点点延迟 48 | jmp $+2 ; 一点点延迟 49 | 50 | leave ; 恢复栈帧 51 | ret 52 | 53 | global outw 54 | outw: 55 | push ebp; 56 | mov ebp, esp ; 保存帧 57 | 58 | mov edx, [ebp + 8]; port 59 | mov eax, [ebp + 12]; value 60 | out dx, ax; 将 ax 中的 16 bit 输入出到 端口号 dx 61 | 62 | jmp $+2 ; 一点点延迟 63 | jmp $+2 ; 一点点延迟 64 | jmp $+2 ; 一点点延迟 65 | 66 | leave ; 恢复栈帧 67 | ret 68 | 69 | 70 | global inl ; 将 inl 导出 71 | inl: 72 | push ebp; 73 | mov ebp, esp ; 保存帧 74 | 75 | xor eax, eax ; 将 eax 清空 76 | mov edx, [ebp + 8]; port 77 | in eax, dx; 将端口号 dx 的 32 bit 输入到 eax 78 | 79 | jmp $+2 ; 一点点延迟 80 | jmp $+2 ; 一点点延迟 81 | jmp $+2 ; 一点点延迟 82 | 83 | leave ; 恢复栈帧 84 | ret 85 | 86 | global outl 87 | outl: 88 | push ebp; 89 | mov ebp, esp ; 保存帧 90 | 91 | mov edx, [ebp + 8]; port 92 | mov eax, [ebp + 12]; value 93 | out dx, eax; 将 eax 中的 32 bit 输入出到 端口号 dx 94 | 95 | jmp $+2 ; 一点点延迟 96 | jmp $+2 ; 一点点延迟 97 | jmp $+2 ; 一点点延迟 98 | 99 | leave ; 恢复栈帧 100 | ret 101 | -------------------------------------------------------------------------------- /src/kernel/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void tss_init(); 4 | extern void memory_map_init(); 5 | extern void mapping_init(); 6 | extern void arena_init(); 7 | 8 | extern void interrupt_init(); 9 | extern void clock_init(); 10 | extern void timer_init(); 11 | extern void syscall_init(); 12 | extern void task_init(); 13 | extern void fpu_init(); 14 | extern void pci_init(); 15 | 16 | extern void pbuf_init(); 17 | extern void netif_init(); 18 | extern void loopif_init(); 19 | extern void eth_init(); 20 | extern void arp_init(); 21 | extern void ip_init(); 22 | extern void icmp_init(); 23 | extern void udp_init(); 24 | extern void tcp_init(); 25 | 26 | extern void dhcp_init(); 27 | 28 | extern void socket_init(); 29 | extern void pkt_init(); 30 | extern void raw_init(); 31 | 32 | void kernel_init() 33 | { 34 | tss_init(); // 初始化任务状态段 35 | memory_map_init(); // 初始化物理内存数组 36 | mapping_init(); // 初始化内存映射 37 | arena_init(); // 初始化内核堆内存 38 | 39 | interrupt_init(); // 初始化中断 40 | timer_init(); // 初始化定时器 41 | clock_init(); // 初始化时钟 42 | fpu_init(); // 初始化 FPU 浮点运算单元 43 | pci_init(); // 初始化 PCI 总线 44 | 45 | syscall_init(); // 初始化系统调用 46 | task_init(); // 初始化任务 47 | 48 | pbuf_init(); // 初始化 pbuf 49 | netif_init(); // 初始化 netif 50 | loopif_init(); // 初始化 loopif 51 | eth_init(); // 初始化 Ethernet 协议 52 | arp_init(); // 初始化 ARP 协议 53 | ip_init(); // 初始化 IP 协议 54 | icmp_init(); // 初始化 ICMP 协议 55 | udp_init(); // 初始化 UDP 协议 56 | tcp_init(); // 初始化 TCP 协议 57 | 58 | dhcp_init(); // 初始化 DHCP 协议 59 | 60 | socket_init(); // 初始化 socket 61 | pkt_init(); // 初始化 pkt 62 | raw_init(); // 初始化原始套接字 63 | 64 | set_interrupt_state(true); 65 | } 66 | -------------------------------------------------------------------------------- /src/kernel/mio.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | u8 minb(u32 addr) 4 | { 5 | return *((volatile u8 *)addr); 6 | } 7 | 8 | u16 minw(u32 addr) 9 | { 10 | return *((volatile u16 *)addr); 11 | } 12 | 13 | u32 minl(u32 addr) 14 | { 15 | return *((volatile u32 *)addr); 16 | } 17 | 18 | void moutb(u32 addr, u8 value) 19 | { 20 | *((volatile u8 *)addr) = value; 21 | } 22 | 23 | void moutw(u32 addr, u16 value) 24 | { 25 | *((volatile u16 *)addr) = value; 26 | } 27 | 28 | void moutl(u32 addr, u32 value) 29 | { 30 | *((volatile u32 *)addr) = value; 31 | } 32 | -------------------------------------------------------------------------------- /src/kernel/onix.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static u32 __attribute__((section(".onix.addr"))) kernel_addr = 0x20000; 7 | static u32 __attribute__((section(".onix.size"))) kernel_size = 0; 8 | static u32 __attribute__((section(".onix.chksum"))) kernel_chksum = 0; 9 | static u32 __attribute__((section(".onix.magic"))) kernel_magic = ONIX_MAGIC; 10 | 11 | static u8 magic_msg[] = "kernel magic check failure!!!"; 12 | static u8 crc32_msg[] = "kernel crc32 check failure!!!"; 13 | static char *video = (char *)0xb8000; 14 | 15 | extern void hang(); 16 | 17 | static void show_message(char *msg, int len) 18 | { 19 | for (int i = 0; i < len; i++) 20 | video[i * 2] = msg[i]; 21 | } 22 | 23 | err_t onix_init() 24 | { 25 | if (kernel_magic != ONIX_MAGIC) 26 | { 27 | show_message(magic_msg, sizeof(magic_msg)); 28 | goto failure; 29 | } 30 | 31 | u32 size = kernel_size; 32 | u32 chksum = kernel_chksum; 33 | 34 | kernel_chksum = 0; 35 | kernel_size = 0; 36 | 37 | u32 result = eth_fcs((void *)kernel_addr, size); 38 | if (result != chksum) 39 | { 40 | show_message(crc32_msg, sizeof(crc32_msg)); 41 | goto failure; 42 | } 43 | return EOK; 44 | failure: 45 | hang(); 46 | } 47 | -------------------------------------------------------------------------------- /src/kernel/printk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static char buf[1024]; 7 | 8 | int printk(const char *fmt, ...) 9 | { 10 | va_list args; 11 | int i; 12 | 13 | va_start(args, fmt); 14 | 15 | i = vsprintf(buf, fmt, args); 16 | 17 | va_end(args); 18 | 19 | device_t *device = device_find(DEV_CONSOLE, 0); 20 | device_write(device->dev, buf, i, 0, 0); 21 | 22 | return i; 23 | } 24 | -------------------------------------------------------------------------------- /src/kernel/schedule.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | section .text 4 | 5 | global task_switch 6 | task_switch: 7 | push ebp 8 | mov ebp, esp 9 | 10 | push ebx 11 | push esi 12 | push edi 13 | 14 | mov eax, esp; 15 | and eax, 0xfffff000; current 16 | 17 | mov [eax], esp 18 | 19 | mov eax, [ebp + 8]; next 20 | mov esp, [eax] 21 | 22 | pop edi 23 | pop esi 24 | pop ebx 25 | pop ebp 26 | 27 | ret 28 | -------------------------------------------------------------------------------- /src/kernel/start.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | magic equ 0xe85250d6 4 | i386 equ 0 5 | length equ header_end - header_start 6 | 7 | section .multiboot2 8 | header_start: 9 | dd magic ; 魔数 10 | dd i386 ; 32位保护模式 11 | dd length ; 头部长度 12 | dd -(magic + i386 + length); 校验和 13 | 14 | ; 结束标记 15 | dw 0 ; type 16 | dw 0 ; flags 17 | dd 8 ; size 18 | header_end: 19 | 20 | extern onix_init 21 | extern device_init 22 | extern console_init 23 | extern gdt_init 24 | extern memory_init 25 | extern kernel_init 26 | extern gdt_ptr 27 | 28 | code_selector equ (1 << 3) 29 | data_selector equ (2 << 3) 30 | 31 | section .text 32 | global _start 33 | _start: 34 | push ebx; ards_count 35 | push eax; magic 36 | call onix_init ; 检测内核完整性 37 | call device_init ; 虚拟设备初始化 38 | call console_init ; 控制台初始化 39 | 40 | ; xchg bx, bx 41 | call gdt_init ; 全局描述符初始化 42 | ; xchg bx, bx 43 | 44 | lgdt [gdt_ptr] 45 | 46 | jmp dword code_selector:_next 47 | _next: 48 | 49 | mov ax, data_selector 50 | mov ds, ax 51 | mov es, ax 52 | mov fs, ax 53 | mov gs, ax 54 | mov ss, ax; 初始化段寄存器 55 | 56 | call memory_init ; 内存初始化 57 | ; xchg bx, bx 58 | 59 | mov esp, 0x10000; 修改栈顶 60 | ; xchg bx, bx 61 | call kernel_init ; 内核初始化 62 | 63 | jmp $; 阻塞 64 | -------------------------------------------------------------------------------- /src/kernel/system.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern task_t *task_table[TASK_NR]; 5 | 6 | mode_t sys_umask(mode_t mask) 7 | { 8 | task_t *task = running_task(); 9 | mode_t old = task->umask; 10 | task->umask = mask & 0777; 11 | return old; 12 | } 13 | 14 | int sys_setpgid(int pid, int pgid) 15 | { 16 | task_t *current = running_task(); 17 | 18 | if (!pid) 19 | pid = current->pid; 20 | 21 | if (!pgid) 22 | pgid = current->pid; 23 | 24 | for (int i = 0; i < TASK_NR; i++) 25 | { 26 | task_t *task = task_table[i]; 27 | if (!task) 28 | continue; 29 | if (task->pid != pid) 30 | continue; 31 | if (task_leader(task)) 32 | return -EPERM; 33 | if (task->sid != current->sid) 34 | return -EPERM; 35 | task->pgid = pgid; 36 | return EOK; 37 | } 38 | return -ESRCH; 39 | } 40 | 41 | int sys_getpgrp() 42 | { 43 | task_t *task = running_task(); 44 | return task->pgid; 45 | } 46 | 47 | int sys_setsid() 48 | { 49 | task_t *task = running_task(); 50 | if (task_leader(task)) 51 | return -EPERM; 52 | task->sid = task->pgid = task->pid; 53 | return task->sid; 54 | } 55 | -------------------------------------------------------------------------------- /src/kernel/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define LOGK(fmt, args...) DEBUGK(fmt, ##args) 15 | 16 | int sys_test() 17 | { 18 | char ch; 19 | device_t *device; 20 | 21 | device = device_find(DEV_IDE_CD, 0); 22 | if (!device) 23 | return 0; 24 | 25 | void *buf = (void *)alloc_kpage(1); 26 | memset(buf, 0, PAGE_SIZE); 27 | device_read(device->dev, buf, 2, 0, 0); 28 | free_kpage((u32)buf, 1); 29 | return EOK; 30 | } 31 | -------------------------------------------------------------------------------- /src/kernel/uname.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define LOGK(fmt, args...) DEBUGK(fmt, ##args) 9 | 10 | int sys_uname(utsname_t *buf) 11 | { 12 | if (!memory_access(buf, sizeof(utsname_t), true, running_task()->uid)) 13 | return -EINVAL; 14 | 15 | strncpy(buf->sysname, "onix", 9); 16 | strncpy(buf->nodename, "onix", 9); 17 | strncpy(buf->release, "release", 9); 18 | strncpy(buf->version, ONIX_VERSION, 9); 19 | strncpy(buf->machine, "machine", 9); 20 | return EOK; 21 | } -------------------------------------------------------------------------------- /src/lib/assert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static u8 buf[1024]; 7 | 8 | // 强制阻塞 9 | static void spin(char *name) 10 | { 11 | printf("spinning in %s ...\n", name); 12 | while (true) 13 | ; 14 | } 15 | 16 | void assertion_failure(char *exp, char *file, char *base, int line) 17 | { 18 | printf( 19 | "\n--> assert(%s) failed!!!\n" 20 | "--> file: %s \n" 21 | "--> base: %s \n" 22 | "--> line: %d \n", 23 | exp, file, base, line); 24 | 25 | spin("assertion_failure()"); 26 | 27 | // 不可能走到这里,否则出错; 28 | asm volatile("ud2"); 29 | } 30 | 31 | void panic(const char *fmt, ...) 32 | { 33 | va_list args; 34 | va_start(args, fmt); 35 | int i = vsprintf(buf, fmt, args); 36 | va_end(args); 37 | 38 | printf("!!! panic !!!\n--> %s \n", buf); 39 | spin("panic()"); 40 | 41 | // 不可能走到这里,否则出错; 42 | asm volatile("ud2"); 43 | } -------------------------------------------------------------------------------- /src/lib/crt.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | section .text 4 | global _start 5 | 6 | extern __libc_start_main 7 | extern _init 8 | extern _fini 9 | extern main 10 | 11 | _start: 12 | xor ebp, ebp; 清除栈底,表示程序开场 13 | pop esi; 栈顶参数为 argc 14 | mov ecx, esp; 其次为 argv 15 | 16 | and esp, -16; 栈对齐,SSE 需要 16 字节对齐 17 | push eax; 感觉没什么用 18 | push esp; 用户程序栈最大地址 19 | push edx; 动态链接器 20 | push _fini; libc 析构函数 21 | push _init; libc 构造函数 22 | push ecx; argv 23 | push esi; argc 24 | push main; 主函数 25 | 26 | call __libc_start_main 27 | 28 | ud2; 程序不可能走到这里,不然可能是其他什么地方有问题 -------------------------------------------------------------------------------- /src/lib/crt1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv, char **envp); 6 | 7 | // libc 构造函数 8 | weak void _init() 9 | { 10 | } 11 | 12 | // libc 析构函数 13 | weak void _fini() 14 | { 15 | } 16 | 17 | int __libc_start_main( 18 | int (*main)(int argc, char **argv, char **envp), 19 | int argc, char **argv, 20 | void (*_init)(), 21 | void (*_fini)(), 22 | void (*ldso)(), // 动态连接器 23 | void *stack_end) 24 | { 25 | char **envp = argv + argc + 1; 26 | _init(); 27 | int i = main(argc, argv, envp); 28 | _fini(); 29 | exit(i); 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/fifo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static _inline u32 fifo_next(fifo_t *fifo, u32 pos) 5 | { 6 | return (pos + 1) % fifo->length; 7 | } 8 | 9 | void fifo_init(fifo_t *fifo, char *buf, u32 length) 10 | { 11 | fifo->buf = buf; 12 | fifo->length = length; 13 | fifo->head = 0; 14 | fifo->tail = 0; 15 | } 16 | 17 | bool fifo_full(fifo_t *fifo) 18 | { 19 | bool full = (fifo_next(fifo, fifo->head) == fifo->tail); 20 | return full; 21 | } 22 | 23 | bool fifo_empty(fifo_t *fifo) 24 | { 25 | return (fifo->head == fifo->tail); 26 | } 27 | 28 | char fifo_get(fifo_t *fifo) 29 | { 30 | assert(!fifo_empty(fifo)); 31 | char byte = fifo->buf[fifo->tail]; 32 | fifo->tail = fifo_next(fifo, fifo->tail); 33 | return byte; 34 | } 35 | 36 | void fifo_put(fifo_t *fifo, char byte) 37 | { 38 | while (fifo_full(fifo)) 39 | { 40 | fifo_get(fifo); 41 | } 42 | fifo->buf[fifo->head] = byte; 43 | fifo->head = fifo_next(fifo, fifo->head); 44 | } 45 | -------------------------------------------------------------------------------- /src/lib/math.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | double sin(double x) 4 | { 5 | asm volatile( 6 | "fldl %0 \n" 7 | "fsin \n" 8 | "fstpl %0\n" 9 | : "+m"(x)); 10 | return x; 11 | } 12 | 13 | double cos(double x) 14 | { 15 | asm volatile( 16 | "fldl %0 \n" 17 | "fcos \n" 18 | "fstpl %0\n" 19 | : "+m"(x)); 20 | return x; 21 | } 22 | 23 | double tan(double x) 24 | { 25 | // return sin(x) / cos(x); 26 | asm volatile( 27 | "fldl %0 \n" 28 | "fptan \n" 29 | "fstpl %0\n" 30 | "fstpl %0\n" 31 | : "+m"(x)); 32 | return x; 33 | } 34 | 35 | double sqrt(double x) 36 | { 37 | asm volatile( 38 | "fldl %0 \n" 39 | "fsqrt \n" 40 | "fstpl %0\n" 41 | : "+m"(x)); 42 | return x; 43 | } 44 | 45 | double log2(double x) 46 | { 47 | asm volatile( 48 | "fld1 \n" 49 | "fldl %0 \n" 50 | "fyl2x \n" 51 | "fwait \n" 52 | "fstpl %0\n" 53 | : "+m"(x)); 54 | return x; 55 | } -------------------------------------------------------------------------------- /src/lib/printf.c: -------------------------------------------------------------------------------- 1 | /* (C) Copyright 2022 Steven; 2 | * @author: Steven kangweibaby@163.com 3 | * @date: 2022-06-28 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | static char buf[1024]; 11 | 12 | int printf(const char *fmt, ...) 13 | { 14 | va_list args; 15 | int i; 16 | 17 | va_start(args, fmt); 18 | 19 | i = vsprintf(buf, fmt, args); 20 | 21 | va_end(args); 22 | 23 | write(STDOUT_FILENO, buf, i); 24 | 25 | return i; 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/restorer.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | extern ssetmask 4 | section .text 5 | global restorer 6 | 7 | restorer: 8 | add esp, 4; sig 9 | call ssetmask 10 | add esp, 4; blocked 11 | ; 以下恢复调用方寄存器 12 | pop eax 13 | pop ecx 14 | pop edx 15 | popf 16 | ret 17 | -------------------------------------------------------------------------------- /src/lib/stdlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // 延迟 4 | void delay(u32 count) 5 | { 6 | while (count--) 7 | ; 8 | } 9 | 10 | // 阻塞函数 11 | void hang() 12 | { 13 | while (true) 14 | ; 15 | } 16 | 17 | char toupper(char ch) 18 | { 19 | if (ch >= 'a' && ch <= 'z') 20 | ch -= 0x20; 21 | return ch; 22 | } 23 | 24 | char tolower(char ch) 25 | { 26 | if (ch >= 'A' && ch <= 'Z') 27 | ch += 0x20; 28 | return ch; 29 | } 30 | 31 | // 将 bcd 码转成整数 32 | u8 bcd_to_bin(u8 value) 33 | { 34 | return (value & 0xf) + (value >> 4) * 10; 35 | } 36 | 37 | // 将整数转成 bcd 码 38 | u8 bin_to_bcd(u8 value) 39 | { 40 | return (value / 10) * 0x10 + (value % 10); 41 | } 42 | 43 | // 计算 num 分成 size 的数量 44 | u32 div_round_up(u32 num, u32 size) 45 | { 46 | return (num + size - 1) / size; 47 | } 48 | 49 | // 判断是否是数字 50 | bool isdigit(int c) 51 | { 52 | return c >= '0' && c <= '9'; 53 | } 54 | 55 | int atoi(const char *str) 56 | { 57 | if (str == NULL) 58 | return 0; 59 | int sign = 1; 60 | int result = 0; 61 | if (*str == '-') 62 | { 63 | sign = -1; 64 | str++; 65 | } 66 | for (; *str; str++) 67 | { 68 | result = result * 10 + (*str - '0'); 69 | } 70 | return result * sign; 71 | } 72 | -------------------------------------------------------------------------------- /src/lib/time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MINUTE 60 // 每分钟的秒数 5 | #define HOUR (60 * MINUTE) // 每小时的秒数 6 | #define DAY (24 * HOUR) // 每天的秒数 7 | #define YEAR (365 * DAY) // 每年的秒数,以 365 天算 8 | 9 | // 每个月开始时的已经过去天数 10 | static int month[13] = { 11 | 0, // 这里占位,没有 0 月,从 1 月开始 12 | 0, 13 | (31), 14 | (31 + 29), 15 | (31 + 29 + 31), 16 | (31 + 29 + 31 + 30), 17 | (31 + 29 + 31 + 30 + 31), 18 | (31 + 29 + 31 + 30 + 31 + 30), 19 | (31 + 29 + 31 + 30 + 31 + 30 + 31), 20 | (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31), 21 | (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30), 22 | (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31), 23 | (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30)}; 24 | 25 | time_t startup_time; 26 | int century; 27 | 28 | int elapsed_leap_years(int year) 29 | { 30 | int result = 0; 31 | result += (year - 1) / 4; 32 | result -= (year - 1) / 100; 33 | result += (year + 299) / 400; 34 | result -= (1970 - 1900) / 4; 35 | return result; 36 | } 37 | 38 | bool is_leap_year(int year) 39 | { 40 | return ((year % 4 == 0) && (year % 100 != 0)) || ((year + 1900) % 400 == 0); 41 | } 42 | 43 | void localtime(time_t stamp, tm *time) 44 | { 45 | time->tm_sec = stamp % 60; 46 | 47 | time_t remain = stamp / 60; 48 | 49 | time->tm_min = remain % 60; 50 | remain /= 60; 51 | 52 | time->tm_hour = remain % 24; 53 | time_t days = remain / 24; 54 | 55 | time->tm_wday = (days + 4) % 7; // 1970-01-01 是星期四 56 | 57 | // 这里产生误差显然需要 365 个闰年,不管了 58 | int years = days / 365 + 70; 59 | time->tm_year = years; 60 | int offset = 1; 61 | if (is_leap_year(years)) 62 | offset = 0; 63 | 64 | days -= elapsed_leap_years(years); 65 | time->tm_yday = days % (366 - offset); 66 | 67 | int mon = 1; 68 | for (; mon < 13; mon++) 69 | { 70 | if ((month[mon] - offset) > time->tm_yday) 71 | break; 72 | } 73 | 74 | time->tm_mon = mon - 1; 75 | time->tm_mday = time->tm_yday - month[time->tm_mon] + offset + 1; 76 | } -------------------------------------------------------------------------------- /src/net/chksum.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define CRC_POLY 0xEDB88320 5 | 6 | u32 eth_fcs(void *data, int len) 7 | { 8 | u32 crc = -1; 9 | u8 *ptr = (u8 *)data; 10 | for (int i = 0; i < len; i++) 11 | { 12 | crc ^= ptr[i]; 13 | for (int j = 0; j < 8; j++) 14 | { 15 | if (crc & 1) 16 | crc = (crc >> 1) ^ CRC_POLY; 17 | else 18 | crc >>= 1; 19 | } 20 | } 21 | return ~crc; 22 | } 23 | 24 | static u16 chksum(void *data, int len, u32 sum) 25 | { 26 | u16 *ptr = (u16 *)data; 27 | for (; len > 1; len -= 2) 28 | { 29 | // 防止溢出 30 | if (sum & 0x80000000) 31 | sum = (sum & 0xFFFF) + (sum >> 16); 32 | sum += *ptr++; 33 | } 34 | 35 | if (len == 1) 36 | sum += *(u8 *)ptr; 37 | 38 | while (sum >> 16) 39 | sum = (sum & 0xFFFF) + (sum >> 16); 40 | 41 | return (u16)sum; 42 | } 43 | 44 | u16 ip_chksum(void *data, int len) 45 | { 46 | u16 sum = chksum(data, len, 0); 47 | return ~sum; 48 | } 49 | 50 | u16 inet_chksum(void *data, u16 len, ip_addr_t dst, ip_addr_t src, u16 proto) 51 | { 52 | u32 sum = 0; 53 | u16 *ptr; 54 | 55 | ptr = (u16 *)dst; 56 | sum += *ptr++; 57 | sum += *ptr; 58 | 59 | ptr = (u16 *)src; 60 | sum += *ptr++; 61 | sum += *ptr; 62 | 63 | sum += htons(proto); 64 | sum += htons(len); 65 | 66 | sum = chksum(data, len, sum); 67 | 68 | return (u16)(~sum); 69 | } 70 | -------------------------------------------------------------------------------- /src/net/eth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define LOGK(fmt, args...) DEBUGK(fmt, ##args) 9 | 10 | // 接收以太网帧 11 | err_t eth_input(netif_t *netif, pbuf_t *pbuf) 12 | { 13 | eth_t *eth = (eth_t *)pbuf->eth; 14 | eth->type = ntohs(eth->type); 15 | 16 | err_t ret = pkt_input(netif, pbuf); 17 | if (ret < 0) 18 | return ret; 19 | if (ret > 0) 20 | return EOK; 21 | 22 | switch (eth->type) 23 | { 24 | case ETH_TYPE_IP: 25 | return ip_input(netif, pbuf); // IP 输入 26 | case ETH_TYPE_IPV6: 27 | // LOGK("ETH %m -> %m IP6, %d\n", eth->src, eth->dst, pbuf->length); 28 | break; 29 | case ETH_TYPE_ARP: 30 | return arp_input(netif, pbuf); // ARP 输入 31 | default: 32 | LOGK("ETH %m -> %m UNKNOWN [%04X], %d\n", eth->type, eth->src, eth->dst, pbuf->length); 33 | return -EPROTO; 34 | } 35 | return EOK; 36 | } 37 | 38 | // 发送以太网帧 39 | err_t eth_output(netif_t *netif, pbuf_t *pbuf, eth_addr_t dst, u16 type, u32 len) 40 | { 41 | pbuf->eth->type = htons(type); 42 | eth_addr_copy(pbuf->eth->dst, dst); 43 | eth_addr_copy(pbuf->eth->src, netif->hwaddr); 44 | pbuf->length = sizeof(eth_t) + len; 45 | 46 | netif_output(netif, pbuf); 47 | return EOK; 48 | } 49 | 50 | // 初始化以太网协议 51 | void eth_init() 52 | { 53 | } 54 | -------------------------------------------------------------------------------- /src/net/loopif.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static netif_t *loopif = NULL; 8 | 9 | static void send_packet(netif_t *netif, pbuf_t *pbuf) 10 | { 11 | ip_addr_t addr; 12 | ip_addr_copy(addr, pbuf->eth->ip->dst); 13 | ip_addr_copy(pbuf->eth->ip->dst, pbuf->eth->ip->src); 14 | ip_addr_copy(pbuf->eth->ip->src, addr); 15 | netif_input(netif, pbuf); 16 | } 17 | 18 | void loopif_init() 19 | { 20 | loopif = netif_create(); 21 | loopif->nic_output = send_packet; 22 | 23 | strcpy(loopif->name, "loopback"); 24 | 25 | assert(inet_aton("127.0.0.1", loopif->ipaddr) == EOK); 26 | assert(inet_aton("255.0.0.0", loopif->netmask) == EOK); 27 | 28 | assert(eth_addr_isany(loopif->hwaddr)); 29 | assert(ip_addr_isany(loopif->gateway)); 30 | 31 | loopif->flags = (NETIF_LOOPBACK | 32 | NETIF_IP_RX_CHECKSUM_OFFLOAD | 33 | NETIF_IP_TX_CHECKSUM_OFFLOAD | 34 | NETIF_UDP_RX_CHECKSUM_OFFLOAD | 35 | NETIF_UDP_TX_CHECKSUM_OFFLOAD | 36 | NETIF_TCP_RX_CHECKSUM_OFFLOAD | 37 | NETIF_TCP_TX_CHECKSUM_OFFLOAD); 38 | 39 | device_install(DEV_NET, DEV_NETIF, loopif, loopif->name, 0, netif_ioctl, NULL, NULL); 40 | } 41 | -------------------------------------------------------------------------------- /src/net/pbuf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define LOGK(fmt, args...) DEBUGK(fmt, ##args) 9 | 10 | static list_t free_pbuf_list; 11 | static size_t pbuf_count = 0; 12 | static size_t free_count = 0; 13 | 14 | // 获取空闲缓冲 15 | pbuf_t *pbuf_get() 16 | { 17 | pbuf_t *pbuf = NULL; 18 | if (list_empty(&free_pbuf_list)) 19 | { 20 | u32 page = alloc_kpage(1); 21 | pbuf = (pbuf_t *)page; 22 | list_push(&free_pbuf_list, &pbuf->node); 23 | 24 | page += PAGE_SIZE / 2; 25 | pbuf = (pbuf_t *)page; 26 | list_push(&free_pbuf_list, &pbuf->node); 27 | 28 | pbuf_count += 2; 29 | free_count += 2; 30 | } 31 | pbuf = element_entry(pbuf_t, node, list_popback(&free_pbuf_list)); 32 | 33 | // 应该对齐到 2K 34 | assert(((u32)pbuf & 0x7ff) == 0); 35 | 36 | pbuf->count = 1; 37 | free_count--; 38 | return pbuf; 39 | } 40 | 41 | // 释放缓冲 42 | void pbuf_put(pbuf_t *pbuf) 43 | { 44 | // 应该对齐到 2K 45 | assert(((u32)pbuf & 0x7ff) == 0); 46 | 47 | assert(pbuf->count > 0 && pbuf->count <= 2); 48 | pbuf->count--; 49 | if (pbuf->count > 0) 50 | { 51 | return; 52 | } 53 | 54 | list_push(&free_pbuf_list, &pbuf->node); 55 | free_count++; 56 | // LOGK("pbuf count (%d/%d)\n", free_count, pbuf_count); 57 | } 58 | 59 | // 初始化数据包缓冲 60 | void pbuf_init() 61 | { 62 | list_init(&free_pbuf_list); 63 | } 64 | -------------------------------------------------------------------------------- /src/net/port.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int port_get(port_map_t *map, u16 port) 6 | { 7 | if (port == 0) 8 | { 9 | port = 4096; 10 | while (bitmap_test(&map->map, port++)) 11 | ; 12 | bitmap_set(&map->map, port, true); 13 | return port; 14 | } 15 | 16 | if (bitmap_test(&map->map, port)) 17 | { 18 | return -EOCCUPIED; 19 | } 20 | 21 | bitmap_set(&map->map, port, true); 22 | return port; 23 | } 24 | 25 | void port_put(port_map_t *map, u16 port) 26 | { 27 | assert(port > 0); 28 | assert(bitmap_test(&map->map, port)); 29 | bitmap_set(&map->map, port, false); 30 | } 31 | 32 | void port_init(port_map_t *map) 33 | { 34 | map->buf = alloc_kpage(2); 35 | bitmap_init(&map->map, (char *)map->buf, PAGE_SIZE * 2, 0); 36 | } -------------------------------------------------------------------------------- /src/utils/cdrom.mk: -------------------------------------------------------------------------------- 1 | 2 | $(BUILD)/kernel.iso : \ 3 | $(BUILD)/kernel.bin \ 4 | $(SRC)/utils/grub.cfg \ 5 | $(BUILTIN_APPS) \ 6 | $(BUILD)/mono.wav \ 7 | $(BUILD)/stereo.wav \ 8 | 9 | # 检测内核文件是否合法 10 | grub-file --is-x86-multiboot2 $< 11 | # 创建 iso 目录 12 | mkdir -p $(BUILD)/iso/boot/grub 13 | # 拷贝内核文件 14 | cp $< $(BUILD)/iso/boot 15 | # 创建 bin 目录 16 | mkdir -p $(BUILD)/iso/bin 17 | # 创建 dev,mnt 目录 18 | mkdir -p $(BUILD)/iso/dev 19 | mkdir -p $(BUILD)/iso/data 20 | mkdir -p $(BUILD)/iso/mnt 21 | # 拷贝应用程序 22 | cp $(BUILD)/builtin/*.out $(BUILD)/iso/bin 23 | # 拷贝数据 24 | cp $(BUILD)/mono.wav $(BUILD)/iso/data 25 | cp $(BUILD)/stereo.wav $(BUILD)/iso/data 26 | 27 | # 拷贝 grub 配置文件 28 | cp $(SRC)/utils/grub.cfg $(BUILD)/iso/boot/grub 29 | # 生成 iso 文件 30 | grub-mkrescue -o $@ $(BUILD)/iso 31 | cp $@ $(BUILD)/onix_$(ONIX_VERSION).iso 32 | 33 | .PHONY: bochsb 34 | bochsb: $(BUILD)/kernel.iso 35 | bochs -q -f ../bochs/bochsrc.grub -unlock 36 | 37 | QEMU_CDROM := -drive file=$(BUILD)/kernel.iso,media=cdrom,if=ide # 光盘镜像 38 | 39 | QEMU_CDROM_BOOT:= -boot d 40 | 41 | .PHONY: qemu-cd 42 | qemu-cd: $(BUILD)/kernel.iso $(IMAGES) 43 | $(QEMU) $(QEMU_CDROM) $(QEMU_CDROM_BOOT) \ 44 | # $(QEMU_DEBUG) 45 | 46 | .PHONY: qemug-cd 47 | qemug-cd: $(BUILD)/kernel.iso $(IMAGES) 48 | $(QEMU) \ 49 | $(QEMU_CDROM) \ 50 | $(QEMU_CDROM_BOOT) \ 51 | $(QEMU_DEBUG) 52 | 53 | .PHONY:cdrom 54 | cdrom: $(BUILD)/kernel.iso $(IMAGES) 55 | - -------------------------------------------------------------------------------- /src/utils/cmd.mk: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: bochs 3 | bochs: $(IMAGES) 4 | bochs -q -f ../bochs/bochsrc -unlock 5 | 6 | .PHONY: bochsg 7 | bochsg: $(IMAGES) 8 | bochs-gdb -q -f ../bochs/bochsrc.gdb -unlock 9 | 10 | QEMU:= qemu-system-i386 # 虚拟机 11 | QEMU+= -m 32M # 内存 12 | QEMU+= -audiodev pa,id=snd # 音频设备 13 | QEMU+= -machine pcspk-audiodev=snd # pcspeaker 设备 14 | QEMU+= -device sb16,audiodev=snd # Sound Blaster 16 15 | QEMU+= -rtc base=localtime # 设备本地时间 16 | QEMU+= -chardev stdio,mux=on,id=com1 # 字符设备 1 17 | # QEMU+= -chardev vc,mux=on,id=com1 # 字符设备 1 18 | # QEMU+= -chardev vc,mux=on,id=com2 # 字符设备 2 19 | # QEMU+= -chardev udp,mux=on,id=com2,port=6666,ipv4=on # 字符设备 2 20 | QEMU+= -serial chardev:com1 # 串口 1 21 | # QEMU+= -serial chardev:com2 # 串口 2 22 | # QEMU+= -netdev bridge,id=eth0 # 网络设备 23 | QEMU+= -netdev tap,id=eth0,ifname=tap0,script=no,downscript=no # 网络设备 24 | QEMU+= -device e1000,netdev=eth0,mac=5A:5A:5A:5A:5A:33 # 网卡 e1000 25 | # QEMU+= -object filter-dump,id=f1,netdev=eth0,file=$(BUILD)/dump.pcap 26 | 27 | QEMU_DISK := -drive file=$(BUILD)/master.img,if=ide,index=0,media=disk,format=raw # 主硬盘 28 | QEMU_DISK += -drive file=$(BUILD)/slave.img,if=ide,index=1,media=disk,format=raw # 从硬盘 29 | QEMU_DISK += -drive file=$(BUILD)/floppya.img,if=floppy,index=0,media=disk,format=raw # 软盘a 30 | 31 | QEMU_DISK_BOOT:=-boot c 32 | 33 | QEMU_DEBUG:= -s -S 34 | 35 | .PHONY: qemu 36 | qemu: $(IMAGES) $(TAP0) 37 | $(QEMU) $(QEMU_DISK) $(QEMU_DISK_BOOT) 38 | 39 | .PHONY: qemug 40 | qemug: $(IMAGES) $(TAP0) 41 | $(QEMU) $(QEMU_DISK) $(QEMU_DISK_BOOT) $(QEMU_DEBUG) 42 | 43 | # VMWare 磁盘转换 44 | 45 | $(BUILD)/master.vmdk: $(BUILD)/master.img 46 | qemu-img convert -O vmdk $< $@ 47 | 48 | .PHONY:vmdk 49 | vmdk: $(BUILD)/master.vmdk 50 | -------------------------------------------------------------------------------- /src/utils/deer.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StevenBaby/onix/9157a4e202f6f274f9a4fb222f551afb5e3ecc2a/src/utils/deer.mp3 -------------------------------------------------------------------------------- /src/utils/generate.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import os 4 | import binascii 5 | import shutil 6 | try: 7 | from elftools.elf.elffile import ELFFile 8 | except ImportError: 9 | print(''' 10 | import elftools failure. 11 | You may need to install elftools packet using either 12 | pip install pyelftools 13 | or in Archlinux 14 | sudo pacman -S python-pyelftools 15 | ''') 16 | exit(-1) 17 | 18 | DIRNAME = os.path.dirname(os.path.abspath(__file__)) 19 | BUILD = os.path.abspath(os.path.join(DIRNAME, "../../build")) 20 | KERNELRAWBIN = os.path.join(BUILD, 'kernel.raw.bin') 21 | SYSTEMRAWBIN = os.path.join(BUILD, 'system.raw.bin') 22 | KERNELBIN = os.path.join(BUILD, 'kernel.bin') 23 | 24 | 25 | def main(): 26 | start = 0x8000 27 | size = os.path.getsize(SYSTEMRAWBIN) - start 28 | 29 | with open(SYSTEMRAWBIN, 'rb') as file: 30 | data = file.read() 31 | chksum = binascii.crc32(data[start:]) 32 | print(f"system.bin chksum: {chksum} size {size}") 33 | 34 | shutil.copy(KERNELRAWBIN, KERNELBIN) 35 | 36 | with open(KERNELBIN, 'rb+') as file: 37 | elf = ELFFile(file) 38 | section = elf.get_section_by_name(".onix.magic") 39 | offset = section["sh_offset"] 40 | file.seek(offset) 41 | magic = int.from_bytes(file.read(4), "little") 42 | if magic != 0x20220205: 43 | print("magic check error") 44 | return 45 | 46 | section = elf.get_section_by_name(".onix.size") 47 | offset = section["sh_offset"] 48 | file.seek(offset) 49 | file.write(size.to_bytes(4, "little")) 50 | print(f"write size {size} offset {offset:x} success") 51 | 52 | section = elf.get_section_by_name(".onix.chksum") 53 | offset = section["sh_offset"] 54 | file.seek(offset) 55 | file.write(chksum.to_bytes(4, "little")) 56 | print(f"write size {chksum:#x} offset {offset:x} success:") 57 | 58 | 59 | if __name__ == '__main__': 60 | main() 61 | -------------------------------------------------------------------------------- /src/utils/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default=0 3 | 4 | menuentry "Onix" { 5 | multiboot2 /boot/kernel.bin 6 | } -------------------------------------------------------------------------------- /src/utils/master.sfdisk: -------------------------------------------------------------------------------- 1 | label: dos 2 | label-id: 0x00000000 3 | device: master.img 4 | unit: sectors 5 | sector-size: 512 6 | 7 | master.img1 : start= 2048, size= 30208, type=83 8 | -------------------------------------------------------------------------------- /src/utils/net.mk: -------------------------------------------------------------------------------- 1 | IFACE:=$(shell sudo ip -o -4 route show to default | awk '{print $$5}') 2 | 3 | TAP0:=/sys/class/net/tap0 4 | 5 | .SECONDARY: $(TAP0) 6 | 7 | # 网桥 IP 地址 8 | GATEWAY:=172.16.16.1 9 | 10 | $(TAP0): 11 | sudo ip tuntap add mode tap $(notdir $@) user $(USER) 12 | sudo ip addr add $(GATEWAY)/24 dev $(notdir $@) 13 | sudo ip link set dev $(notdir $@) up 14 | 15 | sudo sysctl net.ipv4.ip_forward=1 16 | sudo iptables -t nat -A POSTROUTING -s $(GATEWAY)/24 -o $(IFACE) -j MASQUERADE 17 | sudo iptables -A FORWARD -i $(notdir $@) -o $(IFACE) -j ACCEPT 18 | 19 | tap0: $(TAP0) 20 | - 21 | -------------------------------------------------------------------------------- /src/utils/network.conf: -------------------------------------------------------------------------------- 1 | ipaddr=172.16.16.11 2 | netmask=255.255.255.0 3 | gateway=172.16.16.1 4 | -------------------------------------------------------------------------------- /src/utils/resolv.conf: -------------------------------------------------------------------------------- 1 | nameserver=114.114.114.114 2 | -------------------------------------------------------------------------------- /src/utils/slave.sfdisk: -------------------------------------------------------------------------------- 1 | label: dos 2 | label-id: 0x00000000 3 | device: slave.img 4 | unit: sectors 5 | sector-size: 512 6 | 7 | slave.img1 : start= 2048, size= 63472, type=83 8 | -------------------------------------------------------------------------------- /tests/basic/call.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | extern exit 4 | 5 | test: 6 | push $ 7 | ret 8 | 9 | global main 10 | main: 11 | ; push 5 12 | ; push eax 13 | 14 | ; pop ebx 15 | ; pop ecx 16 | 17 | ; pusha 18 | 19 | ; popa 20 | 21 | call test 22 | 23 | push 0; 传递参数 24 | call exit 25 | -------------------------------------------------------------------------------- /tests/basic/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | char message[] = "hello world!!!\n"; 5 | char buf[1024]; // .bss 6 | 7 | int main() 8 | { 9 | printf(message); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /tests/basic/hello.s: -------------------------------------------------------------------------------- 1 | .file "hello.c" 2 | .text 3 | .globl message 4 | .data 5 | .align 4 6 | .type message, @object 7 | .size message, 16 8 | message: 9 | .string "hello world!!!\n" 10 | .globl buf 11 | .bss 12 | .align 32 13 | .type buf, @object 14 | .size buf, 1024 15 | buf: 16 | .zero 1024 17 | .text 18 | .globl main 19 | .type main, @function 20 | main: 21 | pushl $message 22 | call printf 23 | addl $4, %esp 24 | movl $0, %eax 25 | ret 26 | .size main, .-main 27 | .section .note.GNU-stack,"",@progbits 28 | -------------------------------------------------------------------------------- /tests/basic/makefile: -------------------------------------------------------------------------------- 1 | 2 | CFLAGS:= -m32 # 32 位的程序 3 | CFLAGS+= -Qn # 去掉 gcc 版本信息 4 | # CFLAGS+= -fno-builtin # 不需要 gcc 内置函数 5 | # CFLAGS+= -nostdinc # 不需要标准头文件 6 | CFLAGS+= -fno-pic # 不需要位置无关的代码 position independent code 7 | # CFLAGS+= -fno-pie # 不需要位置无关的可执行程序 position independent executable 8 | # CFLAGS+= -nostdlib # 不需要标准库 9 | CFLAGS+= -fno-stack-protector # 不需要栈保护 10 | CFLAGS+= -fomit-frame-pointer # 不需要栈帧 11 | CFLAGS+= -mpreferred-stack-boundary=2 # 不需要栈对齐 12 | CFLAGS+= -fno-asynchronous-unwind-tables # 不需要 CFI 信息 13 | CFLAGS:=$(strip ${CFLAGS}) 14 | 15 | .PHONY: hello.s 16 | hello.s: hello.c 17 | gcc $(CFLAGS) -S $< -o $@ 18 | 19 | .PHONY: params.s 20 | params.s: params.c 21 | gcc $(CFLAGS) -S $< -o $@ 22 | 23 | 24 | .PHONY: clean 25 | clean: 26 | rm -rf *.o 27 | rm -rf *.out 28 | -------------------------------------------------------------------------------- /tests/basic/params.c: -------------------------------------------------------------------------------- 1 | 2 | // int add(int x, int y) 3 | // { 4 | // int z = x + y; 5 | // return z; 6 | // } 7 | 8 | int main() 9 | { 10 | // int a = 5; 11 | // int b = 3; 12 | // int c = add(a, b); 13 | char *ptr = alloca(20); 14 | ptr[5] = 0xaa; 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tests/basic/params.s: -------------------------------------------------------------------------------- 1 | .file "params.c" 2 | .text 3 | .globl main 4 | .type main, @function 5 | main: 6 | pushl %ebp 7 | movl %esp, %ebp 8 | subl $4, %esp 9 | movl $4, %eax 10 | subl $1, %eax 11 | addl $32, %eax 12 | movl $4, %ecx 13 | movl $0, %edx 14 | divl %ecx 15 | sall $2, %eax 16 | subl %eax, %esp 17 | movl %esp, %eax 18 | addl $15, %eax 19 | shrl $4, %eax 20 | sall $4, %eax 21 | movl %eax, -4(%ebp) 22 | movl -4(%ebp), %eax 23 | addl $5, %eax 24 | movb $-86, (%eax) 25 | movl $0, %eax 26 | leave 27 | ret 28 | .size main, .-main 29 | .section .note.GNU-stack,"",@progbits 30 | -------------------------------------------------------------------------------- /tests/basic/test_syscall.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | section .text 4 | global main 5 | main: 6 | 7 | ; write(stdout, message, len) 8 | mov eax, 4; write 9 | mov ebx, 1; stdout 10 | mov ecx, message; buffer 11 | mov edx, message.end - message 12 | int 0x80 13 | 14 | ; exit(0) 15 | mov eax, 1; exit 16 | mov ebx, 0; status 17 | int 0x80 18 | 19 | section .data 20 | message: 21 | db "hello world!!!", 10, 13, 0 22 | .end: 23 | -------------------------------------------------------------------------------- /tests/basic/types.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef struct descriptor /* 共 8 个字节 */ 5 | { 6 | unsigned short limit_low; // 段界限 0 ~ 15 位 7 | unsigned int base_low : 24; // 基地址 0 ~ 23 位 16M 8 | unsigned char type : 4; // 段类型 9 | unsigned char segment : 1; // 1 表示代码段或数据段,0 表示系统段 10 | unsigned char DPL : 2; // Descriptor Privilege Level 描述符特权等级 0 ~ 3 11 | unsigned char present : 1; // 存在位,1 在内存中,0 在磁盘上 12 | unsigned char limit_high : 4; // 段界限 16 ~ 19; 13 | unsigned char available : 1; // 该安排的都安排了,送给操作系统吧 14 | unsigned char long_mode : 1; // 64 位扩展标志 15 | unsigned char big : 1; // 32 位 还是 16 位; 16 | unsigned char granularity : 1; // 粒度 4KB 或 1B 17 | unsigned char base_high; // 基地址 24 ~ 31 位 18 | } _packed descriptor; 19 | 20 | int main() 21 | { 22 | printf("size of u8 %d\n", sizeof(u8)); 23 | printf("size of u16 %d\n", sizeof(u16)); 24 | printf("size of u32 %d\n", sizeof(u32)); 25 | printf("size of u64 %d\n", sizeof(u64)); 26 | printf("size descriptor %d\n", sizeof(descriptor)); 27 | 28 | descriptor des; 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /tests/basic/variables.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | printf("%d %d\n"); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/net/net.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import logging 3 | import warnings 4 | from scapy.all import ( 5 | TunTapInterface, 6 | Ether, 7 | ARP, 8 | IP, 9 | UDP, 10 | TCP, 11 | ICMP, 12 | ) 13 | 14 | logging.getLogger("scapy").setLevel(logging.ERROR) 15 | warnings.filterwarnings("ignore", module="scapy") 16 | 17 | logging.basicConfig( 18 | stream=sys.stdout, 19 | level=logging.DEBUG, 20 | format='[%(asctime)s] [%(module)s:%(lineno)d] %(levelname)s %(message)s',) 21 | 22 | logger = logging.getLogger("net") 23 | 24 | 25 | rmac = '5a:5a:5a:5a:5a:44' 26 | raddr = '192.168.111.44' 27 | 28 | lmac = '5a:5a:5a:5a:5a:33' 29 | laddr = '192.168.111.33' 30 | 31 | bmac = '5a:5a:5a:5a:5a:22' 32 | baddr = '192.168.111.22' 33 | 34 | hmac = '5a:5a:5a:5a:5a:11' 35 | haddr = '192.168.111.11' 36 | 37 | gateway = '192.168.111.2' 38 | gmac = None 39 | 40 | broadcast = "ff:ff:ff:ff:ff:ff" 41 | 42 | taps = {i: None for i in range(3)} 43 | 44 | 45 | def get_tap(i: int = 1) -> TunTapInterface: 46 | global taps 47 | if i not in taps: 48 | return None 49 | if not taps[i]: 50 | taps[i] = TunTapInterface(f'tap{i}') 51 | return taps[i] 52 | 53 | 54 | def get_gmac(tap): 55 | global gmac 56 | if gmac: 57 | return gmac 58 | pkt = Ether(src=bmac, dst=broadcast) 59 | pkt /= ARP(pdst=gateway) 60 | out = tap.sr1(pkt, verbose=False) 61 | gmac = out[Ether].src 62 | return gmac 63 | -------------------------------------------------------------------------------- /tests/net/pkt.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from scapy.all import *\n", 10 | "from scapy.all import (\n", 11 | " Ether,\n", 12 | " ARP,\n", 13 | ")\n", 14 | "\n", 15 | "from net import (\n", 16 | " get_tap,\n", 17 | " rmac,\n", 18 | " raddr,\n", 19 | " broadcast,\n", 20 | " laddr,\n", 21 | " lmac,\n", 22 | ")" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "tap = get_tap(1)" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "# 测试 packet\n", 41 | "\n", 42 | "pkt = Ether(src=rmac, dst=lmac)\n", 43 | "pkt /= \"hello pkt\"\n", 44 | "tap.send(pkt)" 45 | ] 46 | } 47 | ], 48 | "metadata": { 49 | "kernelspec": { 50 | "display_name": "test", 51 | "language": "python", 52 | "name": "python3" 53 | }, 54 | "language_info": { 55 | "codemirror_mode": { 56 | "name": "ipython", 57 | "version": 3 58 | }, 59 | "file_extension": ".py", 60 | "mimetype": "text/x-python", 61 | "name": "python", 62 | "nbconvert_exporter": "python", 63 | "pygments_lexer": "ipython3", 64 | "version": "3.9.16" 65 | }, 66 | "orig_nbformat": 4 67 | }, 68 | "nbformat": 4, 69 | "nbformat_minor": 2 70 | } 71 | -------------------------------------------------------------------------------- /tests/net/raw.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from scapy.all import *\n", 10 | "from scapy.all import (\n", 11 | " Ether,\n", 12 | " ARP,\n", 13 | " IP,\n", 14 | " Raw,\n", 15 | ")\n", 16 | "\n", 17 | "from net import (\n", 18 | " get_tap,\n", 19 | " lmac,\n", 20 | " laddr,\n", 21 | " haddr,\n", 22 | " hmac,\n", 23 | ")" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "tap = get_tap(1)" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "pkt = Ether(dst=lmac, src=hmac)\n", 42 | "pkt /= IP(\n", 43 | " dst=laddr,\n", 44 | " src=\"192.168.111.1\",\n", 45 | " proto='tcp',\n", 46 | ")\n", 47 | "pkt /= \"hello raw socket\"\n", 48 | "tap.send(pkt)\n" 49 | ] 50 | } 51 | ], 52 | "metadata": { 53 | "kernelspec": { 54 | "display_name": "test", 55 | "language": "python", 56 | "name": "python3" 57 | }, 58 | "language_info": { 59 | "codemirror_mode": { 60 | "name": "ipython", 61 | "version": 3 62 | }, 63 | "file_extension": ".py", 64 | "mimetype": "text/x-python", 65 | "name": "python", 66 | "nbconvert_exporter": "python", 67 | "pygments_lexer": "ipython3", 68 | "version": "3.9.16" 69 | }, 70 | "orig_nbformat": 4 71 | }, 72 | "nbformat": 4, 73 | "nbformat_minor": 2 74 | } 75 | -------------------------------------------------------------------------------- /tests/net/tcp_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | 4 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 6 | s.connect(('172.16.16.11', 6666)) 7 | 8 | data = s.recv(1024) 9 | print(data.decode()) 10 | 11 | msg = f"hello tcp server {time.time()}" 12 | s.send(msg.encode()) 13 | 14 | time.sleep(1) 15 | s.close() 16 | -------------------------------------------------------------------------------- /tests/net/tcp_close.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | 4 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 6 | 7 | print("bind...") 8 | s.bind(('0.0.0.0', 7777)) 9 | 10 | print("listen...") 11 | s.listen(5) 12 | 13 | print("accept...") 14 | conn, addr = s.accept() 15 | print(conn) 16 | print(addr) 17 | 18 | # time.sleep(1) 19 | 20 | print("close...") 21 | conn.close() 22 | s.close() 23 | -------------------------------------------------------------------------------- /tests/net/tcp_nagle.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | 4 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 6 | 7 | print("bind...") 8 | s.bind(('0.0.0.0', 7777)) 9 | 10 | print("listen...") 11 | s.listen(5) 12 | 13 | print("accept...") 14 | conn, addr = s.accept() 15 | print(conn) 16 | print(addr) 17 | 18 | while True: 19 | data = conn.recv(1024) 20 | print("recv:", data.decode()) 21 | 22 | print("close...") 23 | conn.close() 24 | s.close() 25 | -------------------------------------------------------------------------------- /tests/net/tcp_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | 4 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 6 | 7 | print("bind...") 8 | s.bind(('0.0.0.0', 7777)) 9 | 10 | print("listen...") 11 | s.listen(5) 12 | 13 | print("accept...") 14 | conn, addr = s.accept() 15 | print(conn) 16 | print(addr) 17 | 18 | data = conn.recv(1024) 19 | print("recv:", data.decode()) 20 | conn.send(f"hello client {int(time.time())}".encode()) 21 | time.sleep(1) 22 | 23 | print("close...") 24 | conn.close() 25 | s.close() 26 | -------------------------------------------------------------------------------- /tests/net/tcp_surge.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import threading 3 | import time 4 | 5 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 7 | 8 | print("bind...") 9 | s.bind(('0.0.0.0', 7777)) 10 | 11 | print("listen...") 12 | s.listen(5) 13 | 14 | print("accept...") 15 | conn, addr = s.accept() 16 | print(conn) 17 | print(addr) 18 | 19 | 20 | def send_data(): 21 | while True: 22 | conn.send(("B" * 4096).encode()) 23 | print("sent.") 24 | 25 | 26 | def recv_data(): 27 | while True: 28 | data = conn.recv(4096) 29 | print(f"recv: {len(data)}") 30 | 31 | 32 | send_thread = threading.Thread(target=send_data, daemon=True) 33 | recv_thread = threading.Thread(target=recv_data, daemon=True) 34 | 35 | send_thread.start() 36 | recv_thread.start() 37 | 38 | send_thread.join() 39 | recv_thread.join() 40 | 41 | print("close...") 42 | conn.close() 43 | s.close() 44 | -------------------------------------------------------------------------------- /tests/net/udp_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | from net import ( 4 | haddr, 5 | laddr 6 | ) 7 | 8 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 9 | print(s) 10 | 11 | s.bind((haddr, 7777)) 12 | s.connect((laddr, 6666)) 13 | 14 | 15 | while True: 16 | print("receivng...") 17 | message = s.recv(1024) 18 | print("recved:", message) 19 | s.send(bytes(f"udp ack message {int(time.time())}", encoding='ascii')) 20 | break 21 | 22 | s.close() 23 | --------------------------------------------------------------------------------