实习了三个多月,期间发生了很多事情。但是总的来说也是迈出了第一步。但是由于我本身太菜了,开始的又很晚,所以找暑期实习的过程又十分的坎坷。在6月初的时候,研究了一下MPSC和MPMC的无锁队列,对CAS和false sharing之间有了更多了解,也让我对操作系统产生了更多的兴趣。因此在搜索的过程中,发现了MIT 6.S081这门课程,打算了解一下,因此打算一边找实习一边学。
定一个小目标,一个月的时间做完相关实验。废话不多说let's go
说实话,我一直认为我还是比较了解linux系统的,就算不是使用很多的原理性的问题,我在使用编写C++程序的时候,尤其是简单程序的优化的时候,往往会想能不能减少系统调用从而减少用户态到内核态之间的转换来降低系统开销。但是一些实验依旧给我带来了很多麻烦,尤其是xargs这个实验,之前很少使用,但是在实现的时候给我造成了一些麻烦。
sleep
easy难度,本质上来说,就是调用实现好的sleep做一层输入输出,类似于用户封装,我是这么理解的。很简单。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"int main(int argc, char *argv[])
{if (argc != 2){fprintf(2, "something error in sleep");exit(1);}sleep(atoi(argv[1]));exit(0);
}
pingpong
同样也是easy难度,是对于pipe的运用。也很简单。只要涉及到一些并发编程都会涉及到,毕竟不可能都用shm吧,哈哈
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"int main()
{int pid, fd1[2], fd2[2];if (pipe(fd1) < 0){fprintf(2, "someting error in pingppong(pipe1 error)");}if (pipe(fd2) < 0){fprintf(2, "someting error in pingppong(pipe2 error)");}pid = fork();if (pid < 0){fprintf(2, "someting error in pingppong(fork error)");}else if (pid == 0){close(fd1[0]);close(fd2[1]);char rec;read(fd2[0], &rec, 1);fprintf(1, "%d: received ping\n", getpid());write(fd1[1], &rec, 1);exit(0);}else{close(fd1[1]);close(fd2[0]);char send = 'a';write(fd2[1], &send, 1);wait(&pid);char rec;read(fd1[0], &rec, 1);if (rec != send){fprintf(2, "something error in pingpong : receive char is not equal send char\n");exit(1);}fprintf(1, "%d: received pong\n", getpid());}exit(0);
}
primes
标注的是moderate/hard,但是难度实际上还可以。本质上就是一个递归算法,终止条件就是read() == 0,也就是父进程的fd关闭了。需要注意判断条件,要不然会无限递归
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"void mkpipe(int fdR)
{int mode = 0;if (read(fdR, &mode, 4) == 0){close(fdR);exit(0);}fprintf(1, "prime %d\n", mode);int cfd[2], pid;pipe(cfd);pid = fork();if (pid < 0){fprintf(2, "something error int primes , which pid is %d\n", getpid());exit(1);}else if (pid == 0){close(cfd[1]);mkpipe(cfd[0]);}else{close(cfd[0]);int num;while (read(fdR, &num, 4) != 0){if (num % mode != 0){write(cfd[1], &num, 4);}}close(cfd[1]);wait(0);exit(0);}
}int main()
{int fd[2], pid;pipe(fd);pid = fork();if (pid < 0){fprintf(2, "something error int primes , which pid is %d\n", getpid());exit(1);}else if (pid == 0){close(fd[1]);mkpipe(fd[0]);}else{close(fd[0]);for (int i = 2; i <= 35; ++i){write(fd[1], &i, 4);}close(fd[1]);wait(0);}exit(0);
}
find
这个我个人认为还是有些难度的,但是有一点好处就是给出了ls的实现,实际上如果仿照ls的话还是比较好实现的。需要注意的是,如果当前的st.type依旧为T_DIR需要继续递归调用,要不然的话无法深层递归目录,会导致用例过去不,只停留在当前路径中。
#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;for (p = path + strlen(path); p >= path && *p != '/'; --p){}++p;int len = strlen(p);if (len >= DIRSIZ){return p;}memmove(buf, p, len);buf[len] = '\0';return buf;
}void find(char *path, char *name)
{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:if (!strcmp(name, fmtname(path))){fprintf(1, "%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;if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)continue;memmove(p, de.name, DIRSIZ);p[DIRSIZ] = 0;if (stat(buf, &st) < 0){printf("find: cannot stat %s\n", buf);continue;}if (!strcmp(name, fmtname(buf))){fprintf(1, "%s\n", buf);}if (st.type == T_DIR){find(buf, name);}}break;}close(fd);
}int main(int argc, char *argv[])
{if (argc < 2){fprintf(2, "usage: find [dir] filename\n");exit(1);}char *name = argv[argc - 1];for (int i = 1; i < argc - 1; i++){find(argv[i], name);}if (argc == 2){find(".", name);}exit(0);
}
xargs
这个我之间很少接触,我的理解就是相比于直接用管道 | , | xargs 的好处就是可以将管道前的执行结果以参数的形式传递给后面的函数,因为直接 | 的话 ,本质上是直接从0来读前一个命令的执行结果,这样的话会有一个问题,对于像cat这类参数来说,不支持从stdin处读入数据作为自己的参数,xargs解决了这个问题,就比如 find xxx | cat 并不会读出对应文件中的数据而是会单纯打印路径。
还有一个,就是注意要循环读,因为find比较慢。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#define MAXARGS 10
#define BUFSZ 512int main(int argc, char *argv[])
{char *buf[MAXARGS];int arg = 0;for (int i = 1; i < argc; ++i){buf[arg] = argv[i];++arg;}char args[BUFSZ];int len = 0, n;while ((n = read(0, args + len, BUFSZ)) != 0){len += n;}char *p = args;for (int i = 0; i < len; ++i){if (args[i] == '\n'){int pid = fork();if (pid < 0){fprintf(2, "something error in xargs : fork() failed");exit(1);}else if (pid == 0){int xarg = arg;buf[xarg] = p;args[i] = 0;buf[++xarg] = 0;exec(buf[0], buf);exit(0);}else if (pid > 0){p = &args[i + 1];wait(0);}}}exit(0);
}