x01.os.24: 来点代码
x01.treeos
这是在 deepin 上编译生成 linux-0.12 的学习代码,下载链接:x01.treeos
- 参考必备: Linux 内核完全注释 提取码: xuyb ,或者到 oldlinux 获取更多资源。
0.01-200412: 打个招呼
操作系统引导进入 main.c 的第一件事就是打个招呼: printk("Hello x01.treeos!");
, 其简单实现 printk.c 如下:
#include <stdarg.h>
#include <string.h>
#define Origin_x (*(unsigned char*)0x90000)
#define Origin_y (*(unsigned char*)0x90001)
#define Video_base ((unsigned char*)0xb8000)
int video_x, video_y;
void init_video()
{
video_x = Origin_x;
video_y = Origin_y;
}
static void print_char(char c, int x, int y, char color)
{
unsigned char* video = Video_base;
video[(y*80+x) * 2] = c;
video[(y*80+x)*2+1] = color;
}
int print_color(const char* s, char color)
{
int col = 0;
for (int i=0; i < strlen(s); i++) {
if (i != 0 && i % 80 == 0) {
video_y ++;
col -= i;
}
if (s[i] == '\n') {
video_y ++;
col -= i;
continue;
}
print_char(s[i], video_x + col++, video_y, color);
}
video_y ++;
}
static char buf[1024];
int printk(const char * fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i = vsprintf(buf, fmt, args);
va_end(args);
print_color(buf, 0x07);
}
0.02-200421: 中断调用
发生中断时,ss, esp, eflags, cs, eip 依次入栈,中断返回时出栈。
divide_error:
pushl $do_divide_error
no_error_code:
xchgl %eax, (%esp)
pushl %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
pushl $0
lea 44(%esp), %edx
pushl %edx
movl $0x10, %edx
mov %dx, %ds
mov %dx, %es
mov %dx, %fs
call *%eax
addl $8, %esp
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
0.03-200426: 系统调用
- 编制处理函数
- 在 unistd.h 中添加功能号和原型定义
- 在 linux/sys.h 中添加函数外部声明,并在 sys_call_table 中添加函数名
- 在 system_call.S 中将 nr_system_calls 增 1
- 在 lib/ 添加系统调用库函数的实现
.align 4
system_call:
push %ds
push %es
push %fs
pushl %eax
pushl %edx
pushl %ecx
pushl %ebx
movl $0x10, %edx
mov %dx, %ds
mov %dx, %es
movl $0x17, %edx
mov %dx, %fs
cmpl NR_syscalls, %eax
jae bad_sys_call
call *sys_call_table(, %eax, 4)
pushl %eax
2: movl current, %eax
cmpl $0, state(%eax)
jne reschedule
cmpl $0, counter(%eax)
je reschedule
ret_from_sys_call:
movl current, %eax
cmpl task, %eax
je 3f
cmpw $0x0f, CS(%esp)
jne 3f
cmpw $0x17, OLDSS(%esp)
jne 3f
movl signal(%eax), %ebx
movl blocked(%eax), %ecx
notl %ecx
andl %ebx, %ecx
bsfl %ecx, %ecx
je 3f
btrl %ecx, %ebx
movl %ebx, signal(%eax)
incl %ecx
pushl %ecx
call do_signal
popl %ecx
testl %eax, %eax
jne 2b
3: popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4, %esp
pop %fs
pop %es
pop %ds
iret
0.04-200708: 硬盘
- 硬盘 I/O 端口及寄存器
Register | primary | secondary | 读时 | 写时 |
---|---|---|---|---|
Command Block Registers | 0x1f0 | 0x170 | Data | Data |
0x1f1 | 0x171 | Error | 写前预补偿 | |
0x1f2 | 0x172 | Sector Count | Secotr Count | |
0x1f3 | 0x173 | LBA Low / 起始扇区 | 同读 | |
0x1f4 | 0x174 | LBA Mid / HD_LCYL | 同读 | |
0x1f5 | 0x175 | LBA High / HD_HCYL | 同读 | |
0x1f6 | 0x176 | Device | Device | |
0x1f7 | 0x177 | Status | Command | |
Control Block Registers | 0x3f6 | 0x376 | Alternate status | Device Control |
- Device Register
bit | value | 说明 |
---|---|---|
7 | 1 | |
6 | L | 0:CHS, 1:LBA |
5 | 1 | |
4 | DRV | 0: master, 1:slave |
3 | HS3 | L=1时, LBA 27 bit |
2 | HS2 | L=1时, LBA 26 bit |
1 | HS1 | L=1时, LBA 25 bit |
0 | HS0 | L=1时, LBA 24 bit |
- Status Register
bit | value | 说明 |
---|---|---|
7 | BSY | 为 1 时其他位无效 |
6 | DRDY | Dirve Ready |
5 | DF/SE | Device Fault / Stream Error |
4 | # | Command dependent (formerly DSC bit) |
3 | DRQ | Data request (ready to transfer data) |
2 | - | Obsolete |
1 | - | Obsolete |
0 | ERR | Error (an error occurred) |
- Device Control Register
bit | value | 说明 |
---|---|---|
7 | HOB | High Order Byte (defined by 48-bit Address feature set) |
6 | - | |
5 | - | |
4 | - | |
3 | - | |
2 | SRST | Software Rest |
1 | IEN | Interrupt Enable |
0 | 0 |
0.05-230115: 进程
- 进程 0 的创建
初始化采取手工的方式,创建 INIT_TASK
, 进入用户模式,即通过 iret
指令给 ss, esp, eflags, cs, eip
赋值。
#define move_to_user_mode() \
__asm__("movl %%esp, %%eax\n\t" \
"pushl $0x17\n\t" /*ss*/ \
"pushl %%eax\n\t" /*esp*/ \
"pushfl\n\t" /*eflags*/ \
"pushl $0x0f\n\t" /*cs*/ \
"pushl $1f\n\t" /*eip*/ \
"iret\n" \
"1:\tmovl $0x17,%%eax\n\t" \
"movw %%ax, %%ds\n\t" \
"movw %%ax, %%es\n\t" \
"movw %%ax, %%fs\n\t" \
"movw %%ax, %%gs" \
:::"ax")
- 进程的运行
进程 0 采取复制的方式,调用 fork() => copy_process()
创建进程 1, 此时 fork()
返回进程号 1,继续 pause() => schedule() => switch_to()
运行进程 1。需要注意,copy_process()
创建进程 1 时 tss.eax=0; tss.eip=eip
, 其中 eip
为中断返回的下一指令处, 即fork()
中 if (__res >= 0) return __res;
处。进程切换时自动恢复 tr
, 则由进程 1 tss
中恢复的返回值 eax
为 0, eip
指令地址为 fork()
中的 if (__res >= 0) return __res;
处。则 if (!fork())
为真,进入 init()
创建进程 2: execve("/bin/sh", argv_rc, envp_rc);
即 shell 处理 .rc 文件。然后不断循环,设置 tty
, 创建子进程。
Linux 制作
- 创建虚拟盘 c.img 使用
bximage
即可 - 创建分区 (m: 帮助, n: 创建新分区, a: 添加引导标记, p: 查看, w: 保存)
sudo fdisk c.img
- 映射到 /dev/mapper
sudo kpartx -av c.img
ls -l /dev/mapper
- 根据映射 loop 格式化
sudo mkfs.ext4 /dev/mapper/loop0p1
- 安装 grub
sudo mount /dev/mapper/loop0p1 /mnt
sudo grub-install --boot-directory=/mnt/ /dev/loop0
- 复制 vmlinz initrd.img
sudo cp /boot/vmlinuz-4.15.0-30deepin-generic /mnt/boot/vmlinuz
sudo cp /boot/initrd.img-4.15.0-30deepin-generic /mnt/boot/initrd.img
- 创建配置文件 grub.cfg,复制到 /mnt/boot/grub/ 中
default=0
timeout=10
insmod ext2
set root='hd0,msdos1'
menuentry 'x01.treeos' {
insmod gzio
insmod part_msdos
insmod ext2
set root='hd0,msdos1'
linux /boot/vmlinuz ro --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1
initrd /boot/initrd.img
}
- Make LiveCD 脚本
# 取消注释即可下载
#wget http://kernel.org/pub/linux/kernel/v4.x/linux-4.10.9.tar.xz
#wget http://busybox.net/downloads/busybox-1.24.2.tar.bz2
#wget http://kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.xz
mkdir isoimage
tar -xvf linux-4.10.9.tar.xz
tar -xvf busybox-1.25.1.tar.bz2
tar -xvf syslinux-6.03.tar.gz
cd busybox-1.25.1
make distclean defconfig
sed -i "s/.*CONFIG_STATIC.*/CONFIG_STATIC=y/" .config
make busybox install
cd _install
rm -f linuxrc
mkdir dev proc sys
echo '#!/bin/sh' > init
echo 'dmesg -n 1' >> init
echo 'mount -t devtmpfs none /dev' >> init
echo 'mount -t proc none /proc' >> init
echo 'mount -t sysfs none /sys' >> init
echo 'setsid cttyhack /bin/sh' >> init
chmod +x init
find . | cpio -R root:root -H newc -o | gzip > ../../isoimage/rootfs.gz
cd ../../linux-4.10.9
make mrproper defconfig bzImage
cp arch/x86/boot/bzImage ../isoimage/kernel.gz
cd ../isoimage
cp ../syslinux-6.03/bios/core/isolinux.bin .
cp ../syslinux-6.03/bios/com32/elflink/ldlinux/ldlinux.c32 .
echo 'default kernel.gz initrd=rootfs.gz' > ./isolinux.cfg
xorriso \
-as mkisofs \
-o ../minimal_linux_live.iso \
-b isolinux.bin \
-c boot.cat \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
./
cd ..
0.06-230226:跟着代码走
文件,内存,IO设备等互为表里,牵连甚多,只能跟着代码走了,好在 CLK(Comment Linux Kernl) 所述甚详,可反复看。
制作根文件,需使用 sls1.0-img 提取码: 35wb, 参照 CLK 进行操作. 由于复制会忽略软链接 cpp 等,为使创建的根文件编译 linux-0.12 成功,需进入 /usr/local/bin 创建软链接 cpp, strip 等,例如: ln -s /usr/local/lib/gcc-cpp cpp
. sls1.0-img 已作了精简,如果使用 oldliux 网站的原始根文件 rootimage-0.12-hd, 需注意有的内容不在一个分区中。