操作系统原理与实践4--进程运行轨迹的跟踪与统计

“操作系统原理与实践”实验报告

进程运行轨迹的跟踪与统计

进程运行轨迹的跟踪与统计的实验报告

一、实验目的

  • 掌握Linux下的多进程编程技术;
  • 通过对进程运行轨迹的跟踪来形象化进程的概念;
  • 在进程运行轨迹跟踪的基础上进行相应的数据统计,从而能对进程调度算法进行实际的量化评价,更进一步加深对调度和调度算法的理解,获得能在实际操作系统上对调度算法进行实验数据对比的直接经验。

二、实验内容

进程从创建(Linux下调用fork())到结束的整个过程就是进程的生命期,进程在其生命期中的运行轨迹实际上就表现为进程状态的多次切换,如进程创建以后会成为就绪态;当该进程被调度以后会切换到运行态;在运行的过程中如果启动了一个文件读写操作,操作系统会将该进程切换到阻塞态(等待态)从而让出CPU;当文件读写完毕以后,操作系统会在将其切换成就绪态,等待进程调度算法来调度该进程执行……

三、实验步骤

1、分析实验内容

进程的产生、运行(调度)及消亡,在linux0.11中主要在以下函数中。除进程0之外的所有其他进程均由父进程产生,即系统运行后手工产生进程0,然后由进程0产生进程1,再由进程1产生其他进程。而进程1是不会消亡的,它也是所有僵尸进程的父进程,在一个进程结束后如果它的父进程已经退出,则该进程的父进程会被手工修改为进程1,然后在进程调度的过程中再由系统收回全部资源。也就是说在一个进程结束后,并不是马上销毁所有有关内容,比如task[]指针,只有系统在进行调度时发现僵尸进程时才会收回。

当然,系统中的shell进程也是个特殊进程,一旦退出则系统会马上再生成一个shell进程,因为没有shell进程系统是无法管理整个机器的。而一个进程的建立到结束,有以下几个步骤。fork()函数用来生成一个进程,具体的生成时是由copy_process()函数来将父进程的PCB等数据结构复制一份来生成子进程的(这也是为什么在实验中进行记录的文件只需要在进程0中打开一次即可)。一个新生成的进程其状态为就绪态(TASK_RUNNING),在linux 0.11中进程状态其实只有就绪(运行)、等待、退出,处于就绪态的进程一旦得到CPU就进入到运行态。这也是系统调度的基础,就是看所有就绪态的进程哪个应该被运行(看时间片)。生成的进程状态有两种改变情形,一种是被抢占(时间片调度),一种是主动让出(等待)。其中抢占只发生在调度时,等待则有以下几种情形,sys_pause(主动暂停),sys_waitpid(等待子进程结束),sleep_on(睡眠)以及不可中断睡眠,而睡眠的进程被唤醒则有主动唤醒及信号唤醒。被唤醒的进程则参与调度,会再一次使用CPU。进程运行结束或被手工杀死后,进程状态会变为僵尸进程,会在调度过程中被释放。

通过以上分析可知,如果要记录进程状态的变化,则要修改以下函数,具体的修改代码见下一部分:fork.c中的copy_process函数;sched.c中的schedule函数、sys_pause函数、sleep_on函数、interruptible_sleep_on函数、wake_up函数;exit.c中的do_exit函数、sys_waitpid函数;在其中关于进程状态改变地方进行记录即可。

2、具体修改内容

2.1、fork.c文件中函数的修改

在这个文件中主要修改部分为进程生成部分代码,需要修改的函数为copy_process。该函数用来生成一个子进程中最重要的部分,即进程的数据结构部分。在一个进程新建过程中可能会发生调度,但此时进程新建过程并未完成,不能运行,所以在此过程中先将进程状态修改为不可中断等待,此状态不需要记录,因为此时进程并未新建完成。但由于进程状态中没有新建状态,所以在此处记录两次,一次是新建,然后马上修改为就绪。


int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
        long ebx,long ecx,long edx,
        long fs,long es,long ds,
        long eip,long cs,long eflags,long esp,long ss)
{
    struct task_struct *p;
    int i;
    struct file *f;
    p = (struct task_struct *) get_free_page();
    if (!p)
        return -EAGAIN;
    task[nr] = p;
    *p = *current;    /* NOTE! this doesn't copy the supervisor stack */
    p->state = TASK_UNINTERRUPTIBLE; //进程在建立过程中为防止被调度,设置为不可中断等待。
    p->pid = last_pid; //进程的id
    p->father = current->pid; //进程的父进程id
    p->counter = p->priority; //进程的优先级,这个只能通过sys_nice来修改。
    。。。
    set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
    set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
    /*添加代码进行进程信息记录,新建的进程均要记录两次N,J*/
    fprintk(3,"%ld\t%c\t%ld\n",last_pid,'N',jiffies);
    /*添加完毕*/
    /*添加就绪状态*/
    fprintk(3,"%ld\t%c\t%ld\n",last_pid,'J',jiffies);
    /*添加完毕*/
    p->state = TASK_RUNNING;    /* do this last, just in case 到此进程准备完毕,可以运行,则状态修改为就绪 */

    return last_pid; //返回新建子进程的ID
}

2.2、sched.c文件的修改

在这个文件中要修改的函数有以下几个:

  • 2.2.1、schedule()

这个是系统的调度函数,在这个函数中先要检查是否有可断进程需要唤醒,如果有则先唤醒这些进程。然后循环检查进程指针数组中就绪的进程有哪个应该被调度执行,如果没有则运行进程0,进程0是唯一一个不管是什么状态均可以运行的进程。因为系统中不可能会出现一个进程也没有的情况,否则系统无法运行。而进程0也不会退出。在这个函数中需要修改的有以下几处,一个是信号唤醒的进程状态记录,另一个是被调度到的进程,在修改被调度到的进程时要对上一个运行的进程状态进行判断,如果上一个(即current)进程此时的状态是就绪态(TASK_RUNNING),则说明上一个进程是被抢占的(可能是时间片用完,或其他进程的优先级提高),则也要记录上一个进程的状态改变(由运行态变成就绪态)。

修改说明:


void schedule(void)
{
    int i,next,c;
    struct task_struct ** p;
/* check alarm, wake up any interruptible tasks that have got a signal */

    for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
        if (*p) {
            if ((*p)->alarm && (*p)->alarm < jiffies) {
                    (*p)->signal |= (1<<(SIGALRM-1));
                    (*p)->alarm = 0;
                }
            if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) && (*p)->state==TASK_INTERRUPTIBLE)
            {
                /*添加进程唤醒记录*/
                if((*p)->state != TASK_RUNNING)
                    fprintk(3,"%ld\t%c\t%ld\n",(*p)->pid,'J',jiffies);
                /*添加完毕*/
                (*p)->state=TASK_RUNNING;
            }
        }

/* this is the scheduler proper: */

    while (1) {
        c = -1;
        next = 0;
        i = NR_TASKS;
        p = &task[NR_TASKS];
        while (--i) {
            if (!*--p)
                continue;
            if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
                c = (*p)->counter, next = i;
        }
        if (c) break;
        for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
            if (*p)
                (*p)->counter = ((*p)->counter >> 1) +
                        (*p)->priority;
    }
    /*调度进程到运行态*/
    //if(next!=current->pid && task[next]->state!=0)
    if(task[next]->pid != current->pid)
    {
        //判断当前正在运行的进程状态是否为TASK_RUNNING,
        //如果是,则表明当前的进程是时间片到期被抢走的,这时当前进程的状态还应是TASK_RUNNING,
        //如果不是,则说明当前进程是主动让出CPU,则状态应为其他Wait状态。
        if(current->state == TASK_RUNNING)
        {
            //记录当前进程的状态为J,在此处,当前进程由运行态转变为就绪态。
            fprintk(3,"%ld\t%c\t%ld\n",current->pid,'J',jiffies);
        }
        fprintk(3,"%ld\t%c\t%ld\n",task[next]->pid,'R',jiffies);
    }
    /**/
    switch_to(next);
}
  • 2.2.2、sys_pause函数的修改

这个函数是进程主动进行等待,让出CPU,然后让系统进行进程调度。此时要记录进程的状态改变为TASK_INTERRUPTIBLE(在记录时为等待)。


int sys_pause(void)
{
    /*添加进程睡眠记录*/
    if(current->state != TASK_INTERRUPTIBLE)
        fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies);
    /*添加完毕*/
    current->state = TASK_INTERRUPTIBLE;    
    schedule();
    return 0;
}

2.2.3、sleep_on及 interruptible_sleep_on函数

这两个函数功能类似,不同之处在于interruptible_sleep_on函数中进程的状态改变为可中断,即可由其他信号等事件唤醒。而sleep_on函数中进程的状态为不可中断,仅可由wake_up函数进行唤醒。


void sleep_on(struct task_struct **p)
{
    struct task_struct *tmp;

    if (!p)
        return;
    if (current == &(init_task.task))
        panic("task[0] trying to sleep");
    tmp = *p;
    *p = current;

    /*添加进程睡眠记录*/
    if(current->state != TASK_UNINTERRUPTIBLE)
        fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies);
    /*添加完毕*/

    current->state = TASK_UNINTERRUPTIBLE;
    schedule(); //调度后要查看等待进程链上是否有要唤醒的进程。
    if (tmp)
    {
        /*记录进程被唤醒*/
        if(tmp->state != TASK_RUNNING)
            fprintk(3,"%ld\t%c\t%ld\n",tmp->pid,'J',jiffies);
        /*添加完毕*/
        tmp->state=0; //TASK_RUNNING(0)

    }
}

void interruptible_sleep_on(struct task_struct **p)
{
    struct task_struct *tmp;

    if (!p)
        return;
    if (current == &(init_task.task))
        panic("task[0] trying to sleep");
    tmp=*p;
    *p=current;
repeat:    
    /*添加进程睡眠记录*/
    if(current->state != TASK_INTERRUPTIBLE)
        fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies);
    /*添加完毕*/

    current->state = TASK_INTERRUPTIBLE;

    schedule();
    if (*p && *p != current) {

        /*添加进程唤醒记录*/
        if ((*p)->state !=0)
            fprintk(3,"%ld\t%c\t%ld\n",(**p).pid,'J',jiffies);
        /*添加完毕*/
        (**p).state=0;
        goto repeat;
    }
    *p=NULL;
    if (tmp)
    {
        /*添加进程唤醒记录*/
        if (tmp->state !=0)
            fprintk(3,"%ld\t%c\t%ld\n",tmp->pid,'J',jiffies);
        /*添加完毕*/
        tmp->state=0;

    }
}

2.2.4、wake_up函数

该函数用来唤醒等待的进程,尤其指不可中断等待的进程。


void wake_up(struct task_struct **p)
{
    if (p && *p) {

        /*添加进程唤醒记录*/
        if((**p).state !=0)
            fprintk(3,"%ld\t%c\t%ld\n",(**p).pid,'J',jiffies);
        /*添加完毕*/
        (**p).state=0;
        *p=NULL;
    }
}

2.3、exit.c文件的修改

  • 2.3.1、do_exit函数的修改

该函数中关闭进程所占用资源,然后将进程状态修改为TASK_ZOMBIE,设置退出代码,最后通知其父进程进行最后的清理,如果找不到父进程,则将其父进程手工修改为进程1,然后在系统调度时再统一进行清理。该函数中需要修改的地方仅有一处,记录进程退出即可。


int do_exit(long code)
{
    int i;
    free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
    free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
    for (i=0 ; i<NR_TASKS ; i++)
        if (task[i] && task[i]->father == current->pid) {
            task[i]->father = 1;
            if (task[i]->state == TASK_ZOMBIE)
                /* assumption task[1] is always init */
                (void) send_sig(SIGCHLD, task[1], 1);
        }
    for (i=0 ; i<NR_OPEN ; i++)
        if (current->filp[i])
            sys_close(i);
    iput(current->pwd);
    current->pwd=NULL;
    iput(current->root);
    current->root=NULL;
    iput(current->executable);
    current->executable=NULL;
    if (current->leader && current->tty >= 0)
        tty_table[current->tty].pgrp = 0;
    if (last_task_used_math == current)
        last_task_used_math = NULL;
    if (current->leader)
        kill_session();
    current->state = TASK_ZOMBIE;
    /*添加进程退出记录*/
    fprintk(3,"%ld\t%c\t%ld\n",current->pid,'E',jiffies);
    /*添加完毕*/
    current->exit_code = code;
    tell_father(current->father);
    schedule();
    return (-1);    /* just to suppress warnings */
}
  • 2.3.2、sys_waitpid函数的修改

该函数主要用来等待子进程结束,函数中将当前进程状态修改为可中断等待,然后退出,所以只需要记录进程状态修改为等待即可。


int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
    int flag, code;
    struct task_struct ** p;

    verify_area(stat_addr,4);
repeat:
    flag=0;
    for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
        if (!*p || *p == current)
            continue;
        if ((*p)->father != current->pid)
            continue;
        if (pid>0) {
            if ((*p)->pid != pid)
                continue;
        } else if (!pid) {
            if ((*p)->pgrp != current->pgrp)
                continue;
        } else if (pid != -1) {
            if ((*p)->pgrp != -pid)
                continue;
        }
        switch ((*p)->state) {
            case TASK_STOPPED:
                if (!(options & WUNTRACED))
                    continue;
                put_fs_long(0x7f,stat_addr);
                return (*p)->pid;
            case TASK_ZOMBIE:
                current->cutime += (*p)->utime;
                current->cstime += (*p)->stime;
                flag = (*p)->pid;
                code = (*p)->exit_code;
                release(*p);
                put_fs_long(code,stat_addr);
                return flag;
            default:
                flag=1;
                continue;
        }
    }
    if (flag) {
        if (options & WNOHANG)
            return 0;
        current->state=TASK_INTERRUPTIBLE;
        /*添加进程睡眠记录*/
        fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies);
        /*添加完毕*/
        schedule();
        if (!(current->signal &= ~(1<<(SIGCHLD-1))))
            goto repeat;
        else
            return -EINTR;
    }
    return -ECHILD;
}

2.4、记录进程状态

要想记录进程的状态修改,必须在进程0中将记录文件打开,然后在内核中对进程状态进行记录。但由于系统提供的写文件函数无法实现,故需要对系统进行以下两处修改:第一在main函数中打开记录文件;这样由于是在进程0中打开的,后面的进程均继承了该文件句柄。第二新添加内核写文件记录的函数。

  • 2.4.1、main函数及init函数的修改

main函数中要修改的部分为在进入用户态之后,马上打开记录文件,此时系统开始进程0的代码。


void main(void)        /* This really IS void, no error here. */
{            /* The startup routine assumes (well, ...) this */
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
     ROOT_DEV = ORIG_ROOT_DEV;
     drive_info = DRIVE_INFO;
    memory_end = (1<<20) + (EXT_MEM_K<<10);
    memory_end &= 0xfffff000;
    if (memory_end > 16*1024*1024)
        memory_end = 16*1024*1024;
    if (memory_end > 12*1024*1024) 
        buffer_memory_end = 4*1024*1024;
    else if (memory_end > 6*1024*1024)
        buffer_memory_end = 2*1024*1024;
    else
        buffer_memory_end = 1*1024*1024;
    main_memory_start = buffer_memory_end;
#ifdef RAMDISK
    main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif
    mem_init(main_memory_start,memory_end);
    trap_init();
    blk_dev_init();
    chr_dev_init();
    tty_init();
    time_init();
    sched_init();
    buffer_init(buffer_memory_end);
    hd_init();
    floppy_init();
    sti();
    move_to_user_mode();
    /*由于要在内核中记录所有的进程信息,并写到文件中,所以要在进程0中打开文件句柄
    所以在此处要添加打开文件工作*/
    setup((void *) &drive_info); //初始化磁盘工作
    (void) open("/dev/tty0",O_RDWR,0);  //打开终端
    (void) dup(0); //复制终端句柄
    (void) dup(0); //复制文件描述符2也和/dev/tty0关联
    //下面是添加的打开第三个文件描述符
    (void)open("/var/process.log",O_CREAT|O_TRUNC|O_WRONLY,0666); //权限为所有人可读写
    /*添加完毕*/
    if (!fork()) {        /* we count on this going ok */
        init();
    }
/*
 *   NOTE!!   For any other task 'pause()' would mean we have to get a
 * signal to awaken, but task0 is the sole exception (see 'schedule()')
 * as task 0 gets activated at every idle moment (when no other tasks
 * can run). For task0 'pause()' just means we go check if some other
 * task can run, and if not we return here.
 */
    for(;;) pause(); //进程0的等待。
}

//init函数要修改的部分为,由于句柄及文件操作部分已在main中完成,则init中关于此部分的代码必须删除。

void init(void)
{
    int pid,i;
    /*由于要在进程0中打开文件,所以以下四行语句被前称到init执行之前。所以这里要注释掉这4句。*/
    //setup((void *) &drive_info);
    //(void) open("/dev/tty0",O_RDWR,0);
    //(void) dup(0);
    //(void) dup(0);
    /*注释完毕*/
    printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,

2.4.2、记录文件的函数

由于系统提供的写文件函数是在用户态下,所以实验无法使用,故新添加一个内核中的文件操作函数(该函数由老师提供,并未做任何修改)。


//为访问记录文件而重写的写入文件内容函数
/*增加fprintk*/
#include <linux/sched.h>
#include <sys/stat.h>
static char logbuf[1024];
/**/
int fprintk(int fd, const char *fmt, ...)
{
    va_list args;
    int count;
    struct file * file;
    struct m_inode * inode;

    va_start(args, fmt);
    count=vsprintf(logbuf,fmt,args);
    va_end(args);
    //如果指定的文件号小于3,则表示输出到stdout\stderr,则直接调用sys_write
    if(fd <3)
    {
        __asm__("push %%fs\n\t"
            "push %%ds\n\t"
            "pop %%fs\n\t"
            "pushl %0\n\t"
            "pushl $logbuf\n\t"
            "pushl %1\n\t"
            "call sys_write\n\t"
            "addl $8,%%esp\n\t" //内核态操作
            "popl %0\n\t"
            "pop %%fs"
            ::"r" (count),"r"(fd):"ax","cx","dx");

    }
    else //否则均假定全部为3
    {
        //从进程0中取得文件句柄
        if(!(file=task[0]->filp[fd]))
            return 0;
        //文件节点
        inode=file->f_inode;

        //调用file_write写入到指定的文件中即var/process.log
        __asm__("push %%fs\n\t"
            "push %%ds\n\t"
            "pop %%fs\n\t"
            "pushl %0\n\t"
            "pushl $logbuf\n\t"
            "pushl %1\n\t"
            "pushl %2\n\t"
            "call file_write\n\t"
            "addl $12,%%esp\n\t" //用户态??
            "popl %0\n\t"
            "pop %%fs"
            ::"r" (count),"r"(file),"r"(inode):"ax","cx","dx");
    }

    //
    return count;

}

2.5、关于调度算法的修改

调度算法的修改过于复杂,为了观察调度算法改变后对进程运行等的影响,此次实验仅修改系统的调度时间片,即将调度时间片改大一倍来观察对进程的影响结果。修改之处为sched.h文件中全局常量HZ,将其扩大一倍。即:

//#define HZ 100

define HZ 200

实验结果见下部分。

3、实验结果及分析

3.1、实验结果

将修改后的系统进行重新编译后在虚拟机中运行,并使用老师提供的进程函数process.c生成一定量的进程,然后记录进程的状态,生成process_1.log文件,将其拷贝到ubuntu下,使用老师提供的统计工具stat_log.py进行统计分析,得到分析结果文件log_1.txt。

修改系统调度时间,然后再重新编译后运行,用同一个顺序运行进程测试程序,记录进程状态,生成process_2.log文件,将其拷贝到ubuntu下,进行统计分析,得到文件log_2.txt。

本次实验仅使用最简单的方式每次生成6个进程。Process.c文件见下:


int main(int argc, char * argv[])
{
    if(!fork())
        cpuio_bound(10,0,1);
    if(!fork())
        cpuio_bound(10,1,0);
    if(!fork())
        cpuio_bound(10,1,1);
    if(!fork())
        cpuio_bound(5,1,1);
    if(!fork())
        cpuio_bound(5,1,0);
    if(!fork())
        cpuio_bound(10,5,0);


    return 0;
}

实验中使用的命令顺序为:process → exit → process → exit → sync;两次均使用同一个顺序,保证生成的进程数量相同。其中exit命令是为了观察当shell进程结束后系统的新shell进程的产生。

3.2、实验数据及分析

3.2.1、进程记录数据


process_1.log文件部分内容        process_2.log文件部分内容
1 N 48     进程1生成,此时时间片未被修改    1 N 95 进程1生成,此时时间片被修改为21 J 48                    1 J 95
0 J 48  进程0让出CPU            0 J 95 进程0让出CPU
1 R 48  进程1被调度状态由J → R        1 R 95 进程1被调度状态由J → R
2 N 49  进程2生成            2 N 96 进程2生成
2 J 49                    2 J 96
1 W 49  进程1等待进程2结束        1 W 96 进程1等待进程2结束
2 R 49  进程2被调度            2 R 96 进程2被调度
3 N 64  进程3生成            3 N 126 进程3生成
3 J 64                    3 J 126
2 J 64 进程2被抢占让出CPU        2 J 127 进程2被抢占让出CPU
3 R 64 进程3被调度            3 R 127 进程3被调度
3 W 68                    3 W 139
2 R 68                    2 R 139
2 E 74 进程2结束,唤醒父进程1        2 E 146 进程2结束,唤醒父进程1
1 J 74                    1 J 146
1 R 74 进程1被调度            1 R 146 进程1被调度
4 N 74 进程4生成,shell进程        4 N 146 进程4生成,shell进程
4 J 74                    4 J 147
1 W 74                    1 W 147
4 R 74                    4 R 147
…                    …
4 R 1068                4 R 2388
4 E 1069 进程4退出,exit命令的结果    4 E 2391 进程4退出,exit命令的结果
7 J 1069                7 J 2391
1 J 1069                1 J 2391
1 R 1069 进程1被调度,            7 R 2391
14 N 1072 进程14生成,新shell进程    7 E 2392
14 J 1072                1 R 2392 进程1被调度,
…                    14 N 2398 进程14生成,新shell进程    

                    14 J 2398

3.2.2、进程记录统计分析数据

log_1.txt文件内容:


(Unit: tick)
Process   Turnaround   Waiting   CPU Burst   I/O Burst
      0         1929        65           8         406
      1         1660         0           9        1651
      2           25         4          21           0
      3            4         0           4           0
      4          995       140          40         815
      5            3         0           3           0
      6            6         1           5           0
      7          267        81           1         185
      8          269       223          46           0
      9          267       222          45           0
     10          265       220          45           0
     11          283       222          61           0
     12          281       221          60           0
     13            2         0           2           0
     14          614       125          40         449
     15            3         1           2           0
     16            6         1           5           0
     17          237        81           1         155
     18          254       209          45           0
     19          253       207          46           0
     20          251       206          45           0
     21          251       205          46           0
     22          249       204          45           0
     23            3         0           3           0
     24          731         4          39         688
     25            3         1           2           0
     26            0         0           0           0
Average:      337.44     97.89
Throughout: 1.13/s

log_2.txt文件内容:


(Unit: tick)
Process   Turnaround   Waiting   CPU Burst   I/O Burst
      0         5140       131          55         704
      1         4351         3          15        4333
      2           50        12          38           0
      3           13         1          12           0
      4         2245       130          85        2029
      5            6         1           5           0
      6           13         1          12           0
      7          744        83           1         660
      8          769       603         166           0
      9          748       424         101         223
     10          766       432         129         205
     11          763       597         166           0
     12          761       595         166           0
     13            6         1           5           0
     14         2020       218          80        1722
     15            6         1           5           0
     16           12         1          11           0
     17          611        82           1         527
     18          618       497         121           0
     19          616       422         101          93
     20          614       410         101         103
     21          632       496         136           0
     22          629       494         135           0
     23            5         0           5           0
     24          804         9          79         716
     25            6         1           5           0
     26            1         1           0           0
Average:      849.96    209.11
Throughout: 0.52/s

观察上述两组数据,可以看出:第一,系统中进程0、1以及shell进程是占用系统最多的进程,也是系统中必须存在的进程,也可以得出实际上系统大部分时间都在等待中。第二,在修改了时间片后,进程占用时间也在修改,几乎也是一倍。要以看出一个好的调度算法对系统是很重要的。

后记:

在本次实验中可以看到,记录文件并未记录到进程0的诞生,这是因为进程0不是通过fork产生的,而是由系统手动生成的,具体情况此次实验并未深入研究。

附件:本次实验的实验楼截图

图片描述

图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值