进程运行轨迹追踪统计

实验内容

N、J、R、W 和 E ,分别表示进程新建(N)、进入就绪态(J)、进入运行态(R)、进入阻塞态(W) 和退出(E);

process.c是多进程的样本程序,实现如下功能:

所有子进程都并行运行,每个子进程的实际运行时间一般不超过 30 秒;
父进程向标准输出打印所有子进程的 id,并在所有子进程都退出后才退出;

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>

#define HZ	100

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

int main(int argc, char * argv[])
{
	if(!fork()){
        printf( "i am  child [%d] and my parent is [%d]\n",getpid(),getppid());
        cpuio_bound(10, 1, 0);
    }
    if(!fork()){
        printf( "i am  child [%d] and my parent is [%d]\n",getpid(),getppid());
        cpuio_bound(10, 0, 1);
    } 
    if(!fork()){
        printf( "i am  child [%d] and my parent is [%d]\n",getpid(),getppid());
        cpuio_bound(10, 1, 9);
    } 
    if(!fork()){
        printf( "i am  child [%d] and my parent is [%d]\n",getpid(),getppid());
        cpuio_bound(10, 9, 1);
    }     
    wait(NULL);
    wait(NULL);
    wait(NULL);
    return 0;
}

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)模拟1sI/O操作 */
		sleep_time=0;
		while (sleep_time < io_time)
		{
			sleep(1);
			sleep_time++;
		}
		last -= sleep_time;
	}
}

进程运行轨迹的跟踪:

基本任务是在内核中维护一个日志文件 /var/process.log,把从操作系统启动到系统关机过程中所有进程的运行轨迹都记录在这一 log 文件中。
在修改过的 0.11 上运行样本程序,通过分析 log 文件,统计该程序建立的所有进程的等待时间、完成时间(周转时间)和运行时间,然后计算平均等待时间,平均完成时间和吞吐量。可以自己编写统计程序,也可以使用 python 脚本程序—— stat_log.py(在 /home/teacher/ 目录下) ——进行统计。

修改 0.11 进程调度的时间片,然后再运行同样的样本程序,统计同样的时间数据,和原有的情况对比,体会不同时间片带来的差异。

实验步骤

要想记录进程的轨迹,需要写一个log文件,开辟一段空间给它,需要修改linux-0.11/init/main.c中的代码

/*main.c中的函数main()中,在代码
move_to_user_mode();
/***************添加开始***************/
setup((void *) &drive_info);
// 建立文件描述符0和/dev/tty0的关联
(void) open("/dev/tty0",O_RDWR,0);    

//文件描述符1也和/dev/tty0关联
(void) dup(0);        

// 文件描述符2也和/dev/tty0关联
(void) dup(0);        
(void) open("/var/process.log",O_CREAT|O_TRUNC|O_WRONLY,0666);

/***************添加结束***************/

编写向log文件写东西的函数(不能直接用write,因为write是用户态函数,内核中不能使用),linux-0.11/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);
/* 如果输出到stdout或stderr,直接调用sys_write即可 */
    if (fd < 3)
    {
        __asm__("push %%fs\n\t"
            "push %%ds\n\t"
            "pop %%fs\n\t"
            "pushl %0\n\t"
        /* 注意对于Windows环境来说,是_logbuf,下同 */
            "pushl $logbuf\n\t" 
            "pushl %1\n\t"
        /* 注意对于Windows环境来说,是_sys_write,下同 */
            "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;

        __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;
}

寻找状态切换点

修改linux-0.11/kernel/fork.c中的copy_process函数(2处)

/*第一处,进程新建,在创建tss之前*/
//...
//p->start_time = jiffies;
fprintk(3,"%d\tN\t%d\n",p->pid,jiffies);
//p->tss.back_link = 0;

/*第二处,进程就绪*/

//p->state = TASK_RUNNING;
fprintk(3,"%d\tJ\t%d\n",p->pid,jiffies);
//return last_pid;

修改linux-0.11/kernel/sched.c中的sleep_on()和interruptible_sleep_on()函数

/*第一处,进程阻塞,sleep_on()*/
//...
//current->state = TASK_UNINTERRUPTIBLE;
fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
//schedule();
//...
/*第二处,进程阻塞,interruptible_sleep_on()*/
//...
//repeat:    current->state = TASK_INTERRUPTIBLE;
fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
//schedule();
//...
    /*第三处,进程就绪,interruptible_sleep_on()*/
//...
//(**p).state=0;
fprintk(3,"%d\tJ\t%d\n",(*p)->pid,jiffies);
//goto repeat;
//...
    /*第四处,进程就绪,interruptible_sleep_on()*/
//...
//tmp->state=0;
fprintk(3,"%d\tJ\t%d\n",tmp->pid,jiffies);
//}

修改linux-0.11/kernel/sched.c中的wake_up()

void wake_up(struct task_struct **p)
{
    if (p && *p) {
        (**p).state=0;
        /*
        *唤醒 最后进入等待序列的 进程
        */
        fprintk(3,"%d\tJ\t%d\n",(*p)->pid,jiffies);
        *p=NULL;
    }
}

修改linux-0.11/kernel/sched.c中的schedule()

/* switch_to(next)前面新增以下代码:if判断next是不是当前正处于运行态的进程。因为如果二者一样,这种情况下相当于进程的状态没有改变*/
/*编号为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);
}

修改linux-0.11/kernel/sched.c中的sys_pause()和sys_waitpid()

int sys_pause(void)
{
    current->state = TASK_INTERRUPTIBLE;
    /*
    *当前进程  运行 => 可中断睡眠
    */
    if(current->pid != 0)
        fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
    schedule();
    return 0;
}
/*linux-0.11/kernel/exit.c中的sys_waitpid()*/

//...
//current->state=TASK_INTERRUPTIBLE;
fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
//schedule();
//...

修改linux-0.11/kernel/exit.c中的do_exit()函数

//...
//current->state = TASK_ZOMBIE;
fprintk(3,"%d\tE\t%d\n",current->pid,jiffies);
//current->exit_code = code;
//...

挂载文件系统,将oslab/process.c复制到~/oslab/hdc/usr/root 下

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

编译修改过的内核,启动bochs

cd ~/oslab/linux-0.11
make clean
make all
../run

bochs下编译process.c,并运行

gcc -o process process.c 
./process.c

查看生成的process.log字节数

ls -l /var

关闭bochs,挂载文件系统,将hdc/var/process.log拷贝到~/oslab/ 下,并浏览

sudo ./mount-hdc
cp ./hdc/var/process.log ./
more process.log

将teacher目录下的stat_log.py这个python程序拷贝到~/oslab/ 下

cd ~/oslab
cp /home/teacher/stat_log.py ./
#统计PID为0 1 2 3 4 5的进程
./stat_log.py ./process.log 0 1 2 3 4 5 -g

原创作者主页:https://www.lanqiao.cn/users/1121608

文章地址 https://www.lanqiao.cn/courses/reports/1383040/

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用系统调用`getrusage()`来获取进程运行时间。具体步骤如下: 1. 在进程开始运行时记录当前时间,可以使用`gettimeofday()`或`clock_gettime()`等函数获取当前时间。 2. 在进程结束时再次获取当前时间。 3. 调用`getrusage()`函数获取进程的用户时间和系统时间。 4. 计算进程的总运行时间,即结束时间减去开始时间。 5. 输出进程的总运行时间、用户时间和系统时间。 以下是一个基本的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <sys/resource.h> int main() { struct timeval start_time, end_time; struct rusage usage; // 获取进程开始时间 gettimeofday(&start_time, NULL); // 进行一些操作,例如循环计算 int i, sum = 0; for (i = 0; i < 100000000; i++) { sum += i; } // 获取进程结束时间 gettimeofday(&end_time, NULL); // 获取进程的用户时间和系统时间 getrusage(RUSAGE_SELF, &usage); // 计算进程的总运行时间 double start_sec = (double)start_time.tv_sec + (double)start_time.tv_usec / 1000000.0; double end_sec = (double)end_time.tv_sec + (double)end_time.tv_usec / 1000000.0; double total_time = end_sec - start_sec; // 输出进程的总运行时间、用户时间和系统时间 printf("Total time: %f s\n", total_time); printf("User time: %ld.%06ld s\n", usage.ru_utime.tv_sec, usage.ru_utime.tv_usec); printf("System time: %ld.%06ld s\n", usage.ru_stime.tv_sec, usage.ru_stime.tv_usec); return 0; } ``` 注意:`getrusage()`函数返回的时间单位是微秒,需要进行转换才能得到以秒为单位的时间。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值