本次实验目的:
- 掌握Linux下的多进程编程技术;
- 通过对进程运行轨迹的跟踪来形象化进程的概念;
- 在进程运行轨迹跟踪的基础上进行相应的数据统计,从而能对进程调度算法进行实际的量化评价,更进一步加深对调度和调度算法的理解,获得能在实际操作系统上对调度算法进行实验数据对比的直接经验
简单点,就是:
- 维护一个process.log日志文件
- 写个内核级的函数fprintk 能直接打印到日志文件
- 跟踪进程调度( 某个进程改变状态的时候输出一下它的状态以及进程号)
- 自己写个process.c程序,手动fork几个进程,方便观察信息
- 改变时间片,体会调度算法
首先看一下日志文件的要求:
/var/process.log
pid X time
- pid是进程的Id
- X可以是N,J,R,W和E中的任意一个分别代表了 进程新建(N),
进入就绪状态(J),进入运行状态(R),进入阻塞态(W )和退出(E); - time表示进程x发生的时间,代表了过去的滴答数(trick);
- 用(\t)分隔
好吧,废话不多说,开撸:
一:修改init/main.c文件
首先,我们需要创建日志文件,由于这个日志文件需要记录所有进程的情况,所以我们选择在文件系统加载的时候创建该日志文件(也就把日志上升到跟屏幕输出一样的地位),普通文件不能在系统内核态运行的时候一直保持持续写入的状态而且该文件的句柄只能留在一个程序手中,我们要在多个程序中对其进行写入.
void main(void) /* This really IS void, no error here. */
{ /* The startup routine assumes (well, ...) this */
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
ROOT_DEV = ORIG_ROOT_DEV;
drive_info = DRIVE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
if (memory_end > 16*1024*1024)
memory_end = 16*1024*1024;
if (memory_end > 12*1024*1024)
buffer_memory_end = 4*1024*1024;
else if (memory_end > 6*1024*1024)
buffer_memory_end = 2*1024*1024;
else
buffer_memory_end = 1*1024*1024;
main_memory_start = buffer_memory_end;
#ifdef RAMDISK
main_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif
mem_init(main_memory_start,memory_end);
trap_init();
blk_dev_init();
chr_dev_init();
tty_init();
time_init();
sched_init();
buffer_init(buffer_memory_end);
hd_init();
floppy_init();
sti();
move_to_user_mode();
/*
*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*/
/*添加结束*/
if (!fork()) { /* we count on this going ok */
init();
}
/*
* NOTE!! For any other task 'pause()' would mean we have to get a
* signal to awaken, but task0 is the sole exception (see 'schedule()')
* as task 0 gets activated at every idle moment (when no other tasks
* can run). For task0 'pause()' just means we go check if some other
* task can run, and if not we return here.
*/
for(;;) pause();
}
这段代码建立了文件描述符0、1和2,它们分别就是stdin、stdout和stderr。这三者的值是系统标准(Windows也是如此),不可改变。可以把log文件的描述符关联到3。文件系统初始化,描述符0、1和2关联之后,才能打开log文件,开始记录进程的运行轨迹。
0666并不是 文件描述符,只是权限标记
二.修改kernel/printk.c文件,添加打印功能
我们平时用c语言的时候习惯用printf 向屏幕输出文件
然而 Linux中更习惯用printfk,因为它添加文件描述符这个参数,表示了该向哪儿打印;
printk()的使用方式类同与C标准库函数fprintf(),唯一的区别是第一个参数是文件描述符,而不是文件指针。例如:
fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'R', jiffies); //向log文件输出
/*
* linux/kernel/printk.c
*
* (C) 1991 Linus Torvalds
*/
/*
* When in kernel-mode, we cannot use printf, as fs is liable to
* point to 'interesting' things. Make a printf with fs-saving, and
* all is well.
*/
#include <stdarg.h>
#include <stddef.h>
#include <linux/sched.h>
#include <sys/stat.h>
#include <linux/kernel.h>
static char buf[1024];
extern int vsprintf(char * buf, const char * fmt, va_list args);
int printk(const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
__asm__("push %%fs\n\t"
"push %%ds\n\t"
"pop %%fs\n\t"
"pushl %0\n\t"
"pushl $buf\n\t"
"pushl $0\n\t"
"call tty_write\n\t"
"addl $8,%%esp\n\t"
"popl %0\n\t"
"pop %%fs"
::"r" (i):"ax","cx","dx");
return i;
}
/*
* write by sunner
*/
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;
}
三、跟踪进程状态.
内核表示 | 含义 |
---|---|
TASK_RUNNING | 可运行 |
TASK_INTERRUPTIBLE | 可中断的等待状态 |
TASK_UNINTERRUPTIBLE | 不可中断的等待状态 |
TASK_ZOMBIE | 僵死 |
TASK_STOPPED | 暂停 |
TASK_SWAPPING | 换入/换出 |
思考一下 什么时候进程的状态会改变 ?##
- 创建的时候
- 调度的时候
- 进程销毁的时候
那我们就要去跟踪这三个过程对应的.c文件
- fork.c
- sched.c
- exit.c
时间滴答:
jiffies,滴答
jiffies在kernel/sched.c文件中定义为一个全局变量:
long volatile jiffies=0;
它记录了从开机到当前时间的时钟中断发生次数。在kernel/sched.c文件中的sched_init()函数中,时钟中断处理函数被设置为:
set_intr_gate(0x20,&timer_interrupt);
而在kernel/system_call.s文件中将timer_interrupt定义为:
timer_interrupt:
……
incl jiffies #增加jiffies计数值
……
jiffies表示从开机时到现在发生的时钟中断次数
来依次修改:
(1) 首先看系统调用fork
sys_fork:
call find_empty_process
……
push %gs //传递一些参数
pushl %esi
pushl %edi
pushl %ebp
pushl %eax
call copy_process //调用copy_process实现进程创建
addl $20,%esp
真正实现进程创建函数的是:copy_process 它在kernel/fork.c中定义为:
int copy_process(int nr,……)
{
struct task_struct *p;
……
p = (struct task_struct *) get_free_page(); //获得一个task_struct结构体空间
……
p->pid = last_pid;
……
p->start_time = jiffies; //设置start_time为jiffies
……
p->state = TASK_RUNNING; //设置进程状态为就绪。所有就绪进程的状态都是
//TASK_RUNNING(0),被全局变量current指向的
//是正在运行的进程。
//所以我们应该在每次状态改变的时候进行跟踪
return last_pid;
}
修改为:
p->alarm = 0;
p->leader = 0; /* process leadership doesn't inherit */
p->utime = p->stime = 0;
p->cutime = p->cstime = 0;
p->start_time = jiffies;
/*
*新建一个进程
*/
fprintk(3,"%d\tN\t%d\n",p->pid,jiffies);//表示进程刚建立
p->tss.back_link = 0;
p->tss.esp0 = PAGE_SIZE + (long) p;
同时,进程新建也变成就绪状态
if (last_task_used_math == current)
__asm__("clts ; fnsave %0"::"m" (p->tss.i387));
if (copy_mem(nr,p)) {
task[nr] = NULL;
free_page((long) p);
return -EAGAIN;
}
for (i=0; i<NR_OPEN;i++)
if ((f=p->filp[i]))
f->f_count++;
if (current->pwd)
current->pwd->i_count++;
if (current->root)
current->root->i_count++;
if (current->executable)
current->executable->i_count++;
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->state = TASK_RUNNING; /* do this last, just in case */
/*
*新建 => 就绪
*/
fprintk(3,"%d\tJ\t%d\n",p->pid,jiffies);
return last_pid;
}
所以修改的完整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)
{
struct task_struct *p;
int i;
struct file *f;
p = (struct task_struct *) get_free_page();
if (!p)
return -EAGAIN;
task[nr] = p;
*p = *current; /* NOTE! this doesn't copy the supervisor stack */
p->state = TASK_UNINTERRUPTIBLE;
p->pid = last_pid;
p->father = current->pid;
p->counter = p->priority;
p->signal = 0;
p->alarm = 0;
p->leader = 0; /* process leadership doesn't inherit */
p->utime = p->stime = 0;
p->cutime = p->cstime = 0;
p->start_time = jiffies;
/*
*新建一个进程
*/
fprintk(3,"%d\tN\t%d\n",p->pid,jiffies);
p->tss.back_link = 0;
p->tss.esp0 = PAGE_SIZE + (long) p;
p->tss.ss0 = 0x10;
p->tss.eip = eip;
p->tss.eflags = eflags;
p->tss.eax = 0;
p->tss.ecx = ecx;
p->tss.edx = edx;
p->tss.ebx = ebx;
p->tss.esp = esp;
p->tss.ebp = ebp;
p->tss.esi = esi;
p->tss.edi = edi;
p->tss.es = es & 0xffff;
p->tss.cs = cs & 0xffff;
p->tss.ss = ss & 0xffff;
p->tss.ds = ds & 0xffff;
p->tss.fs = fs & 0xffff;
p->tss.gs = gs & 0xffff;
p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = 0x80000000;
if (last_task_used_math == current)
__asm__("clts ; fnsave %0"::"m" (p->tss.i387));
if (copy_mem(nr,p)) {
task[nr] = NULL;
free_page((long) p);
return -EAGAIN;
}
for (i=0; i<NR_OPEN;i++)
if ((f=p->filp[i]))
f->f_count++;
if (current->pwd)
current->pwd->i_count++;
if (current->root)
current->root->i_count++;
if (current->executable)
current->executable->i_count++;
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->state = TASK_RUNNING; /* do this last, just in case */
/*
*新建 => 就绪
*/
fprintk(3,"%d\tJ\t%d\n",p->pid,jiffies);
return last_pid;
}
(2)修改sched.c文件
有了上次的经验,我们知道要跟踪的是什么了:
进程p->state改变的时候就需要打印信息(跟踪嘛)
调度的时候:
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;
/*可中断睡眠 => 就绪*/
fprintk(3,"%d\tJ\t%d\n",(*p)->pid,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;
}
/*编号为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);
}
switch_to(next);
}
系统暂停,进程又运行变中断
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;
}
进程睡眠
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;
/*
*当前进程进程 => 不可中断睡眠
*/
fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
schedule();
if (tmp)
{
tmp->state=0;
/*
*原等待队列 第一个进程 => 唤醒(就绪)
*/
fprintk(3,"%d\tJ\t%d\n",tmp->pid,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;
/*
*这一部分属于 唤醒队列中间进程,通过goto实现唤醒 队列头进程 过程中Wait
*/
fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
schedule();
if (*p && *p != current) {
(**p).state=0;
/*
*当前进程进程 => 可中断睡眠 同上
*/
fprintk(3,"%d\tJ\t%d\n",(*p)->pid,jiffies);
goto repeat;
}
*p=NULL;
if (tmp)
{
tmp->state=0;
/*
*原等待队列 第一个进程 => 唤醒(就绪)
*/
fprintk(3,"%d\tJ\t%d\n",tmp->pid,jiffies);
}
}
唤醒进程,进入就绪状态
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;
}
}
所以完整的sched.c文件为:
/*
* linux/kernel/sched.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'sched.c' is the main kernel file. It contains scheduling primitives
* (sleep_on, wakeup, schedule etc) as well as a number of simple system
* call functions (type getpid(), which just extracts a field from
* current-task
*/
#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)
{
int i,j = 4096-sizeof(struct task_struct);
printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);
i=0;
while (i<j && !((char *)(p+1))[i])
i++;
printk("%d (of %d) chars free in kernel stack\n\r",i,j);
}
void show_stat(void)
{
int i;
for (i=0;i<NR_TASKS;i++)
if (task[i])
show_task(i,task[i]);
}
#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 };
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*/
void math_state_restore()
{
if (last_task_used_math == current)
return;
__asm__("fwait");
if (last_task_used_math) {
__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
}
last_task_used_math=current;
if (current->used_math) {
__asm__("frstor %0"::"m" (current->tss.i387));
} else {
__asm__("fninit"::);
current->used_math=1;
}
}
/*
* 'schedule()' is the scheduler function. This is GOOD CODE! There
* probably won't be any reason to change this, as it should work well
* in all circumstances (ie gives IO-bound processes good response etc).
* The one thing you might take a look at is the signal-handler code here.
*
* NOTE!! Task 0 is the 'idle' task, which gets called when no other
* tasks can run. It can not be killed, and it cannot sleep. The 'state'
* information in task[0] is never used.
*/
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;
/*可中断睡眠 => 就绪*/
fprintk(3,"%d\tJ\t%d\n",(*p)->pid,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;
}
/*编号为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);
}
switch_to(next);
}
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;
}
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;
/*
*当前进程进程 => 不可中断睡眠
*/
fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
schedule();
if (tmp)
{
tmp->state=0;
/*
*原等待队列 第一个进程 => 唤醒(就绪)
*/
fprintk(3,"%d\tJ\t%d\n",tmp->pid,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;
/*
*这一部分属于 唤醒队列中间进程,通过goto实现唤醒 队列头进程 过程中Wait
*/
fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
schedule();
if (*p && *p != current) {
(**p).state=0;
/*
*当前进程进程 => 可中断睡眠 同上
*/
fprintk(3,"%d\tJ\t%d\n",(*p)->pid,jiffies);
goto repeat;
}
*p=NULL;
if (tmp)
{
tmp->state=0;
/*
*原等待队列 第一个进程 => 唤醒(就绪)
*/
fprintk(3,"%d\tJ\t%d\n",tmp->pid,jiffies);
}
}
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;
}
}
/*
* OK, here are some floppy things that shouldn't be in the kernel
* proper. They are here because the floppy needs a timer, and this
* was the easiest way of doing it.
*/
static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL};
static int mon_timer[4]={0,0,0,0};
static int moff_timer[4]={0,0,0,0};
unsigned char current_DOR = 0x0C;
int ticks_to_floppy_on(unsigned int nr)
{
extern unsigned char selected;
unsigned char mask = 0x10 << nr;
if (nr>3)
panic("floppy_on: nr>3");
moff_timer[nr]=10000; /* 100 s = very big :-) */
cli(); /* use floppy_off to turn it off */
mask |= current_DOR;
if (!selected) {
mask &= 0xFC;
mask |= nr;
}
if (mask != current_DOR) {
outb(mask,FD_DOR);
if ((mask ^ current_DOR) & 0xf0)
mon_timer[nr] = HZ/2;
else if (mon_timer[nr] < 2)
mon_timer[nr] = 2;
current_DOR = mask;
}
sti();
return mon_timer[nr];
}
void floppy_on(unsigned int nr)
{
cli();
while (ticks_to_floppy_on(nr))
sleep_on(nr+wait_motor);
sti();
}
void floppy_off(unsigned int nr)
{
moff_timer[nr]=3*HZ;
}
void do_floppy_timer(void)
{
int i;
unsigned char mask = 0x10;
for (i=0 ; i<4 ; i++,mask <<= 1) {
if (!(mask & current_DOR))
continue;
if (mon_timer[i]) {
if (!--mon_timer[i])
wake_up(i+wait_motor);
} else if (!moff_timer[i]) {
current_DOR &= ~mask;
outb(current_DOR,FD_DOR);
} else
moff_timer[i]--;
}
}
#define TIME_REQUESTS 64
static struct timer_list {
long jiffies;
void (*fn)();
struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;
void add_timer(long jiffies, void (*fn)(void))
{
struct timer_list * p;
if (!fn)
return;
cli();
if (jiffies <= 0)
(fn)();
else {
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
if (!p->fn)
break;
if (p >= timer_list + TIME_REQUESTS)
panic("No more time requests free");
p->fn = fn;
p->jiffies = jiffies;
p->next = next_timer;
next_timer = p;
while (p->next && p->next->jiffies < p->jiffies) {
p->jiffies -= p->next->jiffies;
fn = p->fn;
p->fn = p->next->fn;
p->next->fn = fn;
jiffies = p->jiffies;
p->jiffies = p->next->jiffies;
p->next->jiffies = jiffies;
p = p->next;
}
}
sti();
}
void do_timer(long cpl)
{
extern int beepcount;
extern void sysbeepstop(void);
if (beepcount)
if (!--beepcount)
sysbeepstop();
if (cpl)
current->utime++;
else
current->stime++;
if (next_timer) {
next_timer->jiffies--;
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();
}
}
if (current_DOR & 0xf0)
do_floppy_timer();
if ((--current->counter)>0) return;
current->counter=0;
if (!cpl) return;
schedule();
}
int sys_alarm(long seconds)
{
int old = current->alarm;
if (old)
old = (old - jiffies) / HZ;
current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
return (old);
}
int sys_getpid(void)
{
return current->pid;
}
int sys_getppid(void)
{
return current->father;
}
int sys_getuid(void)
{
return current->uid;
}
int sys_geteuid(void)
{
return current->euid;
}
int sys_getgid(void)
{
return current->gid;
}
int sys_getegid(void)
{
return current->egid;
}
int sys_nice(long increment)
{
if (current->priority-increment>0)
current->priority -= increment;
return 0;
}
void sched_init(void)
{
int i;
struct desc_struct * p;
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
p = gdt+2+FIRST_TSS_ENTRY;
for(i=1;i<NR_TASKS;i++) {
task[i] = NULL;
p->a=p->b=0;
p++;
p->a=p->b=0;
p++;
}
/* Clear NT, so that we won't have troubles with that later on */
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
ltr(0);
lldt(0);
outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
set_intr_gate(0x20,&timer_interrupt);
outb(inb_p(0x21)&~0x01,0x21);
set_system_gate(0x80,&system_call);
}
(3)退出时候,修改exit.c
int do_exit(long code)
{
int i;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
for (i=0 ; i<NR_TASKS ; i++)
if (task[i] && task[i]->father == current->pid) {
task[i]->father = 1;
if (task[i]->state == TASK_ZOMBIE)
{/* assumption task[1] is always init */
(void) send_sig(SIGCHLD, task[1], 1);
}
}
for (i=0 ; i<NR_OPEN ; i++)
if (current->filp[i])
sys_close(i);
iput(current->pwd);
current->pwd=NULL;
iput(current->root);
current->root=NULL;
iput(current->executable);
current->executable=NULL;
if (current->leader && current->tty >= 0)
tty_table[current->tty].pgrp = 0;
if (last_task_used_math == current)
last_task_used_math = NULL;
if (current->leader)
kill_session();
current->state = TASK_ZOMBIE;
/*
*退出一个进程
*/
fprintk(3,"%d\tE\t%d\n",current->pid,jiffies);
current->exit_code = code;
tell_father(current->father);
schedule();
return (-1); /* just to suppress warnings */
}
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;
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;
/*
*当前进程 => 等待
*/
fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
schedule();
if (!(current->signal &= ~(1<<(SIGCHLD-1))))
goto repeat;
else
return -EINTR;
}
return -ECHILD;
}
所以要完整的exit.c代码
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;
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;
/*
*当前进程 => 等待
*/
fprintk(3,"%d\tW\t%d\n",current->pid,jiffies);
schedule();
if (!(current->signal &= ~(1<<(SIGCHLD-1))))
goto repeat;
else
return -EINTR;
}
return -ECHILD;
}
好了,其实这时候我们已经进程跟踪好了(从一个进程的"出生"到"死亡")!
//这时候,应该编译内核了
make all
四,来写一个程序 fork多个子进程方便 跟踪:
process.c
#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(&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;
}
}
因为这个程序需要在linux0.11中使用,老规矩先挂载上hdc上,再把写好的process.c导入/usr/root/中
sudo ./mount-hdc
cp process.c ./hdc/usr/root/
运行
./run
gcc -o process process.c
./process
运行完process 就可以看看日志文件了
日志文件在/var/process.log
可以运行
sync
同步 将缓冲区信息 加载到硬盘
再关闭linux0.11
挂载到hdc后 查看process.log
我的process.log
其实我们要用py脚本进行查看,更方便地看出一个进程各种状态的时间.stat_log.py这个脚本应该在cms可以下载
#!/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 ./the path to your process.log 22
//22代表进程号
比如我的结果.不要在意没对齐.
好了,这样我们就做好第一步.
修改时间片,体会进程调度:
,时间片的初值就是进程0的priority,即宏INIT_TASK中定义的:
define INIT_TASK \
{ 0,15,15, //分别对应state;counter;和priority;
所以我们要修改的是sched.h的init_task的第三个值,将其变成30试试!!
修改linux-0.11/include/linux/sched.h中
#define INIT_TASK \
/* state etc */ { 0,15,30, \
/* signals */ 0,{{},},0, \
修改完了重新编译运行
make all
./run
再用stat_log.py去看看~~~~~process.log
网友评论