开始踩坑 MIT 6.S081 自己做操作系统,Lab1

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

sleep

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

int main(int argc, char *argv[])
{
if (argc != 2)
{
        fprintf(2, "error input\n");
        exit(1);
}
int sec = atoi(argv[1]);
sleep(sec);
exit(0);
}
~                                                                                                                                                                                    
~                                   

pingpong

这个涉及的东西比较多, 要解决问题,得先明白问题是什么,

pipe 命令,用来新建一个管道,那么管道到底是什么?  

管道(pipe)是进程用来通讯的共享内存区域。一个进程往管道中写入信息,而其它的进程可以从管道中读出信息。

匿名管道是不命名的,它最初用于在本地系统中父进程与它启动的子进程之间的通信。

参考 https://zhuanlan.zhihu.com/p/58489873

关于dup,参考 https://www.cnblogs.com/pengdonglin137/p/3286627.html

看这段代码,我tm看了好久都没看懂

int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p); //创建一个新的管道并且将读写描述符记录在数组 p 中
if(fork() == 0) {
//unix里面,一切皆文件,所以stdin stdout都可以看成是文件
	close(0); //关闭stdin, 对应文件描述符0,这样0就空出来了
	dup(p[0]); // 把p[0]拷贝到0,因为0已经关闭了(或者说0号位置空缺),这样wc就从管道的p[0]读取数据,而不是stdin
	close(p[0]);
	close(p[1]);
	exec("/bin/wc", argv);
} else {
	write(p[1], "hello world\n", 12);
	close(p[0]);
	close(p[1]);
}

所以管道怎么写我想想看

管道读数据需要重定向输入流,那么可以想到如果要输出数据,就必须重定向输出流

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

int main(int argc, char *argv[]) {
        int p[2];
        int q[2];
    pipe(p);
    pipe(q);
    if(fork() == 0) {
           close(p[1]);// 关闭管道p[1] 为什么要关掉p1? 因为我们要读取p[0],如果不关闭p[1],读写端口同时开启,如果这个时候p[1]又被写入信息,那么会怎么样
           close(q[0]);// 为什么要关掉q0 我的理解是,如果要写入到q1,避免一写入就被读取,那么我们就要关闭q0,读写同时开启不是个好事
           write(q[1], "received pong\n", 16 );
            char buf[1024] = {"\0"};
            read(p[0], buf, 16); //读取父进程发过来的消息,而父进程是通过write(p[1])来写入到p缓冲区的,然后在读端口被子进程读到
            printf("%d: %s",getpid(),buf);
           close(p[0]);
           close(p[1]);
        exit(0);
    } else {
           close(p[0]);
           close(q[1]);
           write(p[1], "received ping\n", 16 );
            char buf[1024] = {"\0"};
            read(q[0], buf, 16);
            printf("%d: %s",getpid(),buf);
           close(q[0]);
           close(q[1]);
        exit(0);
    }

    exit(0);
}
~                            

如果删掉 

close(p[1]);
close(q[0]);

 这4行,那么后面会有并发问题,如下图最下,但删掉了测试还是能通过的

find

主要参考ls,需要修改的地方是传入的参数由一个变为两个,以及控制buf结束符'\0'的位置,还有文件名之间的比较

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

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++;

  // Return blank-padded name.
  memmove(buf, p, strlen(p) + 1);
  return buf;
}

void
find(char *path, char* findName)
{
  char buf[512], *p;
  int fd;
  struct dirent de;
/**
struct dirent   
{   
  long d_ino; //inode number 索引节点号 
     
    off_t d_off; /* offset to this dirent 在目录文件中的偏移  
     
    unsigned short d_reclen; // length of this d_name 文件名长  
     
    unsigned char d_type; // the type of d_name 文件类型 
     
    char d_name [NAME_MAX+1]; // file name (null-terminated) 文件名,最长255字符   
}  
*/
  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:// 如果是文件类型,那么比较,文件名是否匹配,匹配则输出
    if(strcmp(fmtname(path), findName) == 0)
      printf("%s\n", path);
//    else
  //    printf("path:%s %d, findName:%s %d\n", fmtname(path),strlen(fmtname(path)), findName, strlen(findName));
    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++ = '/';//buf是一个绝对路径,p是一个文件名,并通过加"/"前缀拼接在buf的后面
    while(read(fd, &de, sizeof(de)) == sizeof(de)){
      if(de.inum == 0) {
        continue;
        }
      memmove(p, de.name, DIRSIZ);//memmove, 把de.name信息复制p,其中de.name是char name[255],代表文件名
      p[strlen(de.name)] = 0; // 设置文件名结束符
        if(strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0) {
                continue;
        }
        find(buf, findName);
    }
    break;
  }
  close(fd);
}

int
main(int argc, char *argv[])
{

  if(argc < 3){
        printf("error argc num");
    exit(0);
  }
  find(argv[1], argv[2]);
  exit(0);
}
           

 xargs

 cat url-list.txt | xargs wget -c

 上面的代码就是通过wget下载url-list.txt里面所有的url链接内容

xargs是什么,就是能接受管道的输出作为命令行参数,运行命令,所以需要调用exec,并把输出存入到参数中,如何读到标准输出(其实输出以后,我们应该从标准输入读),标准输出是什么,也相当于一个管道,我们读的时候read(p[0]),所以我们应该read(0,), 注意不是read(1)

 管道两边是如何达成通信: 参考https://blog.csdn.net/Leafage_M/article/details/78893822 这也解答了我关于读和写阻塞的疑惑

exec执行的其实是 其后面跟着的命令,为什么exec(char[] cmd, char * args[])的时候, args[0]不生效? 哦 好像exec的时候args[0]默认是"exec",它自己,所以你改了也没用

#include "kernel/types.h" 
#include "user/user.h" 
 
int main(int argc, char *argv[]) { 
    int p[2];  
    pipe(p); 
    char buf[1024] = {"\0"}; 
    char* params[100]; // 用来保存命令行参数
    int index = 0; 
    int k; 
    for(int i=1; i< argc;i++){ 
         params[index++] = argv[i];   
        } 
     while((k = read(0, buf, 1024)) > 0 ) { //0代表的是管道的0,也就是我们从管道读取,而不是标准输入stdin,比如 echo ab | xargs echo cd,我们读取0其实读到的是 ab
        char tmp[1024] = {"\0"}; 
        params[index] = tmp;  
       for(int i=0; i< strlen(buf); i++) { 
         if(buf[i] == '\n') { 
                if (fork() ==0) { //创建子线程执行命令
                   exec(argv[1], params);       
                } 
                wait(0);  //父线程等待子线程执行完
         } 
         else { 
          tmp[i] = buf[i]; //读取左边管道的输出作为输入 比如 echo abc | xargs echo def 读取的就是abc
         } 
//       printf("buf %d: %c\n",i,  buf[i]); 
       } 
     } 
    exit(0); 
}
              

exec为啥要把echo传进去,然后又只用params的从1开始的元素,好像是exec执行的时候,比如

exec(echo, params),其实是这样的exec(echo,["echo", "abc"]),这样其实执行的就是echo 加后面的"abc",至于为啥要这么设置,而不是直接传一个["abc"]进去,我也不知道

我草,我发现把问题在博客中写出来,问题就解决了?以后遇见问题,就写博客。。。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值