xv6_and_unix_utilities
Xv6 and Unix utilities
好哦,完成 mit 6.1810 2023 中 lab :Xv6 and Unix utilities了,准备开始下一章的学习了,xv6还是挺好玩的

sleep
根据题目描述,和 user/ 中的其他程序的逻辑可以发现 命令行参数传递是通过 main的argc 和argv去传递的,其中argc 中存储着参数的个数,argv存储着参数名,没有参数的时候argc = 1,argv[0] = program_name。
然后在 “user/user.h” 中有sleep 函数
所以 只需要直接调用sleep函数就好了, 把time上限也做一个设置(防止睡死过去了
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]){
int time = 0;
if (argc == 1 || argc > 2){
printf("Error: Please provide the number of seconds to sleep\n e.g. sleep 5\n");
exit(1);
}
time = atoi(argv[1]);
if (time > 0 && time < 0x1000){
printf("Zzzzzzzzz.....\n");
sleep(time);
}
exit(0);
}
然后在Makefile中 添加 sleep这个文件 就好了
UPROGS=\
$U/_cat\
$U/_echo\
$U/_forktest\
$U/_grep\
$U/_init\
$U/_kill\
$U/_ln\
$U/_ls\
$U/_mkdir\
$U/_rm\
$U/_sh\
$U/_stressfs\
$U/_usertests\
$U/_grind\
$U/_wc\
$U/_zombie\
$U/_sleep\

pingpong


通过阅读资料可以发现,pingpong 可以分解为两个问题,分别是 如何创建子进程,父子进程之间如何通信
创建子进程可以使用 fork() 函数创建,进程之间通信可以使用pipe 这个函数去创建一个管道,管道的本质就是内存共享,不同进程可以共享同一块内存
管道是单向的,其中 int pN[2] = pipe() 这段代码中,pN[0] 是 读端,pN[1] 是写端,所以 两个进程互相通信 需要两个管道
那 如何判断父子进程呢? 可以通过 fork的返回值来判断,为了保证执行顺序按照 ping - > pong 这个顺序去执行,在 ping 后 父进程应该等待子进程 senddata过来 才输出pong,这时候可以使用wait函数来解决这个问题
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]){
// pN[0] read pN[1] write
int p1[2]; // pipe from parent to child
int p2[2]; // pipe from child to parent
pipe(p1);
pipe(p2);
int pid = fork();
if (pid > 0){
char byte = 'A';
write(p1[1],&byte,1);
wait(0); // wait child process exit
read(p2[0],&byte,1);
if(byte == 'B'){
printf("%d: received pong\n", getpid());
}
close(p1[1]);
close(p2[0]);
exit(0);
}
else if (pid == 0){
char buf;
read(p1[0],&buf,1);
if(buf == 'A'){
printf("%d: received ping\n", getpid());
buf = 'B';
write(p2[1],&buf,1);
}
close(p1[0]);
close(p2[1]);
exit(0);
}
else{
printf("Error: fork error!\n");
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
exit(0);
}
}

primes
这个题挺有意思的,素数筛,不过想到一种更简单的方法,筛的逻辑在子进程里面实现就好了
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void sieve(int read_fd) {
int p;
if (read(read_fd, &p, sizeof(int)) == 0) {
close(read_fd);
exit(0);
}
printf("prime %d\n", p);
int next_read_fd[2];
pipe(next_read_fd);
if (fork() == 0) {
close(next_read_fd[1]);
sieve(next_read_fd[0]);
} else {
close(next_read_fd[0]);
int num;
while (read(read_fd, &num, sizeof(int)) != 0) {
if (num % p != 0) {
write(next_read_fd[1], &num, sizeof(int));
}
}
close(next_read_fd[1]);
close(read_fd);
wait(0);
exit(0);
}
}
int main(int argc, char *argv[]){
int nums[34];
for (int i = 0; i < 34; i++) {
nums[i] = i + 2;
}
int initial_fd[2];
pipe(initial_fd);
if (fork() == 0) {
close(initial_fd[1]);
sieve(initial_fd[0]);
} else {
close(initial_fd[0]);
for (int i = 0; i < 34; i++) {
write(initial_fd[1], &nums[i], sizeof(int));
}
close(initial_fd[1]);
wait(0);
exit(0);
}
return 0;
}
find

find,想到的搜索策略是bfs?从传过来的path判断 path里每个项的类型,如果是dir就递归下去,如果是file就判断file name是不是要find的目标,如果是. ..就跳过,所以能得到几个新的问题,怎么样获取path里的条目有什么,这些条目的类型是什么
ls.c source code
#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));
return buf;
}
void
ls(char *path)
{
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, O_RDONLY)) < 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_DEVICE:
case T_FILE:
printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("ls: 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("ls: cannot stat %s\n", buf);
continue;
}
printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
}
break;
}
close(fd);
}
int
main(int argc, char *argv[])
{
int i;
if(argc < 2){
ls(".");
exit(0);
}
for(i=1; i<argc; i++)
ls(argv[i]);
exit(0);
}
有两个 比较关键的函数和结构体
stat 和 fstat 这两个函数

嗯,通过阅读ls.c可以发现想要获得一个条目是目录 还是文件,可以通过 int fd = open(“current_dir”,0);struct stat st; fstat(fd,&st); 来获得
这是struct stat的定义
#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
};
只需要判断 stat.type就能知道条目的类型是 directory 还是file 还是 Device
可以通过读fd的方式来获得条目的信息,用struct dirent 去存储条目的信息
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, "Error: findcannot stat %s\n", buf);
continue;
}
struct dirent的定义
// Directory is a file containing a sequence of dirent structures.
#define DIRSIZ 14
struct dirent {
ushort inum;
char name[DIRSIZ];
};
通过 dirent.name 可以获得条目的名字
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/fcntl.h"
void find(char *path, char *filename) {
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
// open path
if((fd = open(path, 0)) < 0) {
fprintf(2, "Error: find cannot open %s\n", path);
return;
}
// get directory statistics
if(fstat(fd, &st) < 0) {
fprintf(2, "Error: find cannot stat %s\n", path);
close(fd);
return;
}
// ensure it is a directory
if(st.type != T_DIR) {
fprintf(2, "Error: find %s is not a directory\n", path);
close(fd);
return;
}
// read the directory entries
strcpy(buf, path);
p = buf + strlen(buf);
*p++ = '/';
// get each item
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, "Error: findcannot stat %s\n", buf);
continue;
}
// Skip "." and ".."
if(strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)
continue;
if(st.type == T_DIR) {
// find sub directory
find(buf, filename);
} else if(st.type == T_FILE) {
// find the file
if(strcmp(de.name, filename) == 0) {
printf("%s\n", buf);
}
}
}
close(fd);
}
int main(int argc, char *argv[]) {
if(argc < 3) {
fprintf(2, "e.g. find <path> <filename>\n");
exit(1);
}
find(argv[1], argv[2]);
exit(0);
}

xargs

通过分析他给的样例加搜索可以发现 xargs是一个批处理 args的命令,其作用是从 前一个进程的输出中args,每个arg以 ‘ ‘ ‘\n’分割,批量的处理这些arg
| 符号在shell中可以做到将前一个进程的 fd 1 重定向到后一个进程的fd 0中,本质上是通过dup 通过交换进程的文件描述符实现的
所以处理的策略是,从标准输入中读取数据,读到\n或者 ‘ ‘表示读完一个arg了,用fork去创建一个子进程,将args传递过去执行
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/fcntl.h"
#define MAXARGS 0x20
#define BUFSIZE 0x500
int main(int argc, char *argv[]) {
char buf[BUFSIZE];
int input_data_len = 0;
char *args[MAXARGS];
int i;
if (argc == 1) {
fprintf(2, "e.g. find . | xargs file\n");
exit(1);
}
if (argc > MAXARGS - 5) {
fprintf(2, "Error: too many arguments\n");
exit(1);
}
for (i = 0; i < argc; i++) {
args[i] = argv[i];
}
while (read(0, buf + input_data_len, 1) == 1) {
if (buf[input_data_len] == '\n' || buf[input_data_len] == ' ') {
buf[input_data_len] = 0;
args[argc] = buf;
args[argc + 1] = 0;
if (fork() == 0) {
exec(argv[1], args + 1);
fprintf(2, "exec failed\n");
exit(1);
} else {
wait(0);
}
input_data_len = 0;
} else {
input_data_len++;
if (input_data_len >= BUFSIZE - 1) {
fprintf(2, "Error: input too long\n");
exit(1);
}
}
}
if (input_data_len > 0) {
buf[input_data_len] = 0;
args[argc] = buf;
args[argc + 1] = 0;
if (fork() == 0) {
exec(argv[1], args);
fprintf(2, "exec failed\n");
exit(1);
} else {
wait(0);
}
}
exit(0);
}