Xv6
Lab: Xv6 and Unix utilities
pingpong
pingpong (easy) Write a program that uses UNIX system calls to ‘‘ping-pong’’ a byte between two processes over a pair of pipes, one for each direction. The parent should send a byte to the child; the child should print “: received ping”, where is its process ID, write the byte on the pipe to the parent, and exit; the parent should read the byte from the child, print “: received pong”, and exit. Your solution should be in the file user/pingpong.c.
Some hints:
- Use pipe to create a pipe.
- Use fork to create a child.
- Use read to read from the pipe, and write to write to the pipe.
- Use getpid to find the process ID of the calling process.
- Add the program to UPROGS in Makefile.
- User programs on xv6 have a limited set of library functions available to them. You can see the list in user/user.h; the source (other than for system calls) is in user/ulib.c, user/printf.c, and user/umalloc.c. Run the program from the xv6 shell and it should produce the following output:
$ make qemu
...
init: starting sh
$ pingpong
4: received ping
3: received pong
$
Your solution is correct if your program exchanges a byte between two processes and produces output as shown above. pingpong诚然是比较简单的一个程序,但是需要了解的system function如fork和pipe却都并不简单.
fork
使用chatgpt可以大概了解fork的 阅读xv6-book 也可以大概了解fork.
dirent stat
#define DIRSIZ 14
struct dirent {
ushort inum;
char name[DIRSIZ];
char isvalid;
};
``` c
struct stat {
short type; // 文件类型,如 T_DIR, T_FILE 等
int dev; // 磁盘设备号
uint ino; // inode 号
short nlink; // 硬链接数
uint size; // 文件大小(字节)
uint mtime; // 最近的修改时间
};
暂时就了解这么多吧,真正底层的东西在后面文件系统中会详细学到.
int fd;
// dirent 即目录项,
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "ls: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_FILE:
printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
break;
ls和find会利用到stat结构体中的type来判断文件类型
buf缓冲区
使用buf缓冲区来作为绝对路径,使用char *p来作为文件名,通过加’/‘前缀拼接在buf后面
p = buf + strlen(buf);
void find(char *path,char *target){
int fd;
struct dirent dt;
struct stat st;
char buf[512],*p;
if ((fd = open(path,0)) < 0)
{
printf("cann't not open %s\n",path);
return;
}
if(fstat(fd,&st)<0){
printf("can't stat %s\n",path);
close(fd);
return;
}
if(st.type!=T_DIR){
fprintf(2,"Usage: find: %s is not a directory",path);
close(fd);
return;
}
strcpy(buf,path);
//buf是绝对路径,p是一个文件名,通过加'/'前缀拼接在buf后面
p=buf+strlen(buf);
*p++='/';
//读取fd,如果read返回字节数和de长度相等则循环
while(read(fd,&dt,sizeof(dt))==sizeof(dt)){
if(dt.inum==0){
continue;
}
if(!(strcmp(dt.name,"."))||!(strcmp(dt.name,".."))){
continue;
}
memmove(p,dt.name,DIRSIZ);
//设置文件名结束符
p[DIRSIZ]=0;
//int stat(char*,DIRSIZ);
//stat 系统调用,可以获得一个已存在文件的模式,并将此模式赋值给它的副本
if(stat(buf,&st)<0){
fprintf(2,"find: can't stat %s\n",buf);
continue;
}
if(st.type==T_DIR){
find(buf,target);
}
else if(st.type==T_FILE&&!strcmp(dt.name,target)){
printf("%s\n",buf);
}
}
}
system call
在你开始写代码之前,请阅读xv6手册《book-riscv-rev1》的第2章、第4章的第4.3节和第4.4节以及相关源代码文件:
- 系统调用的用户空间代码在user/user.h和user/usys.pl中
- 内核空间代码是kernel/syscall.h、kernel/syscall.c
- 与进程相关的代码是kernel/proc.h和kernel/proc.c