本Lab包括五个简单程序的实现,初步熟悉系统调用接口。
笔者用时约6h(我太菜辣)
根据文档说明,我们需要把写的每个程序文件放在user
文件夹下,并且在MakeFile
的UPROGS
添加相应的程序名,这样子就可以在qemu中直接用命令行指令调用相应的程序啦。如下图所示。
sleep
sleep程序接收一个参数,调用库函数sleep
进行睡眠即可,没啥好说滴。
#include "kernel/types.h"
#include "user/user.h"int main(int argc, char* argv[])
{if (argc != 2) { fprintf(2, "usage: sleep time\n");exit(1);}sleep(atoi(argv[1]));exit(0);
}
pingpong
该程序的要求就是创建一个子进程,父进程为子进程发送一个字节,子进程接收到字节之后,打印ping,向父进程发送一个字节,父进程接收到该字节之后打印pong,程序结束。
做法就是打开两个管道,一个用于父进程写子进程读,另一个则相反。
#include "kernel/types.h"
#include "user/user.h"int main(int argc, char* argv[])
{if (argc != 1) { fprintf(2, "usage: pingpong\n");exit(1);}int p_father_to_child[2];int p_child_to_father[2];if (pipe(p_father_to_child) == -1 || pipe(p_child_to_father) == -1) {fprintf(2, "failed to open pipe\n");exit(1);}char buf = 'C';int exit_state = 0;// child processif (fork() == 0) {close(p_father_to_child[1]);close(p_child_to_father[0]);if (read(p_father_to_child[0], &buf, 1) != 1) {fprintf(2, "failed to read pipe in child\n");exit_state = 1;}fprintf(1, "%d: received ping\n", getpid());if (write(p_child_to_father[1], &buf, 1) != 1) {fprintf(2, "failed to write pipe in child\n");exit_state = 1;}exit(exit_state);}// father processclose(p_father_to_child[0]);close(p_child_to_father[1]);if (write(p_father_to_child[1], &buf, 1) != 1) {fprintf(2, "failed to write pipe in parent\n");exit_state = 1;}wait(0);if (read(p_child_to_father[0], &buf, 1) != 1) {fprintf(2, "failed to read pipe in parent\n");exit_state = 1;}fprintf(1, "%d: received pong\n", getpid());exit(exit_state);
}
primes
该题目要求我们写一个并发运行的素数筛,其思想大致如下:
建立若干个进程,前一个进程的输出为后一个进程的输入(用管道进行连接);
第一个进程没有输入,向管道直接输出2~35;第二个进程以第一个进程的输出作为输入(2~35),向终端输出2(素数),并筛掉2的倍数,向下一个管道输出非2倍数的数,以此类推,直到没有数输出为止即可。
#include "kernel/types.h"
#include "user/user.h"#define RD 0
#define WR 1
const uint INT_SIZE = sizeof(int);void getprime(int left_p[]) {// check if there has a primeint prime, i;if (read(left_p[RD], &prime, INT_SIZE) == 0) {close(left_p[RD]);exit(0);}fprintf(1, "prime %d\n", prime);// create the pipe to the right neighborint right_p[2];pipe(right_p);// read the data from left neighborwhile (read(left_p[RD], &i, INT_SIZE) != 0) {if (i % prime != 0) write(right_p[WR], &i, INT_SIZE);}close(left_p[RD]);close(right_p[WR]);if (fork() == 0) {getprime(right_p);} else {close(right_p[RD]);wait(0);}}int main(int argc, char* argv[])
{if (argc != 1) { fprintf(1, "usage: primes\n");exit(1);}int p[2], i;pipe(p);for (i = 2; i <= 35; i ++ ) {write(p[WR], &i, INT_SIZE);}close(p[WR]);if (fork() == 0) {getprime(p);} else {close(p[RD]);wait(0);}exit(0);
}
find
该题目要求写一个程序,在指定的目录中查找与给定文件名相同的文件,若查找到了则输出文件路径(从指定目录开始的路径)。
根据提示,查看user/ls.c
文件中ls
程序的实现,其中使用fstat
函数查看当前路径的类型(目录或文件)
类似的,我们实现的文件查找也使用fstat函数判断路径类型,如果当前路径是文件,则与给定的文件名比较(相等则输出);如果当前路径是目录,则遍历目录中的内容进行递归查找(注意不要对.
和..
递归)
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"void find_file(char* path, const char* filename) {struct dirent de;struct stat st;int fd;char buf[512], *p;if ((fd = open(path, 0)) < 0) {fprintf(2, "find: cannot open %s\n", path);exit(1);}if (fstat(fd, &st) < 0) {fprintf(2, "find: cannot stat %s\n", path);close(fd);exit(1);}if (st.type == T_FILE) {fprintf(2, "usgae: find \n");close(fd);exit(1);}if (strlen(path) + 1 + DIRSIZ + 1 > sizeof buf) {fprintf(2, "find: path too long\n");close(fd);exit(1);}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) {fprintf(2, "find: cannot stat %s\n", buf);continue;}if (st.type == T_FILE) {if (strcmp(p, filename) == 0) fprintf(1, "%s\n", buf);} else if (st.type == T_DIR && strcmp(p, ".") != 0 && strcmp(p, "..") != 0) {find_file(buf, filename);}}close(fd);
}int main(int argc, char* argv[])
{if (argc != 3) {fprintf(2, "usgae: find \n");exit(1);}find_file(argv[1], argv[2]);exit(0);
}
xargs
该题要求实现一个程序,每从标准输入中读取一行,就调用一次命令(参数是命令)。
这个其实不难(但是我一开始把buf开大了,调了一个小时),大概就是从标准输入读取一行,然后分割成若干个命令行参数,创建一个子进程后使用ecex
执行命令即可。
#include "kernel/types.h"
#include "kernel/param.h"
#include "user/user.h"#define LINE 1024int read_line(int fd, char* buf, int len) {char* p = buf;int i = 0;while (i < len - 1) {int res = read(fd, p, 1);if (res == -1) return -1;else if (res == 0)break;p ++;i ++;if (*(p - 1) == '\n') break;}*p = 0;return i;
}int main(int argc, char* argv[])
{if (argc == 1) {fprintf(2, "usage: xargs command [args]\n");exit(1);}char* args[MAXARG + 1];memset(args, 0, sizeof args);int args_num = argc - 1;int i, j, k;for (i = 1; i < argc; i ++ )args[i - 1] = argv[i]; char buf[LINE];int res;while ( (res = read_line(0, buf, LINE)) != 0 ) {if (res == -1) {fprintf(2, "xargs: cannot read line from standard input\n");exit(1);}if (buf[res - 1] != '\n' && res == LINE) {fprintf(2, "xargs: line too long\n");exit(1);}// split the argsbuf[-- res] = '\0';i = 0, k = args_num;while (i < res) {if (buf[i] == ' ') i ++;else {j = i;while (j < res && buf[j] != ' ')j ++;// buf[i ~ j - 1] is an argumentif (k == MAXARG) {fprintf(2, "xargs: too many arguments\n");}args[k] = (char*)malloc(sizeof(char) * (j - i + 1));memcpy(args[k], buf + i, j - i);args[k][j - i + 1] = '\0';k ++;i = j;}}if (fork() == 0) {exec(args[0], args);fprintf(2, "xargs: command %s not found\n", args[0]);}wait(0);// free the malloc spacefor (i = args_num; i < k; i ++ ) free(args[i]), args[i] = 0;}exit(0);
}