第十五章 终章 呈现系统交互界面(下)

本文最后更新于:1 年前

第十五章 终章 呈现系统交互界面(下)

写在前面

这里15.5的时候卡了我三个多小时,原因是在sh编译时遇到了很多困难,庆幸自己没有放弃坚持了下来,要不可能后悔死。

加载用户进程

userprog/exec.h创建

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef __USERPROG__EXEC_H
#define __USERPROG__EXEC_H

#include "stdint.h"
#include "string.h"
#include "../thread/thread.h"

static bool segment_load(int32_t fd, uint32_t offset, uint32_t filesz, uint32_t vaddr);
static int32_t load(const char* pathname);
int32_t sys_execv(const char* path, const char* argv[]);

#endif

userprog/exec.c创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include "exec.h"
#include "../thread/thread.h"
#include "stdio-kernel.h"
#include "../fs/fs.h"
#include "string.h"
#include "global.h"
#include "memory.h"

extern void intr_exit(void);
typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off;
typedef uint16_t Elf32_Half;

/* 32位elf头 */
struct Elf32_Ehdr {
unsigned char e_ident[16];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
};

/* 程序头表Program header.就是段描述头 */
struct Elf32_Phdr {
Elf32_Word p_type; // 见下面的enum segment_type
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
};

/* 段类型 */
enum segment_type {
PT_NULL, // 忽略
PT_LOAD, // 可加载程序段
PT_DYNAMIC, // 动态加载信息
PT_INTERP, // 动态加载器名称
PT_NOTE, // 一些辅助信息
PT_SHLIB, // 保留
PT_PHDR // 程序头表
};


/* 将文件描述符fd指向的文件中,偏移为offset,大小为filesz的段加载到虚拟地址为vaddr的内存 */
static bool segment_load(int32_t fd, uint32_t offset, uint32_t filesz, uint32_t vaddr) {
uint32_t vaddr_first_page = vaddr & 0xfffff000; // vaddr地址所在的页框
uint32_t size_in_first_page = PG_SIZE - (vaddr & 0x00000fff); // 加载到内存后,文件在第一个页框中占用的字节大小
uint32_t occupy_pages = 0;
/* 若一个页框容不下该段 */
if (filesz > size_in_first_page) {
uint32_t left_size = filesz - size_in_first_page;
occupy_pages = DIV_ROUND_UP(left_size, PG_SIZE) + 1; // 1是指vaddr_first_page
} else {
occupy_pages = 1;
}

/* 为进程分配内存 */
uint32_t page_idx = 0;
uint32_t vaddr_page = vaddr_first_page;
while (page_idx < occupy_pages) {
uint32_t* pde = pde_ptr(vaddr_page);
uint32_t* pte = pte_ptr(vaddr_page);

/* 如果pde不存在,或者pte不存在就分配内存.
* pde的判断要在pte之前,否则pde若不存在会导致
* 判断pte时缺页异常 */
if (!(*pde & 0x00000001) || !(*pte & 0x00000001)) {
if (get_a_page(PF_USER, vaddr_page) == NULL) {
return false;
}
} // 如果原进程的页表已经分配了,利用现有的物理页,直接覆盖进程体
vaddr_page += PG_SIZE;
page_idx++;
}
sys_lseek(fd, offset, SEEK_SET);
sys_read(fd, (void*)vaddr, filesz);
return true;
}

/* 从文件系统上加载用户程序pathname,成功则返回程序的起始地址,否则返回-1 */
static int32_t load(const char* pathname) {
int32_t ret = -1;
struct Elf32_Ehdr elf_header;
struct Elf32_Phdr prog_header;
memset(&elf_header, 0, sizeof(struct Elf32_Ehdr));

int32_t fd = sys_open(pathname, O_RDONLY);
if (fd == -1) {
return -1;
}

if (sys_read(fd, &elf_header, sizeof(struct Elf32_Ehdr)) != sizeof(struct Elf32_Ehdr)) {
ret = -1;
goto done;
}

/* 校验elf头 */
if (memcmp(elf_header.e_ident, "\177ELF\1\1\1", 7) \
|| elf_header.e_type != 2 \
|| elf_header.e_machine != 3 \
|| elf_header.e_version != 1 \
|| elf_header.e_phnum > 1024 \
|| elf_header.e_phentsize != sizeof(struct Elf32_Phdr)) {
ret = -1;
goto done;
}

Elf32_Off prog_header_offset = elf_header.e_phoff;
Elf32_Half prog_header_size = elf_header.e_phentsize;

/* 遍历所有程序头 */
uint32_t prog_idx = 0;
while (prog_idx < elf_header.e_phnum) {
memset(&prog_header, 0, prog_header_size);

/* 将文件的指针定位到程序头 */
sys_lseek(fd, prog_header_offset, SEEK_SET);

/* 只获取程序头 */
if (sys_read(fd, &prog_header, prog_header_size) != prog_header_size) {
ret = -1;
goto done;
}

/* 如果是可加载段就调用segment_load加载到内存 */
if (PT_LOAD == prog_header.p_type) {
if (!segment_load(fd, prog_header.p_offset, prog_header.p_filesz, prog_header.p_vaddr)) {
ret = -1;
goto done;
}
}

/* 更新下一个程序头的偏移 */
prog_header_offset += elf_header.e_phentsize;
prog_idx++;
}
ret = elf_header.e_entry;
done:
sys_close(fd);
return ret;
}

/* 用path指向的程序替换当前进程 */
int32_t sys_execv(const char* path, const char* argv[]) {
uint32_t argc = 0;
while (argv[argc]) {
argc++;
}
int32_t entry_point = load(path);
if (entry_point == -1) { // 若加载失败则返回-1
return -1;
}

struct task_struct* cur = running_thread();
/* 修改进程名 */
memcpy(cur->name, path, TASK_NAME_LEN);

/* 修改栈中参数 */
struct intr_stack* intr_0_stack = (struct intr_stack*)((uint32_t)cur + PG_SIZE - sizeof(struct intr_stack));
/* 参数传递给用户进程 */
intr_0_stack->ebx = (int32_t)argv;
intr_0_stack->ecx = argc;
intr_0_stack->eip = (void*)entry_point;
/* 使新用户进程的栈地址为最高用户空间地址 */
intr_0_stack->esp = (void*)0xc0000000;

/* exec不同于fork,为使新进程更快被执行,直接从中断返回 */
asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (intr_0_stack) : "memory");
return 0;
}

command/prog_no_arg.c创建

1
2
3
4
5
6
7
#include "stdio.h"

int main(void){
printf("prog_no_arg from disk\n");
while(1);
return 0;
}

command/compile.sh创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
####  此脚本应该在command目录下执行

if [[ ! -d "../lib" || ! -d "../build" ]];then
echo "dependent dir don\`t exist!"
cwd=$(pwd)
cwd=${cwd##*/}
cwd=${cwd%/}
if [[ $cwd != "command" ]];then
echo -e "you\`d better in command dir\n"
fi
exit
fi

BIN="prog_no_arg"
CFLAGS="-Wall -c -fno-builtin -W -Wstrict-prototypes \
-Wmissing-prototypes -Wsystem-headers"
### LIB= "../lib"
OBJS="../build/string.o ../build/syscall.o \
../build/stdio.o ../build/assert.o"
DD_IN=$BIN
DD_OUT="/home/podest/bochs/hd60M.img"


gcc -m32 $CFLAGS -I "../lib" -o $BIN".o" $BIN".c"
ld -m elf_i386 -e main $BIN".o" $OBJS -o $BIN
SEC_CNT=$(ls -l $BIN|awk '{printf("%d", ($5+511)/512)}')

if [[ -f $BIN ]];then
dd if=./$DD_IN of=$DD_OUT bs=512 \
count=$SEC_CNT seek=300 conv=notrunc
fi

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#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"
#include "../shell/shell.h"

int main(void) {
put_str("I am kernel\n");
init_all();
intr_enable();
/************* 写入应用程序 *************/
uint32_t file_size = 4777;
uint32_t sec_cnt = DIV_ROUND_UP(file_size, 512);
struct disk* sda = &channels[0].devices[0];
void* prog_buf = sys_malloc(file_size);
ide_read(sda, 300, prog_buf, sec_cnt);
int32_t fd = sys_open("/prog_no_arg", O_CREAT|O_RDWR);
if (fd != -1) {
if(sys_write(fd, prog_buf, file_size) == -1) {
printk("file write error!\n");
while(1);
}
}
/************* 写入应用程序结束 *************/
cls_screen();
console_put_str("[rabbit@localhost /]$ ");
while(1);
return 0;
}

/* init进程 */
void init(void) {
uint32_t ret_pid = fork();
if(ret_pid) { // 父进程
while(1);
} else { // 子进程
my_shell();
}
PANIC("init: should not be here");
}

shell脚本编译成功

图为命令行运行界面

运行结果

图为bochs运行界面

command/start.S创建

1
2
3
4
5
6
7
8
9
10
[bits 32]
extern main
extern exit
section .text
global _start
_start:
;下面这两个要和execv中load之后指定的寄存器一致
push ebx ;压入argv
push ecx ;压入argc
call main

command/prog_arg.c创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "stdio.h"
#include "../lib/user/syscall.h"
#include "../lib/string.h"


int main(int argc, char** argv){
int arg_idx = 0;
while(arg_idx < argc){
printf("argv[%d] is %s\n", arg_idx, argv[arg_idx]);
arg_idx++;
}
int pid = fork();
if (pid){
int delay = 900000;
while(delay--);
printf("\n i'm father prog, my pid%d,i will show process list\n", getpid());
ps();
}else{
char abs_path[512] = {0};
printf("\n i'm chhild prog, my pid: %d, i will exec %s right now\n", getpid(), argv[1]);
if (argv[1][0] != '/'){
getcwd(abs_path, 512);
strcat(abs_path, "/");
strcat(abs_path, argv[1]);
execv(abs_path, argv);
}else{
execv(argv[1], argv);
}
}
while(1);
return 0;
}

command/compile2.sh创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
####  此脚本应该在command目录下执行

if [[ ! -d "../lib" || ! -d "../build" ]];then
echo "dependent dir don\`t exist!"
cwd=$(pwd)
cwd=${cwd##*/}
cwd=${cwd%/}
if [[ $cwd != "command" ]];then
echo -e "you\`d better in command dir\n"
fi
exit
fi

BIN="prog_arg"
CFLAGS="-Wall -c -fno-builtin -W -Wstrict-prototypes \
-Wmissing-prototypes -Wsystem-headers"
#LIBS= "-I ../lib -I ../lib/user -I ../fs"
OBJS="../build/string.o ../build/syscall.o \
../build/stdio.o ../build/assert.o start.o"
DD_IN=$BIN
DD_OUT="/home/podest/bochs/Seven.img"

nasm -f elf ./start.S -o ./start.o
ar rcs simple_crt.a $OBJS start.o

gcc -m32 $CFLAGS -I "../lib/" -I "../lib/kernel/" -I "../lib/user/" -I "../kernel/" -I "../device/" -I "../thread/" -I "../userprog/" -I "../fs/" -I "../shell/" -o $BIN".o" $BIN".c"
ld -m elf_i386 $BIN".o" simple_crt.a -o $BIN
SEC_CNT=$(ls -l $BIN|awk '{printf("%d", ($5+511)/512)}')

if [[ -f $BIN ]];then
dd if=./$DD_IN of=$DD_OUT bs=512 \
count=$SEC_CNT seek=300 conv=notrunc
fi

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#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"
#include "../shell/shell.h"

int main(void) {
put_str("I am kernel\n");
init_all();
intr_enable();


/************* 写入应用程序 *************/
uint32_t file_size = 5329;
uint32_t sec_cnt = DIV_ROUND_UP(file_size, 512);
struct disk* sda = &channels[0].devices[0];
void* prog_buf = sys_malloc(file_size);
ide_read(sda, 300, prog_buf, sec_cnt);
int32_t fd = sys_open("/prog_arg", O_CREAT|O_RDWR);
if (fd != -1) {
if(sys_write(fd, prog_buf, file_size) == -1) {
printk("file write error!\n");
while(1);
}
}
/************* 写入应用程序结束 *************/
cls_screen();
console_put_str("[rabbit@localhost /]$ ");
while(1);
return 0;
}


/* init进程 */
void init(void) {
uint32_t ret_pid = fork();
if(ret_pid) { // 父进程
while(1);
} else { // 子进程
my_shell();
}
PANIC("init: should not be here");
}

运行结果

图为bochs运行界面

实现系统调用wait和exit

thread/thread.h修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* 进程或线程的pcb,程序控制块 */
struct task_struct
{
uint32_t* self_kstack; // 各内核线程都用自己的内核栈
pid_t pid;
enum task_status status;
char name[TASK_NAME_LEN];
uint8_t priority;
uint8_t ticks; // 每次在处理器上执行的时间嘀嗒数
/* 此任务自上cpu运行后至今占用了多少cpu嘀嗒数,
* 也就是此任务执行了多久*/
uint32_t elapsed_ticks;
/* general_tag的作用是用于线程在一般的队列中的结点 */
struct list_elem general_tag;
/* all_list_tag的作用是用于线程队列thread_all_list中的结点 */
struct list_elem all_list_tag;
uint32_t* pgdir; // 进程自己页表的虚拟地址
struct virtual_addr userprog_vaddr; // 用户进程的虚拟地址
struct mem_block_desc u_block_desc[DESC_CNT]; // 用户进程内存块描述符
int8_t exit_status;
uint32_t stack_magic; // 用这串数字做栈的边界标记,用于检测栈的溢出
int32_t fd_table[MAX_FILES_OPEN_PER_PROC]; // 已打开文件数组
uint32_t cwd_inode_nr; //进程所在工作目录inode的编号
int16_t parent_pid; // 父进程的pid
};

kernel/memory.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 根据物理页框地址pg_phy_addr在相应的内存池的位图清0,不改动页表*/
void free_a_phy_page(uint32_t pg_phy_addr) {
struct pool* mem_pool;
uint32_t bit_idx = 0;
if (pg_phy_addr >= user_pool.phy_addr_start) {
mem_pool = &user_pool;
bit_idx = (pg_phy_addr - user_pool.phy_addr_start) / PG_SIZE;
} else {
mem_pool = &kernel_pool;
bit_idx = (pg_phy_addr - kernel_pool.phy_addr_start) / PG_SIZE;
}
bitmap_set(&mem_pool->pool_bitmap, bit_idx, 0);
}

thread/thread.c新增+修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/* pid的位图,最大支持1024个pid */
uint8_t pid_bitmap_bits[128] = {0};
/* pid池 */
struct pid_pool {
struct bitmap pid_bitmap; // pid位图
uint32_t pid_start; // 起始pid
struct lock pid_lock; // 分配pid锁
}pid_pool;

/* 初始化pid池 */
static void pid_pool_init(void) {
pid_pool.pid_start = 1;
pid_pool.pid_bitmap.bits = pid_bitmap_bits;
pid_pool.pid_bitmap.btmp_bytes_len = 128;
bitmap_init(&pid_pool.pid_bitmap);
lock_init(&pid_pool.pid_lock);
}


/* 分配pid */
static pid_t allocate_pid(void) {
lock_acquire(&pid_pool.pid_lock);
int32_t bit_idx = bitmap_scan(&pid_pool.pid_bitmap, 1);
bitmap_set(&pid_pool.pid_bitmap, bit_idx, 1);
lock_release(&pid_pool.pid_lock);
return (bit_idx + pid_pool.pid_start);
}


/* 释放pid */
void release_pid(pid_t pid) {
lock_acquire(&pid_pool.pid_lock);
int32_t bit_idx = pid - pid_pool.pid_start;
bitmap_set(&pid_pool.pid_bitmap, bit_idx, 0);
lock_release(&pid_pool.pid_lock);
}


/* 回收thread_over的pcb和页表,并将其从调度队列中去除 */
void thread_exit(struct task_struct* thread_over, bool need_schedule) {
/* 要保证schedule在关中断情况下调用 */
intr_disable();
thread_over->status = TASK_DIED;

/* 如果thread_over不是当前线程,就有可能还在就绪队列中,将其从中删除 */
if (elem_find(&thread_ready_list, &thread_over->general_tag)) {
list_remove(&thread_over->general_tag);
}
if (thread_over->pgdir) { // 如是进程,回收进程的页表
mfree_page(PF_KERNEL, thread_over->pgdir, 1);
}

/* 从all_thread_list中去掉此任务 */
list_remove(&thread_over->all_list_tag);

/* 回收pcb所在的页,主线程的pcb不在堆中,跨过 */
if (thread_over != main_thread) {
mfree_page(PF_KERNEL, thread_over, 1);
}

/* 归还pid */
release_pid(thread_over->pid);

/* 如果需要下一轮调度则主动调用schedule */
if (need_schedule) {
schedule();
PANIC("thread_exit: should not be here\n");
}
}


/* 比对任务的pid */
static bool pid_check(struct list_elem* pelem, int32_t pid) {
struct task_struct* pthread = elem2entry(struct task_struct, all_list_tag, pelem);
if (pthread->pid == pid) {
return true;
}
return false;
}


/* 根据pid找pcb,若找到则返回该pcb,否则返回NULL */
struct task_struct* pid2thread(int32_t pid) {
struct list_elem* pelem = list_traversal(&thread_all_list, pid_check, pid);
if (pelem == NULL) {
return NULL;
}
struct task_struct* thread = elem2entry(struct task_struct, all_list_tag, pelem);
return thread;
}

userprog/wait_exit.h创建

1
2
3
4
5
6
#ifndef __USERPROG_WAITEXIT_H
#define __USERPROG_WAITEXIT_H
#include "../thread/thread.h"
pid_t sys_wait(int32_t* status);
void sys_exit(int32_t status);
#endif

userprog/wait_exit.c创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "wait_exit.h"
#include "global.h"
#include "debug.h"
#include "../thread/thread.h"
#include "list.h"
#include "stdio-kernel.h"
#include "memory.h"
#include "bitmap.h"
#include "../fs/fs.h"

/* 释放用户进程资源:
* 1 页表中对应的物理页
* 2 虚拟内存池占物理页框
* 3 关闭打开的文件 */
static void release_prog_resource(struct task_struct* release_thread) {
uint32_t* pgdir_vaddr = release_thread->pgdir;
uint16_t user_pde_nr = 768, pde_idx = 0;
uint32_t pde = 0;
uint32_t* v_pde_ptr = NULL; // v表示var,和函数pde_ptr区分

uint16_t user_pte_nr = 1024, pte_idx = 0;
uint32_t pte = 0;
uint32_t* v_pte_ptr = NULL; // 加个v表示var,和函数pte_ptr区分

uint32_t* first_pte_vaddr_in_pde = NULL; // 用来记录pde中第0个pte的地址
uint32_t pg_phy_addr = 0;

/* 回收页表中用户空间的页框 */
while (pde_idx < user_pde_nr) {
v_pde_ptr = pgdir_vaddr + pde_idx;
pde = *v_pde_ptr;
if (pde & 0x00000001) { // 如果页目录项p位为1,表示该页目录项下可能有页表项
first_pte_vaddr_in_pde = pte_ptr(pde_idx * 0x400000); // 一个页表表示的内存容量是4M,即0x400000
pte_idx = 0;
while (pte_idx < user_pte_nr) {
v_pte_ptr = first_pte_vaddr_in_pde + pte_idx;
pte = *v_pte_ptr;
if (pte & 0x00000001) {
/* 将pte中记录的物理页框直接在相应内存池的位图中清0 */
pg_phy_addr = pte & 0xfffff000;
free_a_phy_page(pg_phy_addr);
}
pte_idx++;
}
/* 将pde中记录的物理页框直接在相应内存池的位图中清0 */
pg_phy_addr = pde & 0xfffff000;
free_a_phy_page(pg_phy_addr);
}
pde_idx++;
}

/* 回收用户虚拟地址池所占的物理内存*/
uint32_t bitmap_pg_cnt = (release_thread->userprog_vaddr.vaddr_bitmap.btmp_bytes_len) / PG_SIZE;
uint8_t* user_vaddr_pool_bitmap = release_thread->userprog_vaddr.vaddr_bitmap.bits;
mfree_page(PF_KERNEL, user_vaddr_pool_bitmap, bitmap_pg_cnt);

/* 关闭进程打开的文件 */
uint8_t fd_idx = 3;
while(fd_idx < MAX_FILES_OPEN_PER_PROC) {
if (release_thread->fd_table[fd_idx] != -1) {
sys_close(fd_idx);
}
fd_idx++;
}
}

/* list_traversal的回调函数,
* 查找pelem的parent_pid是否是ppid,成功返回true,失败则返回false */
static bool find_child(struct list_elem* pelem, int32_t ppid) {
/* elem2entry中间的参数all_list_tag取决于pelem对应的变量名 */
struct task_struct* pthread = elem2entry(struct task_struct, all_list_tag, pelem);
if (pthread->parent_pid == ppid) { // 若该任务的parent_pid为ppid,返回
return true; // list_traversal只有在回调函数返回true时才会停止继续遍历,所以在此返回true
}
return false; // 让list_traversal继续传递下一个元素
}

/* list_traversal的回调函数,
* 查找状态为TASK_HANGING的任务 */
static bool find_hanging_child(struct list_elem* pelem, int32_t ppid) {
struct task_struct* pthread = elem2entry(struct task_struct, all_list_tag, pelem);
if (pthread->parent_pid == ppid && pthread->status == TASK_HANGING) {
return true;
}
return false;
}

/* list_traversal的回调函数,
* 将一个子进程过继给init */
static bool init_adopt_a_child(struct list_elem* pelem, int32_t pid) {
struct task_struct* pthread = elem2entry(struct task_struct, all_list_tag, pelem);
if (pthread->parent_pid == pid) { // 若该进程的parent_pid为pid,返回
pthread->parent_pid = 1;
}
return false; // 让list_traversal继续传递下一个元素
}

/* 等待子进程调用exit,将子进程的退出状态保存到status指向的变量.
* 成功则返回子进程的pid,失败则返回-1 */
pid_t sys_wait(int32_t* status) {
struct task_struct* parent_thread = running_thread();

while(1) {
/* 优先处理已经是挂起状态的任务 */
struct list_elem* child_elem = list_traversal(&thread_all_list, find_hanging_child, parent_thread->pid);
/* 若有挂起的子进程 */
if (child_elem != NULL) {
struct task_struct* child_thread = elem2entry(struct task_struct, all_list_tag, child_elem);
*status = child_thread->exit_status;

/* thread_exit之后,pcb会被回收,因此提前获取pid */
uint16_t child_pid = child_thread->pid;

/* 2 从就绪队列和全部队列中删除进程表项*/
thread_exit(child_thread, false); // 传入false,使thread_exit调用后回到此处
/* 进程表项是进程或线程的最后保留的资源, 至此该进程彻底消失了 */

return child_pid;
}

/* 判断是否有子进程 */
child_elem = list_traversal(&thread_all_list, find_child, parent_thread->pid);
if (child_elem == NULL) { // 若没有子进程则出错返回
return -1;
} else {
/* 若子进程还未运行完,即还未调用exit,则将自己挂起,直到子进程在执行exit时将自己唤醒 */
thread_block(TASK_WAITING);
}
}
}

/* 子进程用来结束自己时调用 */
void sys_exit(int32_t status) {
struct task_struct* child_thread = running_thread();
child_thread->exit_status = status;
if (child_thread->parent_pid == -1) {
PANIC("sys_exit: child_thread->parent_pid is -1\n");
}

/* 将进程child_thread的所有子进程都过继给init */
list_traversal(&thread_all_list, init_adopt_a_child, child_thread->pid);

/* 回收进程child_thread的资源 */
release_prog_resource(child_thread);

/* 如果父进程正在等待子进程退出,将父进程唤醒 */
struct task_struct* parent_thread = pid2thread(child_thread->parent_pid);
if (parent_thread->status == TASK_WAITING) {
thread_unblock(parent_thread);
}

/* 将自己挂起,等待父进程获取其status,并回收其pcb */
thread_block(TASK_HANGING);
}

lib/user/syscall.c新增

1
2
3
4
5
6
7
8
9
10

/* 以状态status退出 */
void exit(int32_t status) {
_syscall1(SYS_EXIT, status);
}

/* 等待子进程,子进程状态存储到status */
pid_t wait(int32_t* status) {
return _syscall1(SYS_WAIT, status);
}

lib/user/syscall.h修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
enum SYSCALL_NR {
SYS_GETPID,
SYS_WRITE,
SYS_MALLOC,
SYS_FREE,
SYS_FORK,
SYS_READ,
SYS_PUTCHAR,
SYS_CLEAR,
SYS_GETCWD,
SYS_OPEN,
SYS_CLOSE,
SYS_LSEEK,
SYS_UNLINK,
SYS_MKDIR,
SYS_OPENDIR,
SYS_CLOSEDIR,
SYS_CHDIR,
SYS_RMDIR,
SYS_READDIR,
SYS_REWINDDIR,
SYS_STAT,
SYS_PS,
SYS_EXECV,
SYS_EXIT,
SYS_WAIT
};

userprog/syscall-init.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* 初始化系统调用 */
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] = console_put_char;
syscall_table[SYS_CLEAR] = cls_screen;
syscall_table[SYS_GETCWD] = sys_getcwd;
syscall_table[SYS_OPEN] = sys_open;
syscall_table[SYS_CLOSE] = sys_close;
syscall_table[SYS_LSEEK] = sys_lseek;
syscall_table[SYS_UNLINK] = sys_unlink;
syscall_table[SYS_MKDIR] = sys_mkdir;
syscall_table[SYS_OPENDIR] = sys_opendir;
syscall_table[SYS_CLOSEDIR] = sys_closedir;
syscall_table[SYS_CHDIR] = sys_chdir;
syscall_table[SYS_RMDIR] = sys_rmdir;
syscall_table[SYS_READDIR] = sys_readdir;
syscall_table[SYS_REWINDDIR] = sys_rewinddir;
syscall_table[SYS_STAT] = sys_stat;
syscall_table[SYS_PS] = sys_ps;
syscall_table[SYS_EXECV] = sys_execv;
syscall_table[SYS_EXIT] = sys_exit;
syscall_table[SYS_WAIT] = sys_wait;
put_str("syscall_init done\n");
}

command/start.S修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[bits 32]
extern main
extern exit
section .text
global _start
_start:
;下面这两个要和execv中load之后指定的寄存器一致
push ebx ;压入argv
push ecx ;压入argc
call main

;将main的返回值通过栈传给exit,gcc用eax存储返回值,这是ABI规定的
push eax
call exit
;exit不会返回

command/cat.c创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "syscall.h"
#include "stdio.h"
#include "string.h"
#include "../fs/fs.h"
int main(int argc, char** argv) {
if (argc > 2) {
printf("cat: argument error\n");
exit(-2);
}

if (argc == 1) {
char buf[512] = {0};
read(0, buf, 512);
printf("%s",buf);
exit(0);
}

int buf_size = 1024;
char abs_path[512] = {0};
void* buf = malloc(buf_size);
if (buf == NULL) {
printf("cat: malloc memory failed\n");
return -1;
}
if (argv[1][0] != '/') {
getcwd(abs_path, 512);
strcat(abs_path, "/");
strcat(abs_path, argv[1]);
} else {
strcpy(abs_path, argv[1]);
}
int fd = open(abs_path, O_RDONLY);
if (fd == -1) {
printf("cat: open: open %s failed\n", argv[1]);
return -1;
}
int read_bytes= 0;
while (1) {
read_bytes = read(fd, buf, buf_size);
if (read_bytes == -1) {
break;
}
write(1, buf, read_bytes);
}
free(buf);
close(fd);
return 66;
}

command/compile3.sh创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
####  此脚本应该在command目录下执行

if [[ ! -d "../lib" || ! -d "../build" ]];then
echo "dependent dir don\`t exist!"
cwd=$(pwd)
cwd=${cwd##*/}
cwd=${cwd%/}
if [[ $cwd != "command" ]];then
echo -e "you\`d better in command dir\n"
fi
exit
fi

BIN="cat"
CFLAGS="-Wall -c -fno-builtin -W -Wstrict-prototypes \
-Wmissing-prototypes -Wsystem-headers -fno-stack-protector"
#LIBS= "-I ../lib -I ../lib/user -I ../fs"
OBJS="../build/string.o ../build/syscall.o \
../build/stdio.o ../build/assert.o start.o"
DD_IN=$BIN
DD_OUT="/home/podest/bochs/hd60M.img"

nasm -f elf ./start.S -o ./start.o
ar rcs simple_crt.a $OBJS start.o

gcc -m32 $CFLAGS -I "../lib/" -I "../lib/kernel/" -I "../lib/user/" -I "../kernel/" -I "../device/" -I "../thread/" -I "../userprog/" -I "../fs/" -I "../shell/" -o $BIN".o" $BIN".c"
ld -m elf_i386 $BIN".o" simple_crt.a -o $BIN
SEC_CNT=$(ls -l $BIN|awk '{printf("%d", ($5+511)/512)}')

if [[ -f $BIN ]];then
dd if=./$DD_IN of=$DD_OUT bs=512 \
count=$SEC_CNT seek=300 conv=notrunc
fi

shell/shell.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/* 简单的shell */
void my_shell(void) {
cwd_cache[0] = '/';
while (1) {
print_prompt();
memset(final_path, 0, MAX_PATH_LEN);
memset(cmd_line, 0, MAX_PATH_LEN);
readline(cmd_line, MAX_PATH_LEN);
if (cmd_line[0] == 0) { // 若只键入了一个回车
continue;
}

argc = -1;
argc = cmd_parse(cmd_line, argv, ' ');
if (argc == -1) {
printf("num of arguments exceed %d\n", MAX_ARG_NR);
continue;
}
if (!strcmp("ls", argv[0])) {
buildin_ls(argc, argv);
} else if (!strcmp("cd", argv[0])) {
if (buildin_cd(argc, argv) != NULL) {
memset(cwd_cache, 0, MAX_PATH_LEN);
strcpy(cwd_cache, final_path);
}
} else if (!strcmp("pwd", argv[0])) {
buildin_pwd(argc, argv);
} else if (!strcmp("ps", argv[0])) {
buildin_ps(argc, argv);
} else if (!strcmp("clear", argv[0])) {
buildin_clear(argc, argv);
} else if (!strcmp("mkdir", argv[0])){
buildin_mkdir(argc, argv);
} else if (!strcmp("rmdir", argv[0])){
buildin_rmdir(argc, argv);
} else if (!strcmp("rm", argv[0])) {
buildin_rm(argc, argv);
} else { // 如果是外部命令,需要从磁盘上加载
int32_t pid = fork();
if (pid) { // 父进程
int32_t status;
int32_t child_pid = wait(&status); // 此时子进程若没有执行exit,my_shell会被阻塞,不再响应键入的命令
if (child_pid == -1) { // 按理说程序正确的话不会执行到这句,fork出的进程便是shell子进程
PANIC("my_shell: no child\n");
}
printf("child_pid %d, it's status: %d\n", child_pid, status);
} else { // 子进程
make_clear_abs_path(argv[0], final_path);
argv[0] = final_path;
/* 先判断下文件是否存在 */
struct stat file_stat;
memset(&file_stat, 0, sizeof(struct stat));
if (stat(argv[0], &file_stat) == -1) {
printf("my_shell: cannot access %s: No such file or directory\n", argv[0]);
exit(-1);
} else {
execv(argv[0], argv);
}
}
}
int32_t arg_idx = 0;
while(arg_idx < MAX_ARG_NR) {
argv[arg_idx] = NULL;
arg_idx++;
}
}
PANIC("my_shell: should not be here");

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#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"
#include "../shell/shell.h"

int main(void) {
put_str("I am kernel\n");
init_all();
intr_enable();


/************* 写入应用程序 *************/
uint32_t file_size = 5553;
uint32_t sec_cnt = DIV_ROUND_UP(file_size, 512);
struct disk* sda = &channels[0].devices[0];
void* prog_buf = sys_malloc(file_size);
ide_read(sda, 300, prog_buf, sec_cnt);
int32_t fd = sys_open("/cat", O_CREAT|O_RDWR);
if (fd != -1) {
if(sys_write(fd, prog_buf, file_size) == -1) {
printk("file write error!\n");
while(1);
}
}
/************* 写入应用程序结束 *************/
cls_screen();
console_put_str("[rabbit@localhost /]$ ");
thread_exit(running_thread(), true);
return 0;
}


/* init进程 */
void init(void) {
uint32_t ret_pid = fork();
if(ret_pid) { // 父进程
int status;
int child_pid;
while(1){
child_pid = wait(&status);
printf("I'm init, My pid is 1, I recieve a child, It's pid is %d, status is %d\n", child_pid, status);
}
} else { // 子进程
my_shell();
}
PANIC("init: should not be here");
}

运行结果

图为bochs运行界面

实现管道

shell/pipe.h创建

1
2
3
4
5
6
7
8
9
10
11
#ifndef __SHELL_PIPE_H
#define __SHELL_PIPE_H
#include "stdint.h"
#include "global.h"

#define PIPE_FLAG 0xFFFF
bool is_pipe(uint32_t local_fd);
int32_t sys_pipe(int32_t pipefd[2]);
uint32_t pipe_read(int32_t fd, void* buf, uint32_t count);
uint32_t pipe_write(int32_t fd, const void* buf, uint32_t count);
#endif

shell/pipe.c创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include "pipe.h"
#include "memory.h"
#include "../fs/fs.h"
#include "../fs/file.h"
#include "ioqueue.h"
#include "../thread/thread.h"

/* 判断文件描述符local_fd是否是管道 */
bool is_pipe(uint32_t local_fd) {
uint32_t global_fd = fd_local2global(local_fd);
return file_table[global_fd].fd_flag == PIPE_FLAG;
}

/* 创建管道,成功返回0,失败返回-1 */
int32_t sys_pipe(int32_t pipefd[2]) {
int32_t global_fd = get_free_slot_in_global();

/* 申请一页内核内存做环形缓冲区 */
file_table[global_fd].fd_inode = get_kernel_pages(1);

/* 初始化环形缓冲区 */
ioqueue_init((struct ioqueue*)file_table[global_fd].fd_inode);
if (file_table[global_fd].fd_inode == NULL) {
return -1;
}

/* 将fd_flag复用为管道标志 */
file_table[global_fd].fd_flag = PIPE_FLAG;

/* 将fd_pos复用为管道打开数 */
file_table[global_fd].fd_pos = 2;
pipefd[0] = pcb_fd_install(global_fd);
pipefd[1] = pcb_fd_install(global_fd);
return 0;
}

/* 从管道中读数据 */
uint32_t pipe_read(int32_t fd, void* buf, uint32_t count) {
char* buffer = buf;
uint32_t bytes_read = 0;
uint32_t global_fd = fd_local2global(fd);

/* 获取管道的环形缓冲区 */
struct ioqueue* ioq = (struct ioqueue*)file_table[global_fd].fd_inode;

/* 选择较小的数据读取量,避免阻塞 */
uint32_t ioq_len = ioq_length(ioq);
uint32_t size = ioq_len > count ? count : ioq_len;
while (bytes_read < size) {
*buffer = ioq_getchar(ioq);
bytes_read++;
buffer++;
}
return bytes_read;
}

/* 往管道中写数据 */
uint32_t pipe_write(int32_t fd, const void* buf, uint32_t count) {
uint32_t bytes_write = 0;
uint32_t global_fd = fd_local2global(fd);
struct ioqueue* ioq = (struct ioqueue*)file_table[global_fd].fd_inode;

/* 选择较小的数据写入量,避免阻塞 */
uint32_t ioq_left = bufsize - ioq_length(ioq);
uint32_t size = ioq_left > count ? count : ioq_left;

const char* buffer = buf;
while (bytes_write < size) {
ioq_putchar(ioq, *buffer);
bytes_write++;
buffer++;
}
return bytes_write;
}

fs/fs.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

/* 关闭文件描述符fd指向的文件,成功返回0,否则返回-1 */
int32_t sys_close(int32_t fd) {
int32_t ret = -1; // 返回值默认为-1,即失败
if (fd > 2) {
uint32_t global_fd = fd_local2global(fd);
if (is_pipe(fd)) {
/* 如果此管道上的描述符都被关闭,释放管道的环形缓冲区 */
if (--file_table[global_fd].fd_pos == 0) {
mfree_page(PF_KERNEL, file_table[global_fd].fd_inode, 1);
file_table[global_fd].fd_inode = NULL;
}
ret = 0;
} else {
ret = file_close(&file_table[global_fd]);
}
running_thread()->fd_table[fd] = -1; // 使该文件描述符位可用
}
return ret;
}

/* 从文件描述符fd指向的文件中读取count个字节到buf,若成功则返回读出的字节数,到文件尾则返回-1 */
int32_t sys_read(int32_t fd, void* buf, uint32_t count) {
ASSERT(buf != NULL);
int32_t ret = -1;
uint32_t global_fd = 0;
if (fd < 0 || fd == stdout_no || fd == stderr_no) {
printk("sys_read: fd error\n");
} else if (fd == stdin_no) {
/* 标准输入有可能被重定向为管道缓冲区, 因此要判断 */
if (is_pipe(fd)) {
ret = pipe_read(fd, buf, count);
} else {
char* buffer = buf;
uint32_t bytes_read = 0;
while (bytes_read < count) {
*buffer = ioq_getchar(&kbd_buf);
bytes_read++;
buffer++;
}
ret = (bytes_read == 0 ? -1 : (int32_t)bytes_read);
}
} else if (is_pipe(fd)) { /* 若是管道就调用管道的方法 */
ret = pipe_read(fd, buf, count);
} else {
global_fd = fd_local2global(fd);
ret = file_read(&file_table[global_fd], buf, count);
}
return ret;
}

/* 将buf中连续count个字节写入文件描述符fd,成功则返回写入的字节数,失败返回-1 */
int32_t sys_write(int32_t fd, const void* buf, uint32_t count) {
if (fd < 0) {
printk("sys_write: fd error\n");
return -1;
}
if (fd == stdout_no) {
/* 标准输出有可能被重定向为管道缓冲区, 因此要判断 */
if (is_pipe(fd)) {
return pipe_write(fd, buf, count);
} else {
char tmp_buf[1024] = {0};
memcpy(tmp_buf, buf, count);
console_put_str(tmp_buf);
return count;
}
} else if (is_pipe(fd)){ /* 若是管道就调用管道的方法 */
return pipe_write(fd, buf, count);
} else {
uint32_t _fd = fd_local2global(fd);
struct file* wr_file = &file_table[_fd];
if (wr_file->fd_flag & O_WRONLY || wr_file->fd_flag & O_RDWR) {
uint32_t bytes_written = file_write(wr_file, buf, count);
return bytes_written;
} else {
console_put_str("sys_write: not allowed to write file without flag O_RDWR or O_WRONLY\n");
return -1;
}
}
}

userprog/fork.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/* 更新inode打开数 */
static void update_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) {
if (is_pipe(local_fd)) {
file_table[global_fd].fd_pos++;
} else {
file_table[global_fd].fd_inode->i_open_cnts++;
}
}
local_fd++;
}
}

userprog/wait_exit.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/* 释放用户进程资源: 
* 1 页表中对应的物理页
* 2 虚拟内存池占物理页框
* 3 关闭打开的文件 */
static void release_prog_resource(struct task_struct* release_thread) {
uint32_t* pgdir_vaddr = release_thread->pgdir;
uint16_t user_pde_nr = 768, pde_idx = 0;
uint32_t pde = 0;
uint32_t* v_pde_ptr = NULL; // v表示var,和函数pde_ptr区分

uint16_t user_pte_nr = 1024, pte_idx = 0;
uint32_t pte = 0;
uint32_t* v_pte_ptr = NULL; // 加个v表示var,和函数pte_ptr区分

uint32_t* first_pte_vaddr_in_pde = NULL; // 用来记录pde中第0个pte的地址
uint32_t pg_phy_addr = 0;

/* 回收页表中用户空间的页框 */
while (pde_idx < user_pde_nr) {
v_pde_ptr = pgdir_vaddr + pde_idx;
pde = *v_pde_ptr;
if (pde & 0x00000001) { // 如果页目录项p位为1,表示该页目录项下可能有页表项
first_pte_vaddr_in_pde = pte_ptr(pde_idx * 0x400000); // 一个页表表示的内存容量是4M,即0x400000
pte_idx = 0;
while (pte_idx < user_pte_nr) {
v_pte_ptr = first_pte_vaddr_in_pde + pte_idx;
pte = *v_pte_ptr;
if (pte & 0x00000001) {
/* 将pte中记录的物理页框直接在相应内存池的位图中清0 */
pg_phy_addr = pte & 0xfffff000;
free_a_phy_page(pg_phy_addr);
}
pte_idx++;
}
/* 将pde中记录的物理页框直接在相应内存池的位图中清0 */
pg_phy_addr = pde & 0xfffff000;
free_a_phy_page(pg_phy_addr);
}
pde_idx++;
}

/* 回收用户虚拟地址池所占的物理内存*/
uint32_t bitmap_pg_cnt = (release_thread->userprog_vaddr.vaddr_bitmap.btmp_bytes_len) / PG_SIZE;
uint8_t* user_vaddr_pool_bitmap = release_thread->userprog_vaddr.vaddr_bitmap.bits;
mfree_page(PF_KERNEL, user_vaddr_pool_bitmap, bitmap_pg_cnt);

/* 关闭进程打开的文件 */
uint8_t local_fd = 3;
while(local_fd < MAX_FILES_OPEN_PER_PROC) {
if (release_thread->fd_table[local_fd] != -1) {
if (is_pipe(local_fd)) {
uint32_t global_fd = fd_local2global(local_fd);
if (--file_table[global_fd].fd_pos == 0) {
mfree_page(PF_KERNEL, file_table[global_fd].fd_inode, 1);
file_table[global_fd].fd_inode = NULL;
}
} else {
sys_close(local_fd);
}
}
local_fd++;
}
}

增加系统调用(略)

syscall.c
syscall.h
syscall_init.c

command/prog_pipe.c创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "../lib/stdint.h"
#include "../lib/stdio.h"
#include "../lib/user/syscall.h"
#include "../lib/string.h"

int main(int argc, char** argv) {
int32_t fd[2] = {-1};
pipe(fd);
int32_t pid = fork();
if(pid) { // 父进程
close(fd[0]); // 关闭输入
write(fd[1], "Hi, my son, I love you!", 24);
printf("\nI`m father, my pid is %d\n", getpid());
return 8;
} else {
close(fd[1]); // 关闭输出
char buf[32] = {0};
read(fd[0], buf, 24);
printf("\nI`m child, my pid is %d\n", getpid());
printf("I`m child, my father said to me: \"%s\"\n", buf);
return 9;
}
}

command/compile4.sh创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
####  此脚本应该在command目录下执行

if [[ ! -d "../lib" || ! -d "../build" ]];then
echo "dependent dir don\`t exist!"
cwd=$(pwd)
cwd=${cwd##*/}
cwd=${cwd%/}
if [[ $cwd != "command" ]];then
echo -e "you\`d better in command dir\n"
fi
exit
fi

BIN="prog_pipe"
CFLAGS="-Wall -c -fno-builtin -W -Wstrict-prototypes \
-Wmissing-prototypes -Wsystem-headers -fno-stack-protector"
#LIBS= "-I ../lib -I ../lib/user -I ../fs"
OBJS="../build/string.o ../build/syscall.o \
../build/stdio.o ../build/assert.o start.o"
DD_IN=$BIN
DD_OUT="/home/podest/bochs/hd60M.img"

nasm -f elf ./start.S -o ./start.o
ar rcs simple_crt.a $OBJS start.o

gcc -m32 $CFLAGS -I "../lib/" -I "../lib/kernel/" -I "../lib/user/" -I "../kernel/" -I "../device/" -I "../thread/" -I "../userprog/" -I "../fs/" -I "../shell/" -o $BIN".o" $BIN".c"
ld -m elf_i386 $BIN".o" simple_crt.a -o $BIN
SEC_CNT=$(ls -l $BIN|awk '{printf("%d", ($5+511)/512)}')

if [[ -f $BIN ]];then
dd if=./$DD_IN of=$DD_OUT bs=512 \
count=$SEC_CNT seek=300 conv=notrunc
fi

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#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"
#include "../shell/shell.h"
#include "../shell/pipe.h"

void init(void);

int main(void) {
put_str("I am kernel\n");
init_all();

/************* 写入应用程序 *************/
uint32_t file_size = 5343;
uint32_t sec_cnt = DIV_ROUND_UP(file_size, 512);
struct disk* sda = &channels[0].devices[0];
void* prog_buf = sys_malloc(file_size);
ide_read(sda, 300, prog_buf, sec_cnt);
int32_t fd = sys_open("/prog_pipe", O_CREAT|O_RDWR);
if (fd != -1) {
if(sys_write(fd, prog_buf, file_size) == -1) {
printk("file write error!\n");
while(1);
}
}
/************* 写入应用程序结束 *************/
cls_screen();
console_put_str("[rabbit@localhost /]$ ");
thread_exit(running_thread(), true);
return 0;
}

/* init进程 */
void init(void) {
uint32_t ret_pid = fork();
if(ret_pid) { // 父进程
int status;
int child_pid;
/* init在此处不停的回收僵尸进程 */
while(1) {
child_pid = wait(&status);
printf("I`m init, My pid is 1, I recieve a child, It`s pid is %d, status is %d\n", child_pid, status);
}
} else { // 子进程
my_shell();
}
PANIC("init: should not be here");
}

makefile修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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 $(BUILD_DIR)/shell.o \
$(BUILD_DIR)/buildin_cmd.o $(BUILD_DIR)/assert.o $(BUILD_DIR)/exec.o \
$(BUILD_DIR)/wait_exit.o $(BUILD_DIR)/pipe.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 lib/user/assert.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 lib/user/syscall.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 lib/kernel/stdio-kernel.h userprog/exec.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/syscall.o: lib/user/syscall.c lib/user/syscall.h fs/file.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 device/ioqueue.h device/keyboard.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)/shell.o: shell/shell.c shell/shell.h kernel/global.h lib/stdint.h lib/string.h \
lib/user/syscall.h lib/stdio.h fs/file.h kernel/debug.h shell/buildin_cmd.h fs/fs.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/buildin_cmd.o: shell/buildin_cmd.c shell/buildin_cmd.h fs/file.h fs/fs.h kernel/debug.h \
lib/string.h lib/user/syscall.h fs/dir.h fs/fs.h lib/stdio.h shell/shell.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/assert.o: lib/user/assert.c lib/user/assert.h lib/stdio.h lib/stdint.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/exec.o: userprog/exec.c userprog/exec.h thread/thread.h lib/stdint.h \
lib/kernel/list.h kernel/global.h lib/kernel/bitmap.h kernel/memory.h \
lib/kernel/stdio-kernel.h fs/fs.h lib/string.h lib/stdint.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/wait_exit.o: userprog/wait_exit.c userprog/wait_exit.h \
thread/thread.h lib/stdint.h lib/kernel/list.h \
kernel/global.h lib/kernel/bitmap.h kernel/memory.h kernel/debug.h \
thread/thread.h lib/kernel/stdio-kernel.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/pipe.o: shell/pipe.c shell/pipe.h lib/stdint.h kernel/memory.h \
lib/kernel/bitmap.h kernel/global.h lib/kernel/list.h fs/fs.h fs/file.h \
device/ide.h thread/sync.h thread/thread.h fs/dir.h fs/inode.h fs/fs.h \
device/ioqueue.h thread/thread.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 all

mk_dir:
if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi

hd:
dd if=$(BUILD_DIR)/kernel.bin \
of=/home/podest/bochs/hd60M.img \
bs=512 count=200 seek=9 conv=notrunc

clean:
cd $(BUILD_DIR) && rm -f ./*

build: $(BUILD_DIR)/kernel.bin

all: mk_dir build hd

运行结果

图为bochs运行界面

shell/pipe.h新增

1
void sys_fd_redirect(uint32_t old_local_fd, uint32_t new_local_fd);

shell/pipe.c新增

1
2
3
4
5
6
7
8
9
10
11
/* 将文件描述符old_local_fd重定向为new_local_fd */
void sys_fd_redirect(uint32_t old_local_fd, uint32_t new_local_fd) {
struct task_struct* cur = running_thread();
/* 针对恢复标准描述符 */
if (new_local_fd < 3) {
cur->fd_table[old_local_fd] = new_local_fd;
} else {
uint32_t new_global_fd = cur->fd_table[new_local_fd];
cur->fd_table[old_local_fd] = new_global_fd;
}
}

shell/shell.h修改

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef __SHELL_SHELL_H
#define __SHELL_SHELL_H

#include "stdint.h"

void print_prompt(void);
static void readline(char* buf,int32_t count);
void my_shell(void);
static int32_t cmd_parse(char* cmd_str,char** argv,char token);
static void cmd_execute(uint32_t argc, char** argv);

#endif

shell/shell.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* 执行命令 */
static void cmd_execute(uint32_t argc, char** argv) {
if (!strcmp("ls", argv[0])) {
buildin_ls(argc, argv);
} else if (!strcmp("cd", argv[0])) {
if (buildin_cd(argc, argv) != NULL) {
memset(cwd_cache, 0, MAX_PATH_LEN);
strcpy(cwd_cache, final_path);
}
} else if (!strcmp("pwd", argv[0])) {
buildin_pwd(argc, argv);
} else if (!strcmp("ps", argv[0])) {
buildin_ps(argc, argv);
} else if (!strcmp("clear", argv[0])) {
buildin_clear(argc, argv);
} else if (!strcmp("mkdir", argv[0])){
buildin_mkdir(argc, argv);
} else if (!strcmp("rmdir", argv[0])){
buildin_rmdir(argc, argv);
} else if (!strcmp("rm", argv[0])) {
buildin_rm(argc, argv);
} else if (!strcmp("help", argv[0])) {
buildin_help(argc, argv);
} else { // 如果是外部命令,需要从磁盘上加载
int32_t pid = fork();
if (pid) { // 父进程
int32_t status;
int32_t child_pid = wait(&status); // 此时子进程若没有执行exit,my_shell会被阻塞,不再响应键入的命令
if (child_pid == -1) { // 按理说程序正确的话不会执行到这句,fork出的进程便是shell子进程
PANIC("my_shell: no child\n");
}
printf("child_pid %d, it's status: %d\n", child_pid, status);
} else { // 子进程
make_clear_abs_path(argv[0], final_path);
argv[0] = final_path;

/* 先判断下文件是否存在 */
struct stat file_stat;
memset(&file_stat, 0, sizeof(struct stat));
if (stat(argv[0], &file_stat) == -1) {
printf("my_shell: cannot access %s: No such file or directory\n", argv[0]);
exit(-1);
} else {
execv(argv[0], argv);
}
}
}
}

fs/fs.h新增

1
void sys_help(void);

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
* 显示系统支持的内部命令 */
void sys_help(void) {
printk("\
buildin commands:\n\
ls: show directory or file information\n\
cd: change current work directory\n\
mkdir: create a directory\n\
rmdir: remove a empty directory\n\
rm: remove a regular file\n\
pwd: show current work directory\n\
ps: show process information\n\
clear: clear screen\n\
shortcut key:\n\
ctrl+l: clear screen\n\
ctrl+u: clear input\n\n");
}

shell/buildin_cmd.h新增

1
void buildin_help(uint32_t argc, char** argv);

shell/buildin_cmd.c新增

1
2
3
4
/* 显示内建命令列表 */
void buildin_help(uint32_t argc, char** argv) {
help();
}

增加系统调用(略)

syscall.c
syscall.h
syscall_init.c

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#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"
#include "../shell/shell.h"
#include "../shell/pipe.h"

void init(void);

int main(void) {
put_str("I am kernel\n");
init_all();

/************* 写入应用程序 *************/
uint32_t file_size = 5580;
uint32_t sec_cnt = DIV_ROUND_UP(file_size, 512);
struct disk* sda = &channels[0].devices[0];
void* prog_buf = sys_malloc(file_size);
ide_read(sda, 300, prog_buf, sec_cnt);
int32_t fd = sys_open("/cat", O_CREAT|O_RDWR);
if (fd != -1) {
if(sys_write(fd, prog_buf, file_size) == -1) {
printk("file write error!\n");
while(1);
}
}
/************* 写入应用程序结束 *************/
cls_screen();
console_put_str("[rabbit@localhost /]$ ");
thread_exit(running_thread(), true);
return 0;
}

/* init进程 */
void init(void) {
uint32_t ret_pid = fork();
if(ret_pid) { // 父进程
int status;
int child_pid;
/* init在此处不停的回收僵尸进程 */
while(1) {
child_pid = wait(&status);
printf("I`m init, My pid is 1, I recieve a child, It`s pid is %d, status is %d\n", child_pid, status);
}
} else { // 子进程
my_shell();
}
PANIC("init: should not be here");
}

makefile修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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 $(BUILD_DIR)/shell.o \
$(BUILD_DIR)/buildin_cmd.o $(BUILD_DIR)/assert.o $(BUILD_DIR)/exec.o \
$(BUILD_DIR)/wait_exit.o $(BUILD_DIR)/pipe.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 lib/user/assert.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 lib/user/syscall.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 lib/kernel/stdio-kernel.h userprog/exec.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/syscall.o: lib/user/syscall.c lib/user/syscall.h fs/file.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 device/ioqueue.h device/keyboard.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)/shell.o: shell/shell.c shell/shell.h lib/stdint.h fs/fs.h \
lib/user/syscall.h lib/stdio.h lib/stdint.h kernel/global.h lib/user/assert.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/buildin_cmd.o: shell/buildin_cmd.c shell/buildin_cmd.h fs/file.h fs/fs.h kernel/debug.h \
lib/string.h lib/user/syscall.h fs/dir.h fs/fs.h lib/stdio.h shell/shell.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/assert.o: lib/user/assert.c lib/user/assert.h lib/stdio.h lib/stdint.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/exec.o: userprog/exec.c userprog/exec.h thread/thread.h lib/stdint.h \
lib/kernel/list.h kernel/global.h lib/kernel/bitmap.h kernel/memory.h \
lib/kernel/stdio-kernel.h fs/fs.h lib/string.h lib/stdint.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/wait_exit.o: userprog/wait_exit.c userprog/wait_exit.h \
thread/thread.h lib/stdint.h lib/kernel/list.h \
kernel/global.h lib/kernel/bitmap.h kernel/memory.h kernel/debug.h \
thread/thread.h lib/kernel/stdio-kernel.h
$(CC) $(CFLAGS) $< -o $@

$(BUILD_DIR)/pipe.o: shell/pipe.c shell/pipe.h lib/stdint.h kernel/memory.h \
lib/kernel/bitmap.h kernel/global.h lib/kernel/list.h fs/fs.h fs/file.h \
device/ide.h thread/sync.h thread/thread.h fs/dir.h fs/inode.h fs/fs.h \
device/ioqueue.h thread/thread.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 all

mk_dir:
if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi

hd:
dd if=$(BUILD_DIR)/kernel.bin \
of=/home/podest/bochs/hd60M.img \
bs=512 count=200 seek=9 conv=notrunc

clean:
cd $(BUILD_DIR) && rm -f ./*

build: $(BUILD_DIR)/kernel.bin

all: mk_dir build hd

运行结果

图为bochs运行界面

写在后面

终于完成,写个简单的总结吧。从7月5日拿到这本书到今天8月10日,历时1个月多一点的时间,总的来说效果还是非常好的,这也是我真正意义上的第一个项目,接触了许多硬件交互、汇编,还有os的一些算法,等以后能力提升了可能再会翻看一下这本书的某些章节,补充下博客,不过那都是后话了。虽然成品还是非常简陋的,不过还是非常高兴的。更令我自己没想到的是,这一个月同步进行了数学建模的集训、模拟和学生会的一些琐事,最终也是全部顺利完成了。下一步的话,准备回顾一下寒假时学的计网,写一下自顶向下的lab,实操一下,也会同步在博客上。顺带提一下,os的全部代码我上传到github上了,有兴趣的同学可以自行翻阅。