Compare commits

..

7 Commits
util ... lock

Author SHA1 Message Date
ridethepig
d4798926a3 make grade 80/80 with comment 2023-02-04 14:17:55 +00:00
ridethepig
c67450b32b temp 2023-02-04 13:38:39 +00:00
ridethepig
8728adbd0b Memory allocator ok 2023-02-02 14:36:27 +00:00
Frans Kaashoek
ad57ec8cb9 Lock lab 2022-11-09 20:27:22 -05:00
Frans Kaashoek
4b46c0c6eb Use O_RDONLY instead of 0 2022-09-11 13:51:11 -04:00
Robert Morris
463ae0abc3 handle negative arguments to sleep() a little better 2022-09-09 09:17:37 -04:00
Frans Kaashoek
f5b93ef12f Update acks
61810
2022-08-25 14:20:52 -04:00
37 changed files with 1322 additions and 387 deletions

View File

@ -106,7 +106,7 @@ endif
ifdef KCSAN
CFLAGS += -DKCSAN
KCSANFLAG = -fsanitize=thread
KCSANFLAG = -fsanitize=thread -fno-inline
endif
# Disable PIE when possible (for Ubuntu 16.10 toolchain)
@ -188,11 +188,6 @@ UPROGS=\
$U/_grind\
$U/_wc\
$U/_zombie\
$U/_sleep\
$U/_pingpong\
$U/_primes\
$U/_find\
$U/_xargs\

36
README
View File

@ -6,7 +6,7 @@ ACKNOWLEDGMENTS
xv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer
to Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,
2000)). See also https://pdos.csail.mit.edu/6.828/, which provides
2000)). See also https://pdos.csail.mit.edu/6.1810/, which provides
pointers to on-line resources for v6.
The following people have made contributions: Russ Cox (context switching,
@ -14,29 +14,31 @@ locking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin
Clements.
We are also grateful for the bug reports and patches contributed by
Takahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, Ian Chen, Dan
Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi, eyalz800, Nelson
Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel Filardo, flespark,
Peter Froehlich, Yakir Goaron, Shivam Handa, Matt Harvey, Bryan Henry,
jaichenhengjie, Jim Huang, Matúš Jókay, Alexander Kapshuk, Anders
Kaseorg, kehao95, Wolfgang Keller, Jungwoo Kim, Jonathan Kimmitt,
Eddie Kohler, Vadim Kolontsov, Austin Liew, l0stman, Pavan
Maddamsetti, Imbar Marinescu, Yandong Mao, Matan Shabtay, Hitoshi
Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel Nider,
OptimisticSide, Greg Price, Jude Rich, Ayan Shafqat, Eldar Sehayek,
Yongming Shen, Fumiya Shigemitsu, Cam Tenny, tyfkda, Warren Toomey,
Stephen Tu, Rafael Ubal, Amane Uehara, Pablo Ventura, Xi Wang, Keiichi
Watanabe, Nicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy
Zheng, ZhUyU1997, and Zou Chang Wei.
Takahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, carlclone, Ian
Chen, Dan Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi,
eyalz800, Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel
Filardo, flespark, Peter Froehlich, Yakir Goaron, Shivam Handa, Matt
Harvey, Bryan Henry, jaichenhengjie, Jim Huang, Matúš Jókay, John
Jolly, Alexander Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller,
Jungwoo Kim, Jonathan Kimmitt, Eddie Kohler, Vadim Kolontsov, Austin
Liew, l0stman, Pavan Maddamsetti, Imbar Marinescu, Yandong Mao, Matan
Shabtay, Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel
Nider, Hayato Ohhashi, OptimisticSide, Harry Porter, Greg Price, Jude
Rich, segfault, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya
Shigemitsu, Cam Tenny, tyfkda, Warren Toomey, Stephen Tu, Rafael Ubal,
Amane Uehara, Pablo Ventura, Xi Wang, WaheedHafez, Keiichi Watanabe,
Nicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng,
ZhUyU1997, and Zou Chang Wei.
The code in the files that constitute xv6 is
Copyright 2006-2020 Frans Kaashoek, Robert Morris, and Russ Cox.
Copyright 2006-2022 Frans Kaashoek, Robert Morris, and Russ Cox.
ERROR REPORTS
Please send errors and suggestions to Frans Kaashoek and Robert Morris
(kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching
operating system for MIT's 6.S081, so we are more interested in
operating system for MIT's 6.1810, so we are more interested in
simplifications and clarifications than new features.
BUILDING AND RUNNING XV6

View File

@ -1 +1 @@
LAB=util
LAB=lock

58
grade-lab-lock Executable file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env python3
import re
from gradelib import *
r = Runner(save("xv6.out"))
@test(0, "running kalloctest")
def test_kalloctest():
r.run_qemu(shell_script([
'kalloctest'
]), timeout=200)
@test(10, "kalloctest: test1", parent=test_kalloctest)
def test_kalloctest_test1():
r.match('^test1 OK$')
@test(10, "kalloctest: test2", parent=test_kalloctest)
def test_kalloctest_test2():
r.match('^test2 OK$')
@test(10, "kalloctest: test3", parent=test_kalloctest)
def test_kalloctest_test3():
r.match('^test3 OK$')
@test(10, "kalloctest: sbrkmuch")
def test_sbrkmuch():
r.run_qemu(shell_script([
'usertests sbrkmuch'
]), timeout=90)
r.match('^ALL TESTS PASSED$')
@test(0, "running bcachetest")
def test_bcachetest():
r.run_qemu(shell_script([
'bcachetest'
]), timeout=90)
@test(10, "bcachetest: test0", parent=test_bcachetest)
def test_bcachetest_test0():
r.match('^test0: OK$')
@test(10, "bcachetest: test1", parent=test_bcachetest)
def test_bcachetest_test1():
r.match('^test1 OK$')
@test(19, "usertests")
def test_usertests():
r.run_qemu(shell_script([
'usertests -q'
]), timeout=300)
r.match('^ALL TESTS PASSED$')
@test(1, "time")
def test_time():
check_time()
run_tests()

View File

@ -1,86 +0,0 @@
#!/usr/bin/env python3
import re
from gradelib import *
r = Runner(save("xv6.out"))
@test(5, "sleep, no arguments")
def test_sleep_no_args():
r.run_qemu(shell_script([
'sleep'
]))
r.match(no=["exec .* failed", "$ sleep\n$"])
@test(5, "sleep, returns")
def test_sleep_no_args():
r.run_qemu(shell_script([
'sleep',
'echo OK'
]))
r.match('^OK$', no=["exec .* failed", "$ sleep\n$"])
@test(10, "sleep, makes syscall")
def test_sleep():
r.run_qemu(shell_script([
'sleep 10',
'echo FAIL'
]), stop_breakpoint('sys_sleep'))
r.match('\\$ sleep 10', no=['FAIL'])
@test(20, "pingpong")
def test_pingpong():
r.run_qemu(shell_script([
'pingpong', 'echo OK'
]))
r.match('^\\d+: received ping$', '^\\d+: received pong$', '^OK$')
@test(20, "primes")
def test_primes():
r.run_qemu(shell_script([
'primes', 'echo OK'
]))
args = ['prime %d' % i for i in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]]
args.append('^OK$')
r.match(*args)
@test(10, "find, in current directory")
def test_find_curdir():
fn = random_str()
r.run_qemu(shell_script([
'echo > %s' % fn,
'find . %s' % fn
]))
r.match('./%s' % fn)
@test(10, "find, recursive")
def test_find_recursive():
needle = random_str()
dirs = [random_str() for _ in range(3)]
r.run_qemu(shell_script([
'mkdir %s' % dirs[0],
'echo > %s/%s' % (dirs[0], needle),
'mkdir %s/%s' % (dirs[0], dirs[1]),
'echo > %s/%s/%s' % (dirs[0], dirs[1], needle),
'mkdir %s' % dirs[2],
'echo > %s/%s' % (dirs[2], needle),
'find . %s' % needle
]))
r.match('./%s/%s' % (dirs[0], needle),
'./%s/%s/%s' % (dirs[0], dirs[1], needle),
'./%s/%s' % (dirs[2], needle))
@test(19, "xargs")
def test_xargs():
r.run_qemu(shell_script([
'sh < xargstest.sh',
'echo DONE',
], 'DONE'))
matches = re.findall("hello", r.qemu.output)
assert_equal(len(matches), 3, "Number of appearances of 'hello'")
@test(1, "time")
def test_time():
check_time()
run_tests()

View File

@ -33,13 +33,34 @@ struct {
struct buf head;
} bcache;
#define NLOCK_BUCKETS 13
// #define ORIGINAL
struct spinlock hash_locks[NLOCK_BUCKETS];
static char lock_names [NLOCK_BUCKETS][20];
struct hash_tbl {
struct buf* buf;
struct hash_tbl *next;
} hash_tbl;
struct hash_tbl* hash_entry[NLOCK_BUCKETS];
struct hash_tbl hash_tbls[NBUF];
void
binit(void)
{
struct buf *b;
initlock(&bcache.lock, "bcache");
for (int i = 0; i < NLOCK_BUCKETS; ++ i) {
snprintf(lock_names[i], 20, "bcache.lk%d", i);
initlock(&hash_locks[i], lock_names[i]);
hash_entry[i] = 0;
}
for (int i = 0; i < NBUF; ++ i) {
hash_tbls[i].buf = &bcache.buf[i];
hash_tbls[i].next = 0;
}
// Create linked list of buffers
bcache.head.prev = &bcache.head;
bcache.head.next = &bcache.head;
@ -58,12 +79,69 @@ binit(void)
static struct buf*
bget(uint dev, uint blockno)
{
struct buf *b;
struct buf *b = 0;
// acquire(&bcache.lock);
#ifndef ORIGINAL
int bucket = blockno % NLOCK_BUCKETS;
acquire(&hash_locks[bucket]);
struct hash_tbl* h = hash_entry[bucket];
struct hash_tbl* ph = 0;
while (h != 0) {
if (h->buf->dev == dev && h->buf->blockno == blockno) {
b = h->buf;
b->refcnt ++;
release(&hash_locks[bucket]);
// release(&bcache.lock);
acquiresleep(&b->lock);
return b;
}
h = h->next;
}
release(&hash_locks[bucket]);
// release the unfound bucket, or there'll be deadlock
acquire(&bcache.lock);
// after bcache.lock is aquired, only b->refcnt maybe out of our control
// so, have to aquire hash_locks[victim_bucket] before test refcnt
for(int i = 0; i < NBUF; ++ i){
b = &bcache.buf[i];
int victim_bucket = b->blockno % NLOCK_BUCKETS;
acquire(&hash_locks[victim_bucket]);
if(b->refcnt == 0) {
b->dev = dev;
b->blockno = blockno;
b->valid = 0;
b->refcnt = 1;
// try to release hash entry from the old bucket
h = hash_entry[victim_bucket];
ph = 0;
while (h != 0) {
if (h->buf->dev == b->dev && h->buf->blockno == b->blockno) {
if (ph) ph->next = h->next;
else hash_entry[victim_bucket] = h->next;
break;
}
ph = h;
h = h->next;
}
release(&hash_locks[victim_bucket]);
release(&bcache.lock);
// this should avoid deadlock and meanwhile guard the hashtable
// because after b is set, the only thing we need is hashtable rather than buf attributes
h = &hash_tbls[i];
acquire(&hash_locks[bucket]);
h->next = hash_entry[bucket];
hash_entry[bucket] = h;
release(&hash_locks[bucket]);
acquiresleep(&b->lock);
return b;
}
release(&hash_locks[victim_bucket]);
}
#else
// Is the block already cached?
for(b = bcache.head.next; b != &bcache.head; b = b->next){
// for(b = bcache.head.next; b != &bcache.head; b = b->next){
for (b = &bcache.buf[0]; b < &bcache.buf[NBUF]; ++ b) {
if(b->dev == dev && b->blockno == blockno){
b->refcnt++;
release(&bcache.lock);
@ -74,7 +152,8 @@ bget(uint dev, uint blockno)
// Not cached.
// Recycle the least recently used (LRU) unused buffer.
for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
// for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
for(b = &bcache.buf[0]; b < &bcache.buf[NBUF]; ++ b){
if(b->refcnt == 0) {
b->dev = dev;
b->blockno = blockno;
@ -85,6 +164,7 @@ bget(uint dev, uint blockno)
return b;
}
}
#endif
panic("bget: no buffers");
}
@ -121,19 +201,25 @@ brelse(struct buf *b)
releasesleep(&b->lock);
acquire(&bcache.lock);
// if you use bcache lock, test0 fails without doubt
// the reason why this works is that, whenever b->refcnt is written or read
// its 'hash_locks[H(b->blockno)]' will always been locked
// the reason why b->blockno is valid is that, only when refcnt==0 could this field be written
// but here it must be non-zero before lock aquired
int bucket = b->blockno % NLOCK_BUCKETS;
acquire(&hash_locks[bucket]);
b->refcnt--;
if (b->refcnt == 0) {
// no one is waiting for it.
b->next->prev = b->prev;
b->prev->next = b->next;
b->next = bcache.head.next;
b->prev = &bcache.head;
bcache.head.next->prev = b;
bcache.head.next = b;
}
// if (b->refcnt == 0) {
// // no one is waiting for it.
// b->next->prev = b->prev;
// b->prev->next = b->next;
// b->next = bcache.head.next;
// b->prev = &bcache.head;
// bcache.head.next->prev = b;
// bcache.head.next = b;
// }
release(&bcache.lock);
release(&hash_locks[bucket]);
}
void

View File

@ -8,6 +8,10 @@ struct spinlock;
struct sleeplock;
struct stat;
struct superblock;
#ifdef LAB_NET
struct mbuf;
struct sock;
#endif
// bio.c
void binit(void);
@ -117,6 +121,10 @@ void initlock(struct spinlock*, char*);
void release(struct spinlock*);
void push_off(void);
void pop_off(void);
int atomic_read4(int *addr);
#ifdef LAB_LOCK
void freelock(struct spinlock*);
#endif
// sleeplock.c
void acquiresleep(struct sleeplock*);
@ -187,3 +195,44 @@ void virtio_disk_intr(void);
// number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
#ifdef LAB_PGTBL
// vmcopyin.c
int copyin_new(pagetable_t, char *, uint64, uint64);
int copyinstr_new(pagetable_t, char *, uint64, uint64);
#endif
// stats.c
void statsinit(void);
void statsinc(void);
// sprintf.c
int snprintf(char*, int, char*, ...);
#ifdef KCSAN
void kcsaninit();
#endif
#ifdef LAB_NET
// pci.c
void pci_init();
// e1000.c
void e1000_init(uint32 *);
void e1000_intr(void);
int e1000_transmit(struct mbuf*);
// net.c
void net_rx(struct mbuf*);
void net_tx_udp(struct mbuf*, uint32, uint16, uint16);
// sysnet.c
void sockinit(void);
int sockalloc(struct file **, uint32, uint16, uint16);
void sockclose(struct sock *);
int sockread(struct sock *, uint64, int);
int sockwrite(struct sock *, uint64, int);
void sockrecvudp(struct mbuf*, uint32, uint16, uint16);
#endif

View File

@ -1,10 +1,17 @@
struct file {
#ifdef LAB_NET
enum { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE, FD_SOCK } type;
#else
enum { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE } type;
#endif
int ref; // reference count
char readable;
char writable;
struct pipe *pipe; // FD_PIPE
struct inode *ip; // FD_INODE and FD_DEVICE
#ifdef LAB_NET
struct sock *sock; // FD_SOCK
#endif
uint off; // FD_INODE
short major; // FD_DEVICE
};
@ -38,3 +45,4 @@ struct devsw {
extern struct devsw devsw[];
#define CONSOLE 1
#define STATS 2

View File

@ -295,11 +295,11 @@ ilock(struct inode *ip)
struct buf *bp;
struct dinode *dip;
if(ip == 0 || ip->ref < 1)
if(ip == 0 || atomic_read4(&ip->ref) < 1)
panic("ilock");
acquiresleep(&ip->lock);
if(ip->valid == 0){
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
dip = (struct dinode*)bp->data + ip->inum%IPB;
@ -320,7 +320,7 @@ ilock(struct inode *ip)
void
iunlock(struct inode *ip)
{
if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1)
if(ip == 0 || !holdingsleep(&ip->lock) || atomic_read4(&ip->ref) < 1)
panic("iunlock");
releasesleep(&ip->lock);
@ -416,7 +416,6 @@ bmap(struct inode *ip, uint bn)
brelse(bp);
return addr;
}
panic("bmap: out of range");
}
@ -447,7 +446,7 @@ itrunc(struct inode *ip)
bfree(ip->dev, ip->addrs[NDIRECT]);
ip->addrs[NDIRECT] = 0;
}
ip->size = 0;
iupdate(ip);
}

View File

@ -19,14 +19,19 @@ struct run {
};
struct {
struct spinlock lock;
struct run *freelist;
struct spinlock lock[NCPU];
struct run *freelist[NCPU];
} kmem;
static char kmem_lock_names [NCPU][10]; // ugly but have-to impl
void
kinit()
{
initlock(&kmem.lock, "kmem");
for (int i = 0; i < NCPU; ++ i) {
snprintf(kmem_lock_names[i], NELEM(kmem_lock_names[0]), "kmem%d", i);
initlock(&kmem.lock[i], kmem_lock_names[i]);
}
freerange(end, (void*)PHYSTOP);
}
@ -55,11 +60,13 @@ kfree(void *pa)
memset(pa, 1, PGSIZE);
r = (struct run*)pa;
acquire(&kmem.lock);
r->next = kmem.freelist;
kmem.freelist = r;
release(&kmem.lock);
push_off();
int _cpu = cpuid();
acquire(&kmem.lock[_cpu]);
r->next = kmem.freelist[_cpu];
kmem.freelist[_cpu] = r;
release(&kmem.lock[_cpu]);
pop_off();
}
// Allocate one 4096-byte page of physical memory.
@ -69,13 +76,27 @@ void *
kalloc(void)
{
struct run *r;
acquire(&kmem.lock);
r = kmem.freelist;
push_off();
int _cpu = cpuid();
acquire(&kmem.lock[_cpu]);
r = kmem.freelist[_cpu];
if(r)
kmem.freelist = r->next;
release(&kmem.lock);
kmem.freelist[_cpu] = r->next;
else {
for (int i = 0, found = 0; i < NCPU && !found; ++ i) {
if (i != _cpu) {
acquire(&kmem.lock[i]);
if (kmem.freelist[i]) {
r = kmem.freelist[i];
kmem.freelist[i] = r->next;
found = 1;
}
release(&kmem.lock[i]);
}
}
}
release(&kmem.lock[_cpu]);
pop_off();
if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;

323
kernel/kcsan.c Normal file
View File

@ -0,0 +1,323 @@
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "spinlock.h"
#include "riscv.h"
#include "proc.h"
#include "defs.h"
//
// Race detector using gcc's thread sanitizer. It delays all stores
// and loads and monitors if any other CPU is using the same address.
// If so, we have a race and print out the backtrace of the thread
// that raced and the thread that set the watchpoint.
//
//
// To run with kcsan:
// make clean
// make KCSAN=1 qemu
//
// The number of watch points.
#define NWATCH (NCPU)
// The number of cycles to delay stores, whatever that means on qemu.
//#define DELAY_CYCLES 20000
#define DELAY_CYCLES 200000
#define MAXTRACE 20
int
trace(uint64 *trace, int maxtrace)
{
uint64 i = 0;
push_off();
uint64 fp = r_fp();
uint64 ra, low = PGROUNDDOWN(fp) + 16, high = PGROUNDUP(fp);
while(!(fp & 7) && fp >= low && fp < high){
ra = *(uint64*)(fp - 8);
fp = *(uint64*)(fp - 16);
trace[i++] = ra;
if(i >= maxtrace)
break;
}
pop_off();
return i;
}
struct watch {
uint64 addr;
int write;
int race;
uint64 trace[MAXTRACE];
int tracesz;
};
struct {
struct spinlock lock;
struct watch points[NWATCH];
int on;
} tsan;
static struct watch*
wp_lookup(uint64 addr)
{
for(struct watch *w = &tsan.points[0]; w < &tsan.points[NWATCH]; w++) {
if(w->addr == addr) {
return w;
}
}
return 0;
}
static int
wp_install(uint64 addr, int write)
{
for(struct watch *w = &tsan.points[0]; w < &tsan.points[NWATCH]; w++) {
if(w->addr == 0) {
w->addr = addr;
w->write = write;
w->tracesz = trace(w->trace, MAXTRACE);
return 1;
}
}
panic("wp_install");
return 0;
}
static void
wp_remove(uint64 addr)
{
for(struct watch *w = &tsan.points[0]; w < &tsan.points[NWATCH]; w++) {
if(w->addr == addr) {
w->addr = 0;
w->tracesz = 0;
return;
}
}
panic("remove");
}
static void
printtrace(uint64 *t, int n)
{
int i;
for(i = 0; i < n; i++) {
printf("%p\n", t[i]);
}
}
static void
race(char *s, struct watch *w) {
uint64 t[MAXTRACE];
int n;
n = trace(t, MAXTRACE);
printf("== race detected ==\n");
printf("backtrace for racing %s\n", s);
printtrace(t, n);
printf("backtrace for watchpoint:\n");
printtrace(w->trace, w->tracesz);
printf("==========\n");
}
// cycle counter
static inline uint64
r_cycle()
{
uint64 x;
asm volatile("rdcycle %0" : "=r" (x) );
return x;
}
static void delay(void) __attribute__((noinline));
static void delay() {
uint64 stop = r_cycle() + DELAY_CYCLES;
uint64 c = r_cycle();
while(c < stop) {
c = r_cycle();
}
}
static void
kcsan_read(uint64 addr, int sz)
{
struct watch *w;
acquire(&tsan.lock);
if((w = wp_lookup(addr)) != 0) {
if(w->write) {
race("load", w);
}
release(&tsan.lock);
return;
}
release(&tsan.lock);
}
static void
kcsan_write(uint64 addr, int sz)
{
struct watch *w;
acquire(&tsan.lock);
if((w = wp_lookup(addr)) != 0) {
race("store", w);
release(&tsan.lock);
}
// no watchpoint; try to install one
if(wp_install(addr, 1)) {
release(&tsan.lock);
// XXX maybe read value at addr before and after delay to catch
// races of unknown origins (e.g., device).
delay();
acquire(&tsan.lock);
wp_remove(addr);
}
release(&tsan.lock);
}
// tsan.on will only have effect with "make KCSAN=1"
void
kcsaninit(void)
{
initlock(&tsan.lock, "tsan");
tsan.on = 1;
__sync_synchronize();
}
//
// Calls inserted by compiler into kernel binary, except for this file.
//
void
__tsan_init(void)
{
}
void
__tsan_read1(uint64 addr)
{
if(!tsan.on)
return;
// kcsan_read(addr, 1);
}
void
__tsan_read2(uint64 addr)
{
if(!tsan.on)
return;
kcsan_read(addr, 2);
}
void
__tsan_read4(uint64 addr)
{
if(!tsan.on)
return;
kcsan_read(addr, 4);
}
void
__tsan_read8(uint64 addr)
{
if(!tsan.on)
return;
kcsan_read(addr, 8);
}
void
__tsan_read_range(uint64 addr, uint64 size)
{
if(!tsan.on)
return;
kcsan_read(addr, size);
}
void
__tsan_write1(uint64 addr)
{
if(!tsan.on)
return;
// kcsan_write(addr, 1);
}
void
__tsan_write2(uint64 addr)
{
if(!tsan.on)
return;
kcsan_write(addr, 2);
}
void
__tsan_write4(uint64 addr)
{
if(!tsan.on)
return;
kcsan_write(addr, 4);
}
void
__tsan_write8(uint64 addr)
{
if(!tsan.on)
return;
kcsan_write(addr, 8);
}
void
__tsan_write_range(uint64 addr, uint64 size)
{
if(!tsan.on)
return;
kcsan_write(addr, size);
}
void
__tsan_atomic_thread_fence(int order)
{
__sync_synchronize();
}
uint32
__tsan_atomic32_load(uint *ptr, uint *val, int order)
{
uint t;
__atomic_load(ptr, &t, __ATOMIC_SEQ_CST);
return t;
}
void
__tsan_atomic32_store(uint *ptr, uint val, int order)
{
__atomic_store(ptr, &val, __ATOMIC_SEQ_CST);
}
// We don't use this
void
__tsan_func_entry(uint64 pc)
{
}
// We don't use this
void
__tsan_func_exit(void)
{
}

View File

@ -12,6 +12,9 @@ main()
{
if(cpuid() == 0){
consoleinit();
#if defined(LAB_LOCK)
statsinit();
#endif
printfinit();
printf("\n");
printf("xv6 kernel is booting\n");
@ -28,11 +31,18 @@ main()
iinit(); // inode table
fileinit(); // file table
virtio_disk_init(); // emulated hard disk
#ifdef LAB_NET
pci_init();
sockinit();
#endif
userinit(); // first user process
#ifdef KCSAN
kcsaninit();
#endif
__sync_synchronize();
started = 1;
} else {
while(started == 0)
while(atomic_read4((int *) &started) == 0)
;
__sync_synchronize();
printf("hart %d starting\n", cpuid());

View File

@ -68,6 +68,9 @@ pipeclose(struct pipe *pi, int writable)
}
if(pi->readopen == 0 && pi->writeopen == 0){
release(&pi->lock);
#ifdef LAB_LOCK
freelock(&pi->lock);
#endif
kfree((char*)pi);
} else
release(&pi->lock);

View File

@ -295,6 +295,14 @@ r_sp()
return x;
}
static inline uint64
r_fp()
{
uint64 x;
asm volatile("mv %0, s0" : "=r" (x) );
return x;
}
// read and write tp, the thread pointer, which xv6 uses to hold
// this core's hartid (core number), the index into cpus[].
static inline uint64
@ -344,6 +352,9 @@ typedef uint64 *pagetable_t; // 512 PTEs
#define PTE_X (1L << 3)
#define PTE_U (1L << 4) // user can access
// shift a physical address to the right place for a PTE.
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)

View File

@ -8,12 +8,52 @@
#include "proc.h"
#include "defs.h"
#ifdef LAB_LOCK
#define NLOCK 500
static struct spinlock *locks[NLOCK];
struct spinlock lock_locks;
void
freelock(struct spinlock *lk)
{
acquire(&lock_locks);
int i;
for (i = 0; i < NLOCK; i++) {
if(locks[i] == lk) {
locks[i] = 0;
break;
}
}
release(&lock_locks);
}
static void
findslot(struct spinlock *lk) {
acquire(&lock_locks);
int i;
for (i = 0; i < NLOCK; i++) {
if(locks[i] == 0) {
locks[i] = lk;
release(&lock_locks);
return;
}
}
panic("findslot");
}
#endif
void
initlock(struct spinlock *lk, char *name)
{
lk->name = name;
lk->locked = 0;
lk->cpu = 0;
#ifdef LAB_LOCK
lk->nts = 0;
lk->n = 0;
findslot(lk);
#endif
}
// Acquire the lock.
@ -25,12 +65,21 @@ acquire(struct spinlock *lk)
if(holding(lk))
panic("acquire");
#ifdef LAB_LOCK
__sync_fetch_and_add(&(lk->n), 1);
#endif
// On RISC-V, sync_lock_test_and_set turns into an atomic swap:
// a5 = 1
// s1 = &lk->locked
// amoswap.w.aq a5, a5, (s1)
while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
;
while(__sync_lock_test_and_set(&lk->locked, 1) != 0) {
#ifdef LAB_LOCK
__sync_fetch_and_add(&(lk->nts), 1);
#else
;
#endif
}
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
@ -108,3 +157,61 @@ pop_off(void)
if(c->noff == 0 && c->intena)
intr_on();
}
// Read a shared 32-bit value without holding a lock
int
atomic_read4(int *addr) {
uint32 val;
__atomic_load(addr, &val, __ATOMIC_SEQ_CST);
return val;
}
#ifdef LAB_LOCK
int
snprint_lock(char *buf, int sz, struct spinlock *lk)
{
int n = 0;
if(lk->n > 0) {
n = snprintf(buf, sz, "lock: %s: #test-and-set %d #acquire() %d\n",
lk->name, lk->nts, lk->n);
}
return n;
}
int
statslock(char *buf, int sz) {
int n;
int tot = 0;
acquire(&lock_locks);
n = snprintf(buf, sz, "--- lock kmem/bcache stats\n");
for(int i = 0; i < NLOCK; i++) {
if(locks[i] == 0)
break;
if(strncmp(locks[i]->name, "bcache", strlen("bcache")) == 0 ||
strncmp(locks[i]->name, "kmem", strlen("kmem")) == 0) {
tot += locks[i]->nts;
n += snprint_lock(buf +n, sz-n, locks[i]);
}
}
n += snprintf(buf+n, sz-n, "--- top 5 contended locks:\n");
int last = 100000000;
// stupid way to compute top 5 contended locks
for(int t = 0; t < 5; t++) {
int top = 0;
for(int i = 0; i < NLOCK; i++) {
if(locks[i] == 0)
break;
if(locks[i]->nts > locks[top]->nts && locks[i]->nts < last) {
top = i;
}
}
n += snprint_lock(buf+n, sz-n, locks[top]);
last = locks[top]->nts;
}
n += snprintf(buf+n, sz-n, "tot= %d\n", tot);
release(&lock_locks);
return n;
}
#endif

View File

@ -5,5 +5,9 @@ struct spinlock {
// For debugging:
char *name; // Name of lock.
struct cpu *cpu; // The cpu holding the lock.
#ifdef LAB_LOCK
int nts;
int n;
#endif
};

91
kernel/sprintf.c Normal file
View File

@ -0,0 +1,91 @@
#include <stdarg.h>
#include "types.h"
#include "param.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "file.h"
#include "riscv.h"
#include "defs.h"
static char digits[] = "0123456789abcdef";
static int
sputc(char *s, char c)
{
*s = c;
return 1;
}
static int
sprintint(char *s, int xx, int base, int sign)
{
char buf[16];
int i, n;
uint x;
if(sign && (sign = xx < 0))
x = -xx;
else
x = xx;
i = 0;
do {
buf[i++] = digits[x % base];
} while((x /= base) != 0);
if(sign)
buf[i++] = '-';
n = 0;
while(--i >= 0)
n += sputc(s+n, buf[i]);
return n;
}
int
snprintf(char *buf, int sz, char *fmt, ...)
{
va_list ap;
int i, c;
int off = 0;
char *s;
if (fmt == 0)
panic("null fmt");
va_start(ap, fmt);
for(i = 0; off < sz && (c = fmt[i] & 0xff) != 0; i++){
if(c != '%'){
off += sputc(buf+off, c);
continue;
}
c = fmt[++i] & 0xff;
if(c == 0)
break;
switch(c){
case 'd':
off += sprintint(buf+off, va_arg(ap, int), 10, 1);
break;
case 'x':
off += sprintint(buf+off, va_arg(ap, int), 16, 1);
break;
case 's':
if((s = va_arg(ap, char*)) == 0)
s = "(null)";
for(; *s && off < sz; s++)
off += sputc(buf+off, *s);
break;
case '%':
off += sputc(buf+off, '%');
break;
default:
// Print unknown % sequence to draw attention.
off += sputc(buf+off, '%');
off += sputc(buf+off, c);
break;
}
}
return off;
}

View File

@ -38,6 +38,11 @@ start()
w_mideleg(0xffff);
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
#ifdef KCSAN
// allow supervisor to read cycle counter register
w_mcounteren(r_mcounteren()|0x3);
#endif
// configure Physical Memory Protection to give supervisor mode
// access to all of physical memory.
w_pmpaddr0(0x3fffffffffffffull);

69
kernel/stats.c Normal file
View File

@ -0,0 +1,69 @@
#include <stdarg.h>
#include "types.h"
#include "param.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "file.h"
#include "riscv.h"
#include "defs.h"
#define BUFSZ 4096
static struct {
struct spinlock lock;
char buf[BUFSZ];
int sz;
int off;
} stats;
int statscopyin(char*, int);
int statslock(char*, int);
int
statswrite(int user_src, uint64 src, int n)
{
return -1;
}
int
statsread(int user_dst, uint64 dst, int n)
{
int m;
acquire(&stats.lock);
if(stats.sz == 0) {
#ifdef LAB_PGTBL
stats.sz = statscopyin(stats.buf, BUFSZ);
#endif
#ifdef LAB_LOCK
stats.sz = statslock(stats.buf, BUFSZ);
#endif
}
m = stats.sz - stats.off;
if (m > 0) {
if(m > n)
m = n;
if(either_copyout(user_dst, dst, stats.buf+stats.off, m) != -1) {
stats.off += m;
}
} else {
m = -1;
stats.sz = 0;
stats.off = 0;
}
release(&stats.lock);
return m;
}
void
statsinit(void)
{
initlock(&stats.lock, "stats");
devsw[STATS].read = statsread;
devsw[STATS].write = statswrite;
}

View File

@ -55,6 +55,8 @@ sys_sleep(void)
uint ticks0;
argint(0, &n);
if(n < 0)
n = 0;
acquire(&tickslock);
ticks0 = ticks;
while(ticks - ticks0 < n){

View File

@ -1 +1 @@
6
9

189
user/bcachetest.c Normal file
View File

@ -0,0 +1,189 @@
#include "kernel/fcntl.h"
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/riscv.h"
#include "kernel/fs.h"
#include "user/user.h"
void test0();
void test1();
#define SZ 4096
char buf[SZ];
int
main(int argc, char *argv[])
{
test0();
test1();
exit(0);
}
void
createfile(char *file, int nblock)
{
int fd;
char buf[BSIZE];
int i;
fd = open(file, O_CREATE | O_RDWR);
if(fd < 0){
printf("createfile %s failed\n", file);
exit(-1);
}
for(i = 0; i < nblock; i++) {
if(write(fd, buf, sizeof(buf)) != sizeof(buf)) {
printf("write %s failed\n", file);
exit(-1);
}
}
close(fd);
}
void
readfile(char *file, int nbytes, int inc)
{
char buf[BSIZE];
int fd;
int i;
if(inc > BSIZE) {
printf("readfile: inc too large\n");
exit(-1);
}
if ((fd = open(file, O_RDONLY)) < 0) {
printf("readfile open %s failed\n", file);
exit(-1);
}
for (i = 0; i < nbytes; i += inc) {
if(read(fd, buf, inc) != inc) {
printf("read %s failed for block %d (%d)\n", file, i, nbytes);
exit(-1);
}
}
close(fd);
}
int ntas(int print)
{
int n;
char *c;
if (statistics(buf, SZ) <= 0) {
fprintf(2, "ntas: no stats\n");
}
c = strchr(buf, '=');
n = atoi(c+2);
if(print)
printf("%s", buf);
return n;
}
// Test reading small files concurrently
void
test0()
{
char file[2];
char dir[2];
enum { N = 10, NCHILD = 3 };
int m, n;
dir[0] = '0';
dir[1] = '\0';
file[0] = 'F';
file[1] = '\0';
printf("start test0\n");
for(int i = 0; i < NCHILD; i++){
dir[0] = '0' + i;
mkdir(dir);
if (chdir(dir) < 0) {
printf("chdir failed\n");
exit(1);
}
unlink(file);
createfile(file, N);
if (chdir("..") < 0) {
printf("chdir failed\n");
exit(1);
}
}
m = ntas(0);
for(int i = 0; i < NCHILD; i++){
dir[0] = '0' + i;
int pid = fork();
if(pid < 0){
printf("fork failed");
exit(-1);
}
if(pid == 0){
if (chdir(dir) < 0) {
printf("chdir failed\n");
exit(1);
}
readfile(file, N*BSIZE, 1);
exit(0);
}
}
for(int i = 0; i < NCHILD; i++){
wait(0);
}
printf("test0 results:\n");
n = ntas(1);
if (n-m < 500)
printf("test0: OK\n");
else
printf("test0: FAIL\n");
}
// Test bcache evictions by reading a large file concurrently
void test1()
{
char file[3];
enum { N = 200, BIG=100, NCHILD=2 };
printf("start test1\n");
file[0] = 'B';
file[2] = '\0';
for(int i = 0; i < NCHILD; i++){
file[1] = '0' + i;
unlink(file);
if (i == 0) {
createfile(file, BIG);
} else {
createfile(file, 1);
}
}
for(int i = 0; i < NCHILD; i++){
file[1] = '0' + i;
int pid = fork();
if(pid < 0){
printf("fork failed");
exit(-1);
}
if(pid == 0){
if (i==0) {
for (i = 0; i < N; i++) {
readfile(file, BIG*BSIZE, BSIZE);
}
unlink(file);
exit(0);
} else {
for (i = 0; i < N*20; i++) {
readfile(file, 1, BSIZE);
}
unlink(file);
}
exit(0);
}
}
for(int i = 0; i < NCHILD; i++){
wait(0);
}
printf("test1 OK\n");
}

View File

@ -1,5 +1,6 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
char buf[512];
@ -32,7 +33,7 @@ main(int argc, char *argv[])
}
for(i = 1; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){
if((fd = open(argv[i], O_RDONLY)) < 0){
fprintf(2, "cat: cannot open %s\n", argv[i]);
exit(1);
}

View File

@ -1,74 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/stat.h"
#include "kernel/fs.h"
int fmtname(const char *path) {
int p;
for(p=strlen(path); p >= 0 && *(path + p) != '/'; p--);
p++;
return p;
}
void find(const char *path, const 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_DEVICE:
case T_FILE:
if (strcmp(path + fmtname(path), target) == 0) {
printf("%s\n", path);
}
break;
case T_DIR:
if (strcmp(path + fmtname(path), target) == 0) {
printf("%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;
if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("find: cannot stat %s\n", buf);
continue;
}
find(buf, target);
}
break;
}
close(fd);
}
int main(int argc, char** argv) {
if (argc != 3) {
printf("\x1b[31mInvalid arguments\x1b[0m\n");
printf("\x1b[32musage\x1b[0m: find <base directory> <file name to find>\n");
exit(0);
}
find(argv[1], argv[2]);
exit(0);
}

View File

@ -2,6 +2,7 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
char buf[1024];
@ -51,7 +52,7 @@ main(int argc, char *argv[])
}
for(i = 2; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){
if((fd = open(argv[i], O_RDONLY)) < 0){
printf("grep: cannot open %s\n", argv[i]);
exit(1);
}

View File

@ -18,6 +18,7 @@ main(void)
if(open("console", O_RDWR) < 0){
mknod("console", CONSOLE, 0);
mknod("statistics", STATS, 0);
open("console", O_RDWR);
}
dup(0); // stdout

161
user/kalloctest.c Normal file
View File

@ -0,0 +1,161 @@
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/riscv.h"
#include "kernel/memlayout.h"
#include "kernel/fcntl.h"
#include "user/user.h"
#define NCHILD 2
#define N 100000
#define SZ 4096
void test1(void);
void test2(void);
void test3(void);
char buf[SZ];
int
main(int argc, char *argv[])
{
test1();
test2();
test3();
exit(0);
}
int ntas(int print)
{
int n;
char *c;
if (statistics(buf, SZ) <= 0) {
fprintf(2, "ntas: no stats\n");
}
c = strchr(buf, '=');
n = atoi(c+2);
if(print)
printf("%s", buf);
return n;
}
// Test concurrent kallocs and kfrees
void test1(void)
{
void *a, *a1;
int n, m;
printf("start test1\n");
m = ntas(0);
for(int i = 0; i < NCHILD; i++){
int pid = fork();
if(pid < 0){
printf("fork failed");
exit(-1);
}
if(pid == 0){
for(i = 0; i < N; i++) {
a = sbrk(4096);
*(int *)(a+4) = 1;
a1 = sbrk(-4096);
if (a1 != a + 4096) {
printf("wrong sbrk\n");
exit(-1);
}
}
exit(-1);
}
}
for(int i = 0; i < NCHILD; i++){
wait(0);
}
printf("test1 results:\n");
n = ntas(1);
if(n-m < 10)
printf("test1 OK\n");
else
printf("test1 FAIL\n");
}
//
// countfree() from usertests.c
//
int
countfree()
{
uint64 sz0 = (uint64)sbrk(0);
int n = 0;
while(1){
uint64 a = (uint64) sbrk(4096);
if(a == 0xffffffffffffffff){
break;
}
// modify the memory to make sure it's really allocated.
*(char *)(a + 4096 - 1) = 1;
n += 1;
}
sbrk(-((uint64)sbrk(0) - sz0));
return n;
}
// Test stealing
void test2() {
int free0 = countfree();
int free1;
int n = (PHYSTOP-KERNBASE)/PGSIZE;
printf("start test2\n");
printf("total free number of pages: %d (out of %d)\n", free0, n);
if(n - free0 > 1000) {
printf("test2 FAILED: cannot allocate enough memory");
exit(-1);
}
for (int i = 0; i < 50; i++) {
free1 = countfree();
if(i % 10 == 9)
printf(".");
if(free1 != free0) {
printf("test2 FAIL: losing pages\n");
exit(-1);
}
}
printf("\ntest2 OK\n");
}
// Test concurrent kalloc/kfree and stealing
void test3(void)
{
void *a, *a1;
printf("start test3\n");
for(int i = 0; i < NCHILD; i++){
int pid = fork();
if(pid < 0){
printf("fork failed");
exit(-1);
}
if(pid == 0){
if (i == 0) {
for(i = 0; i < N; i++) {
a = sbrk(4096);
*(int *)(a+4) = 1;
a1 = sbrk(-4096);
if (a1 != a + 4096) {
printf("wrong sbrk\n");
exit(-1);
}
}
printf("child done %d\n", i);
exit(0);
} else {
countfree();
printf("child done %d\n", i);
exit(0);
}
}
}
for(int i = 0; i < NCHILD; i++){
wait(0);
}
printf("test3 OK\n");
}

View File

@ -2,6 +2,7 @@
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/fcntl.h"
char*
fmtname(char *path)
@ -30,7 +31,7 @@ ls(char *path)
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
if((fd = open(path, O_RDONLY)) < 0){
fprintf(2, "ls: cannot open %s\n", path);
return;
}

View File

@ -1,27 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char** argv) {
int p[2]; // hell, read from p[0] and write to p[1] no matter in pa/ch
if (pipe(p) == -1) exit(1);
uint8 senddata[] = {0xAC};
uint8 recvdata[] = {0};
int pid = fork();
if (pid == 0) {
// child
if (read(p[0], recvdata, 1) != 1) printf("error child read\n");
close(p[0]);
printf("%d: received ping\n", getpid());
if (write(p[1], senddata, 1) != 1) printf("error child write\n");
close(p[1]);
exit(0);
}
else {
if (write(p[1], senddata, 1) != 1) printf("error parent write\n");
close(p[1]);
if (read(p[0], recvdata, 1) != 1) printf("error parent read\n");
printf("%d: received pong\n", getpid());
close(p[0]);
exit(0);
}
}

View File

@ -1,51 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
void pipeline(int leftfd) {
int pipefd[2];
int p; read(leftfd, &p, sizeof(p));
printf("prime %d\n", p);
int n;
int has_right = 0;
while (read(leftfd, &n, sizeof(n)) > 0) {
if (n % p != 0) {
if (!has_right) {
has_right = 1;
pipe(pipefd);
if (fork() == 0) {
close(pipefd[1]);
pipeline(pipefd[0]);
} else {
close(pipefd[0]);
}
}
write(pipefd[1], &n, sizeof(n));
}
}
close(leftfd);
if (has_right) {
close(pipefd[1]);
while(wait(0) != -1);
}
exit(0);
}
int main(int argc, char** argv) {
int pipefd[2];
pipe(pipefd);
int pid = fork();
if (pid != 0) {
close(pipefd[0]); // no need to read in the feeding proc
for (int i = 2; i <= 35; ++ i) {
write(pipefd[1], &i, sizeof(i));
}
close(pipefd[1]);
while(wait(0) != -1);
exit(0);
}
else {
close(pipefd[1]); // pipeline proc will have its own write pipe
pipeline(pipefd[0]);
}
exit(0);
}

View File

@ -1,17 +0,0 @@
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char** argv) {
if (argc != 2) {
printf("\x1b[31mUnexpected argument\x1b[0m\n");
printf("usage: sleep <ticks>\n");
exit(0);
}
int ticks = atoi(argv[1]);
int ret = sleep(ticks);
if (ret) {
printf("\x1b[31mFailed to sleep\x1b[0m\n");
exit(-1);
}
exit(0);
}

24
user/statistics.c Normal file
View File

@ -0,0 +1,24 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
int
statistics(void *buf, int sz)
{
int fd, i, n;
fd = open("statistics", O_RDONLY);
if(fd < 0) {
fprintf(2, "stats: open failed\n");
exit(1);
}
for (i = 0; i < sz; ) {
if ((n = read(fd, buf+i, sz-i)) < 0) {
break;
}
i += n;
}
close(fd);
return i;
}

24
user/stats.c Normal file
View File

@ -0,0 +1,24 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
#define SZ 4096
char buf[SZ];
int
main(void)
{
int i, n;
while (1) {
n = statistics(buf, SZ);
for (i = 0; i < n; i++) {
write(1, buf+i, 1);
}
if (n != SZ)
break;
}
exit(0);
}

View File

@ -22,6 +22,14 @@ int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
#ifdef LAB_NET
int connect(uint32, uint16, uint16);
#endif
#ifdef LAB_PGTBL
int pgaccess(void *base, int len, void *mask);
// usyscall region
int ugetpid(void);
#endif
// ulib.c
int stat(const char*, struct stat*);
@ -39,3 +47,4 @@ void free(void*);
int atoi(const char*);
int memcmp(const void *, const void *, uint);
void *memcpy(void *, const void *, uint);
int statistics(void*, int);

View File

@ -1,5 +1,6 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
char buf[512];
@ -43,7 +44,7 @@ main(int argc, char *argv[])
}
for(i = 1; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){
if((fd = open(argv[i], O_RDONLY)) < 0){
printf("wc: cannot open %s\n", argv[i]);
exit(1);
}

View File

@ -1,59 +0,0 @@
#include "kernel/param.h"
#include "kernel/types.h"
#include "user/user.h"
static inline int is_ws(const char ch) {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
}
int main(int argc, char** argv) {
int STDIN = 0;
char linebuf[1024];
char* p = linebuf;
char* _argv[MAXARG];
while (read(STDIN, p, 1) > 0) {
if (*p == '\n') {
int i;
int in_ws = 1;
for (i = 0; i < argc - 1; ++i) {
_argv[i] = argv[i + 1];
}
for (char* p0 = linebuf; p0 <= p; ++p0) {
if (is_ws(*p0)) {
if (!in_ws) {
in_ws = 1;
i++;
}
*p0 = '\0';
} else if (in_ws) {
in_ws = 0;
_argv[i] = p0;
}
if (i >= MAXARG) {
printf("warning: too many arguments");
break;
}
}
*p = '\0';
_argv[i] = 0;
// for (int j = 0; j < i; ++ j) {
// printf("%s\n", _argv[j]);
// }
if (fork() == 0) {
exec(_argv[0], _argv);
exit(0);
} else {
p = linebuf;
while (wait(0) != -1)
;
}
} else {
p++;
if (p >= linebuf + sizeof(linebuf)) {
printf("line buffer exceeded\n");
exit(0);
}
}
}
exit(0);
}

View File

@ -1,6 +0,0 @@
mkdir a
echo hello > a/b
mkdir c
echo hello > c/b
echo hello > b
find . b | xargs grep hello