前言
这个博客鸽了好久了,一来自己在准备数学建模,二来自己还是比较难以控制自己啊,还是会忍不住自己玩手机啊,一定要克制住自己啊。然后说一下这个实验吧,首先还是的对自己痛骂一下为啥么我总是犯一些低级错误,最让我头痛的一个bug竟然是if语句如果不带括号的话,其后会执行的语句只有一句。这当真是困扰了我好久啊,把代码真正是对了好几遍,才终于发现了这个问题。然后需要注意的是,那个stat_log.py 是用来检测你的添加fprink() 函数的位置对不对的。需要注意的是进程状态之间的转换为:运行->睡眠 、运行->就绪、睡眠->就绪、就绪->运行 这四种状态的变迁,也就是你自己在查看process.log的时候可以自己看看是否符合这四种状态的改变。需要注意文件中如果有类似 运行->运行 这样的转化不行,意思就是只有当状态改变的时候才需要输入在log文件中。
实验
实验提示
process.c 的编写涉及到 fork() 和 wait() 系统调用,请自行查阅相文献。
0.11 内核修改涉及到 init/main.c、kernel/fork.c 和 kernel/sched.c,开始实验前如果能详细阅读《注释》一书的相关部分,会大有裨益
这是蓝桥实验册中提到的实验提示,在这我就先放出《注释》这本书的链接:链接:百度网盘
提取码:8e26
然后在这就提一嘴fork()函数和wait()函数吧,fork()函数就是创建进程函数,值得一提的是在linux系统中是分为父进程和子进程的,对此,fork()函数的返回值比较特殊,其返回值有两个,如果自己是父进程则返回值为其子进程的pid(process id),linux中对于每个进程都有独属于自己的id,如果没有子进程了,也就是自己就是子进程,则返回值为0。然后wait()函数则是表现为父进程等待子进程的函数,也就是等待子进程终止了,wait()函数才会运行并且把子进程的资源回收掉,否则会一直阻塞的状态。
编写样本程序
所谓样本程序,就是一个生成各种进程的程序。我们的对 0.11 的修改把系统对它们的调度情况都记录到 log 文件中。在修改调度算法或调度参数后再运行完全一样的样本程序,可以检验调度算法的优劣。
理论上,此程序可以在任何 Unix/Linux 上运行,所以建议在 Ubuntu 上调试通过后,再拷贝到 0.11 下运行。
process.c 是样本程序的模板(在 /home/teacher/ 目录下)。
/*
* 此函数按照参数占用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,直到总运行时间超过last为止
* 所有时间的单位为秒
*/
cpuio_bound(int last, int cpu_time, int io_time);
例子:
// 比如一个进程如果要占用10秒的CPU时间,它可以调用:
cpuio_bound(10, 1, 0);
// 只要cpu_time>0,io_time=0,效果相同
copy
// 以I/O为主要任务:
cpuio_bound(10, 0, 1);
// 只要cpu_time=0,io_time>0,效果相同
copy
// CPU和I/O各1秒钟轮回:
cpuio_bound(10, 1, 1);
copy
// 较多的I/O,较少的CPU:
// I/O时间是CPU时间的9倍
cpuio_bound(10, 1, 9);
process.c代码参考如下:
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>
#include<sys/wait.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\n");
exit(0);
}else if(pid1==0){
printf("-----------process1---------- \n");
cpuio_bound(8,4,3);
}
pid2=fork();
if(pid2<0){
printf("error in fork\n");
exit(0);
}else if(pid2==0){
printf("-----------process2---------- \n");
cpuio_bound(8,4,1);
}
pid3=fork();
if(pid3<0){
printf("error in fork\n");
exit(0);
}else if(pid3==0){
printf("-----------process3---------- \n");
cpuio_bound(8,4,2);
}
printf("the id of process is %d\n",getpid());
printf("the id of child process 1 is %d\n",pid1);
printf("the id of child process 3 is %d\n",pid3);
printf("the id of child process 2 is %d\n",pid2);
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;
}
}
在这我创建了8个子进程,在这提一嘴,fork()函数的子进程复制父进程,但是都是fork()代码块之后的,所以这里一共创建了8个子进程,当然这8个子进程是相对于根父进程来说是子进程。
log文件
操作系统启动后先要打开 /var/process.log,然后在每个进程发生状态切换的时候向 log 文件内写入一条记录,其过程和用户态的应用程序没什么两样。然而,因为内核状态的存在,使过程中的很多细节变得完全不一样。
打开 log 文件
为了能尽早开始记录,应当在内核启动时就打开 log 文件。内核的入口是 init/main.c 中的 main()(Windows 环境下是 start()),其中一段代码是:
//……
move_to_user_mode();
if (!fork()) { /* we count on this going ok */
init();
}
//……
这段代码在进程 0 中运行,先切换到用户模式,然后全系统第一次调用 fork() 建立进程 1。进程 1 调用 init()。
在 init()中:
// ……
//加载文件系统
setup((void *) &drive_info);
// 打开/dev/tty0,建立文件描述符0和/dev/tty0的关联
(void) open("/dev/tty0",O_RDWR,0);
// 让文件描述符1也和/dev/tty0关联
(void) dup(0);
// 让文件描述符2也和/dev/tty0关联
(void) dup(0);
// ……
这段代码建立了文件描述符 0、1 和 2,它们分别就是 stdin、stdout 和 stderr。这三者的值是系统标准(Windows 也是如此),不可改变。
可以把 log 文件的描述符关联到 3。文件系统初始化,描述符 0、1 和 2 关联之后,才能打开 log 文件,开始记录进程的运行轨迹。
为了能尽早访问 log 文件,我们要让上述工作在进程 0 中就完成。所以把这一段代码从 init() 移动到 main() 中,放在 move_to_user_mode() 之后(不能再靠前了),同时加上打开 log 文件的代码。
修改后的 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);
/***************添加结束***************/
if (!fork()) { /* we count on this going ok */
init();
}
//……
打开 log 文件的参数的含义是建立只写文件,如果文件已存在则清空已有内容。文件的权限是所有人可读可写。
这样,文件描述符 0、1、2 和 3 就在进程 0 中建立了。根据 fork() 的原理,进程 1 会继承这些文件描述符,所以 init() 中就不必再 open() 它们。此后所有新建的进程都是进程 1 的子孙,也会继承它们。但实际上,init() 的后续代码和 /bin/sh 都会重新初始化它们。所以只有进程 0 和进程 1 的文件描述符肯定关联着 log 文件,这一点在接下来的写 log 中很重要。
写log文件
log 文件将被用来记录进程的状态转移轨迹。所有的状态转移都是在内核进行的。
在内核状态下,write() 功能失效,其原理等同于《系统调用》实验中不能在内核状态调用 printf(),只能调用 printk()。编写可在内核调用的 write() 的难度较大,所以这里直接给出源码。它主要参考了 printk() 和 sys_write() 而写成的:
#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;
}
因为和 printk 的功能近似,建议将此函数放入到 kernel/printk.c 中。fprintk() 的使用方式类同与 C 标准库函数 fprintf(),唯一的区别是第一个参数是文件描述符,而不是文件指针。
例如:
// 向stdout打印正在运行的进程的ID
fprintk(1, "The ID of running process is %ld", current->pid);
// 向log文件输出跟踪进程运行轨迹
fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'R', jiffies);
jiffies
jiffries记录了从开机到当前时间的时钟中断的次数,在这时钟中断间隔时间为10ms.
寻找状态切换点
本实验中有5个状态,分别是创建(N)、运行(R)、就绪(J)、睡眠(W)、退出(E)。 我们要做的就是在适当的位置插入对应的状态。
很显然的,这些状态应该是在创建函数、调度函数、退出函数中。于是应该在 fork.c、sche.c、exit.c文件中添加。
在这我就直接贴代码了,然后关于我自己的一些见解便写在注释中了。
Ctrl+f fprintk能加快定位
fork.c
/*
* linux/kernel/fork.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'fork.c' contains the help-routines for the 'fork' system call
* (see also system_call.s), and some misc functions ('verify_area').
* Fork is rather simple, once you get the hang of it, but the memory
* management can be a bitch. See 'mm/mm.c': 'copy_page_tables()'
*/
#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)
{
unsigned long start;
start = (unsigned long) addr;
size += start & 0xfff;
start &= 0xfffff000;
start += get_base(current->ldt[2]);
while (size>0) {
size -= 4096;
write_verify(start);
start += 4096;
}
}
int copy_mem(int nr,struct task_struct * p)
{
unsigned long old_data_base,new_data_base,data_limit;
unsigned long old_code_base,new_code_base,code_limit;
code_limit=get_limit(0x0f);
data_limit=get_limit(0x17);
old_code_base = get_base(current->ldt[1]);
old_data_base = get_base(current->ldt[2]);
if (old_data_base != old_code_base)
panic("We don't support separate I&D");
if (data_limit < code_limit)
panic("Bad data_limit");
new_data_base = new_code_base = nr * 0x4000000;
p->start_code = new_code_base;
set_base(p->ldt[1],new_code_base);
set_base(p->ldt[2],new_data_base);
if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
printk("free_page_tables: from copy_mem\n");
free_page_tables(new_data_base,data_limit);
return -ENOMEM;
}
return 0;
}
/*
* Ok, this is the main fork-routine. It copies the system process
* information (task[nr]) and sets up the necessary registers. It
* also copies the data segment in it's entirety.
*/
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;
//刚把jiffies设置好,很显然就是这里创建
fprintk(3, "%ld\t%c\t%ld\n", p->pid, 'N', 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 */
//状态改变为TASK_RUNNING,需要添加
fprintk(3, "%ld\t%c\t%ld\n", p->pid, 'J', jiffies);
return last_pid;
}
int find_empty_process(void)
{
int i;
repeat:
if ((++last_pid)<0) last_pid=1;
for(i=0 ; i<NR_TASKS ; i++)
if (task[i] && task[i]->pid == last_pid) goto repeat;
for(i=1 ; i<NR_TASKS ; i++)
if (!task[i])
return i;
return -EAGAIN;
}
sche.c
/*
* linux/kernel/sched.c
*
* (C) 1991 Linus Torvalds LJG
*/
/*
* '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;
//状态由TASK_INTERRUPTIBLE改为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;
}
if (c) break;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p)
(*p)->counter = ((*p)->counter >> 1) +
(*p)->priority;
}/*
在这里我说一下为啥要添加下面这些代码,其实实验册中已经说
了,就是switch_to(next)函数,如果next进程是当前的处于运行态的进程,这样是状态没变的,也就不需要写入log文件中
*/
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)
{
current->state = TASK_INTERRUPTIBLE;
//状态变为TASK_INTERRUPTIBLE需要添加
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;
//状态变为TASK_UNINTERRUPTIBLE需要添加
fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'W', jiffies);
schedule();
if (tmp){
tmp->state=0;
//状态改变,需要添加
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;
//状态变为TASK_INTERRUPTIBLE需要添加
fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'W', jiffies);
schedule();
//由于是可中断睡眠,所以相比于sleep_on()函数多了这个
if (*p && *p != current) {
(**p).state=0;
//状态改变需要添加
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", tmp->pid, 'J', jiffies);
}
}
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;
}
}
//后面没有需要改变的了
/*
* 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);
}
exit.c
/*
* linux/kernel/exit.c
*
* (C) 1991 Linus Torvalds
*/
#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)
{
int i;
if (!p)
return;
for (i=1 ; i<NR_TASKS ; i++)
if (task[i]==p) {
task[i]=NULL;
free_page((long)p);
schedule();
return;
}
panic("trying to release non-existent task");
}
static inline int send_sig(long sig,struct task_struct * p,int priv)
{
if (!p || sig<1 || sig>32)
return -EINVAL;
if (priv || (current->euid==p->euid) || suser())
p->signal |= (1<<(sig-1));
else
return -EPERM;
return 0;
}
static void kill_session(void)
{
struct task_struct **p = NR_TASKS + task;
while (--p > &FIRST_TASK) {
if (*p && (*p)->session == current->session)
(*p)->signal |= 1<<(SIGHUP-1);
}
}
/*
* XXX need to check permissions needed to send signals to process
* groups, etc. etc. kill() permissions semantics are tricky!
*/
int sys_kill(int pid,int sig)
{
struct task_struct **p = NR_TASKS + task;
int err, retval = 0;
if (!pid) while (--p > &FIRST_TASK) {
if (*p && (*p)->pgrp == current->pid)
if ((err=send_sig(sig,*p,1)))
retval = err;
} else if (pid>0) while (--p > &FIRST_TASK) {
if (*p && (*p)->pid == pid)
if ((err=send_sig(sig,*p,0)))
retval = err;
} else if (pid == -1) while (--p > &FIRST_TASK) {
if ((err = send_sig(sig,*p,0)))
retval = err;
} else while (--p > &FIRST_TASK)
if (*p && (*p)->pgrp == -pid)
if ((err = send_sig(sig,*p,0)))
retval = err;
return retval;
}
static void tell_father(int pid)
{
int i;
if (pid)
for (i=0;i<NR_TASKS;i++) {
if (!task[i])
continue;
if (task[i]->pid != pid)
continue;
task[i]->signal |= (1<<(SIGCHLD-1));
return;
}
/* if we don't find any fathers, we just release ourselves */
/* This is not really OK. Must change it to make father 1 */
printk("BAD BAD - no father found\n\r");
release(current);
}
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;//状态变为僵尸进程了,很显然代表退出的标志
current->exit_code = code;
fprintk(3, "%ld\t%c\t%ld\n", current->pid, 'E', jiffies);
tell_father(current->father);
schedule();
return (-1); /* just to suppress warnings */
}
int sys_exit(int error_code)
{
return do_exit((error_code&0xff)<<8);
}
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;
/*fprintk(3, "%ld\t%c\t%ld\n", (*p)->pid, '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;
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;
}
之后./run之后可以利用more /var/process.log 可以来查看process.log 的信息,当然最好将process.log取出来,这就需要利用sudo ./mount-hdc挂载来搞定
其前几行的信息如下:
//这是参考答案
1 N 48 //进程1新建(init())。此前是进程0建立和运行,但为什么没出现在log文件里?
1 J 49 //新建后进入就绪队列
0 J 49 //进程0从运行->就绪,让出CPU
1 R 49 //进程1运行
2 N 49 //进程1建立进程2。2会运行/etc/rc脚本,然后退出
2 J 49
1 W 49 //进程1开始等待(等待进程2退出)
2 R 49 //进程2运行
3 N 64 //进程2建立进程3。3是/bin/sh建立的运行脚本的子进程
3 J 64
2 E 68 //进程2不等进程3退出,就先走一步了
1 J 68 //进程1此前在等待进程2退出,被阻塞。进程2退出后,重新进入就绪队列
1 R 68
4 N 69 //进程1建立进程4,即shell
4 J 69
1 W 69 //进程1等待shell退出(除非执行exit命令,否则shell不会退出)
3 R 69 //进程3开始运行
3 W 75
4 R 75
5 N 107 //进程5是shell建立的不知道做什么的进程
5 J 108
4 W 108
5 R 108
4 J 110
5 E 111 //进程5很快退出
4 R 111
4 W 116 //shell等待用户输入命令。
0 R 116 //因为无事可做,所以进程0重出江湖
4 J 239 //用户输入命令了,唤醒了shell
4 R 239
4 W 240
0 R 240
这是我的:
测试
在Ubuntu下利用命令sudo apt-get install python下载python解释器,然后将process.log以及stat_log.py放到一个文件夹中,先cd 到这个文件夹下,利用命令** chmod +x stat_log.py给stat_log.py** 文件加上执行权限,然后利用命令**./stat_log.py process.log x y z -g来得到各进程的信息(其中x y z代表想要进程信息的pid)
我对于进程0 1 2 3 4 5得到的信息如下:
之后测试自己运行process.c**得到的进程pid即可
其结果如图:
修改时间片
对于时间片counter,由于时间片的初始化操作为:
p->counter = p->priority
只与优先级priority有关,所以只需要修改priority即可在定义priority宏中修改即可
#define INIT_TASK \
{ 0,15,15,
// 上述三个值分别对应 state、counter 和 priority;
不过我自认为利用p->counter = p->priority+k 也可以哈哈哈
后言
离数学建模比赛也快了,也就十多天了,在在比赛未结束之前我将不会再搞lab了。希望自己在国赛中能取得一个好成绩吧哈哈哈。
参考
蓝桥实验楼(自己搜,就不放网址了hhh)
love’s blog
zhangtianshan’s blog