根据文档说明,我们需要把写的每个程序文件放在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 process
if (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 process
close(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 prime
int 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 neighbor
int right_p[2];
pipe(right_p);
// read the data from left neighbor
while (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 1024
int 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 args
buf[-- 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 argument
if (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 space
for (i = args_num; i < k; i ++ )
free(args[i]), args[i] = 0;
}
exit(0);
}