操作系统实验3进程管理

参考了两位大佬的

哈工大《操作系统》实验3实验报告 

哈工大操作系统实验(三)进程管理

1.首先创造日志文件,需要在linux-0.11/init/main.c中添加如下代码

    /*
    *main中打开process.log文件
    */
    setup((void *) &drive_info);//加载文件系统
    (void) open("/dev/tty0",O_RDWR,0);//打开/dev/tty0,建立文件描述符0和/dev/tty0的关联
    (void) dup(0);//让文件描述符1也和/dev/tty0关联
    (void) dup(0);//让文件描述符2也和/dev/tty0关联
//加载日志文件并将其编号变为3
    (void) open("/var/process.log",O_CREAT|O_TRUNC|O_WRONLY,0666);
    /*open dup返回的一定是未使用的最小的描述符数值 参见《UNIX环境高级编程》(第三版) P51*/
    /*添加结束*/

这段代码建立了文件描述符0、1和2,它们分别就是stdin、stdout和stderr。这三者的值是系统标准(Windows也是如此),不可改变。可以把log文件的描述符关联到3。文件系统初始化,描述符0、1和2关联之后,才能打开log文件,开始记录进程的运行轨迹。0666并不是 文件描述符,只是权限标记。修改后如图

2.修改kernel/printk.c文件,添加打印功能

在文件中添加以下代码

头文件:

#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);
    if (fd < 3)
    /* 如果输出到stdout或stderr,直接调用sys_write即可 */
    {
    __asm__("push %%fs\n\t"
    "push %%ds\n\t"
    "pop %%fs\n\t"
    "pushl %0\n\t"
    "pushl $logbuf\n\t" /* 注意对于Windows环境来说,是_logbuf,下同 */
    "pushl %1\n\t"
    "call sys_write\n\t" /* 注意对于Windows环境来说,是_sys_write,下同 */
    "addl $8,%%esp\n\t"
    "popl %0\n\t"
    "pop %%fs"
    ::"r" (count),"r" (fd):"ax","cx","dx");
    }
    else
    /* 假定>=3的描述符都与文件关联。事实上,还存在很多其它情况,这里并没有考虑。*/
    {
    if (!(file=task[0]->filp[fd])) /* 从进程0的文件描述符表中得到文件句柄 */
    return 0;
    inode=file->f_inode;
    __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;
}

 3.跟踪进程的状态

首先要知道在什么情况下进程的状态会发生修改,分别是进程创建的时候,进程调度的时候,进程销毁的时候,因此我们需要修改三个对应的文件去记录进程的修改。

修改/linux-0.11/kernel/forke.c

进程创建函数的是:copy_process,在此函数中添加如下代码

    /*
    *新建一个进程
    */
    fprintk(3,"%d\tN\t%d\n",p->pid,jiffies);
    /*
    *新建 => 就绪
    */
    fprintk(3,"%d\tJ\t%d\n",p->pid,jiffies);

修改后如下图

 

 

同理我们接下来修改/linux-0.11/kernel/sched.c

从以上修改中我们已经知道进程p->state改变的时候就需要打印信息

修改schedule函数添加以下代码(调度时)

          /*可中断睡眠 => 就绪*/
          fprintk(3,"%d\tJ\t%d\n",(*p)->pid,jiffies);
    /*编号为next的进程 运行*/
    if(current->pid != task[next] ->pid)
    {
        /*时间片到时程序 => 就绪*/
        if(current->state == TASK_RUNNING)
            fprintk(3,"%d\tJ\t%d\n",current->pid,jiffies);
        fprintk(3,"%d\tR\t%d\n",task[next]->pid,jiffies);
    }

 系统暂停,进程又运行变中断在sys_pause中添加

    	/*
    	*当前进程  运行 => 可中断睡眠
    	*/
    	if(current->pid != 0)
        	fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);

进程睡眠时,在sleep_on中添加

    /*
    *当前进程进程 => 不可中断睡眠
    */
    fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);

 

        /*
        *原等待队列 第一个进程 => 唤醒(就绪)
        */
        fprintk(3,"%d\tJ\t%d\n",tmp->pid,jiffies);

在 interruptible_sleep_on中添加如下代码

    /*
    *这一部分属于 唤醒队列中间进程,通过goto实现唤醒 队列头进程 过程中Wait
    */
    fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
        /*
        *当前进程进程 => 可中断睡眠 同上
        */
        fprintk(3,"%d\tJ\t%d\n",(*p)->pid,jiffies);

 

        /*
        *原等待队列 第一个进程 => 唤醒(就绪)
        */
        fprintk(3,"%d\tJ\t%d\n",tmp->pid,jiffies);

唤醒进程,进入就绪状态,修改wake_up函数,添加以下代码

        /*
        *唤醒 最后进入等待序列的 进程
        */
        fprintk(3,"%d\tJ\t%d\n",(*p)->pid,jiffies);

最终修改截图如下(每个函数中的修改大致相似,都是追踪状态的变化)

进程销毁的时候,修改exit.c

在do_exit函数中添加如下代码

    /*
    *退出一个进程
    */
    fprintk(3,"%d\tE\t%d\n",current->pid,jiffies);

 在sys_waitpid函数中添加如下代码

        /*
        *当前进程 => 等待
        */
        fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);

 以上两个代码段都是在在状态变更之后插入

以上修改完成后我们可以在ubuntu下Linux文件夹下编译,先make clean再make

4.编译成功后我们需要编写一个程序process.c用来跟踪子进程(注意这个文件要放到Linux内核中,因此我们需要先挂载,然后把文件导入/hdc/usr中,文件代码如下(原博客中此代码有错)

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>

#define HZ    100

void cpuio_bound(int last, int cpu_time, int io_time);

int main(int argc, char * argv[])
{
       pid_t Pid1;
       pid_t Pid2;
       pid_t Pid3;

       Pid1 = fork(); 
       if (Pid1 < 0) printf("error in fork!"); 
       else if (Pid1 == 0) 
            {
                 printf("child process 1:\n");
                 cpuio_bound(5, 2, 2);
            }

       Pid2 = fork();
       if (Pid2 < 0) printf("error in fork!"); 
       else if (Pid2 == 0) 
            {
                 printf("child process 2:\n");
                 cpuio_bound(5, 4, 0);
            }

       Pid3 = fork();
       if (Pid3 < 0) printf("error in fork!"); 
       else if (Pid3 == 0) 
            {
                 printf("child process 3:\n");
                 cpuio_bound(5, 0, 4);
            }

       printf("This process's Pid is %d\n", getpid());
       printf("Pid of child process 1 is %d\n", Pid1);
       printf("Pid of child process 2 is %d\n", Pid2);
       printf("Pid of child process 3 is %d\n", Pid3);
       wait(NULL);
       wait(NULL);
       wait(NULL);
       return 0;
}

/*
 * 此函数按照参数占用CPU和I/O时间
 * last: 函数实际占用CPU和I/O的总时间,不含在就绪队列中的时间,>=0是必须的
 * cpu_time: 一次连续占用CPU的时间,>=0是必须的
 * io_time: 一次I/O消耗的时间,>=0是必须的
 * 如果last > cpu_time + io_time,则往复多次占用CPU和I/O
 * 所有时间的单位为秒
 */
void cpuio_bound(int last, int cpu_time, int io_time)
{
    struct tms start_time, current_time;
    clock_t utime, stime;
    int sleep_time;

    while (last > 0)
    {
        /* CPU Burst */
        times(&start_time);
        /* 其实只有t.tms_utime才是真正的CPU时间。但我们是在模拟一个
         * 只在用户状态运行的CPU大户,就像“for(;;);”。所以把t.tms_stime
         * 加上很合理。*/
        do
        {
            times(&current_time);
            utime = current_time.tms_utime - start_time.tms_utime;
            stime = current_time.tms_stime - start_time.tms_stime;
        } while ( ( (utime + stime) / HZ )  < cpu_time );
        last -= cpu_time;

        if (last <= 0 )
            break;

        /* IO Burst */
        /* 用sleep(1)模拟1秒钟的I/O操作 */
        sleep_time=0;
        while (sleep_time < io_time)
        {
            sleep(1);
            sleep_time++;
        }
        last -= sleep_time;
    }
}

  挂载复制文件进入内核

sudo ./mount-hdc
cp process.c ./hdc/usr/root/

5.打开bochs,编译文件,运行

gcc -o process process.c

./process

可看到如下界面

 

程序运行结束后我们通过sync命令将缓存区命令加载到硬盘

然后我们在/var文件夹下就可以看到process.log文件

6.为了更直观的看出一个进程各种状态的时间.,我们用Python脚本stat_log.py进行查看,脚本代码如下

#!/usr/bin/python
import sys
import copy

P_NULL = 0
P_NEW = 1
P_READY = 2
P_RUNNING = 4
P_WAITING = 8
P_EXIT = 16

S_STATE = 0
S_TIME = 1

HZ = 100

graph_title = r"""
-----===< COOL GRAPHIC OF SCHEDULER >===-----
             [Symbol]   [Meaning]
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~
             number   PID or tick
              "-"     New or Exit 
              "#"       Running
              "|"        Ready
              ":"       Waiting
                    / Running with 
              "+" -|     Ready 
                    \and/or Waiting
-----===< !!!!!!!!!!!!!!!!!!!!!!!!! >===-----
"""

usage = """
Usage: 
%s /path/to/process.log [PID1] [PID2] ... [-x PID1 [PID2] ... ] [-m] [-g]
Example:
# Include process 6, 7, 8 and 9 in statistics only. (Unit: tick)
%s /path/to/process.log 6 7 8 9
# Exclude process 0 and 1 from statistics. (Unit: tick)
%s /path/to/process.log -x 0 1
# Include process 6 and 7 only and print a COOL "graphic"! (Unit: millisecond)
%s /path/to/process.log 6 7 -m -g
# Include all processes and print a COOL "graphic"! (Unit: tick)
%s /path/to/process.log -g
"""

class MyError(Exception):
    pass

class DuplicateNew(MyError):
    def __init__(self, pid):
        args = "More than one 'N' for process %d." % pid
        MyError.__init__(self, args)

class UnknownState(MyError):
    def __init__(self, state):
        args = "Unknown state '%s' found." % state
        MyError.__init__(self, args)

class BadTime(MyError):
    def __init__(self, time):
        args = "The time '%d' is bad. It should >= previous line's time." % time
        MyError.__init__(self, args)

class TaskHasExited(MyError):
    def __init__(self, state):
        args = "The process has exited. Why it enter '%s' state again?" % state
        MyError.__init__(self, args)

class BadFormat(MyError):
    def __init__(self):
        args = "Bad log format"
        MyError.__init__(self, args)

class RepeatState(MyError):
    def __init__(self, pid):
        args = "Previous state of process %d is identical with this line." % (pid)
        MyError.__init__(self, args)

class SameLine(MyError):
    def __init__(self):
        args = "It is a clone of previous line."
        MyError.__init__(self, args)

class NoNew(MyError):
    def __init__(self, pid, state):
        args = "The first state of process %d is '%s'. Why not 'N'?" % (pid, state)
        MyError.__init__(self, args)

class statistics:
    def __init__(self, pool, include, exclude):
        if include:
            self.pool = process_pool()
            for process in pool:
                if process.getpid() in include:
                    self.pool.add(process)
        else:
            self.pool = copy.copy(pool)

        if exclude:
            for pid in exclude:
                if self.pool.get_process(pid):
                    self.pool.remove(pid)
    
    def list_pid(self):
        l = []
        for process in self.pool:
            l.append(process.getpid())
        return l

    def average_turnaround(self):
        if len(self.pool) == 0:
            return 0
        sum = 0
        for process in self.pool:
            sum += process.turnaround_time()
        return float(sum) / len(self.pool)

    def average_waiting(self):
        if len(self.pool) == 0:
            return 0
        sum = 0
        for process in self.pool:
            sum += process.waiting_time()
        return float(sum) / len(self.pool)
    
    def begin_time(self):
        begin = 0xEFFFFF
        for p in self.pool:
            if p.begin_time() < begin:
                begin = p.begin_time()
        return begin

    def end_time(self):
        end = 0
        for p in self.pool:
            if p.end_time() > end:
                end = p.end_time()
        return end

    def throughput(self):
        return len(self.pool) * HZ / float(self.end_time() - self.begin_time())

    def print_graphic(self):
        begin = self.begin_time()
        end = self.end_time()

        print graph_title

        for i in range(begin, end+1):
            line = "%5d " % i
            for p in self.pool:
                state = p.get_state(i)
                if state & P_NEW:
                    line += "-"
                elif state == P_READY or state == P_READY | P_WAITING:
                    line += "|"
                elif state == P_RUNNING:
                    line += "#"
                elif state == P_WAITING:
                    line += ":"
                elif state & P_EXIT:
                    line += "-"
                elif state == P_NULL:
                    line += " "
                elif state & P_RUNNING:
                    line += "+"
                else:
                    assert False
                if p.get_state(i-1) != state and state != P_NULL:
                    line += "%-3d" % p.getpid()
                else:
                    line += "   "
            print line

class process_pool:
    def __init__(self):
        self.list = []
    
    def get_process(self, pid):
        for process in self.list:
            if process.getpid() == pid:
                return process
        return None

    def remove(self, pid):
        for process in self.list:
            if process.getpid() == pid:
                self.list.remove(process)

    def new(self, pid, time):
        p = self.get_process(pid)
        if p:
            if pid != 0:
                raise DuplicateNew(pid)
            else:
                p.states=[(P_NEW, time)]
        else:
            p = process(pid, time)
            self.list.append(p)
        return p

    def add(self, p):
        self.list.append(p)

    def __len__(self):
        return len(self.list)
    
    def __iter__(self):
        return iter(self.list)

class process:
    def __init__(self, pid, time):
        self.pid = pid
        self.states = [(P_NEW, time)]
    
    def getpid(self):
        return self.pid

    def change_state(self, state, time):
        last_state, last_time = self.states[-1]
        if state == P_NEW:
            raise DuplicateNew(pid)
        if time < last_time:
            raise BadTime(time)
        if last_state == P_EXIT:
            raise TaskHasExited(state)
        if last_state == state and self.pid != 0: # task 0 can have duplicate state
            raise RepeatState(self.pid)

        self.states.append((state, time))

    def get_state(self, time):
        rval = P_NULL
        combo = P_NULL
        if self.begin_time() <= time <= self.end_time():
            for state, s_time in self.states:
                if s_time < time:
                    rval = state
                elif s_time == time:
                    combo |= state
                else:
                    break
            if combo:
                rval = combo
        return rval

    def turnaround_time(self):
        return self.states[-1][S_TIME] - self.states[0][S_TIME]

    def waiting_time(self):
        return self.state_last_time(P_READY)

    def cpu_time(self):
        return self.state_last_time(P_RUNNING)

    def io_time(self):
        return self.state_last_time(P_WAITING)

    def state_last_time(self, state):
        time = 0
        state_begin = 0
        for s,t in self.states:
            if s == state:
                state_begin = t
            elif state_begin != 0:
                assert state_begin <= t
                time += t - state_begin
                state_begin = 0
        return time


    def begin_time(self):
        return self.states[0][S_TIME]

    def end_time(self):
        return self.states[-1][S_TIME]
        
# Enter point
if len(sys.argv) < 2:
    print usage.replace("%s", sys.argv[0])
    sys.exit(0)

# parse arguments
include = []
exclude = []
unit_ms = False
graphic = False
ex_mark = False

try:
    for arg in sys.argv[2:]:
        if arg == '-m':
            unit_ms = True
            continue
        if arg == '-g':
            graphic = True
            continue
        if not ex_mark:
            if arg == '-x':
                ex_mark = True
            else:
                include.append(int(arg))
        else:
            exclude.append(int(arg))
except ValueError:
    print "Bad argument '%s'" % arg
    sys.exit(-1)

# parse log file and construct processes
processes = process_pool()

f = open(sys.argv[1], "r")

# Patch process 0's New & Run state
processes.new(0, 40).change_state(P_RUNNING, 40)

try:
    prev_time = 0
    prev_line = ""
    for lineno, line in enumerate(f):

        if line == prev_line:
            raise SameLine
        prev_line = line

        fields = line.split("\t")
        if len(fields) != 3:
            raise BadFormat

        pid = int(fields[0])
        s = fields[1].upper()

        time = int(fields[2])
        if time < prev_time:
            raise BadTime(time)
        prev_time = time

        p = processes.get_process(pid)

        state = P_NULL
        if s == 'N':
            processes.new(pid, time)
        elif s == 'J':
            state = P_READY
        elif s == 'R':
            state = P_RUNNING
        elif s == 'W':
            state = P_WAITING
        elif s == 'E':
            state = P_EXIT
        else:
            raise UnknownState(s)
        if state != P_NULL:
            if not p:
                raise NoNew(pid, s)
            p.change_state(state, time)
except MyError, err:
    print "Error at line %d: %s" % (lineno+1, err)
    sys.exit(0)

# Stats
stats = statistics(processes, include, exclude)
att = stats.average_turnaround()
awt = stats.average_waiting()
if unit_ms:
    unit = "ms"
    att *= 1000/HZ
    awt *= 1000/HZ
else:
    unit = "tick"
print "(Unit: %s)" % unit
print "Process   Turnaround   Waiting   CPU Burst   I/O Burst"
for pid in stats.list_pid():
    p = processes.get_process(pid)
    tt = p.turnaround_time()
    wt = p.waiting_time()
    cpu = p.cpu_time()
    io = p.io_time()

    if unit_ms:
        print "%7d   %10d   %7d   %9d   %9d" % (pid, tt*1000/HZ, wt*1000/HZ, cpu*1000/HZ, io*1000/HZ)
    else:
        print "%7d   %10d   %7d   %9d   %9d" % (pid, tt, wt, cpu, io)
print "Average:  %10.2f   %7.2f" % (att, awt)
print "Throughout: %.2f/s" % (stats.throughput())

if graphic:
    stats.print_graphic()

对脚本添加权限

sudo chmod +x ./stat_log.py

运行脚本./stat_log.py process.log 0 1 2 3 4 5

可以看到如下:

注意:不知道什么原因,在process.log文件中可能存在很多重复行,运行脚本时会报错,此时只需要删除旧文件,再次运行process程序生成新的文件即可。

7.在linux-0.11/include/linux/sched.h的INIT_TASK中修改时间片的大小

修改后如下(0,15,15->0,15,100)

#define INIT_TASK \
/* state etc */	{ 0,15,100, \
/* signals */	0,{{},},0, \
/* ec,brk... */	0,0,0,0,0,0, \
/* pid etc.. */	0,-1,0,0,0, \
/* uid etc */	0,0,0,0,0,0, \
/* alarm */	0,0,0,0,0,0, \
/* math */	0, \
/* fs info */	-1,0022,NULL,NULL,NULL,0, \
/* filp */	{NULL,}, \
	{ \
		{0,0}, \
/* ldt */	{0x9f,0xc0fa00}, \
		{0x9f,0xc0f200}, \
	}, \
/*tss*/	{0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\
	 0,0,0,0,0,0,0,0, \
	 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \
	 _LDT(0),0x80000000, \
		{} \
	}, \
}

再次编译内核,并运行process程序,观察区别。

运行脚本结果如下

至此,程序结束(最后记得修改回原时间片大小)

出现的问题:无法对bochs下的文件进行修改

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.基本系统进程   Csrss.exe:这是子系统服务器进程,负责控制Windows创建或删除线程以及16位的虚拟DOS环境。   System Idle Process:这个进程是作为单线程运行在每个处理器上,并在系统不处理其它线程的时候分派处理器的时间。   Smss.exe:这是一个会话管理系统,负责启动用户会话。   Services.exe:系统服务的管理工具。   Lsass.exe:本地的安全授权服务。   Explorer.exe:资源管理器。   Spoolsv.exe:管理缓冲区打印和传真作业。   Svchost.exe:这个进程要着重说明一下,有不少朋友都有这种错觉:若是在“任务管理器”看到多个Svchost.exe在运行,就觉得是有病毒了。其实并不一定,系统启动的时候,Svchost.exe将检查注册表的位置来创建需要加载的服务列表,如果多个Svchost.exe同时运行,则表明当前有多组服务处于活动状;多个DLL文件正在调用它。   至于其它一些附加进程,大多为系统服务,是可以酌情结束运行的。由于其数量众多,我们在此也不便于一一列举。   在系统资源紧张的情况下,我们可以选择结束一些附加进程,以增加资源,起到优化系统的作用。在排除基本系统及附加进程后,新增的陌生进程就值得被大家怀疑了。 更多内容请看Windows操作系统安装、系统优化大全、系统安全设置专题,或进入讨论组讨论。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值