操作系统入门系列-MIT6.828(操作系统工程)学习条记(三)---- xv6初探与 ...

打印 上一主题 下一主题

主题 378|帖子 378|积分 1134

系列文章目录

操作系统入门系列-MIT6.S081(操作系统)学习条记(一)---- 操作系统介绍与接口示例
操作系统入门系列-MIT6.828(操作系统工程)学习条记(二)----课程实行环境搭建(wsl2+ubuntu+quem+xv6)
操作系统入门系列-MIT6.828(操作系统工程)学习条记(三)---- xv6初探与实行一(Lab: Xv6 and Unix utilities)
操作系统入门系列-MIT6.828(操作系统工程)学习条记(四)---- C语言与盘算机架构(Programming xv6 in C)


  

前言

MIT 6.828课程资料与进度计划表
完成第一节课的学习后,按照课程的计划进度表,应当阅读xv6文档的第一章节,以及完成实行1。
本文主要内容就是对xv6文档(2023年版)和实行(2023年版)的讲解
xv6英文文档—2023
xv6中文文档—2013
xv6实行1原文

一、xv6文档第一章

1. 引论

xv6操作系统提供 Unix 操作系统中的基本接口(由 Ken Thompson 和 Dennis Ritchie 引入),同时模拟 Unix 的内部计划,包括 BSD,Linux,Mac OS X,Solaris (甚至 Microsoft Windows 在某种程度上)都有雷同 Unix 的接口,理解 xv6 是理解这些操作系统的一个精良起点。
首先需要对操作系统有一个系统结构的大致概念,如下图:

如图所示,操作系统的核心就是kernel。kernel起到了桥梁的作用,连接各种应用程序和底层硬件资源。从软件编程者的角度来看,程序员就是利用各种kernel提供的系统调用函数,来实现各种各样的功能。xv6的系统调用函数如下图所示:

反面的实行就是需要调用这些系统调用函数来实现相应的功能,比如在Linux里面常用的find、xargs等命令的浅易版。
在之前的文章中已经将第一章的部分进行了讲解,详情见:
操作系统入门系列-MIT6.S081(操作系统)学习条记(一)---- 操作系统介绍与接口示例
二、实行一

1.启动xv6

详见xv6环境的设置和启动
操作系统入门系列-MIT6.828(操作系统工程)学习条记(二)----课程实行环境搭建(wsl2+ubuntu+quem+xv6)
(1)实行系统目录:
整个实行代码的文件目录如下,我们需要关注:
  1. 1.绿色的”grade-lab-util“可执行文件,它是测试机,用来测试你的实验代码正确性。
  2. 2.kernel文件夹,是xv6的kernel代码
  3. 3.Makefile是用来编译内核的,需要在这里添加参数,我们写的实验代码才能编译进去
  4. 4.user文件夹是我们添加自己实验代码的地方,也就是用户空间       
复制代码

(2)Makefile添加文件

(3)退出xv6
利用”ctrl+a“,后按x退出
(4)测试机打分
需要在lab目录中添加time.txt文件,里面写一个整数,代表你的实行花了多少小时。

2.实现休眠函数

(1)原题如下:

(2)大致内容为:
调用“系统调用函数——sleep()”(一次sleep()系统暂停一个tick),实现输入数字,系统暂停该数字次数的ticks。
参数错误时,要有错误打印
(3)代码如下:
  1. // 参考echo.c、grep.c、rm.c的头文件引用
  2. #include "kernel/types.h"//声明数据类型
  3. #include "kernel/stat.h"//声明文件数据结构
  4. #include "kernel/fcntl.h"//open函数的模式参数申明
  5. #include "user/user.h"//声明各种系统调用函数、以及有用的库函数
  6. int main(int argc, char *argv[])
  7. {
  8.     int number;
  9.     //参数数量不对,打印错误信息
  10.     //参数还有其他错误情况,此处仅考虑数量问题
  11.     if(argc <= 1)
  12.     {
  13.         fprintf(2, "usage: sleep number\n");
  14.         exit(1);
  15.     }
  16.     number = atoi(argv[1]);//字符串转整数
  17.     sleep(number);//系统调用
  18.     exit(0);
  19. }
复制代码
(4)效果如下:
利用官方提供的测试机程序,在lab目录下面运行"./grade-lab-util + 文件名"
  1. ./grade-lab-util sleep
复制代码
测试机自主进行测试,如下图:

(5)总结:
在做实行之前我们需要知道有哪些函数我们是可以调用的,可以检察文件:“user/user.h”
  1. struct stat;
  2. // system calls
  3. int fork(void);
  4. int exit(int) __attribute__((noreturn));
  5. int wait(int*);
  6. int pipe(int*);
  7. int write(int, const void*, int);
  8. int read(int, void*, int);
  9. int close(int);
  10. int kill(int);
  11. int exec(const char*, char**);
  12. int open(const char*, int);
  13. int mknod(const char*, short, short);
  14. int unlink(const char*);
  15. int fstat(int fd, struct stat*);
  16. int link(const char*, const char*);
  17. int mkdir(const char*);
  18. int chdir(const char*);
  19. int dup(int);
  20. int getpid(void);
  21. char* sbrk(int);
  22. int sleep(int);
  23. int uptime(void);
  24. // ulib.c
  25. int stat(const char*, struct stat*);
  26. char* strcpy(char*, const char*);
  27. void *memmove(void*, const void*, int);
  28. char* strchr(const char*, char c);
  29. int strcmp(const char*, const char*);
  30. void fprintf(int, const char*, ...);
  31. void printf(const char*, ...);
  32. char* gets(char*, int max);
  33. uint strlen(const char*);
  34. void* memset(void*, int, uint);
  35. void* malloc(uint);
  36. void free(void*);
  37. int atoi(const char*);
  38. int memcmp(const void *, const void *, uint);
  39. void *memcpy(void *, const void *, uint);
复制代码
3.实现父子进程pingpong打印

(1)原题如下:

(2)大致内容为:
实现过程:父进程向子进程发送ping,子进程打印该信息,后子进程向父进程发送pong,父进程再打印该信息
实现效果为:

(3)代码如下:
分为单管道实现和双管道实现
单管道实现:
  1. #include "kernel/types.h"
  2. #include "kernel/stat.h"
  3. #include "kernel/fcntl.h"
  4. #include "user/user.h"
  5. int main(int argc, char *argv[])
  6. {
  7.     int p[2];
  8.     char buf[512];//用来接收信息的buffer
  9.     pipe(p);//父 <=======> 子
  10.     //子进程执行if,父进程执行else
  11.     if(fork() == 0)
  12.     {
  13.         read(p[0], buf, sizeof(buf));//阻塞,直到父进程写入管道p内容,后读取父进程的信息
  14.         fprintf(1,"%d: received %s\n", getpid(), buf);//打印父进程的信息
  15.         write(p[1], "pong", 5);//使用p的写通道向父进程发送信息
  16.         close(p[0]);//关闭读通道
  17.         close(p[1]);//关闭写通道
  18.         exit(0);//子进程退出
  19.     }
  20.     else
  21.     {
  22.         write(p[1], "ping", 5);//父进程通过p的写通道向子进程发送信息
  23.         wait((int *)0);//等待子进程结束
  24.         read(p[0], buf, sizeof(buf));//读取p管道中子进程的信息
  25.         fprintf(1,"%d: received %s\n", getpid(), buf);//打印子进程的信息
  26.         close(p[0]);//关闭读通道
  27.         close(p[1]);//关闭写通道
  28.     }
  29.     exit(0);
  30. }
复制代码
双管道实现:
  1. #include "kernel/types.h"
  2. #include "kernel/stat.h"
  3. #include "kernel/fcntl.h"
  4. #include "user/user.h"
  5. int main(int argc, char *argv[])
  6. {
  7.     int p[2];
  8.     int p2[2];
  9.     char buf[512];//用来接收信息的buffer
  10.     //向内核申请两个管道
  11.     pipe(p);//父进程写,子进程读:父 ——————> 子
  12.     pipe(p2);//子进程写,父进程读:子 ——————> 父
  13.     //子进程执行if,父进程执行else
  14.     if(fork() == 0)
  15.     {
  16.         close(p[1]);//关闭p的写通道,以便于父进程写完可以直接读取
  17.         read(p[0], buf, sizeof(buf));//阻塞,直到父进程写入管道p内容,后读取父进程的信息
  18.         fprintf(1,"%d: received %s\n", getpid(), buf);//打印父进程的信息
  19.         close(p[0]);//关闭p的读通道
  20.         write(p2[1], "pong", 5);//使用p2的写通道向父进程发送信息
  21.         close(p2[1]);//写完关闭写通道
  22.         close(p2[0]);//关闭p2的读通道
  23.         exit(0);//子进程退出
  24.     }
  25.     else
  26.     {
  27.         write(p[1], "ping", 5);//父进程通过p的写通道向子进程发送信息
  28.         close(p[1]);//写完关闭写通道
  29.         close(p2[1]);//关闭p2的写通道,以便于子进程写完可以直接读取
  30.         read(p2[0], buf, sizeof(buf));//阻塞,直到子进程写入管道p2内容,读取子进程的信息
  31.         fprintf(1,"%d: received %s\n", getpid(), buf);//打印子进程的信息
  32.         close(p[0]);//关闭p的读通道
  33.         close(p2[0]);//关闭p2的读通道
  34.     }
  35.     exit(0);//结束程序
  36. }
复制代码
(4)效果如下:

(5)总结:

4.利用pipeline实现质数检测

(1)原题如下:

(2)大致内容为:
利用pipeline+多进程来检测35以内的质数检测
这个过程比力难想,但是在纸上推演一下流程就懂了,参考文献如下:Bell Labs and CSP Threads
参考伪代码:

参考图:

(3)代码如下:
  1. #include "kernel/types.h"
  2. #include "kernel/stat.h"
  3. #include "kernel/fcntl.h"
  4. #include "user/user.h"
  5. void child(int * p)
  6. {
  7.     int p1[2];
  8.     char num[35];
  9.     int prime;
  10.     int i;
  11.     int ret;
  12.     int number;
  13.     pipe(p1);
  14.     close(p[1]);
  15.     number = 0;
  16.     for(i=0;i<35;i++)
  17.     {
  18.         ret = read(p[0], num+i, 1);
  19.         if(ret == 0)
  20.         {
  21.             break;
  22.         }
  23.         number++;
  24.     }
  25.     if(number == 0)
  26.     {
  27.         exit(0);
  28.     }
  29.     prime = num[0];
  30.     printf("prime %d\n", prime);
  31.     close(p[0]);
  32.     if(fork() == 0)
  33.     {
  34.         child(p1);
  35.     }
  36.     else
  37.     {
  38.         close(p1[0]);
  39.         for(i=1;i<number;i++)
  40.         {
  41.             if((num[i]%prime)!=0)
  42.             {
  43.                 write(p1[1], num+i, 1);
  44.             }
  45.         }
  46.         close(p1[1]);
  47.         wait((int *)0);
  48.     }
  49.     exit(0);
  50. }
  51. int main()
  52. {
  53.     int prime = 2;
  54.     int p[2];
  55.     int i;
  56.     pipe(p);
  57.     if(fork() == 0)
  58.     {
  59.         child(p);
  60.     }
  61.     else
  62.     {
  63.         close(p[0]);
  64.         printf("prime %d\n", prime);
  65.         for(i=3;i<=35;i++)
  66.         {
  67.             if((i%prime)!=0)
  68.             {
  69.                 write(p[1], &i, 1);
  70.             }
  71.         }
  72.         close(p[1]);
  73.         wait((int *)0);
  74.     }
  75.     exit(0);
  76. }
复制代码
(4)效果如下:


(5)总结:
核心是算法题,载体是进程树之间利用pipeline进行读写控制
5.实现find函数(文件查找)

(1)原题如下:

(2)大致内容为:
实现目录下搜刮文件的功能,要求能够找到该文件夹目录下以及其子目录下的指定文件名,输出所有满足文件的路径。(不要求实现正则表达式)(!!!记得规避".“与”…"子文件)(如何访问文件夹可以参考文件ls.c的实现)
  1.   "  .  "  表示当前目录
  2.   "  ..  "  表示当前目录的上一级目录。
  3.   "  ./  "  表示当前目录下的某个文件或文件夹,视后面跟着的名字而定
  4.   "  ../  "   表示当前目录上一级目录的文件或文件夹,视后面跟着的名字而定。
复制代码

(3)代码如下:
  1. #include "kernel/types.h"
  2. #include "kernel/stat.h"
  3. #include "kernel/fcntl.h"
  4. #include "kernel/fs.h"
  5. #include "user/user.h"
  6. char* fmtname(char *path)
  7. {
  8.   char *p;
  9.   // Find first character after last slash.
  10.   for(p=path+strlen(path); p >= path && *p != '/'; p--)
  11.     ;
  12.   p++;
  13.   return p;
  14. }
  15. int find(char *name, char *p)
  16. {
  17.     int fd;
  18.     int path=0;
  19.     char buf[512];
  20.     struct dirent de;
  21.     struct stat st;
  22.     fd = open(p, O_RDONLY);
  23.     if(fd < 0)
  24.     {
  25.         fprintf(2, "fd: can not open %s\n");
  26.         exit(1);
  27.     }
  28.     strcpy(buf, p);
  29.     p = buf+strlen(buf);
  30.     *p++ = '/';
  31.     // printf("%d  %s\n", fd, buf);
  32.     while(read(fd, &de, sizeof(de)) == sizeof(de))
  33.     {
  34.         if(de.inum == 0)
  35.         {
  36.             continue;
  37.         }
  38.         memmove(p, de.name, DIRSIZ);
  39.         p[DIRSIZ] = 0;
  40.         // printf("%s\n", p);
  41.         if(stat(buf, &st) < 0)
  42.         {
  43.             printf("ls: cannot stat %s\n", buf);
  44.             continue;
  45.         }
  46.         // printf("%s %s %d\n", buf, fmtname(buf), st.type);
  47.         // printf("%d %d %d\n", strcmp(fmtname(buf), name), strcmp(fmtname(buf), "."), strcmp(".", "."));
  48.         if (st.type == T_FILE && (strcmp(fmtname(buf), name) == 0))
  49.         {
  50.             printf("%s\n", buf);
  51.             path = 1;
  52.         }
  53.         if (st.type == T_DIR && (strcmp(fmtname(buf), ".") != 0) && (strcmp(fmtname(buf), "..") != 0))
  54.         {
  55.             path = find(name, buf);
  56.         }   
  57.     }
  58.     return path;
  59. }
  60. int main(int argc, char *argv[])
  61. {
  62.     int path;
  63.     if(argc < 3)
  64.     {
  65.         fprintf(2, "fd: find [dir] [filename]\n");
  66.         exit(1);
  67.     }
  68.     path = find(argv[2], argv[1]);
  69.     if(path == 0)
  70.     {
  71.         fprintf(1, "can not find such file\n");
  72.     }
  73.     exit(0);
  74. }
复制代码
(4)效果如下:

(5)总结:
核心知识点是xv6系统下的文件结构体与文件夹结构体,利用递归方法进行实现。
6.实现xargs函数

(1)原题如下:

(2)大致内容为:
实现xargs命令,xargs命令很难简朴理解,发起先查一下命令的用法。
xargs 命令教程
(3)代码如下:
  1. #include "kernel/types.h"
  2. #include "kernel/stat.h"
  3. #include "kernel/fcntl.h"
  4. #include "user/user.h"
  5. #include "kernel/param.h"
  6. int getcmd(char *buf, char**arg, int arg_num)
  7. {
  8.     int i;
  9.     int j;
  10.     j = 0;
  11.     arg[arg_num] = (char *)malloc(30 * sizeof(char));
  12.     for(i=0; i<512; i++)
  13.     {
  14.         if((buf[i] == ' ') || (buf[i] == 10) || (buf[i] == '\0') || (buf[i] == '\n'))
  15.         {
  16.             // printf("%c %d\n", buf[i], buf[i]);
  17.             arg_num++;
  18.             if(buf[i] == 10)
  19.             {
  20.                 // printf("argmun: %d\n", arg_num);
  21.                 return arg_num;
  22.             }
  23.             if(buf[i] == '\0')
  24.             {
  25.                 // printf("argmun: %d\n", arg_num);
  26.                 return arg_num;
  27.             }
  28.             arg[arg_num] = (char *)malloc(30 * sizeof(char));
  29.             // printf("111\n");
  30.             // printf("%s %p kkk\n", arg[arg_num], arg[arg_num]);
  31.             j = 0;
  32.         }
  33.         else
  34.         {
  35.             // printf("%d %d %c %d\n", j, i, buf[i], arg[arg_num][j]);
  36.             arg[arg_num][j] = buf[i];
  37.             // printf("%d %c \n", j, arg[arg_num][j]);
  38.             j++;
  39.         }
  40.     }
  41.     return arg_num;
  42. }
  43. int main(int argc, char *argv[])
  44. {
  45.     char buf[512];
  46.     int num = 0;
  47.     int ret;
  48.     int cmd = 0;
  49.     char *arg[MAXARG];
  50.     int arg_num;
  51.     arg_num = argc - 1;
  52.    
  53.     int i = 0;
  54.     for(i=0; i<arg_num; i++)
  55.     {
  56.         arg[i] = argv[i+1];
  57.     }
  58.     if(argc < 2)
  59.     {
  60.         fprintf(2, "xarg: xarg [process] [args...]\n");
  61.         exit(1);
  62.     }
  63.     int j = 0;
  64.     while(1)
  65.     {
  66.         ret = read(0, buf+j, 1);
  67.         if(ret != 1)
  68.         {
  69.             break;
  70.         }
  71.         else if(buf[j] == '\n')
  72.         {
  73.             // printf("read number is : %d\n%s\n", num, buf);
  74.             j = 0;
  75.             num = 0;
  76.             cmd = getcmd(buf, arg, arg_num);
  77.             // printf("\n\n%d %d\n", cmd, arg_num);
  78.             // printf("%s\n%s\n%s\n%s\n", arg[0], arg[1], arg[2], arg[3]);
  79.             if(cmd == arg_num)
  80.             {
  81.                 printf("%d %d\n", cmd, arg_num);
  82.                 printf("no content of stdin\n");
  83.             }
  84.             else
  85.             {
  86.                 if(fork() == 0)
  87.                 {
  88.                     exec(argv[1], arg);
  89.                     fprintf(2, "exec failed\n");
  90.                     exit(1);
  91.                 }
  92.                 else
  93.                 {
  94.                     wait((int *)0);
  95.                 }
  96.             }
  97.         }
  98.         else
  99.         {
  100.             j++;
  101.             num++;
  102.         }
  103.     }
  104.    
  105.     for(i=0; i<cmd; i++)
  106.     {
  107.         free(arg[i]);
  108.     }
  109.     exit(0);
  110. }
复制代码
(4)效果如下:

(5)总结:
在实现字符串的单词分割的时候,对于指针的操作出现问题。
char **arg的arg[]元素应该动态分配空间,而不是讲temp数组的指针头直接赋值。

总结

课程每年大概有变化,本文按照最新的2023计划表进行学习

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

梦应逍遥

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表