环境搭建
其实没什么好说的,用 Linux 很熟了,在 Debian 上跟步骤 apt install
就行了 (I’m not using arch btw)
盲猜很多人会卡在 git clone
我用的是 2020 版因为我已经做过了 🤣,再回味了一遍
解题思路
踩的一个坑是头文件有依赖关系,必须安装顺序 include
, (linker is awesome btw), 然后 clang-format
自动给我 SortInclude
搞得我莫名其妙会失败,汗
还有就是 VSCode Makefile Extension 会导致 include 有问题,很迷
sleep
属于是练手题,让你知道怎么加 user program
然后知道 argc
, argv
, 转换 string
到 int
就行了?
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main ( int argc , char ** argv )
{
if (argc < 2 )
{
printf ( "usage: sleep <ticks> \n " );
}
sleep ( atoi ( argv [ 1 ]));
exit ( 0 );
}
pingpong
pipe
练手题,要知道 fork
and pipe
的用法,read
, write
, fd
, parent
and child
经典 IPC btw
The wait(0)
function is a system call that suspends the execution of the calling process until one of its child processes terminates or a signal is received.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main ( int argc , char const * argv [] )
{
// index 0 -> in, 1 -> out, like fd 0 1 2
int p2c [ 2 ], c2p [ 2 ];
pipe (p2c); // from parent to child
pipe (c2p); // from child to parent
if ( fork () != 0 )
{ // parent
write ( p2c [ 1 ], "ping" , 4 );
char readin_buf [ 4 ];
read ( c2p [ 0 ], readin_buf, 4 );
printf ( " %d : received pong \n " , getpid ());
wait ( 0 ); // wait for child to finish
}
else
{
char readin_buf [ 4 ];
read ( p2c [ 0 ], readin_buf, 4 );
printf ( " %d : received ping \n " , getpid ());
write ( c2p [ 1 ], readin_buf, 4 );
}
exit ( 0 );
}
$ pingpong
4: received ping
3: received pong
prime
这个还是非常有意思有难度的,和 MapReduce
有点像,又和 functional programming
中的 filter
有点像
中文文档写的不太好,没有很体现 筛 (sieve)
用 -1
作为 sentinel to indicate the end of input
第一次没做出来,再做了一次
要注意关 fd
不然会莫名其妙爆掉,查网上看到的,汗
有时候会纠结要不要 return
, exit
,不过感觉重点是 wait
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void
sieve (int p_left[2])
{
// the 1st input is guaranteed to be prime since non-primes are filterd out
int init_prime;
read (p_left[0], &init_prime, sizeof (init_prime));
if (init_prime == -1)
{
exit (0);
}
printf ("prime %d\n", init_prime);
int p_right[2];
pipe (p_right);
if (fork () == 0)
{
// child
close (p_left[0]);
close (p_right[1]);
sieve (p_right);
}
else
{
// parent
close (p_right[0]);
int i;
while (read (p_left[0], &i, sizeof (i)) && i != -1)
{
if (i % init_prime != 0)
{
write (p_right[1], &i, sizeof (i));
}
}
i = -1;
write (p_right[1], &i, sizeof (i));
wait (0);
}
}
int
main (int argc, char const *argv[])
{
int p[2];
pipe (p);
if (fork () == 0)
{ // child
close (p[1]);
sieve (p);
}
else
{ // parent
close (p[0]); // parent no need to read
int i;
for (i = 2; i <= 35; i++)
{
write (p[1], &i, sizeof (i));
}
i = -1;
write (p[1], &i, sizeof (i));
wait (0);
}
exit (0);
}
find
最无聊的一个,照抄就有
second thought: 写错了,害得后面 xargs test 也错了,💩
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
void
find ( char * path , char * target )
{
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 ;
}
if ( fstat (fd, & st) < 0 )
{
fprintf ( 2 , "find: cannot stat %s\n " , path);
close (fd);
return ;
}
switch (st.type)
{
case T_FILE:
if ( strcmp (path + strlen (path) - strlen (target), target) == 0 )
{
printf ( " %s\n " , path);
}
break ;
case T_DIR:
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 ;
}
// omit . and ..
if ( strcmp (buf + strlen (buf) - 2 , "/." ) != 0
&& strcmp (buf + strlen (buf) - 3 , "/.." ) != 0 )
{
find (buf, target); // recursive find
}
}
break ;
}
close (fd);
}
int
main ( int argc , char * argv [] )
{
if (argc < 3 )
{
exit ( 0 );
}
char target [ 512 ];
target [ 0 ] = '/' ;
strcpy (target + 1 , argv [ 2 ]);
find ( argv [ 1 ], target);
exit ( 0 );
}
xargs
最烦人的一个,莫名其妙会报错,gdb 也没调出来,看其他人才发现理解错 xargs 了 (好像是写错 find 了?idk)
之前没写出来,尝试又重写了一次
主要难点是字符串处理,和理解 xargs
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
// definitely not very readline but it suit the case
char *
readline ( char * buf )
{
char * pos = buf;
while ( read ( 0 , pos, 1 ) != 0 )
{
if ( * pos == ' \n ' || * pos == ' \0 ' )
{
* pos = ' \0 ' ;
return pos;
}
pos ++ ;
}
return 0 ;
}
void
run ( char * program , char * argv [] )
{
if ( fork () == 0 )
{ // child
exec (program, argv);
}
else
{
wait ( 0 );
}
}
int
main ( int argc , char * argv [] )
{
char * program = argv [ 1 ];
char * args [MAXARG];
char readin_buf [ 2048 ];
args [ 0 ] = program;
int i = 1 ;
// 0 is xargs, 1 is program, first arg starts at 2
// this is tricky and ugly but it works so whatever
while (i < argc - 1 )
{
args [i] = argv [i + 1 ];
i ++ ;
}
char * str_end = readline (readin_buf);
while (str_end != 0 )
{
args [i] = readin_buf;
i ++ ;
str_end = readline (str_end + 1 );
}
args [i] = 0 ;
run (program, args);
exit ( 0 );
}
实验心得
水平比第一次做的时候提升了,能写出来了😂
总体写的十分开心
或许可以加个 gdb 实验?虽然 printf
也很够了
sieve 的中文文档不太行
gnu c codestyle 是真的丑
好像比较麻木
make grade
似乎没提到?
== Test sleep, no arguments ==
$ make qemu-gdb
sleep, no arguments: OK (3.3s)
== Test sleep, returns ==
$ make qemu-gdb
sleep, returns: OK (1.4s)
== Test sleep, makes syscall ==
$ make qemu-gdb
sleep, makes syscall: OK (1.2s)
== Test pingpong ==
$ make qemu-gdb
pingpong: OK (1.1s)
== Test primes ==
$ make qemu-gdb
primes: OK (1.2s)
== Test find, in current directory ==
$ make qemu-gdb
find, in current directory: OK (1.3s)
== Test find, recursive ==
$ make qemu-gdb
find, recursive: OK (1.8s)
== Test xargs ==
$ make qemu-gdb
xargs: OK (1.6s)
== Test time ==
time: OK
Score: 100/100