正在学习MIT的6.S081,把做的实验写一写吧。

实验的代码放在了Github上。

第一个实验是Lab util,算是一个热身的实验,没有涉及到系统的底层,就是使用系统调用来完成几个用户模式的小程序。

Boot xv6 (easy)

启动XV6,按照文档执行就ok了。

$ git clone git://g.csail.mit.edu/xv6-labs-2020
$ cd xv6-labs-2020
$ git checkout util
$ make qemu

在XV6中没有ps命令,而是使用Ctrl+p来查看正在运行的进程。

sleep (easy)

这一个就是仿照已有的程序调用一下sleep的系统调用就行了。

在Unix系统里面,默认情况下0代表stdin1代表stdout2代表stderr。这3个文件描述符在进程创建时就已经打开了的(从父进程复制过来的),可以直接使用。而分配文件描述符的时候是从当前未使用的最小的值来分配,因此可以关闭某个文件描述符再通过opendup将该文件描述符分配给其他文件或pipe来实现输入输出重定向。

有一个小坑就是程序执行结束后要用exit(0)来退出,而不是return 0

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h" int
main(int argc, char *argv[])
{
if(argc < 2){
fprintf(2, "Usage: sleep [time]\n");
exit(1);
} int time = atoi(argv[1]);
sleep(time);
exit(0);
}

pingpong (easy)

这一个实验就是用pipe打开两个管道,然后fork出一个子进程,完成要求的操作就行了。

fork函数是一次调用两次返回的函数,在父进程中返回子进程的pid,在子进程中返回0。

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h" int
main(int argc, char *argv[]){
int p2c[2];
int c2p[2];
if(pipe(p2c) < 0){
printf("pipe");
exit(-1);
}
if(pipe(c2p) < 0){
printf("pipe");
exit(-1);
}
int pid = fork();
if(pid == 0){
// child
char buf[10];
read(p2c[0], buf, 10);
printf("%d: received ping\n", getpid());
write(c2p[1], "o", 2);
}else if(pid > 0){
// parent
write(p2c[1], "p", 2);
char buf[10];
read(c2p[0], buf, 10);
printf("%d: received pong\n", getpid());
}
close(p2c[0]);
close(p2c[1]);
close(c2p[0]);
close(c2p[1]);
exit(0);
}

primes (moderate)/(hard)

这一个是实现管道的发明者Doug McIlroy提出的计算素数的方法,该方法类似于筛法,不过是用管道和多线程实现的。



main函数中父进程创建了一个管道,输入2~35。之后通过prime函数来实现输出,如果读取到了超过两个的数,就创建一个新进程来进行后续处理。

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h" void prime(int rd){
int n;
read(rd, &n, 4);
printf("prime %d\n", n);
int created = 0;
int p[2];
int num;
while(read(rd, &num, 4) != 0){
if(created == 0){
pipe(p);
created = 1;
int pid = fork();
if(pid == 0){
close(p[1]);
prime(p[0]);
return;
}else{
close(p[0]);
}
}
if(num % n != 0){
write(p[1], &num, 4);
}
}
close(rd);
close(p[1]);
wait(0);
} int
main(int argc, char *argv[]){
int p[2];
pipe(p); int pid = fork();
if(pid != 0){
// first
close(p[0]);
for(int i = 2; i <= 35; i++){
write(p[1], &i, 4);
}
close(p[1]);
wait(0);
}else{
close(p[1]);
prime(p[0]);
close(p[0]);
}
exit(0);
}

find (moderate)

这一个就是仿照ls.c中的方法,对当前目录排除掉...后进行递归遍历,同时对路径名进行匹配,匹配到了就输出。不过库中没有提供strstr函数,只能自己写了个\(O(n^2)\)的子串匹配算法。

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h" void match(const char* path, const char* name){
//printf("%s %s", path, name);
int pp = 0;
int pa = 0;
while(path[pp] != 0){
pa = 0;
int np = pp;
while(name[pa] != 0){
if (name[pa] == path[np]){
pa++;
np++;
}
else
break;
}
if(name[pa] == 0){
printf("%s\n", path);
return;
}
pp++;
}
} 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, "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", path, st.type, st.ino, st.size);
match(path, name);
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;
if(de.name[0] == '.' && de.name[1] == 0) continue;
if(de.name[0] == '.' && de.name[1] == '.' && de.name[2] == 0) continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("ls: cannot stat %s\n", buf);
continue;
}
find(buf, name);
}
break;
}
close(fd);
} int
main(int argc, char *argv[]){
if (argc < 3){
printf("Usage: find [path] [filename]\n");
exit(-1);
}
find(argv[1], argv[2]);
exit(0);
}

xargs (moderate)

这一个就是从输入中构造出新的argc数组,然后用forkexec执行就行了。大部分时间都用在输入的处理上面了。。库里面没有提供readline函数和split,只能自己写。

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h" char* readline() {
char* buf = malloc(100);
char* p = buf;
while(read(0, p, 1) != 0){
if(*p == '\n' || *p == '\0'){
*p = '\0';
return buf;
}
p++;
}
if(p != buf) return buf;
free(buf);
return 0;
} int
main(int argc, char *argv[]){
if(argc < 2) {
printf("Usage: xargs [command]\n");
exit(-1);
}
char* l;
argv++;
char* nargv[16];
char** pna = nargv;
char** pa = argv;
while(*pa != 0){
*pna = *pa;
pna++;
pa++;
}
while((l = readline()) != 0){
//printf("%s\n", l);
char* p = l;
char* buf = malloc(36);
char* bh = buf;
int nargc = argc - 1;
while(*p != 0){
if(*p == ' ' && buf != bh){
*bh = 0;
nargv[nargc] = buf;
buf = malloc(36);
bh = buf;
nargc++;
}else{
*bh = *p;
bh++;
}
p++;
}
if(buf != bh){
nargv[nargc] = buf;
nargc++;
}
nargv[nargc] = 0;
free(l);
int pid = fork();
if(pid == 0){
// printf("%s %s\n", nargv[0], nargv[1]);
exec(nargv[0], nargv);
}else{
wait(0);
}
}
exit(0);
}

XV6学习(1) Lab util的更多相关文章

  1. XV6学习笔记(2) :内存管理

    XV6学习笔记(2) :内存管理 在学习笔记1中,完成了对于pc启动和加载的过程.目前已经可以开始在c语言代码中运行了,而当前已经开启了分页模式,不过是两个4mb的大的内存页,而没有开启小的内存页.接 ...

  2. xv6学习笔记(5) : 锁与管道与多cpu

    xv6学习笔记(5) : 锁与管道与多cpu 1. xv6锁结构 1. xv6操作系统要求在内核临界区操作时中断必须关闭. 如果此时中断开启,那么可能会出现以下死锁情况: 进程A在内核态运行并拿下了p ...

  3. XV6学习笔记(1) : 启动与加载

    XV6学习笔记(1) 1. 启动与加载 首先我们先来分析pc的启动.其实这个都是老生常谈了,但是还是很重要的(也不知道面试官考不考这玩意), 1. 启动的第一件事-bios 首先启动的第一件事就是运行 ...

  4. xv6学习笔记(3):中断处理和系统调用

    xv6学习笔记(3):中断处理和系统调用 1. tvinit函数 这个函数位于main函数内 表明了就是设置idt表 void tvinit(void) { int i; for(i = 0; i & ...

  5. xv6学习笔记(4) : 进程调度

    xv6学习笔记(4) : 进程 xv6所有程序都是单进程.单线程程序.要明白这个概念才好继续往下看 1. XV6中进程相关的数据结构 在XV6中,与进程有关的数据结构如下 // Per-process ...

  6. XV6学习(2)Lab syscall

    实验的代码放在了Github上. 第二个实验是Lab: system calls. 这个实验主要就是自己实现几个简单的系统调用并添加到XV6中. XV6系统调用 添加系统调用主要有以下几步: 在use ...

  7. XV6学习(16)Lab net: Network stack

    最后一个实验了,代码在Github上. 这一个实验其实挺简单的,就是要实现网卡的e1000_transmit和e1000_recv函数.不过看以前的实验好像还要实现上层socket相关的代码,今年就只 ...

  8. XV6学习(9)Lab cow: Copy-on-write fork

    代码在github上.总体来说如果理解了COW机制的话,这个实验的完成也没有很复杂. 这一个实验是要完成COW(copy on write)fork.在原始的XV6中,fork函数是通过直接对进程的地 ...

  9. XV6学习(11)Lab thread: Multithreading

    代码放在github上. 这一次实验感觉挺简单的,特别是后面两个小实验.主要就是对多线程和锁进行一个学习. Uthread: switching between threads 这一个实验是要实现一个 ...

随机推荐

  1. fiddler抓APP的https接口

    吐槽一下,fiddler这工具很蛋疼,用的时候很好用,出bug的时候死活抓不了: ①为了少踩坑,我们在官网下最新的fiddler,官网:Fiddler - Free Web Debugging Pro ...

  2. 简单了解一下 Nginx

    一.Nginx 基本认识 1.Nginx 是什么? Nginx 是一款开源的.轻量级的.高性能的 HTTP 服务器 以及 反向代理服务器. 特点是 占有内存少.并发能力强. 2.Nginx 用来干什么 ...

  3. Dubbo SPI源码解析①

    目录 0.Java SPI示例 1.Dubbo SPI示例 2.Dubbo SPI源码分析 ​ SPI英文全称为Service Provider Interface.它的作用就是将接口实现类的全限定名 ...

  4. Java安全之Weblogic 2016-0638分析

    Java安全之Weblogic 2016-0638分析 文章首发先知:Java安全之Weblogic 2016-0638分析 0x00 前言 续上篇文的初探weblogic的T3协议漏洞,再谈CVE- ...

  5. spring传播机制注意点

    在同一个类里面spring的传播机制是不起作用的比如说在执行saveA方法的时候调用C方法插入C设置的传播属性是不使用事物 但是执行的效果是saveA方法抛出异常后导致C的记录回滚了也就是说明C方法设 ...

  6. Linux嵌入式学习-交叉编译openssl

    利用arm-none-linux-gnueabi-gcc交叉编译openssl,生成静态库文件libcrypto.a ,libssl.a 1.从openssl官网下载openssl最新版本,我下载的是 ...

  7. [LeetCode]100. Same Tree判断树相同

    dfs遍历一下判断 public boolean isSameTree(TreeNode p, TreeNode q) { if (p==null) { return q == null; } els ...

  8. Mac苹果电脑单片机开发

    1.安装虚拟机 可以阅读往期文章:Mac苹果电脑安装虚拟机 2.在虚拟机上安装CH340驱动,keil4,PZ-ISP, 下载 CH340驱动安装  下载keil4破解及汉化  下载普中科技烧录软件

  9. 神奇的 SQL 之性能优化 → 让 SQL 飞起来

    开心一刻 一天,一个男人去未婚妻家玩,晚上临走时下起了大雨 未婚妻劝他留下来过夜,说完便去准备被褥,准备就绪后发现未婚夫不见了 过了好久,全身淋的像只落汤鸡的未婚夫回来了 未婚妻吃惊的问:" ...

  10. 【并发编程】- 内存模型(针对JSR-133内存模型)篇

    并发编程模型 1.两个关键问题 1)线程之间如何通信 共享内存 程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信 消息传递 程之间没有公共状态,线程之间必须通过发送消息来显式进行通信 ...