Homework1: boot xv6
1.获取xv6源
emiko@emiko-virtual-machine:~/firstlab$ git clone http://github.com/mit-pdos/xv6-public.git
emiko@emiko-virtual-machine:~/firstlab$ cd xv6-public
emiko@emiko-virtual-machine:~/firstlab/xv6-public$ make
2.寻找_start地址,就是内核的入口地址,并设置断点,
emiko@emiko-virtual-machine:~/firstlab/xv6-public$ nm kernel | grep _start
8010a48c D _binary_entryother_start
8010a460 D _binary_initcode_start
0010000c T _start
设置断点查看堆栈寄存器的内容。(在Lab1中已经说过了就不赘述了)。
================================================================================================
Homework2: Shell
sh.c源代码函数分析:
parsecmd(buf): 解析命令。buf为输入的命令。
parseline(&s,es): s为buf的起始地址,es为结束地址的下一个地址。
parsepipe(ps, es): 解析管道命令。
parseexec(ps, es): 解析重定向命令。
parseredirs: 获取重定向命令中的文件。
gettoken():获取参数变量。可以简单理解为获取命令中除<|>符号外的其他部分。以空格作为分割。
这个实验需要完成的任务如下:
1.下载sh.c文件
2.创建一个t.sh文件(用于测试sh.c)粘贴以下命令。
ls > y
cat < y | sort | uniq | wc > y1
cat y1
rm y1
ls | sort | uniq | wc
rm y
测试过程如下:
1.使用gcc sh.c命令,对sh.c进行编译,将会产生一个a.out文件
2.执行./a.out < t.sh将会得到执行结果。
实验任务是补充sh.c中case' ', case <,>,case |,即重定向命令和管道命令。
代码的补充和分析如下:
首先关于case' '下的命令。代码很简单。但在之前,一直照着官方文档里说的,直接执行/bin/ls可以得到结果(就是需要带上目录,不能直接使用ls)试验,但是一直没有成功,感到很疑惑。后来才明白,需要执行execv这个系统调用才能使用这些普通命令。
如果应用程序正常执行完毕,execv永远不会返回;只有当应用程序出错,execv才会返回-1。
case ' ':
ecmd = (struct execcmd*)cmd;
if(ecmd->argv[0] == 0)
_exit(0);
// Your code here ...
if(execv(ecmd->argv[0],ecmd->argv) == -1){
char path[20] = "/bin/";
strcat(path,ecmd->argv[0]);
if(execv(path,ecmd->argv) == -1){
char path1[20]="/usr/bin/";
strcat(path1,ecmd->argv[0]);
if(execv(path1,ecmd->argv)==-1){
fprintf(stderr,"Command %s not found\n",ecmd->argv[0]);
_exit(0);
}
}
}
//用注释中的代码替代以上代码,就可以直接通过/bin/cmd执行普通命令
/* if(execv(ecmd->argv[0],ecmd->argv)==-1){
fprintf(stderr,"Command %s not found\n",ecmd->argv[0]);
_exit(0);
}*/
break;
关于重定向命令,代码逻辑是这样:首先rcmd->fd表示的是文件描述符,stdin标准输入流的文件描述符是0,stdout的是1。如果是 > 命令的话,就关闭标准输出流,相反,关于标准输入流。
case '>':
case '<':
rcmd = (struct redircmd*)cmd;
// Your code here ...
// stdin's file descriptioner is 0, stdout is 1
close(rcmd->fd);
if(open(rcmd->file,rcmd->flags,S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)<0){
fprintf(stderr,"file %s can not find\n",rcmd->file);
_exit(0);
}
runcmd(rcmd->cmd);
break;
在管道命令中,是将前一个命令的输出作为下一个命令的输入。主要是需要理解dup()的作用。结合代码可以理解为:
close(1),在标准输出中,关闭了文件描述符为1的文件,那么就空出来一个表项,dup(p[1])的含义就是把p[1]文件描述符复制到空出来的位置上,从而使得程序向标准输出的内容全部进入到了p[1]所表示的文件中。
case '|':
pcmd = (struct pipecmd*)cmd;
// Your code here ...
if(pipe(p)<0){
fprintf(stderr,"Falied to create a pipe\n");
_exit(0);
}
if(fork1()==0){
close(1);
dup(p[1]);
close(p[0]);
close(p[1]);
runcmd(pcmd->left);
}
if(fork1()==0){
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
runcmd(pcmd->right);
}
close(p[0]);
close(p[1]);
wait(&r);
wait(&r);
break;
实验结果: