MIT 6.828 操作系统课程系列1 Utilities#
https://pdos.csail.mit.edu/6.828/2022/labs/util.html
写代码#
1. sleep#
判断一下参数。把第一个参数转成int。再调sleep(system call)即可。
改好代码后运行
make qemu
即可编译并运行xv6内核。如果编译有错会报错并停止。
内核运行起来后就会进入命令行界面,跟一般linux相似,但是很简陋,缺少各种功能。
ctrl-a
再按x
退出内核。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main(int argc, char *argv[])
{
if(argc < 2){
fprintf(2, "arg error\n");
}
int ticks = atoi(argv[1]);
sleep(ticks);
exit(0);
}
2. pingpong#
起两个pipe。fork。父子进程收发一下消息即可。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main(int argc, char *argv[])
{
char buf[1024];
int fds[2];
if(pipe(fds) < 0){
printf("pipe() failed\n");
exit(1);
}
int pid = fork();
if(pid < 0){
printf("fork failed\n");
exit(1);
}
if(pid == 0){
// child
read(fds[0], buf, 1024);
printf("%d: received ping\n", getpid());
write(fds[1], buf, 1);
} else {
write(fds[1], "x", 1);
read(fds[0], buf, 1024);
printf("%d: received pong\n", pid);
}
close(fds[0]);
close(fds[1]);
exit(0);
}
3. primes#
没仔细看原理。直接起一串进程,不断把一串数字传下去。第一个一定是质数,把它的倍数筛掉,其余的传到下一个进程。
fork后不用的pipe要关掉,否则read会永远block。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int pipe_id = 0;
char buf[8];
int fds[40][2];
int chile_func(){
if(pipe_id > 35)
return 0;
// recv
int data[40];
memset(data, 0, sizeof(data));
close(fds[pipe_id][1]); // close write
int n = 0;
int data_count = 0;
while(1){
n = read(fds[pipe_id][0], buf, 4);
if(n == 0)
break;
data[data_count] = 0;
data[data_count] |= buf[0] << 24;
data[data_count] |= buf[1] << 16;
data[data_count] |= buf[2] << 8;
data[data_count] |= buf[3];
data_count++;
}
close(fds[pipe_id][0]); // close read
if(data_count <= 0)
return 0;
// work
printf("prime %d\n", data[0]);
if(data_count <= 1)
return 0;
pipe_id++;
if(pipe(fds[pipe_id]) < 0){
printf("pipe() failed\n");
exit(1);
}
int pid = fork();
if(pid < 0){
printf("fork failed\n");
exit(1);
}
if(pid == 0){
// child
chile_func();
exit(0);
} else {
close(fds[pipe_id][0]); // close read
for(int i = 1; i < data_count; ++i){
if(data[i] % data[0] != 0){
buf[0] = (data[i] >> 24) & 0xFF;
buf[1] = (data[i] >> 16) & 0xFF;
buf[2] = (data[i] >> 8) & 0xFF;
buf[3] = data[i] & 0xFF;
write(fds[pipe_id][1], buf, 4);
}
}
}
close(fds[pipe_id][1]); // close write
wait(0);
return 0;
}
int main(int argc, char *argv[])
{
if(pipe(fds[pipe_id]) < 0){
printf("pipe() failed\n");
exit(1);
}
int pid = fork();
if(pid < 0){
printf("fork failed\n");
exit(1);
}
if(pid == 0){
// child
chile_func();
exit(1);
} else {
// parent
close(fds[pipe_id][0]); // close read
for(int i = 2; i <= 35; ++i){
buf[0] = (i >> 24) & 0xFF;
buf[1] = (i >> 16) & 0xFF;
buf[2] = (i >> 8) & 0xFF;
buf[3] = i & 0xFF;
write(fds[pipe_id][1], buf, 4);
}
}
close(fds[pipe_id][1]); // close write
wait(0);
exit(0);
}
4. find#
直接抄ls.c。把文件夹的分支改成递归find即可。
fmtname里把空格填充去掉可得到干净的文件名。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
/*
#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
};
*/
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));
memset(buf+strlen(p), 0, DIRSIZ-strlen(p));
return buf;
}
void find(char *path, char *file_name)
{
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;
}
//printf("find %s in %s\n", file_name, path);
if(fstat(fd, &st) < 0){
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_DEVICE:
case T_FILE:
if(strcmp(file_name, fmtname(path)) == 0)
//fprintf(1, "%s\n", path);
printf("%s\n", path);
break;
case T_DIR:
//printf("dir %s\n", path);
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("check %s\n", buf);
if(strcmp(".", fmtname(buf)) == 0 || strcmp("..", fmtname(buf)) == 0)
continue;
//printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
find(buf, file_name);
}
break;
}
close(fd);
}
int main(int argc, char *argv[])
{
find(argv[1], argv[2]);
exit(0);
}
5. xargs#
有很多细节。
得把find.c的输出写到stdout的1。之前输入到stderr的2了,半天取不到数据。或者直接用printf。
管道传参数时,xargs从stdin的0取数据。碰到空格或\n时形成一个参数。
fork一下,子进程里exec执行即可。
拼装exec的argv时最后要有一项0。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
// sh < xargstest.sh
int main(int argc, char *argv[])
{
char wtf;
char buf[256];
memset(buf, 0, sizeof(buf));
char *new_argv[32];
int i = 0;
for(; i < argc - 1; ++i){
new_argv[i] = argv[i + 1];
}
new_argv[i] = buf;
new_argv[i + 1] = 0;
while(read(0, &wtf, 1)){
if(wtf != '\n' && wtf != ' ' && wtf != 0) {
buf[strlen(buf)] = wtf;
//printf("%s\n", buf);
continue;
}
//printf("got %s\n", buf);
int pid = fork();
if(pid < 0){
printf("fork failed\n");
exit(1);
}
if(pid == 0){
// child
exec(argv[1], new_argv);
exit(0);
} else {
wait(0);
memset(buf, 0, sizeof(buf));
}
//sleep(30);
}
return 0;
}
创建一个time.txt填一个数字
make clean
make grade
通过全部测试