对于进程的作用和重要程度,我们进行划分,将TTY进程称为任务,让其运行在ring1,让普通的用户进程运行在ring3。 定义宏区分任务和用户进程
#define NR_TASKS 1 #define NR_PROCS 3在其余的代码中区分task和proc
u8 privilege; u8 rpl; int eflags; for (i = 0; i < NR_TASKS+NR_PROCS; i++) { if (i < NR_TASKS) { /* 任务 */ p_task = task_table + i; privilege = PRIVILEGE_TASK; rpl = RPL_TASK; eflags = 0x1202; /* IF=1, IOPL=1, bit 2 is always 1 */ } else { /* 用户进程 */ p_task = user_proc_table + (i - NR_TASKS); privilege = PRIVILEGE_USER; rpl = RPL_USER; eflags = 0x202; /* IF=1, bit 2 is always 1 */ } strcpy(p_proc->p_name, p_task->name); // name of the process p_proc->pid = i; // pid p_proc->ldt_sel = selector_ldt; memcpy(&p_proc->ldts[0], &gdt[SELECTOR_KERNEL_CS >> 3], sizeof(DESCRIPTOR)); p_proc->ldts[0].attr1 = DA_C | privilege << 5; memcpy(&p_proc->ldts[1], &gdt[SELECTOR_KERNEL_DS >> 3], sizeof(DESCRIPTOR)); p_proc->ldts[1].attr1 = DA_DRW | privilege << 5; p_proc->regs.cs = (0 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl; p_proc->regs.ds = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl; p_proc->regs.es = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl; p_proc->regs.fs = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl; p_proc->regs.ss = (8 & SA_RPL_MASK & SA_TI_MASK) | SA_TIL | rpl; p_proc->regs.gs = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | rpl; p_proc->regs.eip = (u32)p_task->initial_eip; p_proc->regs.esp = (u32)p_task_stack; p_proc->regs.eflags = eflags; p_task_stack -= p_task->stacksize; p_proc++; p_task++; selector_ldt += 1 << 3; } for (i = 0; i < NR_TASKS+NR_PROCS; i++){ init_descriptor(&gdt[selector_ldt>>3], vir2phys(seg2phys(SELECTOR_KERNEL_DS), proc_table[i].ldts), LDT_SIZE * sizeof(DESCRIPTOR) - 1, DA_LDT); p_proc++; selector_ldt += 1 << 3; }1.为进程指定一个对应的TTY,保证某个进程调用printf时,能往对应的控制台输出 在进程表中增加一个成员
typedef struct s_proc { STACK_FRAME regs; /* process registers saved in stack frame */ u16 ldt_sel; /* gdt selector giving ldt base and limit */ DESCRIPTOR ldts[LDT_SIZE]; /* local descriptors for code and data */ int ticks; /* remained ticks */ int priority; u32 pid; /* process id passed in from MM */ char p_name[16]; /* name of the process */ int nr_tty; }PROCESS;为新成员赋初值
proc_table[1].nr_tty = 0; proc_table[2].nr_tty = 1; proc_table[3].nr_tty = 1;2.实现单一格式的printf
int printf(const char *fmt, ...) { int i; //表示可变参数的数量 char buf[256]; // 存格式化好的要输出的内容 va_list arg = (va_list)((char *)(&fmt) + 4); // va_list=char *。获取后面可变参数列表的首地址 i = vsprintf(buf, fmt, arg); // 要输出的内容有了, 长度也有了,调用系统调用来借助系统输出 write(buf, i); return i; }根据%后的字符判断下一个参数类型
int vsprintf(char *buf, const char *fmt, va_list args) { char *p; char tmp[256]; va_list p_next_arg = args; for (p = buf; *fmt; fmt++) { // p是分解出来后保存的目标缓冲区的指针,fmt是被分解的源字符串的指针 if(*fmt != '%') { *p++ = *fmt; continue; } fmt++; switch (*fmt) { case 'x': // %x:16进制整数 itoa(tmp, *((int *) p_next_arg)); // 将整型转成16进制字符串形式存在tmp里 strcpy(p, tmp); // 转换好后copy完到目标buf里 p_next_arg += 4; // 从%x知道类型是整型的,所以4个字节 p += strlen(tmp); break; case 's': break; default: break; } } return (p - buf); }增加一个系统调用write
write: mov eax, _NR_write mov ebx, [esp + 4] mov ecx, [esp + 8] int INT_VECTOR_SYS_CALL ret PUBLIC int sys_write(char* buf, int len, PROCESS* p_proc) // 比write()多一个参数,为了标识调用者进程,将内容输出到进程对应的tty { tty_write(&tty_table[p_proc->nr_tty], buf, len); return 0; } PUBLIC void tty_write(TTY *p_tty, char *buf, int len) { char *p = buf; int i = len; while (i) { // 系统调用的实现就是使用OS的各种函数了 out_char(p_tty->p_console, *p++); i--; } } sys_call: call save push dword [p_proc_ready] ; sys_write比write多一个参数,就是调用该系统调用的进程标识 ; 在这里就是当前进行的指针p_proc_ready sti push ecx push ebx call [sys_call_table + eax * 4] add esp, 4 * 3 mov [esi + EAXREG - P_STACKBASE], eax ; 把返回值放到进程表里对应的eax域 ; 从而保证返回调用者进程(进程恢复)后,寄存器全部恢复进程表 ; 里的对应值后能够得到正确的返回值,也就是EAX里装的是sys_get_ticks的返回值 cli retprintf的调用过程