当前位置: 代码迷 >> 综合 >> Linux0.11 实验四 进程运行轨迹跟踪与统计
  详细解决方案

Linux0.11 实验四 进程运行轨迹跟踪与统计

热度:98   发布时间:2023-12-15 12:02:15.0

实验4 进程运行轨迹跟踪与统计

pid	X	time

其中:- pid是进程的ID

  • X可以是N,J,R,W和E中的任意一个
    • N 进程新建 ,new
    • J 进入就绪态 , J
    • R 进入运行态 , run
    • W 进入阻塞态, wait
    • E 退出 ,exit
  • time表示X发生的时间。这个时间不是物理时间,而是系统的滴答时间(tick)

仔细阅读sched.c文件

本实验主要分析整个的调度流程,主要方法是,当发生状态转换时,进程的state变化时,我们把这个记录记下来。

打印函数

先看看打印函数:

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""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的描述符都与文件关联。事实上,还存在很多其他情况,这里并没有考虑 */{
    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"  //使用file_write,往文件/var/process.log中写入。"addl $12, %%esp\n\t""popl %0\n\t""pop %%fs"::"r" (count), "r" (file), "r" (inode):"ax", "cx", "dx");}return count;
}

linux源码中,只有这几种状态:

#define TASK_RUNNING 0 //正在运行或者已就绪
#define TASK_INTERRUPTIBLE 1 //进程处于可中断等待状态
#define TASK_UNINTERRUPTIBLE 2 //程序处于不可中断等待状态,主要用于I/O操作等待。
#define TASK_ZOMBIE 3 //程序处于僵死状态,已经停止运行,但父进程还没发信号。
#define TASK_STOPPED 4 //程序已停止。

N 进程新建 ,new

新进程在fork的时候产生,fork的关键是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)
{
    ...p->start_time = jiffies;fprintk(3, "%ld\t%c\t%ld\n", last_pid, 'N', jiffies); //新建...p->state = TASK_RUNNING;	/* do this last, just in case */fprintk(3, "%ld\t%c\t%ld\n", last_pid, 'J', jiffies); //完成后,立马进入就绪态。return last_pid;
}

W 进入阻塞态, wait

//系统调用waitpid(),挂起当前进程,直到pid指定的子进程退出。
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
    ...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;elsereturn -EINTR;}return -ECHILD;
}

E 退出 ,exit

int do_exit(long code)
{
    ...current->state = TASK_ZOMBIE;fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'E', jiffies);/* print message end in 3-processTack */current->exit_code = code;tell_father(current->father);schedule();return (-1);	/* just to suppress warnings */
}

就绪与运行

void schedule(void)

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));//到点,给信号alarm(*p)->alarm = 0;}if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) && //除去被block掉的信号,还有其他信号,(*p)->state==TASK_INTERRUPTIBLE) //且进程是可中断的{
    (*p)->state=TASK_RUNNING;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;//检查所有任务中,最需要被调度的进程,将这个号码放入next,这个就是下次要调度的进程。}if (c) break;	/* 找到一个counter不等于0 且是TASK_RUNNING状态中的counter最大的进程;或者当前系统没有一个可以运行的进程,此时c=-1, next=0,进程0得到调度,所以调度算法是不在意进程0的状态是不是TASK_RUNNING,这就意味这进程0可以直接从睡眠切换到运行! */for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)if (*p)(*p)->counter = ((*p)->counter >> 1) +(*p)->priority;}if (task[next]->pid != current->pid) {
    //打印就绪,运行,就切到那个进程。if (current->state == TASK_RUNNING) {
    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);
}

int sys_pause(void)

/** 系统无事可做的时候,进程0会不停地调用sys_pause(),以激活调度算法。此时它的状态可以是等待态,* 等待有其他可运行的进程;也可以是运行态,因为它是唯一一个在CPU上运行的进程,只不过运行的效果是等待。*/
int sys_pause(void)
{
    current->state = TASK_INTERRUPTIBLE;if (current->pid != 0) //进程0死循环,一直调用这个函数。防止一直打印。{
    fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'W', jiffies);} schedule();return 0;
}

void sleep_on(struct task_struct **p)

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插入“等待队列”头部,tmp是原来的头部 */current->state = TASK_UNINTERRUPTIBLE;fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'W', jiffies);schedule();if (tmp) {
    fprintk(3, "%ld\t%c\t%ld\n", tmp->pid, 'J', jiffies);/* print message end in 3-processTack */tmp->state=TASK_RUNNING;	/* 唤醒队列中的上一个(tmp)睡眠进程。0换作TASK_RUNNING更好。在记录进程被唤醒时一定要考虑到这种情况,实验者一定要注意!!! */}
}

void interruptible_sleep_on(struct task_struct **p)

/** TASK_UNINTERRUPTIBLE和TASK_INTERRUPTIBLE的区别在于不可中断的睡眠只能由wake_up()显示唤醒,* 再由上面的 schedule()语句后面的** if (tmp) tmp->state=0;** 依次唤醒,所以不可中断的睡眠一定是严格按照从“队列”(一个依靠放在进程内核栈中的指针变量tmp* 维护的队列)的首部进行唤醒。而对于可中断的进程,除了用wake_up唤醒以外,也可以用信号(给进程* 发送一个信号,实际上就是将进程PCB中维护的一个向量的某一位置位,进程需要在合适的时候处理* 这一位。感兴趣的实验者可以阅读有关代码)来唤醒,如在 schedule()中:** for(p = &LAST_TASK; p > &FRIST_TASK; --p)* if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&* (*p)->state==TASK_INTERRUPTIBLE)* (*p)->state=TASK_RUNNING;//唤醒** 就是当进程是可中断睡眠时,如果遇到一些信号就将其唤醒。这样的唤醒会出现一个问题,那就是可能会* 唤醒等待队列中间的某个进程,此时这个链就需要进行适当调整。interruptible_sleep_on和sleep_on* 函数的主要区别就在这里。*/
void interruptible_sleep_on(struct task_struct **p) //将当前任务置为可中断的等待状态,并放入*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; //等待的任务可中断。schedule();//只有当进程被唤醒才会回到这里。if (*p && *p != current) {
    //此current不等于当时的current,说明队列中间有任务被唤醒了。(**p).state=TASK_RUNNING;//将队头唤醒。fprintk(3, "%ld\t%c\t%ld\n", (*p)->pid, 'J', jiffies);goto repeat;//重新调度。}*p=NULL;if (tmp)//从队头开始,逐个释放。tmp->state=0;fprintk(3, "%ld\t%c\t%ld\n", (*p)->pid, 'J', jiffies);
}

void wake_up(struct task_struct **p)

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

进程运行log分析

1	N	48	copy_process	//进程1新建(init())。此前是进程0建立和运行,我们是在move_to_user_mode后才新建/var/process.log文件进行进程运行轨迹记录的,所以进程0的新建和运行没有出现在log文件里
1	J	48	copy_process	//新建后进入就绪队列
0	J	48	schedule		/* --- 进程0从运行->就绪,让出CPU。(执行的是pause,调用) 1 R 48 schedule 进程1运行 --- */
2	N	49	copy_process	/* --- 进程1建立进程2。 2 J 49 copy_process 进程2会运行/etc/rc脚本,然后退出 --- */
1	W	49	sys_waitpid		//进程1开始等待(等待进程2退出)
2	R	49	schedule		//进程2运行
3	N	64	copy_process	/* --- 进程2建立进程3 3 J 64 copy_process 进程3是/bin/sh建立的运行脚本的子进程 --- */
2	E	68	do_exit			//进程2不等进程3退出,就先走一步了
1	J	68	schedule		/* --- 进程1此前在等待进程2退出,被阻塞。进程2退出后,重新进入就绪队列 1 R 68 schedule 进程1运行 --- */
4	N	69	copy_process	/* --- 进程1建立进程4,即shell 4 J 69 copy_process 进程4进入就绪队列 --- */
1	W	69	sys_waitpid		//进程1等待shell退出(除非执行exit命令,否则shell不会退出)
3	R	69	schedule		//进程3开始运行
3	W	75	sys_pause		//进程3主动睡觉,让出CPU
4	R	75	schedule		//进程4开始运行
5	N	107	copy_process	/* --- 进程5是shell建立的不知道做什么的进程(可能是ls命令?) 5 J 107 copy_process 进程5新建后进入就绪队列 --- */
4	W	108	sleep_on		//进程4进入不可中断睡眠状态
5	R	108	schedule		//进程5开始运行
4	J	110	wake_up			//进程5唤醒睡觉的进程4,进程4进入就绪队列
5	E	110	do_exit			//进程5很快退出
4	R	110	schedule		//进程4执行
4	W	115	interruptible_sleep_on	//shell等待用户输入命令
0	R	115	schedule		//因为无事可做,所以进程0重出江湖
4	J	902	wake_up			//用户开始输入命令了,唤醒了shell(从等待用户输入到用户开始输入命令总共经历了将近8s)
4	R	902	schedule		//进程4执行
4	W	903	interruptible_sleep_on	//shell等待用户输入命令
0	R	903	schedule		//因为无事可做,进程0再次执行
4	J	953	wake_up			//用户接着输入命令,唤醒shell(shell等待了0.5s)
4	R	953	schedule		//进程4执行
4	W	953	interruptible_sleep_on	//shell等待用户输入命令
0	R	953	schedule		//因为无事可做,进程0再次执行
4	J	1003	wake_up		//用户接着输入命令,唤醒shell(shell等待了0.5s)
4	R	1003	schedule	//shell执行
4	W	1003	interruptible_sleep_on	//shell等待用户输入
0	R	1003	schedule	//因为无事可做,进程0再次执行
4	J	1056	wake_up		//用户接着输入命令,唤醒shell(shell等待了0.53s)
4	R	1056	schedule	//shell执行
4	W	1056	interruptible_sleep_on	//shell等待用户输入
0	R	1056	schedule	//因为无事可做,进程0再次执行
4	J	1182	wake_up		//用户接着输入命令,唤醒shell(shell等待了1.26s),该命令输入完毕(用户输入该命令总共经历了将近1.8s)
4	R	1182	schedule	//shell执行
6	N	1184	copy_process	/* --- 进程6是shell建立的不知道做什么的进程 6 J 1185 copy_process 进程6进入就绪队列 --- */
4	W	1185	sys_waitpid		//shell开始等待(等待进程6退出)
6	R	1185	schedule		//进程6开始执行
6	E	1191	do_exit			//进程6很快退出
4	J	1191	schedule		//shell此前在等待进程6退出,被阻塞。进程6退出后,重新进入就绪队列
4	R	1191	schedule		//shell执行
7	N	1192	copy_process	/* --- 进程7是shell建立的不知道做什么的进程 7 J 1192 copy_process 进程7进入就绪队列 --- */
4	J	1193	schedule		/* --- shell从运行->就绪,让出CPU。 7 R 1193 schedule 进程7开始运行 --- */
7	E	1195	do_exit			//进程7很快退出
4	R	1195	schedule		//shell执行
4	W	1196	interruptible_sleep_on	//shell等待用户输入命令
0	R	1196	schedule		//因为无事可做,进程0再次执行
4	J	1251	wake_up			//用户开始输入命令,唤醒了shell(从等待用户输入命令到用户开始输入命令,总共经历了0.55s)
4	R	1251	schedule		//shell执行
4	W	1251	interruptible_sleep_on	//shell等待用户继续输入命令
0	R	1252	schedule		//因为无事可做,进程0再次执行
4	J	2345	wake_up			//用户接着输入命令,唤醒shell(shell等待了0.93s)
4	R	2345	schedule		//shell执行
4	W	2345	interruptible_sleep_on	//shell等待用户输入
0	R	2345	schedule		//因为无事可做,进程0再次执行
4	J	2386	wake_up			//用户接着输入命令,唤醒shell(shell等待了0.41s)
4	R	2386	schedule		//shell执行
4	W	2386	interruptible_sleep_on	//shell等待用户输入
0	R	2386	schedule		//因为无事可做,进程0再次执行
4	J	2823	wake_up			//用户接着输入命令,唤醒shell(shell等待了4.37s)
4	R	2823	schedule		//shell执行
4	W	2823	interruptible_sleep_on	//shell等待用户输入
0	R	2823	schedule		//因为无事可做,进程0再次执行
4	J	2864	wake_up			//用户接着输入命令,唤醒shell(shell等待了0.41s)
4	R	2864	schedule		//shell执行
4	W	2864	interruptible_sleep_on	//shell等待用户输入
0	R	2864	schedule		//因为无事可做,进程0再次执行
4	J	2898	wake_up			//用户接着输入命令,唤醒shell(shell等待了0.34s)
4	R	2898	schedule		//shell执行
4	W	2898	interruptible_sleep_on	//shell等待用户继续输入
0	R	2898	schedule		//因为无事可做,进程0再次执行
4	J	2936	wake_up			//用户接着输入命令,唤醒shell(shell等待了0.38s)
4	R	2936	schedule		//shell执行
4	W	2936	interruptible_sleep_on	//shell等待用户输入
0	R	2936	schedule		//因为无事可做,进程0再次执行
4	J	3046	wake_up			//用户接着输入命令,唤醒shell(shell等待了1.1s)
4	R	3046	schedule		//shell执行
4	W	3046	interruptible_sleep_on	//shell等待用户输入
0	R	3046	schedule		//因为无事可做,进程0再次执行
3	J	3074	schedule		/* --- 进程3从睡眠->就绪。 3 R 3074 schedule 进程3运行(进程3由于长时间睡觉,其优先级变高,进入就绪队列后立即得到调度执行) --- */
3	W	3074	sys_pause		//进程3主动睡觉,让出CPU
0	R	3074	schedule		//因为无事可做,进程0再次执行
4	J	3099	wake_up			//用户接着输入命令,唤醒shell(shell等待了0.53s)
4	R	3100	schedule		//shell执行
4	W	3100	interruptible_sleep_on	//shell等待用户输入
0	R	3100	schedule		//因为无事可做,进程0再次执行
4	J	3416	wake_up			//用户接着输入命令,唤醒shell(shell等待了3.16s)
4	R	3417	schedule		//shell执行
4	W	3417	interruptible_sleep_on	//shell等待用户输入
0	R	3417	schedule		//因为无事可做,进程0再次执行
4	J	3457	wake_up			//用户接着输入命令,唤醒shell(shell等待了0.4s)
4	R	3457	schedule		//shell执行
4	W	3457	interruptible_sleep_on	//shell等待用户输入
0	R	3457	schedule		//因为无事可做,进程0再次执行
4	J	3623	wake_up			//用户接着输入命令,唤醒shell(shell等待了1.66s)
4	R	3623	schedule		//shell执行
4	W	3623	interruptible_sleep_on	//shell等待用户输入
0	R	3623	schedule		//因为无事可做,进程0再次执行
4	J	3687	wake_up			//用户接着输入命令,唤醒shell(shell等待了0.64s)
4	R	3687	schedule		//shell执行
4	W	3687	interruptible_sleep_on	//shell等待用户输入
0	R	3687	schedule....0	R	7494	schedule				//因为无事可做,进程0再次运行
4	J	7560	wake_up					//用户继续输入命令,唤醒shell(shell等待了0.66s)
4	R	7560	schedule				//shell执行
4	W	7560	interruptible_sleep_on	//shell等待用户输入
0	R	7561	schedule				//因为无事可做,进程0再次执行
4	J	7617	wake_up					//用户继续输入命令,唤醒shell(shell等待了0.56s)
4	R	7617	schedule				//shell执行
4	W	7617	interruptible_sleep_on	//shell等待用户输入
0	R	7617	schedule				//因为无事可做,进程0再次执行
4	J	8090	wake_up					//用户继续输入命令,唤醒shell(shell等待了3.73s),该命令输入完毕(用户输入该命令总共经历了67.39s,一分钟的时间,用户输入不可能这么长!那么这个命令是什么?全部是由用户输入?)
4	R	8090	schedule				//shell执行该命令
8	N	8092	copy_process			/* --- shell新建进程8(估计是gcc编译process.c生成可执行文件process的命令)。 8 J 8093 copy_process 进程8新建后进入就绪队列 --- */
4	W	8093	sys_waitpid				//shell开始等待进程8退出
8	R	8093	schedule				//进程8开始运行
9	N	8098	copy_process			/* --- 进程8新建进程9(估计是gcc用于编译process.c的预处理子进程?)。 9 J 8098 copy_process 进程9新建后进入就绪队列 --- */
8	W	8099	sys_waitpid				//进程8开始等待进程9退出
9	R	8099	schedule				//进程9开始运行
9	E	8159	do_exit					//进程9退出(运行了0.6s)
8	J	8159	schedule				/* --- 进程8此前在等待进程9退出,被阻塞。进程9退出后,重新进入就绪队列。 8 R 8159 schedule 进程8运行 --- */
10	N	8160	copy_process			/* --- 进程8新建进程10(估计是gcc用于编译process.c的编译子进程,生成汇编代码?)。 10 J 8161 copy_process 进程10新建后进入就绪队列 --- */
8	W	8161	sys_waitpid				//进程8开始等待进程10退出
10	R	8161	schedule				//进程10开始运行
10	E	8295	do_exit					//进程10退出(运行了1.34s)
8	J	8295	schedule				/* --- 进程8此前在等待进程10退出,被阻塞。进程10退出后,重新进入就绪队列。 8 R 8295 schedule 进程8继续运行 --- */
11	N	8295	copy_process			/* --- 进程8新建进程11(估计是gcc用于编译process.c的汇编子进程,生成二进制目标文件?)。 11 J 8296 copy_process 进程11新建后进入就绪队列 --- */
8	W	8296	sys_waitpid				//进程8开始等待进程11退出
11	R	8296	schedule				//进程11开始运行
11	E	8341	do_exit					//进程11退出(运行了0.45s)
8	J	8341	schedule				/* --- 进程8此前在等待进程11退出,被阻塞。进程11退出后,重新进入就绪队列。 8 R 8341 schedule 进程8继续运行 --- */
12	N	8343	copy_process			/* --- 进程8新建进程12(估计是gcc用于编译process.c的链接子进程,生成可执行文件process?)。 12 J 8344 copy_process 进程12新建后进入就绪队列 --- */
8	W	8344	sys_waitpid				//进程8开始等待进程12退出
12	R	8344	schedule				//进程12开始运行
12	E	8425	do_exit					//进程12退出(运行了0.81s)
8	J	8425	schedule				/* --- 进程8此前在等待进程12退出,被阻塞。进程12退出后,重新进入就绪队列。 8 R 8425 schedule 进程8继续运行 --- */
8	E	8427	do_exit					//进程8退出(gcc编译process.c生成process可执行文件经历了3.34s)
4	J	8427	schedule				/* --- shell此前在等待进程8退出,被阻塞。进程8退出后,重新进入就绪队列 4 R 8427 schedule shell运行 --- */
13	N	8427	copy_process			/* --- shell新建进程13(不知道是做什么的进程)。 13 J 8428 copy_process 进程13新建后进入就绪队列 --- */
4	W	8428	sleep_on				//进程4进入不可中断睡眠状态
13	R	8428	schedule				//进程13开始运行
4	J	8430	wake_up					//进程13唤醒睡觉的进程4,进程4进入就绪队列
13	E	8431	do_exit					//进程13很快退出(运行了0.03s)
4	R	8431	schedule				//shell运行
4	W	8432	interruptible_sleep_on	//shell等待用户输入命令
0	R	8432	schedule				//因为无事可做,进程0再次执行
4	J	9737	wake_up					//用户开始输入命令,唤醒了shell(从等待用户输入命令到用户开始输入命令,总共经历了13s!为什么会这么长时间?)
4	R	9737	schedule				//shell运行
4	W	9737	interruptible_sleep_on	//shell等待用户输入命令
0	R	9737	schedule				//因为无事可做,进程0再次执行
4	J	9790	wake_up					//用户接着输入命令,唤醒了shell(shell等待了0.53s)
4	R	9790	schedule				//shell运行
4	W	9790	interruptible_sleep_on	//shell等待用户输入命令......
0	R	11136	schedule				//因为无事可做,进程0再次执行
4	J	11195	wake_up					//用户接着输入命令,唤醒了shell(shell等待了0.59s)
4	R	11195	schedule				//shell运行
4	W	11195	interruptible_sleep_on	//shell等待用户输入命令
0	R	11196	schedule				//因为无事可做,进程0再次执行
4	J	12048	wake_up					//用户接着输入命令,唤醒了shell(shell等待了8.52s),该命令输入完毕(用户输入该命令总共经历了231s!输入不可能需要这么长时间!说明计时单位并不是10ms???)
4	R	12048	schedule				//shell执行该命令(即我们自己编写的样本程序process可执行文件)
14	N	12049	copy_process			/* --- shell新建进程14(即process.c的main()函数)。 14 J 12049 copy_process 进程14新建后进入就绪队列 --- */
4	W	12049	sys_waitpid				//shell开始等待进程14(process.c的main()函数)退出
14	R	12050	schedule				//进程14(process.c的main()函数)开始运行
15	N	12051	copy_process			/* --- main()函数新建进程15(N1 node)。 15 J 12051 copy_process 进程15(N1 node)新建后进入就绪队列 --- */
16	N	12052	copy_process			/* --- main()函数新建进程16(N2 node)。 16 J 12052 copy_process 进程16(N2 node)新建后进入就绪队列 --- */
14	W	12053	sys_waitpid				//进程14(现在为process.c的main()函数中的Root)开始等待进程15(N1 node)和进程16(N2 node)退出
16	R	12053	schedule				//进程16(N2 node)开始运行
17	N	12053	copy_process			/* --- 进程16新建进程17(N5 node)。 17 J 12053 copy_process 进程17(N5 node)新建后进入就绪队列 --- */
16	W	12054	sys_pause				//进程16(N2 node)主动睡觉
17	R	12054	schedule				//进程17(N5 node)开始运行
17	J	12069	schedule				/* --- 进程17(n5 node)时间片(15个滴答)耗完,从运行->就绪(运行了0.15s),让出CPU。 15 R 12069 schedule 进程15(N1 node)开始运行 --- */
18	N	12069	copy_process			/* --- 进程15(N1 node)新建进程18(N3 node)。 18 J 12069 copy_process 进程18(N3 node)新建后进入就绪队列 --- */
19	N	12070	copy_process			/* --- 进程15(N1 node)新建进程19(N4 node)。 19 J 12070 copy_process 进程19(N4 node)新建后进入就绪队列 --- */
15	J	12084	schedule				/* --- 进程15(N1 node)时间片耗完,从运行->就绪(运行了0.15s),让出CPU。 19 R 12084 schedule 进程19(N4 node)开始运行 --- */
19	J	12099	schedule				/* --- 进程19(N4 node)时间片耗完,从运行->就绪(运行了0.15s),让出CPU。 18 R 12099 schedule 进程18(N3 node)开始运行 --- */
18	J	12114	schedule				/* --- 进程18(N3 node)时间片耗完,从运行->就绪(运行了0.15s),让出CPU。 19 R 12114 schedule 进程19(N4 node)运行 --- */
19	J	12129	schedule				/* --- 进程19(N4 node)时间片耗完,从运行->就绪(运行了0.15s),让出CPU。 18 R 12129 schedule 进程18(N3 node)开始运行 --- */ 
18	J	12144	schedule				/* --- 进程18(N3 node)时间片耗完,从运行->就绪(运行了0.15s),让出CPU。 17 R 12144 schedule 进程17(N5 node)运行 --- */ 
16	J	12159	schedule				/* --- 进程16(N2 node)被唤醒。 17 J 12159 schedule 进程17(N5 node)时间片耗完,从运行->就绪(运行了0.15s),让出CPU。 16 R 12159 schedule 进程16(N2 node)运行 --- */