xv6 6.S081 Lab1: util

DeadPool loves Star發表於2020-12-22

拖了這麼久,終於稍微有時間填坑了。今天介紹xv6的第一個實驗util。程式碼在這裡。廢話不多說,我們開始吧。

寫在前面

參考我的上一篇部落格OS實驗xv6 6.S081 開坑,這裡給出了一些有用的參考資料。

實驗介紹

這是MIT Lab1的官方指導書Lab1 Utilities

Lab1要求我們實現幾個Unix中常用的工具函式:

  • sleep
  • pingpong
  • primes
  • find
  • xargs

開始!

sleep

在這裡插入圖片描述
這個任務要求利用呼叫系統呼叫函式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

在這裡插入圖片描述
本任務要求實現利用管道實現程式間的通訊:父程式傳送ping,子程式收到後傳送pong,父程式收到後將其列印出來
Hints 1:利用pipe()函式建立管道,pipe()函式接收一個長度為2的陣列,陣列下標0為讀端、1為寫端;
Hints 2:利用fork()函式建立新的程式;
Hints 3:利用read、write函式讀寫管道;

繪製出下面這張圖就很好理解這個任務了。
在這裡插入圖片描述
下面給出程式碼實現:

#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

在這裡插入圖片描述
本任務要求完成質數篩選器
具體是什麼意思呢?
它要求用fork和pipe實現:輸入為2 ~ 35,輸出為2 ~ 35間的所有質數,例如:2、3、5、7等。

演算法比較簡單,例如,第一次我們將2 ~ 35給到一個程式,這個程式發現給到的第一個數為2,則輸出2,然後將不能被2除盡的數(3、5、7、9……)傳送給下一個程式,下一個程式發現給到的第一個數為3,則輸出3,然後將不能被3除盡的數(5、7……)傳送給下一個程式……以此類推。我們可以通過下圖說明這個過程。
在這裡插入圖片描述
好了,遞迴建立管道貌似就可以做了。

#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

在這裡插入圖片描述
目標:寫一個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

在這裡插入圖片描述
簡而言之,多引數實現吧,當使用者輸入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][CMDSTYLE];
    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][0] = cmd;
        args[args_num][1] = arg;
        args_num++;
    }

    /* 
        printargs(args, args_num);
        printf("Break Here\n");
     */
    /* 填充exec需要執行的命令至argv2exec */
    int argstartpos = 1;
    char *argv2exec[MAXARG];
    argv2exec[0] = cmd;
    if(argc == 3)
    {
        /* 從2開始 */
        argstartpos++;
        argv2exec[1] = argv[2];
    }
    else if (argc >= 3)
    {
        printf("xargs: too many initial args, only 2 permitted! \n");
        exit();
    }
    for (int i = 0; i < args_num; i++)
    {
        /* code */
        argv2exec[i + argstartpos] = args[i][1];
    }
    argv2exec[args_num + argstartpos] = 0;
    
    /* 執行cmd */
    if((pid = fork()) == 0){   
        exec(cmd, argv2exec);    
    }  
    else
    {
        /* code */
        wait();
    }
    exit();
}

相關文章