拖了这么久,终于稍微有时间填坑了。今天介绍xv6的第一个实验util。代码在这里。废话不多说,我们开始吧。
参考我的上一篇博客OS实验xv6 6.S081 开坑,这里给出了一些有用的参考资料。
这是MIT Lab1的官方指导书Lab1 Utilities
Lab1要求我们实现几个Unix中常用的工具函数:
这个任务要求利用调用系统调用函数sleep完成sleep n。。。这也太简单了吧。。。
但是这里还需要强调一下,在Hints中说得很清楚了,我们调用的是syscall的sleep,这时xv6提供的,而不是Linux环境中的< sys >中的sleep。
在user文件夹下新建sleep.c,判断一下输入格式(纯粹为了加多代码量),调用一下sleep即可。注意导的包,user.h为xv6提供的系统函数,types.h为其提供的变量类型
#include "kernel/types.h"
#include "user/user.h"
const int duration_pos = 1;
typedef enum {wrong_char, success_parse, toomany_char} cmd_parse;
cmd_parse parse_cmd(int argc, char** argv);
int
main(int argc, char** argv){
//printf("%d, %s, %s \n",argc, argv[0], argv[1]);
if(argc == 1){
printf("Please enter the parameters!");
exit();
}
else{
cmd_parse parse_result;
parse_result = parse_cmd(argc, argv);
if(parse_result == toomany_char){
printf("Too many args! \n");
exit();
}
else if(parse_result == wrong_char){
printf("Cannot input alphabet, number only \n");
exit();
}
else{
int duration = atoi(argv[duration_pos]);
//printf("Sleeping %f", duration / 10.0);
sleep(duration);
exit();
}
}
}
cmd_parse
parse_cmd(int argc, char** argv){
if(argc > 2){
return toomany_char;
}
else {
int i = 0;
while (argv[duration_pos][i] != '\0')
{
/* code */
if(!('0' <= argv[duration_pos][i] && argv[duration_pos][i] <= '9')){
return wrong_char;
}
i++;
}
}
return success_parse;
}
本任务要求实现利用管道实现进程间的通信:父进程发送ping,子进程收到后发送pong,父进程收到后将其打印出来
Hints 1:利用pipe()函数创建管道,pipe()函数接收一个长度为2的数组,数组下标0为读端、1为写端;
Hints 2:利用fork()函数创建新的进程;
Hints 3:利用read、write函数读写管道;
#include "kernel/types.h"
#include "user/user.h"
int
main(int argc, char** argv ){
int pid;
int parent_fd[2];
int child_fd[2];
char buf[20];
//为父子进程建立管道
pipe(child_fd);
pipe(parent_fd);
// Child Progress
if((pid = fork()) == 0){
close(parent_fd[1]);
read(parent_fd[0],buf, 4);
printf("%d: received %s\n",getpid(), buf);
close(child_fd[0]);
write(child_fd[1], "pong", sizeof(buf));
exit();
}
// Parent Progress
else{
close(parent_fd[0]);
write(parent_fd[1], "ping",4);
close(child_fd[1]);
read(child_fd[0], buf, sizeof(buf));
printf("%d: received %s\n", getpid(), buf);
exit();
}
}
本任务要求完成质数筛选器
具体是什么意思呢?
它要求用fork和pipe实现:输入为2 ~ 35,输出为2 ~ 35间的所有质数,例如:2、3、5、7等。
算法比较简单,例如,第一次我们将2 ~ 35给到一个进程,这个进程发现给到的第一个数为2,则输出2,然后将不能被2除尽的数(3、5、7、9……)发送给下一个进程,下一个进程发现给到的第一个数为3,则输出3,然后将不能被3除尽的数(5、7……)发送给下一个进程……以此类推。我们可以通过下图说明这个过程。
好了,递归创建管道貌似就可以做了。
#include "kernel/types.h"
#include "user.h"
void generate_nums();
void send_primes(int pd[], int infos[], int infoslen);
void check_pd(int pd[], int len);
void process(int pd[]);
int
main(int argc, char** argv){
//声明管道
int pd[2]; //pipe descriper
//创建管道
pipe(pd);
//check_pd(pd, 2);
int pid;
//Child Process
if((pid = fork()) == 0){
process(pd);
exit();
}
//Parent Process
else{
int nums[34];
generate_nums(nums);
send_primes(pd, nums, 34);
//sleep(10);
exit();
}
}
void process(int pd[]){
int p;
int n;
int len;
int pid;
int pd_child[2];
int infos[34];
int infos_i = 0;
pipe(pd_child);
//check_pd(pd_child, 2);
close(pd[1]);
len = read(pd[0], &p, sizeof(p));
printf("prime %d\n", p);
while(len != 0) {
len = read(pd[0], &n, sizeof(n));
if(n % p != 0){
*(infos + infos_i) = n;
infos_i++;
}
}
close(pd[0]);
if(infos_i == 0) {
exit();
}
// Child Process
if((pid = fork()) == 0){
process(pd_child);
}
// Parent Process
else{
send_primes(pd_child, infos, infos_i);
}
}
void
generate_nums(int nums[34]){
int i = 0;
for(int count = 2; count <= 35; count++){
nums[i] = count;
i++;
}
}
void
check_pd(int pd[], int len){
printf("pd:\n");
for(int i = 0; i < len; i++){
printf("%d \n", pd[i]);
}
}
void
send_primes(int pd[], int infos[], int infoslen){
int info;
close(pd[0]);
for(int i = 0; i < infoslen; i++){
info = infos[i];
write(pd[1],&info,sizeof(info));
}
}
目标:写一个find函数
find函数的作用是什么呢?
其基本用法为 find arg1 arg2, 即在目录arg1下找到arg2。怎么写呢?完全不知道,因为这和file system挂钩。但是,MIT给了我们一个Hint:Look at user/ls.c to see how to read directories。于是就照着ls来写吧。这里有一个红利Bonus,就是可以直接copy grep.c的正则匹配代码,这样就能更快地进行文件的查找。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
//Copy from Grep.c
char buf[1024];
int match(char*, char*);
int matchhere(char*, char*);
int matchstar(int, char*, char*);
int
match(char *re, char *text)
{
if(re[0] == '^')
return matchhere(re+1, text);
do{ // must look at empty string
if(matchhere(re, text))
return 1;
}while(*text++ != '\0');
return 0;
}
// matchhere: search for re at beginning of text
int matchhere(char *re, char *text)
{
if(re[0] == '\0')
return 1;
if(re[1] == '*')
return matchstar(re[0], re+2, text);
if(re[0] == '$' && re[1] == '\0')
return *text == '\0';
if(*text!='\0' && (re[0]=='.' || re[0]==*text))
return matchhere(re+1, text+1);
return 0;
}
// matchstar: search for c*re at beginning of text
int matchstar(int c, char *re, char *text)
{
do{ // a * matches zero or more instances
if(matchhere(re, text))
return 1;
}while(*text!='\0' && (*text++==c || c=='.'));
return 0;
}
/*
find.c
*/
char* fmtname(char *path);
void find(char *path, char *re);
int
main(int argc, char** argv){
if(argc < 2){
printf("Parameters are not enough\n");
}
else{
//在路径path下递归搜索文件
find(argv[1], argv[2]);
}
exit();
}
// 对ls中的fmtname,去掉了空白字符串
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++;
// printf("len of p: %d\n", strlen(p));
if(strlen(p) >= DIRSIZ)
return p;
memset(buf, 0, sizeof(buf));
memmove(buf, p, strlen(p));
//memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
return buf;
}
void
find(char *path, char *re){
// printf("---------------------------------------------\n");
// printf("path:%s\n", path);
// printf("fmtpath:%s\n",fmtname(path));
// printf("re:%s\n", re);
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:
//printf("File re: %s, fmtpath: %s\n", re, fmtname(path));
if(match(re, fmtname(path)))
printf("%s\n", path);
break;
//printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
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;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("find: cannot stat %s\n", buf);
continue;
}
// printf("%s, %d\n",fmtname(buf), strlen(fmtname(buf)));
// printf("%s\n",buf);
// // printf("%d\n",strcmp(".", fmtname(buf)));
// // printf("%d\n",strcmp("..", fmtname(buf)));
char* lstname = fmtname(buf);
// printf("lstname: %s\n", lstname);
if(strcmp(".", lstname) == 0 || strcmp("..", lstname) == 0){
//printf("%s %d %d %d\n", buf, st.type, st.ino, st.size);
continue;
}
else{
//printf("deep: %s %d %d %d\n", buf, st.type, st.ino, st.size);
find(buf, re);
}
//printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
}
break;
}
close(fd);
}
简而言之,多参数实现吧,当用户输入ctrl+d时停止参数的输入。
这里要说明的是:对于输入的命令,我们要用exec执行,其中exec接收两个参数,第一个参数为命令cmd,第二个参数为一个数组,该数组的格式必须为{cmd, “arg1”, “arg2”, …, 0}
代码实现并不难,关键在于要理解xargs的用法。另外,在c语言中,输入ctrl+d后,buf长度为0,可据此来完成对ctrl+d的判断。另外,根据MIT的Hint:kernel/param.h declares MAXARG, which may be useful if you need to declare an argv.,我们可以声明一个数组来保存用户输入的每一个参数。好了,可以写代码了。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user.h"
#include "kernel/fs.h"
#include "kernel/param.h"
#define CMDSTYLE 2
char *cutoffinput(char *buf);
void substring(char s[], char sub[], int pos, int len);
void printargs(char* args[MAXARG][CMDSTYLE], int args_num);
/* 打印参数 */
void
printargs(char* args[MAXARG][CMDSTYLE], int args_num){
for (int i = 0; i < args_num; i++)
{
/* code */
printf("--------------args %d:--------------\n", i + 1);
printf("cmd: %s, arg: %s, argslen: %d \n", args[i][0], args[i][1], strlen(args[i][1]));
}
}
/* 子串 */
void
substring(char s[], char *sub, int pos, int len) {
int c = 0;
while (c < len) {
*(sub + c) = s[pos+c];
c++;
}
*(sub + c) = '\0';
}
/* 截断 '\n' */
char*
cutoffinput(char *buf){
/* 记得要为char *新分配一片地址空间,否则编译器默认指向同一片地址 */
if(strlen(buf) > 1 && buf[strlen(buf) - 1] == '\n'){
char *subbuff = (char*)malloc(sizeof(char) * (strlen(buf) - 1));
substring(buf, subbuff, 0, strlen(buf) - 1);
return subbuff;
}
else
{
char *subbuff = (char*)malloc(sizeof(char) * strlen(buf));
strcpy(subbuff, buf);
return subbuff;
}
}
int
main(int argc, char *argv[])
{
/* code */
int pid;
char buf[MAXPATH];
char *args[MAXARG];
char *cmd;
/* 默认命令为echo */
if(argc == 1){
cmd = "echo";
}
else{
cmd = argv[1];
}
/* 计数器 */
int args_num = 0;
while (1)
{
memset(buf, 0, sizeof(buf));
gets(buf, MAXPATH);
/* printf("buf:%s",buf); */
char *arg = cutoffinput(buf);
/* printf("xargs:gets arg: %s, arglen: %d\n", arg, strlen(arg)); */
/* press ctrl + D */
if(strlen(arg) == 0 || args_num >= MAXARG){
break;
}
args[args_num]= arg;
args_num++;
}
/*
printargs(args, args_num);
printf("Break Here\n");
*/
/* 填充exec需要执行的命令至argv2exec */
int argstartpos = 1;
char *argv2exec[MAXARG];
argv2exec[0] = cmd;
for (; argstartpos < argc; argstartpos++)
{
argv2exec[argstartpos] = argv[argstartpos - 2];
}
for (int i = 0; i < args_num; i++)
{
/* code */
argv2exec[i + argstartpos] = args[i];
}
argv2exec[args_num + argstartpos] = 0;
/* 运行cmd */
if((pid = fork()) == 0){
exec(cmd, argv2exec);
}
else
{
/* code */
wait();
}
exit();
}