lab1 Xv6 and Unix utilities
lab1 Xv6 and Unix utilities
实验结果

相关实验
个人觉得lab1中primes实验是最难的,于是选择先讲解primes,其余按顺序讲解
primes
解决这个问题之前,首先要明确几个结论。1:pipe里有缓冲区的,即write一定的数据后在read,write的数据也是不会丢失,缓冲区的大小跟xv6的具体实现有关。2:针对hints中提到的read returns zero when the write-side of a pipe is closed,但你read要返回0,即所有fork的进程中对应的p[1]都要关闭,read(p[0], &xx, 1)才会返回0,否则会挂起。如下图。

想怎么写之前,应该去看下图所在网页。原本我一头雾水,直到看到下图以后,我采逐渐有了编程思路。

下面说一下,我编程中的关键点。
要有几个pipe,把上图中的一个方框当做进程,左框到右框的数据传输则通过数据传输。2-35个数据,每个数据都有一个pipe,想想就几乎不可能,否则xv6为啥还要提示记得关闭文件描述符,避免文件描述符导致资源耗尽。从小到大,一个pipe呢,似乎可行,但假设你没用其他同步机制,这里就有一个问题,你从pipe[0]读,那么读到后并经过筛选后的数据,如果你直接继续写进pipe[1]的话,原本要写入下一个进程的数据,被你用到这个进程里,不行。如果先存起来,等读完以后再写下一个,你要用什么机制保证,你上一个进程读完以后,下一个进程才会读p[0]。两个pipe就可以解决这样的问题,进程从左边pipe读数据,再把筛选后的数据写到右边pipe,就不会造成上面的问题了。
当然,你观察上图,会发现上一个进程的右边pipe是然是下一个进程的左边pipe。,这似乎会造成编程上的一些混乱,我的解决办法是:建立的4个int xx[2]和一个processindex,建立起两个实际的管道,两个数组 奇数进程的右边pipe是偶数进程的左边pipe。具体看下面代码。
哪个时候关闭文件描述符,你发现第一个或者第二个的子进程和孙子进程,每个管道的读写端都需要,那么要关闭管道的读写端,则应该在fork()之后。
递归(循环)的终止条件,read()要返回0,那么就要关闭管道另一端所有的p[1]。而且从第一次看,如果是再fork()之前read,由于上面的结论,read()的进程只会挂起。如果是在fork()之后对读,可能可以,这个我没仔细考虑,我最终选择的是用上一个进程传递给该进程的数据数量来当终止条件。如果为0,则是最后一个进程了,应该close所有文件描述符然后exit(0);
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
static void PipeExchange(int* Lef, int* Right, int* p1, int* p2, int ProcessIndex);
static void ExchangeNumber(int* a, int* b);
int main(int argc, char* argv[])
{
int LeftPipe[2];
int RightPipe[2];
int PipeTra1[2];//实际建立的管道
int PipeTra2[2];//实际建立的管道
int ProcessIndex;//第一个进程右边的管道是第二个进程左边的管道
int PreSendNum;
int NowSendNum;
int ExChangeNumber;//单个发,通过前面的发送数量确定结束
int ProcessPrimeNumber;
if (pipe(PipeTra1) < 0)
{
fprintf(2, "pipe 1 error");
exit(1);
}
if (pipe(PipeTra2) < 0)
{
fprintf(2, "pipe 2 error");
exit(1);
}
ProcessIndex = 1;
PipeExchange(&LeftPipe[0], &RightPipe[0], &PipeTra1[0], &PipeTra2[0], ProcessIndex);
PreSendNum = (35 - 2 + 1);
NowSendNum = 0;
for(int i = 2; i <= 35; i++)
{
ExChangeNumber = i;
write(LeftPipe[1], &ExChangeNumber, 4);
}
while(1)
{
PipeExchange(&LeftPipe[0], &RightPipe[0], &PipeTra1[0], &PipeTra2[0], ProcessIndex);
if (PreSendNum > 0 )
{
read(LeftPipe[0], &ExChangeNumber, 4);
ProcessPrimeNumber = ExChangeNumber;
fprintf(1, "prime %d\n", ProcessPrimeNumber);
PreSendNum = PreSendNum - 1;
NowSendNum = 0;
for(; PreSendNum > 0; PreSendNum--)//循环结束代表已经全部接受完上一个进程的数据
{
read(LeftPipe[0], &ExChangeNumber, 4);
if ((ExChangeNumber % ProcessPrimeNumber) != 0)
{
write(RightPipe[1], &ExChangeNumber, 4);
NowSendNum = NowSendNum + 1;
}
}
}
else// the last process, no data through pipe to it
{
close(LeftPipe[0]);
close(LeftPipe[1]);
close(RightPipe[0]);
close(RightPipe[1]);
exit(0);
}
if (fork() > 0)
{
close(LeftPipe[0]);
close(LeftPipe[1]);
close(RightPipe[0]);
close(RightPipe[1]);
wait(0);
exit(0);//跳出了循环
}
else
{
ProcessIndex = ProcessIndex + 1;
PreSendNum = 0;
ExchangeNumber(&PreSendNum, &NowSendNum);
}
}
}
static void ExchangeNumber(int* a, int* b)
{
int Temp = 0;
Temp = *a;
*a = *b;
*b = Temp;
}
/**
* @brief 如page的图片所示,第一个进程的右边pipe是第二个进程的左pipe
**/
static void PipeExchange(int* Lef, int* Right, int* p1, int* p2, int ProcessIndex)
{
if ((ProcessIndex % 2) != 0)
{
*(Lef) = *(p1);
*(Lef + 1) = *(p1 + 1);
*(Right ) = *(p2);
*(Right + 1) = *(p2 + 1);
}
else
{
*(Lef) = *(p2);
*(Lef + 1) = *(p2 + 1);
*(Right ) = *(p1);
*(Right + 1) = *(p1 + 1);
}
}
sleep
主要是一些错误的处理,然后调用system call就可以了,没啥好说,详细见代码。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
int n = 0;
if ((argc > 2) || (argc < 2))
{
fprintf(2, "invalid arguments");
exit(1);
}
n = atoi(argv[1]);
sleep(n);
exit(0);
}
pingpong
主要是进程与子进程的同步问题,两个解决方法。第一个方法使用wait(0)来保证下面的代码子进程结束了才会执行;第二个方法采用的是两个pipe,当read(p[0], &xxx, ttt)即pipe的写端没输入数据时,read会挂起,来解决进程与子进程的同步问题。我采用的是第一种方法。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
int p[2];
int ExchangeNum = 2;
if (pipe(p) < 0)
{
fprintf(2, "pipe error");
exit(1);
}
if (fork() > 0)
{
write(p[1], &ExchangeNum, 4);
wait(0);
read(p[0], &ExchangeNum, 4);
fprintf(1, "%d: received pong\n", getpid());
exit(0);
}
else
{
read(p[0], &ExchangeNum, 4);
fprintf(1, "%d: received ping\n", getpid());
write(p[1], &ExchangeNum, 4);
exit(0);
}
}
find
目录是一种特殊的文件,在xv6中,目录的存储是通过struct dirent {
ushort inum;
char name[DIRSIZ];
};inum
inum在操作系统中,是文件的唯一标识符,可以通过inode来唯一确定一个文件。目录的的文件存储的内容就是
由n个struct dirent组成的。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
//static char* fmtname(char *path);
static void DisplaySameFile(char* dir, char* file);
int main(int argc, char* argv[])
{
if (argc != 3)
{
fprintf(2, "invalid varameter input");
exit(1);
}
DisplaySameFile(argv[1], argv[2]);
exit(0);
}
/**
* @brief 在该目录下,如果有相同的文件名,则输出到consle
*/
static void DisplaySameFile(char* dir, char* file)
{
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(dir , 0)) < 0){
fprintf(2, "ls: cannot open %s\n", dir);
exit(1);
}
if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", dir);
close(fd);
exit(1);
}
switch(st.type)
{
case T_FILE:
fprintf(2, "error:dir %s is a file", dir);
exit(1);
break;
case T_DIR:
if(strlen(dir) + 1 + DIRSIZ + 1 > sizeof(buf)){
printf("ls: path too long\n");
break;
}
strcpy(buf, dir);
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("ls: cannot stat %s\n", buf);
continue;
}
if (st.type == T_FILE)
{
if (strcmp(de.name, file) == 0)
{
fprintf(1, "%s\n", buf);
}
}
else if (st.type == T_DIR)
{
if ((strcmp(de.name, ".") != 0) && (strcmp(de.name, "..")))
{
DisplaySameFile(buf, file);
}
}
}
break;
}
close(fd);
return;
}
/*
static 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++;
// Return blank-padded name.
if(strlen(p) >= DIRSIZ)
return p;
memmove(buf, p, strlen(p));
memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
return buf;
}
*/
xargs
怎么判断读到一行结束,'\r'或者'\n'。但你怎么判断读到了最后的,根据例子猜测,应该是根据'\0'来判断,它应该是输入一个字符串,字符串中有几行的字符,所以根据0来判断不用读了。怎么分辨输入的字符串中哪些字符是一个argument呢?根据空格来判断,空格后是下一个argument。
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/param.h"
#define __XARGS_LINN_BUF_NUMBER (512U)
static int ReadLine(char* p);
static int ParaseLine(char* PString, char** Argvs, int* ReadAgvNum);
int main(int argc, char* argv[])
{
if (argc < 2)
{
fprintf(2, "paramter number too few\n");
exit(1);
}
char* Argvs[MAXARG] = {0};
int OrginalArgvNum = argc - 1;
int ExtendArgvNum = 0; //every line argv number
char LineBuf[__XARGS_LINN_BUF_NUMBER] = {0};
int LineResult;
for(int i = 0; i < OrginalArgvNum; i++)
{
Argvs[i] = argv[i + 1];
}
while(1)
{
LineResult = ReadLine(&LineBuf[0]);
if (LineResult == -1)//读完所有的行
{
exit(0);
}
else if (LineResult == -2)
{
fprintf(2, "the input line is too long\n");
exit(1);
}
if (ParaseLine(&LineBuf[0], &Argvs[OrginalArgvNum], &ExtendArgvNum) != 0)
{
fprintf(2, "unown error\n");
exit(1);
}
if (fork() > 0)
{
for(int i = OrginalArgvNum; i < OrginalArgvNum + ExtendArgvNum; i++)
{
Argvs[i] = 0;;
}
Argvs[OrginalArgvNum + ExtendArgvNum] = 0;
}
else
{
exec(Argvs[0], Argvs);
}
}
}
/**
* @brief 正确读完返回返回0, 遇到‘\0'返回-1
*/
static int ReadLine(char* p)
{
char TempP = 0;
int ReadNum = 0;
int i1 = 0;
for (int i = 0; i <__XARGS_LINN_BUF_NUMBER; i++)
{
*(p + i) = 0;
}
do
{
ReadNum = read(0, &TempP, 1 );
if (ReadNum == 1)
{
*(p + i1) = TempP;
i1 = i1 + 1;
if ((TempP == '\n') || (TempP == '\r'))
{
return 0;
}
if (TempP == '\0')
{
return -1;
}
if (i1 == (__XARGS_LINN_BUF_NUMBER - 1))
{
return -2;
}
}
} while (ReadNum == 1);
return 0;
}
/**
* @brief 不考虑一行的参赛+原本的参数个数+1大于MAXARG
* @return 0 is true
* @param
*/
static int ParaseLine(char* PString, char** Argvs, int* ReadAgvNum)
{
int StringNum = 0;
int TempArgNum = 0;
int Start = 0;
StringNum = strlen(PString);
for(int i = 0; i < StringNum; i++)
{
if (*(PString + i) == ' ')
{
*(PString + i) = '\0';
*(Argvs + TempArgNum) = PString + Start;
TempArgNum = TempArgNum + 1;
Start = i + 1;
}
}
if (TempArgNum == 0)//那一行没有空格,就只有一个参数
{
*(Argvs + 0) = PString;
TempArgNum = 1;
*(PString + StringNum - 1) = '\0';
}
else//最后一个argv,但是无‘ ',不应该\n后又\n,根据输入的例子,反正这种可能先不考虑
{
*(Argvs + TempArgNum) = PString + Start;
TempArgNum = TempArgNum + 1;
}
*ReadAgvNum = TempArgNum;
return 0;
}

浙公网安备 33010602011771号