一个操作系统的实现(7)区分任务进程 实现printf

区分任务和用户进程

对于进程的作用和重要程度,我们进行划分,将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的调用过程
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值