进程是操作系统对运行的应用程序的抽象,是进行资源分配的单位。 为了更好的利用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 ,按照如下方式修改就好,同时将init()方法中这部分注释就可以了。
…… 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返回fd2,而不关闭它。通常使用这两个系统调用来重定向一个打开的文件描述符。 当然,大家还要知道进程0在开始的时候,就会在启动内核的时候打开文件了。 //====================================================================================================================================== 之后修改kernel/fork.c kernel/sched.c kernel/exit.c 三个文件,找到进程状态转换的点,并把它输出到log文件之中。 其中fork这个东西很神奇, 声明是这样 pid_t fork(void); pid_t 是一个宏定义,其实就是个int,定义在 #include
#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的修改如下:
//这里需要注意的是:进程推出的时候:先调用exit方法释放除PCB数据结构的页面 其他的所有物理页,将自己的状态置为僵尸状态,然后在通知父进程,请回收PCB所在的物理页,同时将task数据中清空为null, 对于wait系统调用不熟悉的话,请阅读: http://blog.csdn.net/wzx19840423/article/details/6615875 博客进行相关学习 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; } } }最后引用其他人的process.c程序,
#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(¤t_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); }