一、背景
第一个Lab是实现几个shell工具,每个工具都是一个可以独立运行的main函数,会调用系统调用,但其本身并不是系统调用。
实验地址:Lab Xv6 and Unix utilities。
二、sleep
1 问题分析
基于已有的系统调用sleep,实现一个sleep命令,也就是一个main小程序,需要关注以下文件:
- 命令工具属于用户态,只要在user目录下即可,参考user/其他程序,user/ (e.g., user/echo.c, user/grep.c, and user/rm.c)。
- 参数不对需要打印错误信息。
- sleep系统调用已经实现kernel/sysproc.c下的sys_sleep,user/user.h下也已经声明了sleep系统调用,可以触发中断,中断汇编代码在user/usys.S。
- 参考工具函数user/ulib.c。
- 在Makefile文件UPROGS下增加这个程序名,要不然不会编译。
2 代码实现
代码语言:c复制#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main(int argc, char *argv[])
{
if(argc != 2){
fprintf(2, "Usage: sleep param too many...n");
exit(1);
}
int t=atoi(argv[1]);
sleep(t);
exit(0);
}
三、pingpong
1 问题分析
实现进程间通信,这涉及到几点:
- 父进程fork子进程,父进程返回子进程pid,子进程返回0,父子进程共享数据。
- pipe,pipe是单向通信,需要两个才能实现双向通信。
- 涉及read、write、getpid、pipe。
2 代码实现
代码语言:c复制#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#define ReadFd 0
#define WriteFd 1
/**
* pipe是一段内核buffer,包含一对fd,一个用来读,一个用来写
*/
int
main(int argc, char *argv[])
{
if(argc != 1){
fprintf(2, "Usage: sleep param too many...n");
exit(1);
}
int f2c[2];
int c2f[2];
pipe(f2c);
pipe(c2f);
char buf[64];
//fork对于父进程返回子进程ID,对于子进程返回0
int pid=fork();
if(pid>0){
close(c2f[WriteFd]);
close(f2c[ReadFd]);
int n;
//子进程先发送ping,父进程接收
while((n=read(c2f[ReadFd],buf,64))<=0);
fprintf(0,"%d: received %sn",getpid(),buf);
strcpy(buf,"pong");
//收到ping后,父进程发送pong
n=0;
while((n=write(f2c[WriteFd],buf,sizeof(buf)))<=0);
close(f2c[WriteFd]);
close(c2f[ReadFd]);
wait(&pid);
exit(0);
}else{
close(c2f[ReadFd]);
close(f2c[WriteFd]);
strcpy(buf,"ping");
int n=0;
while((n=write(c2f[WriteFd],buf,sizeof(buf)))<=0);
n=0;
while ((n=read(f2c[ReadFd],buf,64))<=0);
fprintf(0,"%d: received %sn",getpid(),buf);
close(f2c[WriteFd]);
close(c2f[ReadFd]);
exit(0);
}
}
pipe是一段内核buffer,包含一对fd,一个用来读,一个用来写。pingpong需要f2c和c2f两对管道来实现父进程向子进程写数据,子进程读,子进程向父进程写数据,父进程读。
四、primes
1 问题分析
多进程来筛选素数,埃氏筛法,并实现CSP模型。
- fork子进程进行下一步筛选,并且父进程需要等待子进程、子孙进程的结束。
- 文件句柄上限是35,超过会报错。
- 递归。
- 及时释放文件句柄,避免阻塞。
- 一定要并发,不能够串行,父进程向管道写入数据时要先fork子进程,尽量提高并行程度。
这里涉及到对pipe的理解。pipe是一段内核buffer,包含一对fd,一个用来读,一个用来写。如果读到空时则阻塞进程,如果向写满的pipe继续写入时也会阻塞。那么就会有一个问题:子进程读到空时阻塞,无法结束,而父进程需要wait子进程,也就不能够结束,是不是就死锁了?
pipe读到空时虽然会阻塞,但要是 write fd完全被关闭,则读进程就会被唤醒,返回0,所以我们一定要及时关闭对应的文件描述符,避免死锁。
2 代码实现
代码语言:c复制#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#define ReadFd 0
#define WriteFd 1
int prime(int fd);
int
main(int argc, char *argv[])
{
if(argc>2)
{
fprintf(2,"usage: primes [number]");
exit(1);
}
int n=35;
if(argc==2)
{
int t=atoi(argv[1]);
if(t<1)
{
fprintf(2,"parameter is invalid!");
exit(1);
}
if(t>35)
n=t;
}
int pp[2];
pipe(pp);
int pid=fork();
if(pid>0){
close(pp[ReadFd]);
for (int i = 2; i <= n; i ){
write(pp[WriteFd],&i,sizeof(int));
}
close(pp[WriteFd]);
wait(&pid);
}else{
close(pp[WriteFd]);
prime(pp[ReadFd]);
close(pp[ReadFd]);
}
exit(0);
}
int prime(int fd){
int first=2;
int buf;
if(read(fd,&buf,sizeof(int))<=0){
exit(0);
}
first=buf;
fprintf(0,"prime %dn",first);
int pp[2];
pipe(pp);
//传递数据前先fork子进程提高并行程度
int pid=fork();
if(pid>0){
close(pp[ReadFd]);
while (read(fd,&buf,sizeof(int))>0){
if(buf%first!=0){
write(pp[WriteFd],&buf,sizeof(int));
}
}
close(pp[WriteFd]);
}else{
close(pp[WriteFd]);
prime(pp[ReadFd]);
close(pp[ReadFd]);
}
wait(&pid);
exit(0);
}
五、find
1 问题分析
从指定目录下寻找目标文件。
- 参考ls.c,读目录。
- 迭代 递归,但是避免对"."、".."进行,否则会死循环。
- make clean可以清除文件系统。
2 涉及的数据结构
代码语言:c复制//目录项
#define DIRSIZ 14
struct dirent {
ushort inum;
char name[DIRSIZ];
};
# 使用read读取目录即可。
代码语言:c复制//文件元信息
#define T_DIR 1 // Directory
#define T_FILE 2 // File
#define T_DEVICE 3 // Device
struct stat {
int dev; // File system's disk device
uint ino; // Inode number
short type; // Type of file
short nlink; // Number of links to file
uint64 size; // Size of file in bytes
};
int fstat(int fd, struct stat*);
3 代码实现
代码语言:c复制#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/fcntl.h"
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));
if (strlen(buf) == 0) return buf;
for (int i = strlen(buf) - 1; i > 0; i--) {
if (buf[i - 1] != ' ') {
buf[i] = '