文章目录

  • 专栏博客链接
  • 相关查阅博客链接
  • 本书中错误勘误
  • 闲聊时刻
  • 实现fork
    • 实现fork的介绍
    • 实现fork的原理
    • 编写完的thread.c(fork_pid)
    • 编写完的thread.h(struct task_struct)
    • 编写完的memory.c(get_a_page_without_opvaddrbitmap)
    • 编写完的fork.c
    • 编写完的fork.h
    • 修改完的syscall.c
    • 修改完的syscall.h
    • 修改完的syscall-init.c
    • 修改完的syscall-init.h
    • 修改完的thread.c(thread_init)
    • 修改完的main.c
    • 修改完的MakeFile
    • make all 验证成果
  • 实现read putchar clear系统调用
    • 修改完的fs.c(sys_read)
    • 修改完的stdio-kernel.c(sys_putchar)
    • 修改完的print.S(cls_screen)
    • 修改完的syscall.c(read putchar clear)
    • 修改完的syscall.h
    • 修改完的syscall-init.c
    • 结束语(上)

专栏博客链接


《操作系统真象还原》从零开始自制操作系统 全章节博客链接


相关查阅博客链接



本书中错误勘误



闲聊时刻


就在昨日 我们的十四章 终于终于落下了帷幕
现在又来到了十五章 哈哈

我记得我在写到第七章的时候 那个时候是军训刚结束后的第三四天
我还一度怀疑自己写不写的完这个操作系统 真的说实话 中间我至少有3-4次打算放弃了 因为真的很多地方出错 都需要自己一点点慢慢调试啊 而且调试起来需要耐心 可能一坐下来就是坐下来一天 整天整天的挑错改Bug

尽管现在的我想到原来自己坐在那里 一瓶水 一个电脑 就是一天的调试时光依然在眼前 那种揪心和苦闷的感觉仍在 但是现在我们已经来到了最后一章了 哈哈 我也不需要去担心自己写不写的完操作系统了 毕竟只有最后一章了 哪里有不写完的道理

还有很多很多想说 想写的话 就留到最后写完之后 代码全发出来的那个博客里面去说吧 哈哈 我看了看最后一章 任务还是没有那么轻松 那我们废话少说 各位看官继续往下看咯


实现fork


实现fork的介绍


哈哈 讲到fork 其实在我刚开始接触到fork的时候 我觉得真的跟书上说的一模一样 我就觉得是一个很高大上的程序 竟然还可以复制进程 哈哈

其实呢 这个fork函数 个人任务还是很接地气的 对于子进程 它只是把目前进程的环境给完完全全复制了一份 虽然是复制了 但是它创建了一个全新的进程 也以为着拥有了独立的内存空间 拥有了独立的页表 它与父进程都是独立的个体了 所以我们肯定也不能保证他们谁先结束 这个就要看schedule调度器的心情了 哈哈

但是由于是复制了 相当于下面执行的程序都是完全一模一样的 他们唯一可能在程序中不同的地方 可能只是getpid()得到的pid编号不同而已 毕竟新进程 在初始化的时候就要赋值pid嘛 对吧

用书上写的一句话 fork就是个叉子 柄部都是一根 在某个位置就一分为二了 但是呢 每个叉子的下面都是一样的 执行流都是一致的
这是我对fork的理解 哈哈


可能有些hxd就要说了 为什么我看的fork程序 子进程和父进程经常做不同的事情 不是说都是一样的执行流吗 我还是忍不住多说两句 各位谅解一下 有些话不写完心里面挺难受的 哈哈
那我们看下面的程序

typedef int pid_t;
int main(void)
{pid_t pid = fork();if(pid != 0)printf("im father process pid = %d\n!",pid);else   printf("im child process pid = %d\n!",pid);return 0;
}

那为什么导致程序进程不一样嘛 那还不是上面pid得到的值不一样
就导致后面的执行流程不一样 这就是我们fork要做的事情了 哈哈 说了那么多感觉还是有点没有说完的感觉
纸上得来终觉浅 绝知此事要躬行 只有代码才是IT人通用的语言 哈哈 各位看官往下看


实现fork的原理


哈哈 尽管现在代码没写 但是刚刚去逛b站 看了半个小时勾勾大战鼠鼠的视频 笑死了 因为我自己看完代码 一行行的分析之后 觉得这部分还是得写写
fork实现的原理 从大方面上说 就是
利用 新建立一个进程 并把新进程的上下文环境给设置好 程序段 变量 虚拟空间位图复制 页表相同也复制一份

之后建立好了新进程了 我们的父进程当然就自然而然返回了 返回值就是子进程的pid 那我们怎么调用子进程呢 子进程跑哪里去了呢

我们的子进程在创建之后 就被放到了进程就绪队列里面去了 然后我们的父进程就自然而然的返回了 当然为了我们子进程也能得到pid 像父进程一样有个fork的返回值 我们这个进程在内核栈设置的函数就是intr_exit 最后要ret回去 等于中断恢复 当然intr_exit我们是为了像父进程一样继续下面的代码 把各种环境恢复

我现在已经把代码写完了 全部的全部我也都理清楚了
最核心的就是 我们把父进程的所用的内存全部复制到我们子进程所分配的内存上面了

就导致父进程的代码段 用户栈 esp也都记录了下来 反正对于这些进程 虚拟地址都是一样的 唯一不一样的只有物理地址而已
而剩余最重要的就是 我们调用了子进程 子进程所放的目标函数是 intr_exit 中断退出函数 中断退出函数就把我们在中断栈中的eaxpop出来了 也就是我们的返回值 那我们怎么让我们的eip变呢 哈哈 这就是交给我们的intr_exit了嘛 还有一个iretd的嘛 哈哈 由于我们调用fork的时候 就留下了返回地址 顺带我们还把栈给复制了 这样子一下子就make sense了嘛


编写完的thread.c(fork_pid)


pid_t fork_pid(void)
{return allocate_pid();
}

编写完的thread.h(struct task_struct)


struct task_struct
{uint32_t* self_kstack;                          //pcb中的 kernel_stack 内核栈pid_t pid;enum task_status status;                        //线程状态uint8_t priority;                    //特权级uint8_t ticks;                     //在cpu 运行的滴答数 看ticks 来判断是否用完了时间片uint32_t elapsed_ticks;                         //一共执行了多久char name[16];struct list_elem general_tag;                   //就绪队列中的连接节点struct list_elem all_list_tag;           //总队列的连接节点int32_t fd_table[MAX_FILES_OPEN_PER_PROC];      //文件描述符数组uint32_t* pgdir;                     //进程自己页表的虚拟地址 线程没有   struct virtual_addr userprog_vaddr;        //用户进程的虚拟空间               struct mem_block_desc u_block_desc[DESC_CNT];   //内存块描述符uint32_t cwd_inode_nr;                //工作目录inode编号int16_t  parent_pid;               //父进程的pid编号uint32_t stack_magic;                //越界检查  因为我们pcb上面的就是我们要用的栈了 到时候还要越界检查
};

编写完的memory.c(get_a_page_without_opvaddrbitmap)


//得到1页大小内存并复制到页表中 专门针对fork时虚拟地址位图无需操作
//因为位图我们后面会复制父进程的 所以当然不用继续对虚拟位图操作了
void* get_a_page_without_opvaddrbitmap(enum pool_flags pf,uint32_t vaddr)
{struct pool* mem_pool = (pf == PF_KERNEL) ? &kernel_pool : &user_pool;lock_acquire(&mem_pool->lock);void* page_phyaddr = palloc(mem_pool);//分配失败if(page_phyaddr == NULL){lock_release(&mem_pool->lock);return NULL;}page_table_add((void*)vaddr,page_phyaddr);lock_release(&mem_pool->lock);return (void*)vaddr;
}

编写完的fork.c


#include "fork.h"
#include "global.h"
#include "stdint.h"
#include "string.h"
#include "memory.h"
#include "interrupt.h"
#include "../thread/sync.h"
#include "../thread/thread.h"
#include "debug.h"
#include "process.h"
#include "stdio-kernel.h"
#include "../fs/file.h"
#include "list.h"extern void intr_exit(void);//复制父进程的pcb
int32_t copy_pcb_vaddrbitmap_stack0(struct task_struct* child_thread,struct task_struct* parent_thread)
{//直接先把pcb所在页 包括内核栈 中断栈全部一起复制过来 其他的需要修改的再一项项改memcpy(child_thread,parent_thread,PG_SIZE);child_thread->pid = fork_pid();child_thread->elapsed_ticks = 0;child_thread->status = TASK_READY; //之后要放到就绪队列 被调度的child_thread->ticks = child_thread->priority; //时间片填满child_thread->parent_pid = parent_thread->pid; //默认是-1 对于子进程的parent_pid 父进程的pidchild_thread->general_tag.prev = child_thread->general_tag.next = NULL; child_thread->all_list_tag.prev = child_thread->all_list_tag.next = NULL;block_desc_init(child_thread->u_block_desc);    //malloc 内存块分配符初始化//虚拟位图需要分配页 的页数 毕竟两个进程不能共享虚拟内存位图嘛 但是我们是需要把父进程的给复制了 uint32_t bitmap_pg_cnt = DIV_ROUND_UP((0xc0000000 - USER_VADDR_START) / PG_SIZE / 8 , PG_SIZE);  void* vaddr_btmp = get_kernel_pages(bitmap_pg_cnt);//复制父进程的虚拟内存位图 并把自己刚分配好的独立位图给我们的子进程赋值memcpy(vaddr_btmp,child_thread->userprog_vaddr.vaddr_bitmap.bits,bitmap_pg_cnt * PG_SIZE);child_thread->userprog_vaddr.vaddr_bitmap.bits = vaddr_btmp;ASSERT(strlen(child_thread->name) < 11); //进程后面加个名字strcat(child_thread->name,"_fork");return 0;
}//设置子进程的进程体 用户栈
//把父进程的内存区全部复制下来 buf_page是因为用户进程间无法共享内存 看不见彼此 只能通过buf_page来作为过渡
void copy_body_stack3(struct task_struct* parent_thread,struct task_struct* child_thread,void* buf_page)
{uint8_t* vaddr_btmp = parent_thread->userprog_vaddr.vaddr_bitmap.bits;uint32_t btmp_bytes_len = parent_thread->userprog_vaddr.vaddr_bitmap.btmp_bytes_len;uint32_t vaddr_start = parent_thread->userprog_vaddr.vaddr_start;uint32_t idx_byte = 0;uint32_t idx_bit = 0;uint32_t prog_vaddr = 0;//根据虚拟内存位图来看 我们只需要把位图中看看哪些页被用了//我们把那些页给复制过去即可 同时也需要把页表在新进程中安装一下while(idx_byte < btmp_bytes_len){if(vaddr_btmp[idx_byte]){idx_bit = 0;while(idx_bit < 8) //一字节8位{if((BITMAP_MASK << idx_bit) & vaddr_btmp[idx_byte]){prog_vaddr = (idx_byte * 8 + idx_bit) * PG_SIZE + vaddr_start;memcpy(buf_page,(void*)prog_vaddr,PG_SIZE);page_dir_activate(child_thread); //切换到用户页表 防止安装到父进程里面去了get_a_page_without_opvaddrbitmap(PF_USER,prog_vaddr);memcpy((void*)prog_vaddr,buf_page,PG_SIZE);page_dir_activate(parent_thread); //切换回父进程}++idx_bit;}}++idx_byte;}
}//给子进程构建内核栈 且设置0 返回值
int32_t build_child_stack(struct task_struct* child_thread)
{//内核栈的最高地址处 intr中断栈最低地址处struct intr_stack* intr_0_stack = \(struct intr_stack*)((uint32_t)child_thread + PG_SIZE - sizeof(struct intr_stack));//返回值 0intr_0_stack->eax = 0;//这里我把其结构体搬过来了/*struct thread_stack{uint32_t ebp;uint32_t ebx;uint32_t edi;uint32_t esi;void (*eip) (thread_func* func,void* func_arg); //和下面的相互照应 以ret 汇编代码进入kernel_thread函数调用void (*unused_retaddr);                         //占位数 在栈顶站住了返回地址的位置 因为是汇编ret thread_func* function;                          //进入kernel_thread要调用的函数地址void* func_arg;                    //参数指针};*///返回地址毕竟是高地址uint32_t* ret_addr_in_thread_stack = (uint32_t*)intr_0_stack - 1;uint32_t* esi_ptr_in_thread_stack =  (uint32_t*)intr_0_stack - 2;uint32_t* edi_ptr_in_thread_stack =  (uint32_t*)intr_0_stack - 3;uint32_t* ebx_ptr_in_thread_stack =  (uint32_t*)intr_0_stack - 4;uint32_t* ebp_ptr_in_thread_stack =  (uint32_t*)intr_0_stack - 5;*ret_addr_in_thread_stack = (uint32_t)intr_exit;//反正之后的pop都会覆盖*esi_ptr_in_thread_stack = *edi_ptr_in_thread_stack = *ebx_ptr_in_thread_stack = \*ebp_ptr_in_thread_stack = 0;//内核栈栈顶child_thread->self_kstack = ebp_ptr_in_thread_stack;return 0;
}//更新inode打开数
void updata_inode_open_cnts(struct task_struct* thread)
{int32_t local_fd = 3,global_fd = 0;while(local_fd < MAX_FILES_OPEN_PER_PROC){global_fd = thread->fd_table[local_fd];ASSERT(global_fd < MAX_FILE_OPEN);if(global_fd != -1)++file_table[global_fd].fd_inode->i_open_cnts;++local_fd;}
}//汇总函数 包装 把父进程资源给子进程
int32_t copy_process(struct task_struct* child_thread,struct task_struct* parent_thread)
{//用于给memcpy 过渡的页面void* buf_page = get_kernel_pages(1);if(buf_page == NULL){printk("copy_process: buf_page alloc fail\n");return -1;}if(copy_pcb_vaddrbitmap_stack0(child_thread,parent_thread) == -1){printk("copy_process: copy_pcb_vaddrbitmap_stack0 fail\n");return -1;}child_thread->pgdir = create_page_dir();printk("child_thread->pgdir %x\nparent_thread->pgdir %x\n",child_thread->pgdir,parent_thread->pgdir);if(child_thread->pgdir == NULL){printk("copy_process: child_thread->pgdir create fail\n");return -1;}copy_body_stack3(parent_thread,child_thread,buf_page);build_child_stack(child_thread);updata_inode_open_cnts(child_thread);mfree_page(PF_KERNEL,buf_page,1);return 0;
}//禁止从内核调用 只能从用户进程调用
pid_t sys_fork(void)
{struct task_struct* parent_thread = running_thread();struct task_struct* child_thread  = get_kernel_pages(1);if(child_thread == NULL)return -1;ASSERT(INTR_OFF == intr_get_status() && parent_thread->pgdir != NULL);if(copy_process(child_thread,parent_thread) == -1)return -1;ASSERT(!elem_find(&thread_ready_list,&child_thread->general_tag));list_append(&thread_ready_list,&child_thread->general_tag);ASSERT(!elem_find(&thread_all_list,&child_thread->all_list_tag));list_append(&thread_all_list,&child_thread->all_list_tag);return child_thread->pid;
}

编写完的fork.h


#ifndef __USERPROG__FORK_H
#define __USERPROG__FORK_H#include "stdint.h"
#include "string.h"
#include "../thread/thread.h"int32_t copy_pcb_vaddrbitmap_stack0(struct task_struct* child_thread,struct task_struct* parent_thread);
void copy_body_stack3(struct task_struct* parent_thread,struct task_struct* child_thread,void* buf_page);
int32_t build_child_stack(struct task_struct* child_thread);
void updata_inode_open_cnts(struct task_struct* thread);
int32_t copy_process(struct task_struct* child_thread,struct task_struct* parent_thread);
pid_t sys_fork(void);#endif

修改完的syscall.c


#include "syscall.h"#define _syscall0(NUMBER) ({ \int retval;      \asm volatile ("int $0x80" : "=a"(retval) : "a"(NUMBER) : "memory"); retval; \})#define _syscall1(NUMBER, ARG1) ({ \int retval;        \asm volatile ("int $0x80" : "=a"(retval) : "a"(NUMBER) , "b"(ARG1) : "memory"); retval; \})#define _syscall2(NUMBER, ARG1, ARG2) ({ \int retval;        \asm volatile ("int $0x80" : "=a"(retval) : "a"(NUMBER) , "b"(ARG1) , "c"(ARG2): "memory"); retval; \})#define _syscall3(NUMBER, ARG1, ARG2, ARG3) ({ \int retval;     \asm volatile ("int $0x80" : "=a"(retval) : "a"(NUMBER) , "b"(ARG1) , "c"(ARG2), "d"(ARG3): "memory"); retval; \})uint32_t getpid(void)
{return _syscall0(SYS_GETPID);
}uint32_t write(int fd,const void* buf,int count)
{return _syscall3(SYS_WRITE,fd,buf,count);
}void* malloc(uint32_t size)
{return _syscall1(SYS_MALLOC,size);
}void free(void* ptr)
{return _syscall1(SYS_FREE,ptr);
}int32_t fork(void)
{return _syscall0(SYS_FORK);
}

修改完的syscall.h


#include "syscall.h"#define _syscall0(NUMBER) ({ \int retval;      \asm volatile ("int $0x80" : "=a"(retval) : "a"(NUMBER) : "memory"); retval; \})#define _syscall1(NUMBER, ARG1) ({ \int retval;        \asm volatile ("int $0x80" : "=a"(retval) : "a"(NUMBER) , "b"(ARG1) : "memory"); retval; \})#define _syscall2(NUMBER, ARG1, ARG2) ({ \int retval;        \asm volatile ("int $0x80" : "=a"(retval) : "a"(NUMBER) , "b"(ARG1) , "c"(ARG2): "memory"); retval; \})#define _syscall3(NUMBER, ARG1, ARG2, ARG3) ({ \int retval;     \asm volatile ("int $0x80" : "=a"(retval) : "a"(NUMBER) , "b"(ARG1) , "c"(ARG2), "d"(ARG3): "memory"); retval; \})uint32_t getpid(void)
{return _syscall0(SYS_GETPID);
}uint32_t write(int fd,const void* buf,int count)
{return _syscall3(SYS_WRITE,fd,buf,count);
}void* malloc(uint32_t size)
{return _syscall1(SYS_MALLOC,size);
}void free(void* ptr)
{return _syscall1(SYS_FREE,ptr);
}int32_t fork(void)
{return _syscall0(SYS_FORK);
}

修改完的syscall-init.c


#include "syscall-init.h"
#include "../lib/user/syscall.h"
#include "stdint.h"
#include "print.h"
#include "interrupt.h"
#include "memory.h"
#include "../thread/thread.h"
#include "../fs/file.h"
#include "fork.h"#define syscall_nr 32
typedef void* syscall;
syscall syscall_table[syscall_nr];uint32_t sys_getpid(void)
{return running_thread()->pid;
}void syscall_init(void)
{put_str("syscall_init start\n");syscall_table[SYS_GETPID] = sys_getpid;syscall_table[SYS_WRITE]  = sys_write;syscall_table[SYS_MALLOC] = sys_malloc;syscall_table[SYS_FREE]   = sys_free;syscall_table[SYS_FORK]   = sys_fork;put_str("syscall_init done\n");
}

修改完的syscall-init.h


#ifndef __USERPROG_SYSCALL_INIT_H
#define __USERPROG_SYSCALL_INIT_H
#include "stdint.h"uint32_t sys_getpid(void);
void syscall_init(void);#endif

修改完的thread.c(thread_init)


void thread_init(void)
{put_str("thread_init start!\n");list_init(&thread_ready_list);list_init(&thread_all_list);lock_init(&pid_lock);process_execute(init,"init");make_main_thread();idle_thread = thread_start("idle",10,idle,NULL); //创建休眠进程put_str("thread_init done!\n");
}

修改完的main.c


#include "print.h"
#include "init.h"
#include "debug.h"
#include "string.h"
#include "memory.h"
#include "../thread/thread.h"
#include "interrupt.h"
#include "../device/console.h"
#include "../device/ioqueue.h"
#include "../device/keyboard.h"
#include "../userprog/process.h"
#include "../lib/user/syscall.h"
#include "../userprog/syscall-init.h"
#include "../lib/stdio.h"
#include "../lib/kernel/stdio-kernel.h"
#include "../fs/fs.h"
#include "../fs/file.h"int main(void)
{put_str("I am kernel\n");init_all();intr_enable();while(1);return 0;
}void init(void)
{uint32_t ret_pid = fork();if(ret_pid)printf("i am father,my pid is %d, ret pid is %d\n",getpid(),ret_pid);else{printf("i am child, my pid is %d, ret pid is %d\n",getpid(),ret_pid);   }while(1);
}

修改完的MakeFile


BUILD_DIR = ./build
ENTRY_POINT = 0xc0001500
AS = nasm
CC = gcc
LD = ld
LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/
ASFLAGS = -f elf
CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes
LDFLAGS =  -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map
OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \$(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o $(BUILD_DIR)/switch.o \$(BUILD_DIR)/debug.o $(BUILD_DIR)/string.o $(BUILD_DIR)/memory.o \$(BUILD_DIR)/bitmap.o $(BUILD_DIR)/thread.o $(BUILD_DIR)/list.o \$(BUILD_DIR)/sync.o $(BUILD_DIR)/console.o $(BUILD_DIR)/keyboard.o \$(BUILD_DIR)/ioqueue.o $(BUILD_DIR)/tss.o $(BUILD_DIR)/process.o \$(BUILD_DIR)/syscall-init.o $(BUILD_DIR)/syscall.o $(BUILD_DIR)/stdio.o \$(BUILD_DIR)/stdio-kernel.o $(BUILD_DIR)/ide.o $(BUILD_DIR)/fs.o $(BUILD_DIR)/inode.o \$(BUILD_DIR)/file.o $(BUILD_DIR)/dir.o $(BUILD_DIR)/fork.o##############     c代码编译     ###############
$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \lib/stdint.h kernel/init.h lib/string.h kernel/memory.h \thread/thread.h kernel/interrupt.h device/console.h \device/keyboard.h device/ioqueue.h userprog/process.h \lib/user/syscall.h userprog/syscall-init.h lib/stdio.h \lib/kernel/stdio-kernel.h fs/file.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \lib/stdint.h kernel/interrupt.h device/timer.h kernel/memory.h \thread/thread.h device/console.h device/keyboard.h userprog/tss.h \userprog/syscall-init.h device/ide.h fs/fs.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h \kernel/kernel.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/kernel/io.h lib/kernel/print.h \kernel/interrupt.h thread/thread.h kernel/debug.h kernel/global.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \lib/kernel/print.h lib/stdint.h kernel/interrupt.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/string.o: lib/string.c lib/string.h \kernel/debug.h kernel/global.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h \lib/stdint.h lib/kernel/bitmap.h kernel/debug.h lib/string.h kernel/global.h \thread/sync.h thread/thread.h lib/kernel/list.h kernel/interrupt.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h kernel/global.h \lib/string.h kernel/interrupt.h lib/kernel/print.h kernel/debug.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h \lib/stdint.h lib/string.h kernel/global.h kernel/memory.h \kernel/debug.h kernel/interrupt.h lib/kernel/print.h \userprog/process.h thread/sync.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/list.o: lib/kernel/list.c lib/kernel/list.h \kernel/interrupt.h lib/stdint.h kernel/debug.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/sync.o: thread/sync.c thread/sync.h \lib/stdint.h thread/thread.h kernel/debug.h kernel/interrupt.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/console.o: device/console.c device/console.h \lib/kernel/print.h thread/sync.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/keyboard.o: device/keyboard.c device/keyboard.h \lib/kernel/print.h lib/kernel/io.h kernel/interrupt.h \kernel/global.h lib/stdint.h device/ioqueue.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/ioqueue.o: device/ioqueue.c device/ioqueue.h \kernel/interrupt.h kernel/global.h kernel/debug.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/tss.o: userprog/tss.c userprog/tss.h \kernel/global.h thread/thread.h lib/kernel/print.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/process.o: userprog/process.c userprog/process.h \lib/string.h kernel/global.h kernel/memory.h lib/kernel/print.h \thread/thread.h kernel/interrupt.h kernel/debug.h device/console.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/syscall-init.o: userprog/syscall-init.c userprog/syscall-init.h \lib/user/syscall.h lib/stdint.h lib/kernel/print.h kernel/interrupt.h thread/thread.h \kernel/memory.h fs/file.h userprog/fork.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/syscall.o: lib/user/syscall.c lib/user/syscall.h $(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/stdio.o: lib/stdio.c lib/stdio.h lib/stdint.h lib/string.h lib/user/syscall.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/stdio-kernel.o: lib/kernel/stdio-kernel.c lib/kernel/stdio-kernel.h \lib/stdio.h device/console.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/ide.o: device/ide.c device/ide.h lib/stdint.h kernel/debug.h \lib/kernel/stdio-kernel.h lib/stdio.h kernel/global.h thread/sync.h \lib/kernel/io.h device/timer.h kernel/interrupt.h lib/kernel/list.h fs/fs.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/fs.o: fs/fs.c fs/fs.h lib/stdint.h kernel/global.h device/ide.h fs/inode.h fs/dir.h \fs/super_block.h lib/kernel/stdio-kernel.h lib/string.h kernel/debug.h lib/kernel/list.h \fs/file.h thread/thread.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/inode.o: fs/inode.c fs/inode.h device/ide.h kernel/debug.h thread/thread.h \kernel/memory.h lib/string.h lib/kernel/list.h kernel/interrupt.h lib/kernel/bitmap.h \fs/file.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/file.o: fs/file.c fs/file.h lib/kernel/stdio-kernel.h thread/thread.h device/ide.h \fs/file.h kernel/global.h kernel/interrupt.h device/console.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/dir.o: fs/dir.c fs/dir.h device/ide.h fs/fs.h fs/inode.h kernel/memory.h lib/string.h lib/stdint.h \lib/kernel/stdio-kernel.h kernel/debug.h fs/file.h kernel/memory.h lib/string.h kernel/debug.h$(CC) $(CFLAGS) $< -o $@$(BUILD_DIR)/fork.o: userprog/fork.c userprog/fork.h kernel/global.h lib/stdint.h lib/string.h \kernel/memory.h kernel/interrupt.h thread/sync.h thread/thread.h  kernel/debug.h userprog/process.h \lib/kernel/stdio-kernel.h fs/file.h lib/kernel/list.h$(CC) $(CFLAGS) $< -o $@##############    汇编代码编译    ###############
$(BUILD_DIR)/kernel.o: kernel/kernel.S$(AS) $(ASFLAGS) $< -o $@$(BUILD_DIR)/print.o: lib/kernel/print.S$(AS) $(ASFLAGS) $< -o $@$(BUILD_DIR)/switch.o: thread/switch.S$(AS) $(ASFLAGS) $< -o $@##############    链接所有目标文件    #############
$(BUILD_DIR)/kernel.bin: $(OBJS)$(LD) $(LDFLAGS) $^ -o $@.PHONY : mk_dir hd clean allmk_dir:if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fihd:dd if=$(BUILD_DIR)/kernel.bin \of=/home/cooiboi/bochs/hd60M.img \bs=512 count=200 seek=9 conv=notruncclean:cd $(BUILD_DIR) && rm -f  ./*build: $(BUILD_DIR)/kernel.binall: mk_dir build hd

make all 验证成果


说实话 这部分我debug大概debug了 四个小时 真的无语
我把原来的内存分配 位图设置 宏定义 各种地方多多少少都找出了错误 当然不用担心哈哈 原来的博客当然我在相对应的地方修改了代码

但总而言之 这里的pid终于的终于 还是弄出来了 不容易啊


实现read putchar clear系统调用


修改完的fs.c(sys_read)


int32_t sys_read(int32_t fd,void* buf,uint32_t count)
{int ret = -1;if(fd < 0 || fd == stdout_no || fd == stderr_no){printk("sys_read: fd error\n");return -1;}else if(fd == stdin_no){char* buffer = buf;uint32_t bytes_read = 0;while(bytes_read < count){*buffer = ioq_getchar(&ioqueue);++bytes_read;++buffer;}ret = (bytes_read == 0) ? -1 : (int32_t)bytes_read;}else{uint32_t _fd = fd_local2global(fd);ret = file_read(&file_table[_fd],buf,count);}   return ret;
}

修改完的stdio-kernel.c(sys_putchar)


void sys_putchar(const char chr)
{console_put_char(chr);
}

修改完的print.S(cls_screen)


global cls_screen:
cls_screen:                               ; 连着set_cursorpushad                                ; 用户态不允许访问显存 只能由中断后进入内核态 故此为系统调用mov ax,SELECTOR_VIDEO                 ; ax做中转mov gs,ax                             ; 先放到gs里面 进入中断后再mov ebx,0mov ecx,80*25                         ;清屏幕.cls:mov word [gs:ebx],0x720           ;黑底白字空格键add ebx,2                              ;一次两字节loop .cls        mov ebx,0jmp set_cursor

修改完的syscall.c(read putchar clear)


int32_t read(int32_t fd,void* buf,uint32_t count)
{return _syscall3(SYS_READ,fd,buf,count);
}void putchar(const char chr)
{return _syscall1(SYS_PUTCHAR,chr);
}void clear(void)
{return _syscall0(SYS_CLEAR);
}

修改完的syscall.h


#ifndef __LIB_USER_SCSCALL_H
#define __LIB_USER_SCSCALL_H
#include "stdint.h"
enum SYSCALL_NR
{SYS_GETPID,SYS_WRITE,SYS_MALLOC,SYS_FREE,SYS_FORK,SYS_READ,SYS_PUTCHAR,SYS_CLEAR
};uint32_t getpid(void);
uint32_t write(int fd,const void* buf,int count);
void* malloc(uint32_t size);
void free(void* ptr);
int32_t fork(void);
int32_t read(int32_t fd,void* buf,uint32_t count);
void putchar(const char chr);
void clear(void);#endif

修改完的syscall-init.c


#include "syscall-init.h"
#include "../lib/user/syscall.h"
#include "stdint.h"
#include "print.h"
#include "interrupt.h"
#include "memory.h"
#include "../thread/thread.h"
#include "../fs/file.h"
#include "fork.h"
#include "../lib/kernel/stdio-kernel.h"#define syscall_nr 32
typedef void* syscall;
syscall syscall_table[syscall_nr];uint32_t sys_getpid(void)
{return running_thread()->pid;
}void syscall_init(void)
{put_str("syscall_init start\n");syscall_table[SYS_GETPID]  = sys_getpid;syscall_table[SYS_WRITE]   = sys_write;syscall_table[SYS_MALLOC]  = sys_malloc;syscall_table[SYS_FREE]    = sys_free;syscall_table[SYS_FORK]    = sys_fork;syscall_table[SYS_READ]    = sys_read;syscall_table[SYS_PUTCHAR] = sys_putchar;syscall_table[SYS_CLEAR]   = cls_screen;put_str("syscall_init done\n");
}

结束语(上)


因为这一章总体上内容还是挺多的 一章写不完 还是分个上中下写吧
那各位看官下一章见!

《操作系统真象还原》第十五章 ---- 实现系统交互 操作系统最终章 四十五天的不易与坚持终完结撒花(上)相关推荐

  1. 《操作系统真象还原》第十五章 ---- 实现系统交互 操作系统最终章 四十五天的不易与坚持终完结撒花 (下)(遗憾告终)

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 部分缩写熟知 实现exec的思路与启发 遗憾告终 专栏博客链接 <操作系统真象还原>从零开始自制操作系统 全章节博客链接 相关查阅 ...

  2. 《操作系统真象还原》第七章

    <操作系统真象还原>第七章 本篇对应书籍第七章的内容 本篇内容介绍了操作系统的中断处理机制,建立中断描述符表,填充门描述符,以及中断处理程序,初始化8259A中断控制器实现外部中断功能,控 ...

  3. 《操作系统真象还原》第十三章 ---- 编写硬盘驱动软件 行百里者半九十终成时喜悦溢于言表

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 部分缩写熟知 闲聊时刻 提前需要准备编写的函数 实现printk 实现sprintf函数 创建从盘 创建从盘的步骤 修改后的bochsrc.d ...

  4. 《操作系统真象还原》第六章 ---- 开启c语言编写函数时代 首挑打印函数小试牛刀 费心讨力重回gcc降级 终尝多日调试之喜悦

    文章目录 专栏博客链接 相关查阅博客链接 本书中错误勘误 部分缩写熟知 修改代码的小闲聊 编写print.S(实现打印函数) print.S代码 print.h代码和stdint.h代码 修改main ...

  5. 操作系统真象还原_第零章_解惑

    第零章 解惑 1.操作系统是什么? 类比一 古代部落害怕手下的人滥用武器伤害他人,所以制造武器是要向部落申请.而人们只有申请的资格,申请结果有部落决定. 设计操作系统的人害怕使用者滥用硬件(例如把操作 ...

  6. 操作系统真象还原第1.5章 NASM汇编学习

    第二章有使用 NASM 汇编写主引导记录 MBR 的内容. 在写第二章的代码前,每天晚上下班后花一些时间简单地回顾了 NASM 汇编的内容,只复习了最简单的语法,之后写 OS 时再边写边查资料. 指令 ...

  7. 【操作系统真象还原】第4章:保护模式入门(4.4~4.5节)

    目录 4.4 处理器微架构简介 4.4.1 流水线 4.4.2 乱序执行 4.4.3 缓存 4.4.4 分支预测 4.5 使用远跳转指令清空流水线,更新段描述符缓冲寄存器 4.6 保护模式之内存段的保 ...

  8. 《操作系统真象还原》第4章 保护模式入门 ing... 持续更新

    目录 文章目录 目录 概述 初见保护模式 代码 32push.S 全局描述符表 段描述符 GDT.LDT及选择子 GDT 选择子正式介绍 LDT 打开A20地址线 保护模式的开关,CR0寄存器的 PE ...

  9. 《操作系统真象还原》从零开始自制操作系统 全流程记录

    文章目录 前引 章节博客链接 实现源码链接 前引 这本<操作系统真象还原>里面一共有十五个章节 大约760页 这些系列博客也是我在做完哈工大操作系统Lab之后 觉得还是有些朦朦胧胧 毅然决 ...

最新文章

  1. python科学计数法转换_对比Python学习Go 基本数据结构
  2. LeetCode 705. Design HashSet (设计哈希集合)
  3. html中评论存储方法,html5基于数据存储的评论留言板demo
  4. 怎么获取php文件,学习猿地-php怎么获取文件修改时间?
  5. DecExpress 帮助网站
  6. 看到别人的简历,mark一下。
  7. 如何产生好的词向量?
  8. uniapp开发hbuilder连接安卓模拟器mumu
  9. MATLAB 如何导入.txt文本
  10. 一、ODI教程--ODI的介绍
  11. PowerDesigner 模型生成转化为sql脚本
  12. 520被女朋友三番两次拉黑后,我用 Python 写了个“舔狗”必备神器
  13. 2021最新最全前端面试题(包含HTML、CSS、JavaScript、Vue、React、浏览器、算法与数据结构等)
  14. 2014年十大最失意的科技大佬:最差CEO揭晓
  15. MATLAB之傅里叶展开(五)
  16. 什么软件可以用蓝牙测试信号,litepoint IQview蓝牙测试仪/无线wifi网络信号测试仪...
  17. CAN总线概况与原理(转)
  18. 区间素数个数 树状数组 HIT 1867 经理的烦恼
  19. 湖广填四川与安岳姓氏源流
  20. 概率图模型学习笔记:HMM、MEMM、CRF

热门文章

  1. 【深度】韦东山:一文看看尽linux对中断处理的前世今生
  2. ASP.NET中的Session和Cookie
  3. linux-文件切割-splitcsplit
  4. 【必备算法】动态规划:LeetCode题(六)322. 零钱兑换,518. 零钱兑换 II
  5. [小程序项目] 使用微信开发者工具 新闻小项目 轮播图 新闻列表页 点击跳转新闻详情页 登录 获取用户基本信息
  6. 输出以个小于1万亿的正整数的拼音读法
  7. 【前端圭臬】二:你知道的和不知道的 HTML
  8. 首屏优化,减少白屏时间
  9. 凡科建站产品体验报告
  10. 食品药品监督管理局:支持推动药品流通企业转型升级