% cd $HOME/cs350-os161/os161-1.11/kern/conf % ./config ASST2 % cd ../compile/ASST2 % make depend % make % make install
DEBUG(DB_EXEC, "ELF: Loading %lu bytes to 0x%lx\n", (unsigned long) filesize, (unsigned long) vaddr);The DEBUG macro and the various flags (like DB_EXEC) are defined in kern/include/lib.h. To control which DEBUG statements produce output, set the dbflags variable, which is found in kern/lib/kprintf.c. For example, setting
u_int32_t dbflags = 0;will turn off all debugging output, while
u_int32_t dbflags = DB_EXEC|DB_THREADS;will turn off everything except DEBUG statements that are labeled with DB_EXEC or DB_THREADS.
You can design your quick-and-dirty write system call so that it only handles writes to standard output, which are indicated by fd = 1 (see the manual page for the write system call). The kernel should direct writes to standard output to the console device, and a quick-and-dirty way for the kernel to do that is to use the kprintf function. You can use a simple kprintf-based implementation of write to test that control is actually getting to the system call handler, and that the handler is able to get the characters that the application wants to print.
Your final implementation of write should not be based on kprintf. One problem with the kprintf implementation of write is that kprintf assumes that its argument is a null-terminated character string. However, in general, the data that an application passes to a write system call may not consist of printable characters and will not, in general, be null-terminated. The kprintf-based write implementatin is just a way to get an initial system call working quickly.
A related constraint is that the stack pointer should always start at an address that is 8-byte aligned (evenly divisible by 8). This is because the largest data types that require alignment (and which might be pushed onto a stack) are 8 bytes long, e.g., a double-precision floating point value.
int main(int argc, char **argv) { int file; if (argc < 3) { errx(1, "Usage: tailargv is a character pointer pointer - it points to an array of character pointers, each of which points to one of the arguments. For example, if the tail program is invoked from the kernel command line like this:"); } file = open(argv[1], O_RDONLY); if (file < 0) { err(1, "%s", argv[1]); } tail(file, atoi(argv[2]), argv[1]); close(file); return 0; }
OS/161 kernel [? for menu]: p testbin/sort foo 100Then the argv and argc variables should be set up as illustrated in the following illustration:
result = vfs_open("string", mode, &2vn);is incorrect and should bring a warning from the compiler. The problem is that vfs_open may modify its first argument, but "string" is an unmodifiable literal. So if, for example, you want to open the console, you need to do something like this instead:
char *console = NULL; console = kstrdup("con:"); result = vfs_open(console,mode,&vn); kfree(console);
You build all of the OS/161 application programs when you run make in the directory cs350-os161/os161-1.11. When you do this, the application program executable files are copied into cs350-os161/root/testbin (or root/bin or root/sbin).
Once your OS/161 kernel has booted, you can launch an application program using the p command from the kernel menu. For example, to run the palin program, which is located in testbin, use the following command
OS/161 kernel [? for menu]: p testbin/palinAs usual, you can pass commands to the kernel on sys161 command line, e.g.,
sys161 kernel "p testbin/palin"Be sure to see the next note, about modifying your kernel
% sys161 kernel "p testbin/palin;q"The idea is that this should first launch the palin application from testbin, and then, when palin is finished, shutdown the kernel using the q command. Unfortunately, these kinds of commands will not work in OS/161 as it is provided to you. The problem is that after creating a new thread to run the palin program, the main kernel thread will immediately read the next command without waiting for the palin application to finish running. In this case, the next command is q, which means that the kernel will shut down immediately, probably before palin has finished running. This is not the behaviour we want.
For this reason, you need to modify the way the kernel launches programs when it receives a p command. After the main kernel thread gets the p command and launches a new thread to run the application program, the main thread should wait for the new thread to finish before continuing to read and process the next kernel menu command. A good place to make this change is in the common_prog function in kern/main/menu.c, since that is where the main kernel thread launches a new thread to run the application.
Once you have implemented the kernel changes needed for waitpid, it should be relatively easy for you to modify common_prog so that the main thread will wait for the newly-created thread. In the meantime, here is a simple (but not very elegant) way to change your kernel so that the main kernel thread will wait for launched application programs to finish. Two changes are needed. In the common_prog function, just before the function returns, add the following code:
#if OPT_A2 while (!one_thread_only()) { clocksleep(1); } #endif /* OPT_A2 */Also, don't forget to add #include "opt-A2.h" to the top of kern/main/menu.c so that this change will take effect. The second change is to add the one_thread_only function to kern/thread/thread.c, like this:
#if OPT_A2 /* * returns true (1) if the number of threads in the system is * equal to 1, otherwise returns fals (0). * * See the usage note in thread.h */ int one_thread_only() { int s; int n; /* numthreads is a shared variable, so turn interrupts off to ensure that we can inspect its value atomically */ s = splhigh(); n = numthreads; splx(s); return(n==1); } #endif /* OPT_A2 */and a corresponding function prototype in kern/include/thread.h:
#if OPT_A2 /* * returns true (1) if the number of threads in the system is * equal to 1, otherwise returns fals (0). * * Usage Note: if this function returns 0 (false), the actual thread * count in the system may have changed by the time the calling * function is able to use the returned value. Thus, a return value * of 0 provides little information to the caller. * However, if the return value is 1 (true), then the calling thread * is the only thread in the system. Unless the caller itself * creates new threads (or unless the kernel spontaneously lauches * new threads, e.g., in response to interrupts), the caller will * know that it is the only thread in the system. */ int one_thread_only(void); #endif /* OPT_A2 */Again, don't forget to include opt-A2.h in both thread.c and thread.h.
After making these changes, rebuild your kernel (make depend, make, make install). When you run the new kernel and launch an application program from the kernel menu with the p command, you should find that the kernel does not prompt for new command until the launched application has finished.
Again, this implementation is just intended to be a stopgap until you have made enough progress on your kernel to allow you to make the main thread wait specifically for the new application thread that it launches. You're not required to use this implementation. However, you must ensure somehow that the main kernel thread waits. Doing so will make your own testing easier, and without that change we will not be able to autotest your code.
# Old/default value # 31 busctl ramsize=524288 # Changed to 2 MB. 31 busctl ramsize=2097152In particular, you will probably have to do this for applications like forktest, farm and sty.