xv6 6.S081 Lab1: util

xv6 6.S081 Lab1: util

    • 写在前面
    • 实验介绍
    • 开始!
      • sleep
      • pingpong
      • Primes
      • Find
      • Xargs

拖了这么久,终于稍微有时间填坑了。今天介绍xv6的第一个实验util。代码在这里。废话不多说,我们开始吧。

写在前面

参考我的上一篇博客OS实验xv6 6.S081 开坑,这里给出了一些有用的参考资料。

实验介绍

这是MIT Lab1的官方指导书Lab1 Utilities

Lab1要求我们实现几个Unix中常用的工具函数:

  • sleep
  • pingpong
  • primes
  • find
  • xargs

开始!

sleep

xv6 6.S081 Lab1: util_第1张图片
这个任务要求利用调用系统调用函数sleep完成sleep n。。。这也太简单了吧。。。
但是这里还需要强调一下,在Hints中说得很清楚了,我们调用的是syscall的sleep,这时xv6提供的,而不是Linux环境中的< sys >中的sleep。

在user文件夹下新建sleep.c,判断一下输入格式(纯粹为了加多代码量),调用一下sleep即可。注意导的包,user.h为xv6提供的系统函数,types.h为其提供的变量类型

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

const int duration_pos = 1;
typedef enum {wrong_char, success_parse, toomany_char} cmd_parse;
cmd_parse parse_cmd(int argc, char** argv);

int 
main(int argc, char** argv){
    //printf("%d, %s, %s \n",argc, argv[0], argv[1]);
    if(argc == 1){
        printf("Please enter the parameters!");
        exit();
    }
    else{
        cmd_parse parse_result;
        parse_result = parse_cmd(argc, argv);
        if(parse_result == toomany_char){
            printf("Too many args! \n");
            exit();
        }
        else if(parse_result == wrong_char){
            printf("Cannot input alphabet, number only \n");
            exit();
        }
        else{
            int duration = atoi(argv[duration_pos]);
            //printf("Sleeping %f", duration / 10.0);
            sleep(duration);
            exit();
        }
        
    }
}

cmd_parse
parse_cmd(int argc, char** argv){
    if(argc > 2){
        return toomany_char;
    }
    else {
        int i = 0;
        while (argv[duration_pos][i] != '\0')
        {
            /* code */
            if(!('0' <= argv[duration_pos][i] && argv[duration_pos][i] <= '9')){
                return wrong_char;
            }
            i++;
        }
        
    }
    return success_parse;
}

pingpong

xv6 6.S081 Lab1: util_第2张图片
本任务要求实现利用管道实现进程间的通信:父进程发送ping,子进程收到后发送pong,父进程收到后将其打印出来
Hints 1:利用pipe()函数创建管道,pipe()函数接收一个长度为2的数组,数组下标0为读端、1为写端;
Hints 2:利用fork()函数创建新的进程;
Hints 3:利用read、write函数读写管道;

绘制出下面这张图就很好理解这个任务了。
xv6 6.S081 Lab1: util_第3张图片
下面给出代码实现:

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

int 
main(int argc, char** argv ){
    int pid;
    int parent_fd[2];
    int child_fd[2];
    char buf[20];
    //为父子进程建立管道
    pipe(child_fd); 
    pipe(parent_fd);

    // Child Progress
    if((pid = fork()) == 0){
        close(parent_fd[1]);
        read(parent_fd[0],buf, 4);
        printf("%d: received %s\n",getpid(), buf);
        close(child_fd[0]);
        write(child_fd[1], "pong", sizeof(buf));
        exit();
    }
    // Parent Progress
    else{
        close(parent_fd[0]);
        write(parent_fd[1], "ping",4);
        close(child_fd[1]);
        read(child_fd[0], buf, sizeof(buf));
        printf("%d: received %s\n", getpid(), buf);
        exit();
    }
    
}

Primes

xv6 6.S081 Lab1: util_第4张图片
本任务要求完成质数筛选器
具体是什么意思呢?
它要求用fork和pipe实现:输入为2 ~ 35,输出为2 ~ 35间的所有质数,例如:2、3、5、7等。

算法比较简单,例如,第一次我们将2 ~ 35给到一个进程,这个进程发现给到的第一个数为2,则输出2,然后将不能被2除尽的数(3、5、7、9……)发送给下一个进程,下一个进程发现给到的第一个数为3,则输出3,然后将不能被3除尽的数(5、7……)发送给下一个进程……以此类推。我们可以通过下图说明这个过程。
xv6 6.S081 Lab1: util_第5张图片
好了,递归创建管道貌似就可以做了。

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


void generate_nums();
void send_primes(int pd[], int infos[], int infoslen);
void check_pd(int pd[], int len);
void process(int pd[]);

int
main(int argc, char** argv){
    //声明管道
    int pd[2];  //pipe descriper
    //创建管道
    pipe(pd);
    //check_pd(pd, 2);
    int pid;

    //Child Process
    if((pid = fork()) == 0){
        process(pd);
        exit();
    }
    //Parent Process
    else{
        int nums[34];
        generate_nums(nums);
        send_primes(pd, nums, 34);
        //sleep(10);
        exit();
    }
    
}

void process(int pd[]){
    int p;
    int n;
    int len;
    int pid;
    int pd_child[2];
    int infos[34];
    int infos_i = 0;
    pipe(pd_child);
    //check_pd(pd_child, 2);
    
    close(pd[1]);
    len = read(pd[0], &p, sizeof(p));
    printf("prime %d\n", p);  
    
    while(len != 0) {
        len = read(pd[0], &n, sizeof(n));
        if(n % p != 0){
            *(infos + infos_i) = n;
            infos_i++;
        }
        
    }

    close(pd[0]);
    
    if(infos_i == 0) {
        exit();
    }
    

    // Child Process
    if((pid = fork()) == 0){
        process(pd_child);
    }
    // Parent Process
    else{
        send_primes(pd_child, infos, infos_i);  
    }
}

void
generate_nums(int nums[34]){
    int i = 0;
    for(int count = 2; count <= 35; count++){
        nums[i] = count;
        i++;
    }
    
}

void 
check_pd(int pd[], int len){
    printf("pd:\n");
    for(int i = 0; i < len; i++){
        printf("%d \n", pd[i]);
    }
}

void
send_primes(int pd[], int infos[], int infoslen){
    int info;
    close(pd[0]);
    for(int i = 0; i < infoslen; i++){
        info = infos[i];
        write(pd[1],&info,sizeof(info));
    }
}


Find

xv6 6.S081 Lab1: util_第6张图片
目标:写一个find函数
find函数的作用是什么呢?
其基本用法为 find arg1 arg2, 即在目录arg1下找到arg2。怎么写呢?完全不知道,因为这和file system挂钩。但是,MIT给了我们一个Hint:Look at user/ls.c to see how to read directories。于是就照着ls来写吧。这里有一个红利Bonus,就是可以直接copy grep.c的正则匹配代码,这样就能更快地进行文件的查找。

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

//Copy from Grep.c
char buf[1024];
int match(char*, char*);
int matchhere(char*, char*);
int matchstar(int, char*, char*);

int
match(char *re, char *text)
{
  if(re[0] == '^')
    return matchhere(re+1, text);
  do{  // must look at empty string
    if(matchhere(re, text))
      return 1;
  }while(*text++ != '\0');
  return 0;
}

// matchhere: search for re at beginning of text
int matchhere(char *re, char *text)
{
  if(re[0] == '\0')
    return 1;
  if(re[1] == '*')
    return matchstar(re[0], re+2, text);
  if(re[0] == '$' && re[1] == '\0')
    return *text == '\0';
  if(*text!='\0' && (re[0]=='.' || re[0]==*text))
    return matchhere(re+1, text+1);
  return 0;
}

// matchstar: search for c*re at beginning of text
int matchstar(int c, char *re, char *text)
{
  do{  // a * matches zero or more instances
    if(matchhere(re, text))
      return 1;
  }while(*text!='\0' && (*text++==c || c=='.'));
  return 0;
}


/*
  find.c
*/
char* fmtname(char *path);
void find(char *path, char *re);

int 
main(int argc, char** argv){
    if(argc < 2){
      printf("Parameters are not enough\n");
    }
    else{
      //在路径path下递归搜索文件 
      find(argv[1], argv[2]);
    }
    exit();
}

// 对ls中的fmtname,去掉了空白字符串
char*
fmtname(char *path)
{
  static char buf[DIRSIZ+1];
  char *p;

  // Find first character after last slash.
  for(p=path+strlen(path); p >= path && *p != '/'; p--)
    ;
  p++;
  // printf("len of p: %d\n", strlen(p));
  if(strlen(p) >= DIRSIZ)
    return p;
  memset(buf, 0, sizeof(buf));
  memmove(buf, p, strlen(p));
  //memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
  return buf;
}

void 
find(char *path, char *re){
  // printf("---------------------------------------------\n");
  // printf("path:%s\n", path);
  // printf("fmtpath:%s\n",fmtname(path));
  // printf("re:%s\n", re);
  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;
  }
  
  switch(st.type){
  case T_FILE:
      //printf("File re: %s, fmtpath: %s\n", re, fmtname(path));
      if(match(re, fmtname(path)))
          printf("%s\n", path);
      break;
          //printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);

  case T_DIR:
      if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
          printf("find: 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("find: cannot stat %s\n", buf);
              continue;
          }
          // printf("%s, %d\n",fmtname(buf), strlen(fmtname(buf)));
          // printf("%s\n",buf);
          // // printf("%d\n",strcmp(".", fmtname(buf)));
          // // printf("%d\n",strcmp("..", fmtname(buf)));
          char* lstname = fmtname(buf);
          // printf("lstname: %s\n", lstname);
          if(strcmp(".", lstname) == 0 || strcmp("..", lstname) == 0){
            //printf("%s %d %d %d\n", buf, st.type, st.ino, st.size);
            continue;
          }
          else{
            //printf("deep: %s %d %d %d\n", buf, st.type, st.ino, st.size);
            find(buf, re);
          }
          //printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
      }
      break;
  }
  close(fd);
}

Xargs

xv6 6.S081 Lab1: util_第7张图片
简而言之,多参数实现吧,当用户输入ctrl+d时停止参数的输入。
这里要说明的是:对于输入的命令,我们要用exec执行,其中exec接收两个参数,第一个参数为命令cmd,第二个参数为一个数组,该数组的格式必须为{cmd, “arg1”, “arg2”, …, 0}

代码实现并不难,关键在于要理解xargs的用法。另外,在c语言中,输入ctrl+d后,buf长度为0,可据此来完成对ctrl+d的判断。另外,根据MIT的Hint:kernel/param.h declares MAXARG, which may be useful if you need to declare an argv.,我们可以声明一个数组来保存用户输入的每一个参数。好了,可以写代码了。

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

#define CMDSTYLE        2

char *cutoffinput(char *buf);
void substring(char s[], char sub[], int pos, int len);
void printargs(char* args[MAXARG][CMDSTYLE], int args_num);

/* 打印参数 */
void 
printargs(char* args[MAXARG][CMDSTYLE], int args_num){
    for (int i = 0; i < args_num; i++)
    {
        /* code */
        printf("--------------args %d:--------------\n", i + 1);
        printf("cmd: %s, arg: %s, argslen: %d \n", args[i][0], args[i][1], strlen(args[i][1]));
    }
    
}
/* 子串 */
void 
substring(char s[], char *sub, int pos, int len) {
   int c = 0;   
   while (c < len) {
      *(sub + c) = s[pos+c];
      c++;
   }
   *(sub + c) = '\0';
}

/* 截断 '\n' */
char* 
cutoffinput(char *buf){
    /* 记得要为char *新分配一片地址空间,否则编译器默认指向同一片地址 */
    if(strlen(buf) > 1 && buf[strlen(buf) - 1] == '\n'){
        char *subbuff = (char*)malloc(sizeof(char) * (strlen(buf) - 1));
        substring(buf, subbuff, 0, strlen(buf) - 1);
        return subbuff;
    }
    else
    {
        char *subbuff = (char*)malloc(sizeof(char) * strlen(buf));
        strcpy(subbuff, buf);
        return subbuff;
    }
}

int 
main(int argc, char *argv[])
{
    /* code */
    int pid;
    char buf[MAXPATH];
    char *args[MAXARG];
    char *cmd;
    /* 默认命令为echo */
    if(argc == 1){
        cmd = "echo";
    }
    else{
        cmd = argv[1];
    }
    /* 计数器 */
    int args_num = 0;
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        gets(buf, MAXPATH);
        /* printf("buf:%s",buf); */
        char *arg = cutoffinput(buf);
        /* printf("xargs:gets arg: %s, arglen: %d\n", arg, strlen(arg)); */
        /* press ctrl + D */
        if(strlen(arg) == 0 || args_num >= MAXARG){
            break;
        }
        args[args_num]= arg;
        args_num++;
    }

    /* 
        printargs(args, args_num);
        printf("Break Here\n");
     */
    /* 填充exec需要执行的命令至argv2exec */
    int argstartpos = 1;
    char *argv2exec[MAXARG];
    argv2exec[0] = cmd;

    for (; argstartpos < argc; argstartpos++)
    {
        argv2exec[argstartpos] = argv[argstartpos - 2];
    }
    
    for (int i = 0; i < args_num; i++)
    {
        /* code */
        argv2exec[i + argstartpos] = args[i];
    }
    argv2exec[args_num + argstartpos] = 0;
    
    /* 运行cmd */
    if((pid = fork()) == 0){   
        exec(cmd, argv2exec);    
    }  
    else
    {
        /* code */
        wait();
    }
    exit();
}



你可能感兴趣的:(操作系统,OS-xv6,linux,xv6,unix,多进程)