Loading

6.S081 lab1 utilities

LAB链接:https://pdos.csail.mit.edu/6.S081/2020/labs/util.html

#sleep

  • main函数有两个参数argcargv,其中argc表示命令行参数个数(argument count),argc表示命令行向量(argument vector),比如本例中的sleep程序,命令行参数个数为2,命令行向量分别为{"sleep",ticks},其中ticks为用户指定的睡眠时钟周期数,当argc!=2时,应该打印错误信息,Xv6默认打开了三个文件描述符,分别为stdin表示标准输入,stdout表示标准输出,stderr表示标准错误,文件描述符分别为整数0,1,2

  • 使用atoi(ascii to integer)函数将字符串转化为整型数字,直接系统调用sleep即可

代码如下:

#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<number>\n");
        exit(1);
    }

    int t = atoi(argv[1]);
    sleep(t);
    exit(0);
}

#pingpong

  • pipe(int *p)系统调用创建一个管道,p[0]代表管道的读端,p[1]代表管道的写端
  • int getpid()返回当前进程的PIDint fork()系统调用对于父进程的返回值为子进程的PID,对于子进程的返回值为0
  • main函数中利用fork()系统调用创建一个子进程。并且分别为父子进程创建两个管道p1和p2。对于父进程,关闭管道p1的读端和p2的写端,利用int write(int fd, char *buf, int n)系统调用往管道p1的写端写入一个字节,之后利用int read(int fd, char *buf, int n)系统调用在管道p2的读端读取一个字节(当p2的读端没有数据时,程序将被阻塞,等待p2的读端传入数据),子进程的操作类似。

代码如下:

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

#define STD_ERR 2
#define READ_END 0
#define WRITE_END 1

int main(int argc, char *argv[])
{
    if (argc != 1){
        fprintf(STD_ERR, "Usage:pingpong\n");
        exit(1);
    }

    int p1[2], p2[2];
    char buf[1];
    pipe(p1), pipe(p2);
    if (fork() == 0){
        close(p1[WRITE_END]);
        close(p2[READ_END]);
        if (read(p1[READ_END], buf, 1) == 1){
            printf("%d: received ping\n", getpid());
            close(p1[WRITE_END]);
        }
        write(p2[WRITE_END], buf, 1);
    }
    else {
        close(p2[WRITE_END]);
        close(p1[READ_END]);
        write(p1[WRITE_END], buf, 1);
        close(p1[WRITE_END]);
        if (read(p2[READ_END], buf, 1) == 1){
            printf("%d: received pong\n", getpid());
            close(p2[READ_END]);
        }
    }

    exit(0);
}

  • main函数中先创建一个管道,并创建一个新进程,对于新进程,执行work函数。对于旧进程,将数字2~35全部写入到管道。
  • work函数的实现逻辑参考上图,关闭pp的写端,从pp中读入一个数字ii一定是质数(如果read函数返回0则说明pp中没有数据,直接关闭pp的读端并退出即可),将i输出后,创建当前进程的管道pc,在当前进程中,关闭pc的读端,并向pc循环写入不能整除i的数字,随后关闭pc的写端和pp的读端。对于当前进程的子进程,关闭pp的读端和pc的写端,递归调用work()

程序代码如下:

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

#define STD_ERR 2
#define READ_END 0
#define WRITE_END 1

void work(int pp[]){
    close(pp[WRITE_END]);
    int i;
    if (read(pp[READ_END], &i, 4) == 0){
        close(pp[READ_END]);
        exit(0);
    }
    printf("prime %d\n",i);
    int pc[2], num; //pipe--child
    pipe(pc);
    if (fork() == 0){
        close(pp[READ_END]);
        close(pc[WRITE_END]);
        work(pc);
    }
    else {
        close(pc[READ_END]);
        while (read(pp[READ_END], &num, 4) != 0){
            if (num % i != 0){
                write(pc[WRITE_END], &num, 4);
            }
        }
        close(pc[WRITE_END]);
        close(pp[READ_END]);
        wait(0);
    }
    exit(0);
}

int main(int argc, char *argv[]){
    if (argc != 1){
        fprintf(STD_ERR, "Usage:primes\n");
        exit(1);
    }

    int pp[2];
    pipe(pp);
    if (fork() == 0){
        work(pp);
    }
    else {
        close(pp[READ_END]);
        for (int i = 2; i <= 35; i++){
            write(pp[WRITE_END], &i, 4);
        }
        close(pp[WRITE_END]);
        wait(0);
    }

    exit(0);
}

#find

  • fmtname函数的作用是以路径path来获取文件名,比如若路径path="a/bc/def",那么fmtname的返回值为def

  • find 函数参考user/ls.c的实现方法。DIRSIZ是文件名的最大长度,struct dirent为目录结构体,有两个属性:inum为目录下的文件数,name为目录名。struct stat为文件结构体 ,其中有用的属性是type,标识当前文件的类型(T_DIR 为目录,T_FILE为文件)。

  • open 打开需要查找的路径path并获取文件描述符fdfstat(int fd, struct stat*) 将文件描述符fd指向的文件信息存入到结构体指针stat中 。st保存了文件的信息,根据属性st.type判断文件的类型,如果当前文件类型为T_FILE则直接比较当前文件名与目标文件名是否相同,若相同,输出当前文件的路径。如果当前文件类型为T_DIR则说明当前文件是一个目录,用read 读取此目录下的所有文件,并对每一个文件递归调用find函数即可。

  • 注意:当目录下文件数为0时,即de.inum==0,不应递归调用。

  • 注意:当递归调用find 函数时,需要忽略....为当前目录,需要忽略,否则造成程序死循环,..为上一层目录,不应在上一层目录搜索。

程序代码如下:

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

#define STD_ERR 2

char *fmtname(char *path){
    static char buf[DIRSIZ + 1];
    char *p;
    for (p = path + strlen(path); p >= path && *p != '/'; p--)
        ;
    p++;
    if (strlen(p) >= DIRSIZ)
        return p;
    memmove(buf, p, strlen(p));
    buf[strlen(p)] = '\0';
    return buf;
}

void find(char *path, char *filename){
   char buf[512], *p;
   int fd;
   struct dirent de;
   struct stat st;

   if ((fd = open(path, 0)) < 0){
       fprintf(STD_ERR, "find: cannot open %s\n",path);
       return;
   }

   if (fstat(fd, &st) < 0){
       fprintf(STD_ERR, "find: cannot stat %s\n",path);
       close(fd);
       return;
   }

   switch(st.type){
   case T_FILE:
       if (strcmp(fmtname(path), filename) == 0){
           printf("%s\n",path);
       }
       break;
   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 (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0) continue;
           find(buf, filename);
       }
       break;
   }
   close(fd);
}

int main(int argc, char *argv[]){     
    if (argc != 3){
        fprintf(STD_ERR, "Usage: find <path> <filename>\n");
        exit(1);
    }

    find(argv[1], argv[2]);
    exit(0);
}

#xargs

  • xargs命令的作用是将标准输入转换成命令行参数,一般配合管道运算符|使用,管道运算符将标准输出转换成后一个命令的标准输入。
  • MAXARG定义在kernel/param.h中,是命令行参数的最大数目。需要执行的命令为argv[1]argv[0]="xargs")。
  • 创建一个大小为MAXARG的字符串数组params存储命令行参数,将argv中的命令行参数拷贝至params中。
  • 从标准输入循环读入命令行参数,若读入的字符为'\n'则说明当前行的参数已经读完,将读入到的命令行参数追加到params中。
  • fork创建子进程,子进程调用exec(filename, *argv)params为命令行参数执行argv[1]命令。父进程调用wait等待子进程结束。

程序代码如下:

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

#define STD_IN 0
#define STD_OUT 1
#define STD_ERR 2

int main(int argc, char *argv[]){
    if (argc < 2){
        fprintf(STD_ERR, "Usage: xargs<arguments>\n");
        exit(1);
    }
    if (argc + 1 > MAXARG){
        fprintf(STD_OUT, "Too many arguments!\n");
        exit(1);
    }

    char buf[512];
    char *params[MAXARG];
    int i;
    for (i = 1; i < argc; i++) params[i - 1] = argv[i];
    for (;;){
        i = 0;
        for (;;){
            int n = read(STD_IN, &buf[i], 1);
            if (n == 0 || buf[i] == '\n') break;
            i++;
        }
        if (i == 0) break;
        buf[i] = '\0';
        params[argc - 1] = buf;
        if (fork() == 0){
            exec(argv[1], params);
        }
        else {
            wait(0);
        }
    }
    exit(0);

}
posted @ 2021-10-02 12:54  Kyoz1  阅读(200)  评论(0)    收藏  举报
7