Lab 4
This commit is contained in:
80
user/dumbfork.c
Normal file
80
user/dumbfork.c
Normal file
@@ -0,0 +1,80 @@
|
||||
// Ping-pong a counter between two processes.
|
||||
// Only need to start one of these -- splits into two, crudely.
|
||||
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
envid_t dumbfork(void);
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who;
|
||||
int i;
|
||||
|
||||
// fork a child process
|
||||
who = dumbfork();
|
||||
|
||||
// print a message and yield to the other a few times
|
||||
for (i = 0; i < (who ? 10 : 20); i++) {
|
||||
cprintf("%d: I am the %s!\n", i, who ? "parent" : "child");
|
||||
sys_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
duppage(envid_t dstenv, void *addr)
|
||||
{
|
||||
int r;
|
||||
|
||||
// This is NOT what you should do in your fork.
|
||||
if ((r = sys_page_alloc(dstenv, addr, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
if ((r = sys_page_map(dstenv, addr, 0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("sys_page_map: %e", r);
|
||||
memmove(UTEMP, addr, PGSIZE);
|
||||
if ((r = sys_page_unmap(0, UTEMP)) < 0)
|
||||
panic("sys_page_unmap: %e", r);
|
||||
}
|
||||
|
||||
envid_t
|
||||
dumbfork(void)
|
||||
{
|
||||
envid_t envid;
|
||||
uint8_t *addr;
|
||||
int r;
|
||||
extern unsigned char end[];
|
||||
|
||||
// Allocate a new child environment.
|
||||
// The kernel will initialize it with a copy of our register state,
|
||||
// so that the child will appear to have called sys_exofork() too -
|
||||
// except that in the child, this "fake" call to sys_exofork()
|
||||
// will return 0 instead of the envid of the child.
|
||||
envid = sys_exofork();
|
||||
if (envid < 0)
|
||||
panic("sys_exofork: %e", envid);
|
||||
if (envid == 0) {
|
||||
// We're the child.
|
||||
// The copied value of the global variable 'thisenv'
|
||||
// is no longer valid (it refers to the parent!).
|
||||
// Fix it and return 0.
|
||||
thisenv = &envs[ENVX(sys_getenvid())];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We're the parent.
|
||||
// Eagerly copy our entire address space into the child.
|
||||
// This is NOT what you should do in your fork implementation.
|
||||
for (addr = (uint8_t*) UTEXT; addr < end; addr += PGSIZE)
|
||||
duppage(envid, addr);
|
||||
|
||||
// Also copy the stack we are currently running on.
|
||||
duppage(envid, ROUNDDOWN(&addr, PGSIZE));
|
||||
|
||||
// Start the child environment running
|
||||
if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0)
|
||||
panic("sys_env_set_status: %e", r);
|
||||
|
||||
return envid;
|
||||
}
|
||||
|
||||
25
user/fairness.c
Normal file
25
user/fairness.c
Normal file
@@ -0,0 +1,25 @@
|
||||
// Demonstrate lack of fairness in IPC.
|
||||
// Start three instances of this program as envs 1, 2, and 3.
|
||||
// (user/idle is env 0).
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who, id;
|
||||
|
||||
id = sys_getenvid();
|
||||
|
||||
if (thisenv == &envs[1]) {
|
||||
while (1) {
|
||||
ipc_recv(&who, 0, 0);
|
||||
cprintf("%x recv from %x\n", id, who);
|
||||
}
|
||||
} else {
|
||||
cprintf("%x loop sending to %x\n", id, envs[1].env_id);
|
||||
while (1)
|
||||
ipc_send(envs[1].env_id, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
24
user/faultalloc.c
Normal file
24
user/faultalloc.c
Normal file
@@ -0,0 +1,24 @@
|
||||
// test user-level fault handler -- alloc pages to fix faults
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
handler(struct UTrapframe *utf)
|
||||
{
|
||||
int r;
|
||||
void *addr = (void*)utf->utf_fault_va;
|
||||
|
||||
cprintf("fault %x\n", addr);
|
||||
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
|
||||
PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("allocating at %x in page fault handler: %e", addr, r);
|
||||
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(handler);
|
||||
cprintf("%s\n", (char*)0xDeadBeef);
|
||||
cprintf("%s\n", (char*)0xCafeBffe);
|
||||
}
|
||||
24
user/faultallocbad.c
Normal file
24
user/faultallocbad.c
Normal file
@@ -0,0 +1,24 @@
|
||||
// test user-level fault handler -- alloc pages to fix faults
|
||||
// doesn't work because we sys_cputs instead of cprintf (exercise: why?)
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
handler(struct UTrapframe *utf)
|
||||
{
|
||||
int r;
|
||||
void *addr = (void*)utf->utf_fault_va;
|
||||
|
||||
cprintf("fault %x\n", addr);
|
||||
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
|
||||
PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("allocating at %x in page fault handler: %e", addr, r);
|
||||
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(handler);
|
||||
sys_cputs((char*)0xDEADBEEF, 4);
|
||||
}
|
||||
14
user/faultbadhandler.c
Normal file
14
user/faultbadhandler.c
Normal file
@@ -0,0 +1,14 @@
|
||||
// test bad pointer for user-level fault handler
|
||||
// this is going to fault in the fault handler accessing eip (always!)
|
||||
// so eventually the kernel kills it (PFM_KILL) because
|
||||
// we outrun the stack with invocations of the user-level handler
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
|
||||
sys_env_set_pgfault_upcall(0, (void*) 0xDeadBeef);
|
||||
*(int*)0 = 0;
|
||||
}
|
||||
19
user/faultdie.c
Normal file
19
user/faultdie.c
Normal file
@@ -0,0 +1,19 @@
|
||||
// test user-level fault handler -- just exit when we fault
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
handler(struct UTrapframe *utf)
|
||||
{
|
||||
void *addr = (void*)utf->utf_fault_va;
|
||||
uint32_t err = utf->utf_err;
|
||||
cprintf("i faulted at va %x, err %x\n", addr, err & 7);
|
||||
sys_env_destroy(sys_getenvid());
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(handler);
|
||||
*(int*)0xDeadBeef = 0;
|
||||
}
|
||||
11
user/faultevilhandler.c
Normal file
11
user/faultevilhandler.c
Normal file
@@ -0,0 +1,11 @@
|
||||
// test evil pointer for user-level fault handler
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
|
||||
sys_env_set_pgfault_upcall(0, (void*) 0xF0100020);
|
||||
*(int*)0 = 0;
|
||||
}
|
||||
12
user/faultnostack.c
Normal file
12
user/faultnostack.c
Normal file
@@ -0,0 +1,12 @@
|
||||
// test user fault handler being called with no exception stack mapped
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void _pgfault_upcall();
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_env_set_pgfault_upcall(0, (void*) _pgfault_upcall);
|
||||
*(int*)0 = 0;
|
||||
}
|
||||
146
user/faultregs.c
Normal file
146
user/faultregs.c
Normal file
@@ -0,0 +1,146 @@
|
||||
// test register restore on user-level page fault return
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
struct regs
|
||||
{
|
||||
struct PushRegs regs;
|
||||
uintptr_t eip;
|
||||
uint32_t eflags;
|
||||
uintptr_t esp;
|
||||
};
|
||||
|
||||
#define SAVE_REGS(base) \
|
||||
"\tmovl %%edi, "base"+0x00\n" \
|
||||
"\tmovl %%esi, "base"+0x04\n" \
|
||||
"\tmovl %%ebp, "base"+0x08\n" \
|
||||
"\tmovl %%ebx, "base"+0x10\n" \
|
||||
"\tmovl %%edx, "base"+0x14\n" \
|
||||
"\tmovl %%ecx, "base"+0x18\n" \
|
||||
"\tmovl %%eax, "base"+0x1c\n" \
|
||||
"\tmovl %%esp, "base"+0x28\n"
|
||||
|
||||
#define LOAD_REGS(base) \
|
||||
"\tmovl "base"+0x00, %%edi\n" \
|
||||
"\tmovl "base"+0x04, %%esi\n" \
|
||||
"\tmovl "base"+0x08, %%ebp\n" \
|
||||
"\tmovl "base"+0x10, %%ebx\n" \
|
||||
"\tmovl "base"+0x14, %%edx\n" \
|
||||
"\tmovl "base"+0x18, %%ecx\n" \
|
||||
"\tmovl "base"+0x1c, %%eax\n" \
|
||||
"\tmovl "base"+0x28, %%esp\n"
|
||||
|
||||
static struct regs before, during, after;
|
||||
|
||||
static void
|
||||
check_regs(struct regs* a, const char *an, struct regs* b, const char *bn,
|
||||
const char *testname)
|
||||
{
|
||||
int mismatch = 0;
|
||||
|
||||
cprintf("%-6s %-8s %-8s\n", "", an, bn);
|
||||
|
||||
#define CHECK(name, field) \
|
||||
do { \
|
||||
cprintf("%-6s %08x %08x ", #name, a->field, b->field); \
|
||||
if (a->field == b->field) \
|
||||
cprintf("OK\n"); \
|
||||
else { \
|
||||
cprintf("MISMATCH\n"); \
|
||||
mismatch = 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
CHECK(edi, regs.reg_edi);
|
||||
CHECK(esi, regs.reg_esi);
|
||||
CHECK(ebp, regs.reg_ebp);
|
||||
CHECK(ebx, regs.reg_ebx);
|
||||
CHECK(edx, regs.reg_edx);
|
||||
CHECK(ecx, regs.reg_ecx);
|
||||
CHECK(eax, regs.reg_eax);
|
||||
CHECK(eip, eip);
|
||||
CHECK(eflags, eflags);
|
||||
CHECK(esp, esp);
|
||||
|
||||
#undef CHECK
|
||||
|
||||
cprintf("Registers %s ", testname);
|
||||
if (!mismatch)
|
||||
cprintf("OK\n");
|
||||
else
|
||||
cprintf("MISMATCH\n");
|
||||
}
|
||||
|
||||
static void
|
||||
pgfault(struct UTrapframe *utf)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (utf->utf_fault_va != (uint32_t)UTEMP)
|
||||
panic("pgfault expected at UTEMP, got 0x%08x (eip %08x)",
|
||||
utf->utf_fault_va, utf->utf_eip);
|
||||
|
||||
// Check registers in UTrapframe
|
||||
during.regs = utf->utf_regs;
|
||||
during.eip = utf->utf_eip;
|
||||
during.eflags = utf->utf_eflags & ~FL_RF;
|
||||
during.esp = utf->utf_esp;
|
||||
check_regs(&before, "before", &during, "during", "in UTrapframe");
|
||||
|
||||
// Map UTEMP so the write succeeds
|
||||
if ((r = sys_page_alloc(0, UTEMP, PTE_U|PTE_P|PTE_W)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(pgfault);
|
||||
|
||||
asm volatile(
|
||||
// Light up eflags to catch more errors
|
||||
"\tpushl %%eax\n"
|
||||
"\tpushfl\n"
|
||||
"\tpopl %%eax\n"
|
||||
"\torl $0x8d5, %%eax\n"
|
||||
"\tpushl %%eax\n"
|
||||
"\tpopfl\n"
|
||||
|
||||
// Save before registers
|
||||
// eflags
|
||||
"\tmov %%eax, %0+0x24\n"
|
||||
// eip
|
||||
"\tleal 0f, %%eax\n"
|
||||
"\tmovl %%eax, %0+0x20\n"
|
||||
"\tpopl %%eax\n"
|
||||
// others
|
||||
SAVE_REGS("%0")
|
||||
|
||||
// Fault at UTEMP
|
||||
"\t0: movl $42, 0x400000\n"
|
||||
|
||||
// Save after registers (except eip and eflags)
|
||||
SAVE_REGS("%1")
|
||||
// Restore registers (except eip and eflags). This
|
||||
// way, the test will run even if EIP is the *only*
|
||||
// thing restored correctly.
|
||||
LOAD_REGS("%0")
|
||||
// Save after eflags (now that stack is back); note
|
||||
// that we were very careful not to modify eflags in
|
||||
// since we saved it
|
||||
"\tpushl %%eax\n"
|
||||
"\tpushfl\n"
|
||||
"\tpopl %%eax\n"
|
||||
"\tmov %%eax, %1+0x24\n"
|
||||
"\tpopl %%eax\n"
|
||||
: : "m" (before), "m" (after) : "memory", "cc");
|
||||
|
||||
// Check UTEMP to roughly determine that EIP was restored
|
||||
// correctly (of course, we probably wouldn't get this far if
|
||||
// it weren't)
|
||||
if (*(int*)UTEMP != 42)
|
||||
cprintf("EIP after page-fault MISMATCH\n");
|
||||
after.eip = before.eip;
|
||||
|
||||
check_regs(&before, "before", &after, "after", "after page-fault");
|
||||
}
|
||||
38
user/forktree.c
Normal file
38
user/forktree.c
Normal file
@@ -0,0 +1,38 @@
|
||||
// Fork a binary tree of processes and display their structure.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define DEPTH 3
|
||||
|
||||
void forktree(const char *cur);
|
||||
|
||||
void
|
||||
forkchild(const char *cur, char branch)
|
||||
{
|
||||
char nxt[DEPTH+1];
|
||||
|
||||
if (strlen(cur) >= DEPTH)
|
||||
return;
|
||||
|
||||
snprintf(nxt, DEPTH+1, "%s%c", cur, branch);
|
||||
if (fork() == 0) {
|
||||
forktree(nxt);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
forktree(const char *cur)
|
||||
{
|
||||
cprintf("%04x: I am '%s'\n", sys_getenvid(), cur);
|
||||
|
||||
forkchild(cur, '0');
|
||||
forkchild(cur, '1');
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
forktree("");
|
||||
}
|
||||
|
||||
20
user/idle.c
Normal file
20
user/idle.c
Normal file
@@ -0,0 +1,20 @@
|
||||
// idle loop
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
binaryname = "idle";
|
||||
|
||||
// Loop forever, simply trying to yield to a different environment.
|
||||
// Instead of busy-waiting like this,
|
||||
// a better way would be to use the processor's HLT instruction
|
||||
// to cause the processor to stop executing until the next interrupt -
|
||||
// doing so allows the processor to conserve power more effectively.
|
||||
while (1) {
|
||||
sys_yield();
|
||||
}
|
||||
}
|
||||
|
||||
29
user/pingpong.c
Normal file
29
user/pingpong.c
Normal file
@@ -0,0 +1,29 @@
|
||||
// Ping-pong a counter between two processes.
|
||||
// Only need to start one of these -- splits into two with fork.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who;
|
||||
|
||||
if ((who = fork()) != 0) {
|
||||
// get the ball rolling
|
||||
cprintf("send 0 from %x to %x\n", sys_getenvid(), who);
|
||||
ipc_send(who, 0, 0, 0);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
uint32_t i = ipc_recv(&who, 0, 0);
|
||||
cprintf("%x got %d from %x\n", sys_getenvid(), i, who);
|
||||
if (i == 10)
|
||||
return;
|
||||
i++;
|
||||
ipc_send(who, i, 0, 0);
|
||||
if (i == 10)
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
33
user/pingpongs.c
Normal file
33
user/pingpongs.c
Normal file
@@ -0,0 +1,33 @@
|
||||
// Ping-pong a counter between two shared-memory processes.
|
||||
// Only need to start one of these -- splits into two with sfork.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
uint32_t val;
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who;
|
||||
uint32_t i;
|
||||
|
||||
i = 0;
|
||||
if ((who = sfork()) != 0) {
|
||||
cprintf("i am %08x; thisenv is %p\n", sys_getenvid(), thisenv);
|
||||
// get the ball rolling
|
||||
cprintf("send 0 from %x to %x\n", sys_getenvid(), who);
|
||||
ipc_send(who, 0, 0, 0);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ipc_recv(&who, 0, 0);
|
||||
cprintf("%x got %d from %x (thisenv is %p %x)\n", sys_getenvid(), val, who, thisenv, thisenv->env_id);
|
||||
if (val == 10)
|
||||
return;
|
||||
++val;
|
||||
ipc_send(who, 0, 0, 0);
|
||||
if (val == 10)
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
53
user/primes.c
Normal file
53
user/primes.c
Normal file
@@ -0,0 +1,53 @@
|
||||
// Concurrent version of prime sieve of Eratosthenes.
|
||||
// Invented by Doug McIlroy, inventor of Unix pipes.
|
||||
// See http://swtch.com/~rsc/thread/.
|
||||
// The picture halfway down the page and the text surrounding it
|
||||
// explain what's going on here.
|
||||
//
|
||||
// Since NENV is 1024, we can print 1022 primes before running out.
|
||||
// The remaining two environments are the integer generator at the bottom
|
||||
// of main and user/idle.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
unsigned
|
||||
primeproc(void)
|
||||
{
|
||||
int i, id, p;
|
||||
envid_t envid;
|
||||
|
||||
// fetch a prime from our left neighbor
|
||||
top:
|
||||
p = ipc_recv(&envid, 0, 0);
|
||||
cprintf("CPU %d: %d ", thisenv->env_cpunum, p);
|
||||
|
||||
// fork a right neighbor to continue the chain
|
||||
if ((id = fork()) < 0)
|
||||
panic("fork: %e", id);
|
||||
if (id == 0)
|
||||
goto top;
|
||||
|
||||
// filter out multiples of our prime
|
||||
while (1) {
|
||||
i = ipc_recv(&envid, 0, 0);
|
||||
if (i % p)
|
||||
ipc_send(id, i, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, id;
|
||||
|
||||
// fork the first prime process in the chain
|
||||
if ((id = fork()) < 0)
|
||||
panic("fork: %e", id);
|
||||
if (id == 0)
|
||||
primeproc();
|
||||
|
||||
// feed all the integers through
|
||||
for (i = 2; ; i++)
|
||||
ipc_send(id, i, 0, 0);
|
||||
}
|
||||
|
||||
31
user/spin.c
Normal file
31
user/spin.c
Normal file
@@ -0,0 +1,31 @@
|
||||
// Test preemption by forking off a child process that just spins forever.
|
||||
// Let it run for a couple time slices, then kill it.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t env;
|
||||
|
||||
cprintf("I am the parent. Forking the child...\n");
|
||||
if ((env = fork()) == 0) {
|
||||
cprintf("I am the child. Spinning...\n");
|
||||
while (1)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
cprintf("I am the parent. Running the child...\n");
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
|
||||
cprintf("I am the parent. Killing the child...\n");
|
||||
sys_env_destroy(env);
|
||||
}
|
||||
|
||||
39
user/stresssched.c
Normal file
39
user/stresssched.c
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
volatile int counter;
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, j;
|
||||
int seen;
|
||||
envid_t parent = sys_getenvid();
|
||||
|
||||
// Fork several environments
|
||||
for (i = 0; i < 20; i++)
|
||||
if (fork() == 0)
|
||||
break;
|
||||
if (i == 20) {
|
||||
sys_yield();
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the parent to finish forking
|
||||
while (envs[ENVX(parent)].env_status != ENV_FREE)
|
||||
asm volatile("pause");
|
||||
|
||||
// Check that one environment doesn't run on two CPUs at once
|
||||
for (i = 0; i < 10; i++) {
|
||||
sys_yield();
|
||||
for (j = 0; j < 10000; j++)
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (counter != 10*10000)
|
||||
panic("ran on two CPUs at once (counter is %d)", counter);
|
||||
|
||||
// Check that we see environments running on different CPUs
|
||||
cprintf("[%08x] stresssched on CPU %d\n", thisenv->env_id, thisenv->env_cpunum);
|
||||
|
||||
}
|
||||
|
||||
17
user/yield.c
Normal file
17
user/yield.c
Normal file
@@ -0,0 +1,17 @@
|
||||
// yield the processor to other environments
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
cprintf("Hello, I am environment %08x.\n", thisenv->env_id);
|
||||
for (i = 0; i < 5; i++) {
|
||||
sys_yield();
|
||||
cprintf("Back in environment %08x, iteration %d.\n",
|
||||
thisenv->env_id, i);
|
||||
}
|
||||
cprintf("All done in environment %08x.\n", thisenv->env_id);
|
||||
}
|
||||
Reference in New Issue
Block a user