CSC443 Project 3
Overview
In this project you will add a new system call to Minix.
The new system call can be invoked from a user process to get the number of child processes it has created that have not yet been waited for.
The Minix process manager server process has all the information needed in its part of the process table to implement this system call.
So adding this system call will follow the same outline as the example mygetpid/mygetppid presented in the lecture.
For this project, you will write a new system routine in the process manager and one new user level "library" routine:
int getnc()
that returns the number of child processes that have not yet been waited for.
A user test program should be written to verify that your getnc system call works as expected. A sample user program is provided below.
Details
The Minix source files are owned by user bin.
So we log in as bin when we intend to modify these files as described below.
- Find an unused integer for your new system call.
There are two files to consult in order to find this integer.
- /usr/src/include/minix/callnr.h - This file contains preprocessor #defines for symbolic constants (macros) for the integers used for system calls.
- /usr/src/servers/pm/table.c - This file contains an array, call_vec of function pointers, each of which points to a function in the process manager to execute. The index in the array corresponds to a system call integer and the function stored at that index is the process manager function that should be executed to implement the system call.
We can identify an unused system call number in table.c by an entry of no_sys. That is, if entry at index ix in the call_vec array is no_sys, then ix is not currently a valid system call integer.
Also in the file callnr.h there will not be a #define for this integer ix.
We need to check to see which number is free, but here I'll assume the integer 32 is not used.
- Add a new entry in callnr.h for the integer we
chose. We will need to select a name for the symbolic constant
and use a #define to give it the integer value we
chose.
E.g., if we choose the name GETNC and the unused integer we found was 32, then we would add this line to callnr.h:
#define GETNC 32
- We now need to change call_vec entry for the same
integer. Replace the no_sys entry with a new function
name.
The naming convention for the system functions in process manager for each system call is do_xxxxx where xxxxx names the system call from the user's perspective.
For example, we could name the funciton
do_getnc
Note that we haven't yet written this function.
- Write the do_getnc function.
Code for this function must be part of the process manager's code.
In principle it can go in any of the existing .c files that are already used to compile and link and produce the pm file that the process manager process executes.
For example, we can put the code in the file:
/usr/src/servers/pm/misc.c
The do_xxxxx functions must have the prototype of a function that has no parameters and returns an int.
The do_getnc function should:
- Calculate the number of children of the calling
process by examining the Process Manager's process
table. Put the number of children calculated
into the reply message: mp_reply in the process
table entry for the calling process.
How do we get the calling process's process table entry?
Answer: The message passing system will have placed a pointer to the calling user process's process table entry into the "global" variable:
struct mproc *mp;
Note that the process manager's portion of the process table is called mproc, is an array of elements of type struct mproc, and is defined in the file:
/usr/src/servers/pm/mproc.h
We will need to examine the entries of struct mproc to locate the pid of the calling process and its parent's pid.
- We will also need to decide on which message format to
use.
As noted in the text there are 7 different formats, but they all contain the same two first members.
The remaining members will be used for reply data and it just depends on how many and what type of values we need to send back.
In the case of do_getnc, we need to send back just one integer (i.e, one int)
But all of the 7 message formats have at least two int's and we don't have to use every member. So we can use any one of the 7 message formats.
In general the only restriction is that the user level "library" routine that gets the reply message from the system routine needs to assume the same message format so that it can properly extract the values that the system routine as inserted into the reply message.
The do_xxxxx function must return an integer. Note: The value that the do_xxxxx returns will be returned to the _syscall function back in the user code.
In _syscall a negative return value will be treated as an error. If the do_xxxxx function encounters an error, it should return one of the standard error values defined in /usr/src/include/errno.h (E.G., EINVAL, EACCES, EBUSY, etc.). In the server processes and the kernel, these int values are negative and returning them will be interpreted as an error by _syscall.
If no error is encoutered, the do_xxxxx function can return any non-negative value.
In general if multiple data items to be returned by the do_xxxxx function, it can put these items into the reply message. If a single int is the only data item to be returned, the do_xxxxx function can also simply return that value and it will be available to the user as the return value of _syscall.
So in the case of getnc the do_getnc function can simply return the number of children. Putting the number of children in the reply message is therefore a bit redundant for getnc. But in the general case the only way to handle multiple data items is to put the items in the reply message.
- Calculate the number of children of the calling
process by examining the Process Manager's process
table. Put the number of children calculated
into the reply message: mp_reply in the process
table entry for the calling process.
- The file proto.h in the process manager directory:
/usr/src/servers/pm/proto.h
has the prototype declarations of the functions used by the process manager.
This header file is included by pm.h which in turn is included by each .c file in the process manager directory.
This allows a function to be called from any of the .c files simply by including the pm.h header.
We need to following this general design and add a prototype declaration for wer new function in the proto.h file.
Use the _PROTOTYPE macro (discussed in class). That is, we should add this:
_PROTOTYPE( int do_getnc, (void) );
- Now that we have made the modifications as bin,
we need to rebuild Minix.
Log in as (or switch to) root and change directories to:
/usr/src/tools
We can type make to get a description of various options of how much to rebuild.
For example, one option is
make clean install
This removes executable files for process manager, file system, etc, and then rebuilds them, and finally "installs" the results in the expected directories.
- Shutdown the system and restart it. E.g., log off other
users, and as root:
shutdown -R
- Log into a user account to create user "library" routines.
That is, log into ordinary user account (not root and not bin).
Create a user account if we haven't already.
(We can use the adduser Minix command. Use man adduser for the manual entry.)
Create a .c file and a .h file for two functions:
#ifndef MYUNISTD_H #define MYUNISTD_H #include <lib.h> /* Make sure this is included! */ int getnc(void); #endif #include "myunistd.h" int getnc() { ... }
The function getnc will send a message to the process manager like this:
int getnc() { message m; int r; r = _syscall(MM, GETNC, &m); ... }
The _syscall function will return whatever our do_getnc returns as will as fill in the message m with the reply message.
So arrange for getnc to extract the count of unwaited for child processes from the message and return that value.
- Also as a user write a test program that creates some
child processes and calls the
getnc. Check that getnc returns the correct number.
Here is a portion of a sample user level test program:
int nc; /* * ... fork some child processes... */ /* Now get the number of unwaited for children */ nc = getnc(); printf("Number of children not waited for = %d\n", getnc()); for(i = 0; i < nc; i++) { n = waitpid(-1, 0, 0); printf("\nchild %d waited for\n", n); printf("Number of children not waited for = %d\n", getnc()); }
Sample output:
Number of children not waited for = 5 child 276 waited for Number of children not waited for = 4 child 277 waited for Number of children not waited for = 3 child 279 waited for Number of children not waited for = 2 child 280 waited for Number of children not waited for = 1 child 278 waited for Number of children not waited for = 0
What to Submit
Turn in to Course Online
- A document describing each of the changes you made to Minix code and user level code to support your new system call.
- A copy of the code for your do_getnc() function
- A screen shot of executing the sample program, testnc.c