哈工大操作系统试验3 进程运行轨迹的跟踪与统计

程序只可以通过stat_log.py的简单检查。

实验报告大体写了一下。

//===================================================================================

单进程编程简单,比较容易实现,CPU利用率低。
多进程编程复杂,需要复杂且灵活的调度算法,充分利用CPU资源.


修改INIT_TASK中的priority就可以改变时间片大小。
变化不大的原因是,子进程连续占用cpu的时间要比时间片大很多。

//====================================================================================

init/main.c

kernel/fork.c

kernel/sched.c

kernel/exit.c

kernel/printk.c

这几个文件

init/main.c 需要在把log文件关联到文件描述符3 ,0 1 2分别是stdin stdout 和 stderr ,按照如下方式修改就好

……
move_to_user_mode();

/***************添加开始***************/
setup((void *) &drive_info);
(void) open("/dev/tty0",O_RDWR,0);	//建立文件描述符0和/dev/tty0的关联
(void) dup(0);		//文件描述符1也和/dev/tty0关联
(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();
}
……
dup是一个系统调用,在unistd.h之中有定义。支持1个参数和2个参数的形式
        int dup(int fd);
  int dup2(int fd1,int fd2);
  两个均为复制一个现存的文件的描述
  两个函数的返回:若成功为新的文件描述,若出错为-1;
  由dup返回的新文件描述符一定是当前可用文件描述中的最小数值。用dup2则可以用fd2参数指定新的描述符数值。如果fd2已经打开,则先关闭。若fd1=fd2,则dup2返回f          d2,而不关闭它。通常使用这两个系统调用来重定向一个打开的文件描述符。

当然,大家还要知道DUP是1,2-苯二甲酸二正十一酯,不溶于水,溶于脂。

这样,就会在启动内核的时候打开文件了。

有一点疑问是,log文件是在进程0之中建立的,那么它会记录到进程0的创建嘛?

//======================================================================================================================================

之后修改kernel/fork.c   kernel/sched.c   kernel/exit.c    三个文件,找到进程状态转换的点,并把它输出到log文件之中。  

其中fork这个东西很神奇,

声明是这样

pid_t fork(void);

pid_t 是一个宏定义,其实就是个int,定义在   #include<sys/type.h>中。

fork这个函数的神奇之处在于,它会返回两次,一次在父进程中,一次在子进程中,子进程返回0,父进程返回进程ID,出错返回-1.

原因是子进程复制时复制了父进程的堆寨段,所以两个进程都会停留在fork函数中,等待返回。

详细内容可见:http://baike.baidu.com/view/1952900.htm

一定要仔细看一看,看不懂就不会写progress.c.........................................



附上一个内核进程状态表

内核表示 含义
TASK_RUNNING 可运行
TASK_INTERRUPTIBLE 可中断的等待状态
TASK_UNINTERRUPTIBLE 不可中断的等待状态
TASK_ZOMBIE 僵死
TASK_STOPPED 暂停
TASK_SWAPPING 换入/换出
基本上在几个文件里搜索上述关键字即可完成修改。

fork.c的修改如下

#include <errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <asm/system.h>

extern void write_verify(unsigned long address);

long last_pid=0;

void verify_area(void * addr,int size)
{
    ...
}

int copy_mem(int nr,struct task_struct * p)
{
   ...
}
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)
{
    
    p->start_time = jiffies;

    /*在上面一行初始化了进程的开始时间所以赶快输出一条进程创建的Log*/

    fprintk(3,"%ld\t%c\t%ld\n",last_pid,'N',jiffies); 
    ...
    p->state = TASK_RUNNING; /* do this last, just in case */ 
    /*在上面一行改变了进程的状态这里输出一个进入就绪队列的Log*/ 
    /*进程中Running表示的是可以运行,并不是正在运行*/
    fprintk(3,"%ld\t%c\t%ld\n",last_pid,'J',jiffies); 


    return last_pid;
}

int find_empty_process(void)
{
...
}

exit.c的修改如下

#include <errno.h>
#include <signal.h>
#include <sys/wait.h>

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <asm/segment.h>

int sys_pause(void);
int sys_close(int fd);

void release(struct task_struct * p)
{
    ...
}

static inline int send_sig(long sig,struct task_struct * p,int priv)
{
    ...
}

static void kill_session(void)
{
   ...
}

int sys_kill(int pid,int sig)
{
    ...
}

static void tell_father(int pid)
{
    ...
}

int do_exit(long code)
{
    ...
}

int sys_exit(int error_code)
{
    ...
}

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; 
                
                /*输出一条进程退出的Log*/

               /*TASK_STOPED状态只是将当前进程转入睡眠状态,收到SIG_CONT信号时会被唤醒*/

               /*TASK_ZOMBIE状态则是当前进程被KILL,并发送信号给父进程*/

               fprintk(3,"%ld\t%c\t%ld\n",flag,'E',jiffies); 
               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;

        /*输出一条等待的Log*/
        /*这里要注意一下输出wait的时候要判断一下 pid 是不是等于0 如果等于0 就不输出Log*/
        /*0号进程是守护进程,cpu空闲的时候一直在waiting,输出它的话是不会通过脚本检查的哦*/
    if (current->pid!=0)
        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;
}
sched.c 的修改如下

#include <linux/sched.h> 
#include <linux/kernel.h> 
#include <linux/sys.h> 
#include <linux/fdreg.h> 
#include <asm/system.h> 
#include <asm/io.h> 
#include <asm/segment.h> 
 
#include <signal.h> 
 
#define _S(nr) (1<<((nr)-1)) 
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) 
 
void show_task(int nr,struct task_struct * p) 
{ 
    ...
} 
 
void show_stat(void) 
{ 
    ...
} 
 
#define LATCH (1193180/HZ) 
 
extern void mem_use(void); 
 
extern int timer_interrupt(void); 
extern int system_call(void); 
 
union task_union { 
    struct task_struct task; 
    char stack[PAGE_SIZE]; 
}; 
 
static union task_union init_task = {INIT_TASK,}; 
 
long volatile jiffies=0; 
long startup_time=0; 
struct task_struct *current = &(init_task.task); 
struct task_struct *last_task_used_math = NULL; 
 
struct task_struct * task[NR_TASKS] = {&(init_task.task), }; 
 
long user_stack [ PAGE_SIZE>>2 ] ; 
 
struct { 
    long * a; 
    short b; 
    } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 }; 

void math_state_restore() 
{ 
    ... 
} 
 

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) 
              { 
                (*p)->state=TASK_RUNNING; 
                /*输出就绪的Log*/
                fprintk(3,"%ld\t%c\t%ld\n",(*p)->pid,'J',jiffies); 
              } 
        } 
 
/* 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(current->state == TASK_RUNNING && current != task[next]) 
        /*输出就绪的Log*/
        fprintk(3,"%ld\t%c\t%ld\n",current->pid,'J',jiffies); 
    if(current != task[next]) 
        /*输出可运行的Log*/
        fprintk(3,"%ld\t%c\t%ld\n",task[next]->pid,'R',jiffies); 
    switch_to(next);         
} 
 
int sys_pause(void) 
{ 
    current->state = TASK_INTERRUPTIBLE;
    /*检查并输出等待的Log*/ 
    if (current->pid != 0)     
        fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies); 
    schedule(); 
    return 0; 
} 
 
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; 
    current->state = TASK_UNINTERRUPTIBLE; 

    /*检查并输出等待的Log*/ 

    if (current->pid != 0) 
      fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies); 
    schedule(); 
    *p = tmp; 
    if (tmp) 
    { 
        tmp->state=TASK_RUNNING; 

        /*输出就绪的Log*/

        fprintk(3,"%ld\t%c\t%ld\n",tmp->pid,'J',jiffies); 
    } 
} 
 
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: current->state = TASK_INTERRUPTIBLE; 

    /*检查并输出等待的Log*/ 

    if (current->pid != 0) 
     fprintk(3,"%ld\t%c\t%ld\n",current->pid,'W',jiffies); 
    schedule(); 
    if (*p && *p != current) { 
        (**p).state=TASK_RUNNING; 

        /*输出就绪的Log*/

        fprintk(3,"%ld\t%c\t%ld\n",(**p).pid,'J',jiffies); 
        goto repeat; 
    } 
    *p=tmp; 
    if (tmp) 
    { 
        tmp->state=TASK_RUNNING; 

        /*输出就绪的Log*/

        fprintk(3,"%ld\t%c\t%ld\n",tmp->pid,'J',jiffies); 
    } 
} 
 
void wake_up(struct task_struct **p) 
{ 
    if (p && *p) { 
        if((**p).state != TASK_RUNNING){ 

             /*输出就绪的Log*/

             fprintk(3,"%ld\t%c\t%ld\n",(**p).pid,'J',jiffies); 
             (**p).state=TASK_RUNNING; 
    } 
    } 
} 
 
...

====================================================================================================================================

最后写一个自己的proress.c。

perror ( )用 来 将 上 一 个 函 数 发 生 错 误 的 原 因 输 出 到 标 准 设备 (stderr) 。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量error 的值来决定要输出的字符串。不加也行。


然后大家可能对,这个主函数不太理解,这和fork的原理有关,很神奇。

具体看上文介绍。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>

#define HZ      100


/*
 * 此函数按照参数占用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;
    }
}

  
   void main()
  
   {
        pid_t c_p1;
        pid_t c_p2;
        pid_t c_p3;
        pid_t c_p4;
          
          if((c_p1 = fork())==0 )
          {
                  printf( "in child1 \n");cpuio_bound( 5 , 2 , 2);
          }
          else if((c_p2 = fork())==0)
          {
                 printf( "in child2 \n");cpuio_bound( 5 , 4 , 0);
          }
          else if((c_p3 = fork())==0)
          {
                  printf( "in child3 \n");cpuio_bound( 5,0 , 4);
          }
          else if((c_p4 = fork())==0)
          {
                 printf( "in child4 \n");cpuio_bound( 4 , 2 , 2);
          }
          else if(c_p1==-1||c_p2==-1||c_p3==-1||c_p4==-1)
          {
                 perror("fork");
                 exit(1);         
          }
          else
          {
                  printf("====================This is parent process====================\n");
                  printf("My pid is %d\n",getpid());
                  printf("The pid of child1 is %d\n",c_p1);
                  printf("The pid of child2 is %d\n",c_p2);
                  printf("The pid of child3 is %d\n",c_p3);
                  printf("The pid of child4 is %d\n",c_p4);
         }
      wait(NULL);
 
  }


  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
fork是一个系统调用,用于创建一个新的进程。在哈尔滨工业大学的操作系统课程中,学生学习到了fork系统调用的使用和原理。 在操作系统中,每个进程都具有一个唯一的进程ID(PID)和一组资源。当调用fork时,当前进程会被复制,创建一个新的子进程。子进程和父进程具有相同的代码、数据和环境变量等。但是,子进程有自己的独立的内存空间。 fork调用返回两次,一次在父进程中返回子进程的PID,一次在子进程中返回0。这样,父进程可以根据返回的PID来判断fork是否成功,并根据需要进行相应的处理。 fork系统调用被广泛应用于多进程编程中。通过利用fork,可以实现并发执行,提高系统的资源利用率和效率。在操作系统课程中,学生通常学习如何使用fork创建子进程,并使用进程间通信机制进行进程间的数据交换和同步。 通过学习fork系统调用,学生可以了解进程的创建和管理,理解进程的概念和特点,并掌握进程间通信和同步的方法。此外,fork也是其他一些高级系统调用(如exec)的基础,对于学生进一步研究和学习操作系统提供了良好的基础。 总之,哈尔滨工业大学操作系统课程中的fork系统调用是学生学习并发编程和进程管理的重要内容,通过掌握这个系统调用,可以为学生提供丰富的编程经验和操作系统理论基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值