MIT 6.s081 实验解析——labs1
make qemu
./grade-lab-util <文件名>
//以sleep为例
./grade-lab-util sleep
第一个实验虽然简单,但可以通过这个程序来了解整个命令的运行过程。我们将sleep命令编译入系统之后,在shell里输入sleep 10,shell接受到这段参数之后,进行判断,这是一个程序调用的指令。
//sh.c对应源码
case EXEC:
ecmd = (struct execcmd*)cmd;
if(ecmd->argv[0] == 0)
exit(1);
exec(ecmd->argv[0], ecmd->argv);
fprintf(2, "exec %s failed\n", ecmd->argv[0]);
break;
可以看到在shell的内部,将第一个参数作为文件名,第二个参数作为接受参数,通过exec系统调用对应程序,这才进入到我们自己写的sleep程序中运行。注意:是将整个argv[]传给了sleep,包括argv[0]。
sleep程序很简单,当参数不为2时,直接警告参数错误,并exit(1)退出。当参数正确时,用atoi函数将字符串转为int,然后直接使用sleep系统调用,最后exit(0).
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc,char* argv[]){
if(argc!=2){
fprintf(2,"error,please input correct time");
exit(1);
}
sleep(atoi(argv[1]));
exit(0);
}
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc,char* argv[]){
int p[2];
int q[2];
pipe(p);
pipe(q);
if (fork() == 0) {
char data;
read(p[0],&data,1);
fprintf(1,"%d: received ping\n", getpid());
close(p[0]);
close(p[1]);
write(q[1], &data, 1);
close(q[0]);
close(q[1]);
exit(0);
} else {
char data;
write(p[1], "1", 1);
close(p[0]);
close(p[1]);
wait(0);
read(q[0],&data,1);
fprintf(1,"%d: received pong\n", getpid());
close(q[0]);
close(q[1]);
exit(0);
}
}
这是一个有些复杂的问题,因为涉及到递归,整体思路可以通过下图来表示:
p = 从左邻居中获取一个数
print p
loop:
n = 从左邻居中获取一个数
if (n不能被p整除)
将n发送给右邻居
代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
__attribute__((noreturn))
void primes(int p[2]){
close(p[1]);
int first;
int data;
if(read(p[0], &first, sizeof(int)) == sizeof(int)){
fprintf(1,"prime %d\n",first);
int q[2];
pipe(q);
while(read(p[0], &data, sizeof(int)) == sizeof(int)){
if(data%first){
write(q[1], &data, sizeof(int));
}
}
close(p[0]);
close(q[1]);
if(fork() == 0){
primes(q);
}else{
close(q[0]);
wait(0);
}
}
exit(0);
}
int main(int argc,char* argv[]){
int p[2];
pipe(p);
for(int i = 2; i <= 35; i++){
write(p[1], &i, sizeof(int));
}
if(fork() == 0){
primes(p);
}else{
close(p[0]);
close(p[1]);
wait(0);
}
exit(0);
}
在编译过程中可能会遇到这样的问题:
一般报错的函数返回值为void,解决办法是在void前加上__attribute__((noreturn))
void
ls(char *path)
{
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "ls: cannot open %s\n", path);
return;
}//若无法打开文件则打印错误
if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", path);
close(fd);
return;
}//若找不到文件描述符对应的文件,则打印错误
switch(st.type){
case T_FILE:
printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
break;//如果是普通的文件,标准化名称后直接打印信息
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("ls: path too long\n");
break;
}
strcpy(buf, path);
p = buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("ls: cannot stat %s\n", buf);
continue;
}
printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
}
break;
}
close(fd);
}
这个思路可以用在find程序上
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
void find(char *path, char *name){
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "find: cannot open %s\n", path);
return;
}//若无法打开文件则打印错误
if(fstat(fd, &st) < 0){
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}//若找不到文件描述符对应的文件,则打印错误
if(st.type != T_DIR){
fprintf(2, "find: it is not a DIR");
close(fd);
return;
}//若找不到文件描述符对应的文件,则打印错误
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("find: path too long\n");
return;
}
strcpy(buf, path);
p = buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;//字符串结束标志
if(stat(buf, &st) < 0){
printf("find: cannot stat %s\n", buf);
continue;
}
if(st.type == T_DIR && strcmp(".",p) != 0 && strcmp("..",p) != 0){
find(buf,name);
}else if(strcmp(name,p) == 0){
printf("%s\n", buf);
}
}
close(fd);
}
int
main(int argc, char *argv[])
{
if(argc != 3){
fprintf(2, "find: the number of parameter is wrong ");
exit(1);
}
find(argv[1],argv[2]);
exit(0);
}
输入参数是path和文件name,流程差不多:
注意点:
1.题目要求不能在.和…中递归,意思是碰到路径是 .和…的要进行判断排除。
2.不能在递归程序最后写exit(0)!!!因为exit操作相当于整个程序退出,如果是在递归第二层exit,并不会返回上一层,而是会直接退出!!!
这道题一开始我并没有看懂他的意思,xargs的实际效果是,将xargs后面跟的参数作为基础参数,然后接受标准输入的参数拼接在基础参数后面执行,遇到\n说明是两条指令,则分开执行。(通过管道过来的参数,也相当于标准输入过来的,因为中间经过了I/O重定向)
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/param.h"
int main(int argc, char *argv[]){
char *p[MAXARG];
int i;
for(i=1; i < argc; i++)
p[i-1] = argv[i];//把参数都放进p
p[argc-1] = malloc(512);//在最后一个参数后面分配空间
p[argc] = 0;//在空间后面+0,表示结束
while(gets(p[argc-1],512)){ //gets函数一次读取一行
if(p[argc-1][0] == 0)break; //已读完
if(p[argc-1][strlen(p[argc-1])-1]=='\n'){
p[argc-1][strlen(p[argc-1])-1] = 0;
} //该函数会将末尾换行保留,故需去掉换行符。
if(fork()==0)
exec(argv[1],p);
else
wait(0);
}
exit(0);
}