区分任务和用户进程
对于进程的作用和重要程度,我们进行划分,将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;
}
编写一个供输出使用的printf()
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
ret
printf的调用过程