HIT-OSlab3

在借助linux0.11内核代码剖析,别人的实验代码,以及操作系统原理实现与实践下完成QAQ

process.c

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/times.h>

#define HZ 100
#define CHILD_PROCESS_NUM 10

void cpuio_bound(int last, int cpu_time, int io_time);

int main(int argc, char * argv[])
{
pid_t c_pr[CHILD_PROCESS_NUM];
int i;
for (i = 0; i < CHILD_PROCESS_NUM; i++)
{
c_pr[i] = fork();
if(c_pr[i] == 0)
{
// child process
cpuio_bound(20, 2 * i, 20 - 2 * i);
return 0; // 执行完cpuio_bound 结束子进程
}
else if(c_pr[i] < 0)
{
printf("Child process %d failed to fork!\n", i + 1);
return -1;
}
}
for (i = 0; i < CHILD_PROCESS_NUM; i++)
{
printf("Child Process ID:%d\n", c_pr[i]);
}

wait();
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(&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;
}
}

main.c

内核状态下只能用printk,根据老师的代码增加fprintk函数,在github上有相关的resource,遂不再记录
因为要早早的开始记录日志文件,所以在进程0开始在用户态执行以后就打开日志文件,开启了多进程视图,这个时间点就在move_to_user_mode()之后,但是还要先挂载上文件系统才能记录。
关键代码如下

setup((void *) &drive_info);
(void) open("/dev/tty0",O_RDWR,0); // 完成了文件系统加载
(void) dup(0);
(void) dup(0);
(void) open("/var/process.log",O_CREAT|O_TRUNC|O_WRONLY,0666);

fork.c

完成多进程日志记录最重要的就是找到内核中关于进程状态代码的切换点,然后添加相关代码完成记录。
fork首先是新建进程的地方,这个地方肯定要记录,真正实现进程创建的函数是copy_process()函数,完成pcb相关设置后将进程状态设置为就绪,即p->state = TASK_RUNNING,这里就要向日志文件写相关记录,分别是新建和记录(其实不难发现,所有应该添加的代码,都应该围绕p->state这种状态设置代码来完成)

fprintk(3, "%ld\t%c\t%ld\n", p->pid, 'N', jiffies);
p->state = TASK_RUNNING; /* do this last, just in case */
fprintk(3, "%ld\t%c\t%ld\n", p->pid, 'J', jiffies);

sched.c

sched.c是内核中有有关进程调度管理的程序,其中包括有关调度的基本函数sleep_on(),wakeup(),schedule()等。
要做出修改的函数有schedule(),sys_pause(),sleep_on(),interruptible_sleep_on(),wake_up()函数
为了方便直接贴出所有代码(懒)


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;
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;
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;
}
}

exit.c

修改do_exit和sys_waitpid

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;
tell_father(current->father);
fprintk(3, "%d\tE\t%d\n", current->pid, jiffies);
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;
}

太难了太难了,内核太难了……

文章作者: Alex
文章链接: http://example.com/2021/08/22/HIT-OSlab3/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Alex's blog~