mit xv6实验01

MIT 操作系统实验lab01 utility

插曲

对于ulib.c的学习,仔细看了下ulib的源代码真的可以学习到很多有用的知识

对于字符串的处理函数, 这里特别要注意很多函数都使用了c字符串一个很不错(也是很头疼)的性质,最后一个字符是’\0’表示字符串结束

char*
strcpy(char *s, const char *t)
{
  char *os;

  os = s;
  while((*s++ = *t++) != 0)
    ;
  return os;
}

int
strcmp(const char *p, const char *q)
{
  while(*p && *p == *q)
    p++, q++;
  return (uchar)*p - (uchar)*q;
}

uint
strlen(const char *s)
{
  int n;

  for(n = 0; s[n]; n++)
    ;
  return n;
}
// 查找某个元素在字符串中的位置, 如果不存在,返回0
char*
strchr(const char *s, char c)
{
  for(; *s; s++)
    if(*s == c)
      return (char*)s;
  return 0;
}

// 读出一行元素的字符串
char*
gets(char *buf, int max)
{
  int i, cc;
  char c;

  for(i=0; i+1 < max; ){
    cc = read(0, &c, 1);
    if(cc < 1)
      break;
    buf[i++] = c;
    if(c == '\n' || c == '\r')
      break;
  }
  buf[i] = '\0';
  return buf;
}

对于内存的函数, 这个函数告诉我们处理指针移动的时候要特别小心,如果源地址在目标地址后面的话,正向复制即可,如果源地址在目标地址前面的话,必须要反向复制。

void* memmove(void* vdst, const void* vsrc, int n) 
{
	char * dst;
	const char *src;
	dst = vdst;
	src = vsrc;
	if (src > dst) {
		while (n -- > 0) {
			*dst++ = *src++;
			}
	} else {
		dst += n;
		src += n;
		while (n -- > 0) {
			*--dst = *--src;
			}
		}
	return vdst;
}

任务部分

任务一 sleep

任务提示我们可以参考rm.c的代码, 从rm.c中我们可以知道参数是含有该程序自身名称的,比如rm file ,rm 是argv[0], file 是argv[1].

// rm.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int
main(int argc, char *argv[])
{
  if (argc < 2) {
    fprintf(2, "Usage:rm file...\n");
    exit(1);
  } 

  for (int i = 1; i < argc; i++) {
    if (unlink(argv[i]) < 0) {
      fprintf(2, "rm:%s failed to delete\n", argv[i]);
      break;
    }
  }
  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, "Usage: sleep time...\n");
    exit(1);
  }

  sleep(atoi(argv[1]));

  exit(0);
}

任务二 pingpong

完成一个接受发的通信,这里我们需要参考xv6的源码中关于管道的部分,给父进程,子进程分别配置一个管道的读口,一个管道的写口。

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"


int 
main(int argc, char* argv[]) {
    int p1[2], p2[2];
    char byte = 'A';
    char buffer[2];
    pipe(p1);
    pipe(p2);
    int pid = fork();
    if (pid < 0) {
        write(2, "forkerror\n", 10);
    }
    if (pid == 0) {
        // 当前为子进程
        close(p1[1]);
        read(p1[0], buffer, 1);
        if (buffer[0] == byte) {
            printf("%d: received ping\n", getpid());
        } 
        close(p1[0]);
        close(p2[0]);
        write(p2[1], &byte, 1);
        close(p2[1]);
        exit(0);
    } else {
        close(p1[0]);
        write(p1[1], &byte, 1);
        close(p1[1]);
        close(p2[1]);
        read(p2[0], buffer + 1, 1);
        if (buffer[1] == byte) {
            printf("%d: received pong\n", getpid());
        }
        close(p2[0]);
        exit(0);
    }

} 

其中用到了 p i p e ( p ) pipe(p) pipe(p), 这个是通过一个数组创建一个读写管道, 还用到了read(文件描述符,地址, 大小)和write(文件描述符,地址,大小), 这里需要与fread做区别,fread(地址,大小,个数,文件描述符).

任务三 primes

完成打印1-35的素数
这里提示我们不可以每个数都开一个进程,否则会爆进程,我们从kernel/param.h中可以看到最多可以开64个进程,因此我们需要在只有数为素数的时候才开进程,用管道进行读写,否则不开

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int isprime(int n) {
    if (n <= 1) return 0; // 0 and 1 are not prime numbers
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            return 0; // Found a divisor, so it's not prime
        }
    }
    return 1; // n is prime
}

int 
main(int argc, char* argv[]) {
    for (int i = 1; i < 36; i++) {
        if (isprime(i)) {
            int p[2];
            pipe(p);
            int buffer;
            int pid = fork();
            if (pid == 0) {
                close(p[1]);
                read(p[0], &buffer, 1);
                int num = buffer;
                printf("prime %d\n", num);
                close(p[0]);
                exit(0);
            } 
            close(p[0]);
            write(p[1], &i, 1);
            close(p[1]);
            wait((int*)0);
        }
    }
    exit(0);
}

任务四 find函数

这里提示我们可以参考ls的实现, 当目标文件是一个文件类型时直接输出,当目标文件为目录的时候,我们需要读取目录项,将其拼成绝对路径后,在使用state(char* path, struct stat * st)来读取相关信息

// find 函数的实现
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"


void find(char* directory, char* file) {
    int fd;
    struct stat st; // 存节点信息
    struct dirent de; // 存目录项
    if ((fd = open(directory, 0)) < 0) {
        fprintf(2, "cannot open%s\n", directory);
        return;
    }
    if ((fstat(fd, &st)) < 0) {
        fprintf(2, "cannot stat %s\n", directory);
        close(fd);
        return;
    }
    // 将目录拼接到最前面
    char buf[512], *p;
    strcpy(buf, directory);
    p = buf + strlen(buf);
    *p++ = '/';

    // 从fd中读取每一个目录项
    while(read(fd, &de, sizeof(de)) == sizeof(de)) {
        if (de.inum == 0) continue; // 无效目录项
        memcpy(p, de.name, strlen(de.name) + 1); // 这里的+1是为了让/0放入
        if (stat(buf, &st) < 0) {
            printf("cannot stat %s\n", buf);
            continue;
        } 
        switch (st.type) {
            case T_FILE:
                if (!strcmp(de.name, file)) {
                    printf("%s\n", buf);
                }
                break;
            case T_DIR:
                if (strcmp(de.name, ".") && strcmp(de.name, "..")) find(buf, file);
                break;
        }

    }
    close(fd);
    return;

}


int main(int argc, char* argv[]) {
    if (argc < 3) {
        write(2, "Usage: find directory file\n", 27);
        exit(1);
    } 
    find(argv[1], argv[2]);
    exit(0);
}

任务五 xargs

xargs的实现是写了最久的,主要要处理很多I/O,给到的经验就是每次while()判断结束退出后,一定要想一想最后一个元素是不是在while循环中处理过,否则很容易导致出错,其次一定malloc之后要free.

// xargs 实现
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/param.h"
#include "user/user.h"
char* readline() {
    char *buf = malloc(1024*sizeof(char));
    char *p = buf;
    while (read(0, p, 1) != 0) {
        if (*p == '\n' || *p == '\0') {
            *p = '\0';
            return buf;
        }
        p++;
    }
    free(buf);
    return 0;
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        write(2, "Usage: xargs cmd [option param]\n", 32);
        exit(1);
    }
    // 先将所有的先前变量读入
    char* nargv[MAXARG];
    for (int i = 1; i < argc; i++) {
        nargv[i - 1] = argv[i];
    }
    

    char* l, *p;
    while((l = readline()) != 0) {
        p = l;
        int nargc = argc - 1;
        char* buf = malloc(128 * sizeof(char));
        char *bp;
        bp = buf;
        while (*p != 0) {
            if (*p == ' ' && bp != buf) { // 枚举到" "加入该变量
                *bp = 0;
                nargv[nargc++] = buf;
                buf = malloc(128 * sizeof(char));
                bp = buf;
            } else {   //枚举整个变量
                *bp = *p;
                bp++;
            }
            p++;
        }
        // 解决最后一个变量, *p = 0 退出时
        if (buf != bp) nargv[nargc++] = buf;
        free(l);
        int pid = fork();
        if (pid == 0) {
            exec(nargv[0], nargv);
            exit(0);
        } else {
            wait((int*)0);
        }
    }
    exit(0);


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值