一个操作系统的实现(5.2)系统调用,进程

主要内容

实现简单的系统调用
实现进程调度

系统调用类似于Windows的API,是应用程序与操作系统之间的桥梁,对于一个程序无法访问的区域,就需要用到系统调用。

1.用寄存器eax来实现消息的传递

%include "sconst.inc"

_NR_get_ticks       equ 0 ; 要跟 global.c 中 sys_call_table 的定义相对应!
INT_VECTOR_SYS_CALL equ 0x90

global	get_ticks ; 导出符号

bits 32
[section .text]

get_ticks:
	mov	eax, _NR_get_ticks
	int	INT_VECTOR_SYS_CALL
	ret

2.初始化系统调用的中断门
将NT_VECTOR_SYS_CALL号中断与sys_call对应

init_idt_desc(INT_VECTOR_SYS_CALL,	DA_386IGate,
		      sys_call,			PRIVILEGE_USER);

3.实现sys_call

sys_call:
        call    save

        sti

        call    [sys_call_table + eax * 4]  //调用sys_call_table[eax],每一个数组指向一个函数
        mov     [esi + EAXREG - P_STACKBASE], eax //将sys_call_table[eax]返回值放入进程表的eax位置,保证进程被恢复后eax中是正确的值

        cli

        ret
PUBLIC	system_call		sys_call_table[NR_SYS_CALL] = {sys_get_ticks};//目前只有一个成员
save:
        pushad          ; `.
        push    ds      ;  |
        push    es      ;  | 保存原寄存器值
        push    fs      ;  |
        push    gs      ; /
        mov     dx, ss
        mov     ds, dx
        mov     es, dx

        mov     esi, esp                    ;esi = 进程表起始地址

        inc     dword [k_reenter]           ;k_reenter++;
        cmp     dword [k_reenter], 0        ;if(k_reenter ==0)
        jne     .1                          ;{
        mov     esp, StackTop               ;  mov esp, StackTop <--切换到内核栈
        push    restart                     ;  push restart
        jmp     [esi + RETADR - P_STACKBASE];  return;
.1:                                         ;} else { 已经在内核栈,不需要再切换
        push    restart_reenter             ;  push restart_reenter
        jmp     [esi + RETADR - P_STACKBASE];  return;
                                            ;}
PUBLIC int sys_get_ticks()
{
	disp_str("+");
	return 0;
}

4.在进程中调用系统调用

运用建立的系统调用来实现判断时间的函数
1.关于时钟中断
他是由一个被称为PIT的芯片触发的,在IBM XT中这个芯片是intel 8253,8253有三个16位计数器,各有不同作用。
Counter0:输出到IRQ0,以便每隔一段时间产生一次时钟中断
Counter1:通常被设为18,以便每隔约15us做一次RAM刷新
Counter2:连接PC喇叭
计数器有一个输入频率,在PC上是1193180Hz,每一个时钟周期,计数器的值减一,当为0时,会触发一个输出,因为这里的计数器为16位,最大值为65535,所以默认的时钟中断频率为18.2Hz,而我们可以通过编程控制8253,改变计数器的计数值。
2.改变计数值
通过相应端口的写操作来实现,Counter0对应操作端口40h
在写操作之前,需要先通过43h端口写8253模式控制寄存器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

所以7,6位为00,5,4位为11,这里使用模式2,第3,2,1位为010,
设置8253

out_byte(TIMER_MODE, RATE_GENERATOR);
        out_byte(TIMER0, (u8) (TIMER_FREQ/HZ) );
        out_byte(TIMER0, (u8) ((TIMER_FREQ/HZ) >> 8));

宏定义

#define TIMER0         0x40 /* I/O port for timer channel 0 */
#define TIMER_MODE     0x43 /* I/O port for timer mode control */
#define RATE_GENERATOR 0x34 /* 00-11-010-0 :
			     * Counter0 - LSB then MSB - rate generator - binary
			     */
#define TIMER_FREQ     1193182L/* clock frequency for timer in PC and AT */
#define HZ             100  /* clock freq (software settable on IBM-PC) */

精确到10ms的延迟函数

PUBLIC void milli_delay(int milli_sec)
{
        int t = get_ticks();

        while(((get_ticks() - t) * 1000 / HZ) < milli_sec) {}
}

1.实现进程的优先级
为每一个进程添加一个变量,进程每获得一个运行周期,这个变量减一,当为0时就不再执行,直到所有进程的变量都变为0时。

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;                 //ticks是递减的,当所有进程都变为0时再把ticks赋值初始值priority
        int priority;             //记录ticks的初值

	u32 pid;                   /* process id passed in from MM */
	char p_name[16];           /* name of the process */
}PROCESS;

为进程赋不同的初值决定优先级

proc_table[0].ticks = proc_table[0].priority = 150;
	proc_table[1].ticks = proc_table[1].priority =  50;
	proc_table[2].ticks = proc_table[2].priority =  30;

2.进程调度

PUBLIC void schedule()
{
	PROCESS* p;
	int	 greatest_ticks = 0;

	while (!greatest_ticks) {
		for (p = proc_table; p < proc_table+NR_TASKS; p++) {
			if (p->ticks > greatest_ticks) {
				greatest_ticks = p->ticks;
				p_proc_ready = p;
			}
		}

		if (!greatest_ticks) {
			for (p = proc_table; p < proc_table+NR_TASKS; p++) {
				p->ticks = p->priority;
			}
		}
	}
}

修改时钟中断后运行

PUBLIC void clock_handler(int irq)
{
	ticks++;
	p_proc_ready->ticks--;

	if (k_reenter != 0) {
		return;
	}

	schedule();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验报告撰写要求 实验报告要求具有以下内容: 一、实验目的 二、实验内容 三、实验要求 四、算法流程图 五、给出测试数据及运行结果 六、实验体会或对改进实验的建议 实验1 进程调度 一、实验目的 通过实验加强对进程调度算法的理解和掌握。 二、实验内容 编写程序实现基于优先级的时间片轮转调度算法。 三、实验要求 1、假定系统有5个进程,每个进程一个进程控制块PCB来代表,进程控制块的结构如下图1.1所示: 进程名 优先级 要求运行时间 已运行时间 进程状态 指针 图1.1 其中: 进程名:作为进程的标识,假设五个进程进程名分别为p1,p2,p3,p4,p5。 指针:进程按顺序排成循环链表,用指针指出下一个进程进程控制块首地址,最后一个进程中的指针指出第一个进程进程控制块首地址。 要求运行时间:假设进程需要运行的单位时间数。 已运行时间:假设进程已经运行的单位时间数,初值为0。 状态:可假设有两种状态,就绪状态和结束状态。进程的初始状态都为就绪状态。 2、每次运行所设计的处理器调度程序调度进程之前,为每个进程随机确定它的要求运行时间。 3、此程序是模拟处理器调度,因此,被选中的进程并不实际启动运行,而是执行 已运行时间+1 来模拟进程的一次运行,表示进程已经运行过一个单位时间。 4、在所设计的程序中应有显示语句,能显示每次被选中的进程名以及运行一次后进程队列的变化。 5、优先级可自己给出初始值,但要求采用动态优先级,可自己设计优先数如何变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值