EEL 602 -
Operating Systems
Spring 2007
Indian Institute of
Technology Delhi, New Delhi
Assignment # 4
Improving Operating System Kernel with New System Calls
The objective of this assignment is to let you gain some hands on experience in modifying the operating system kernel to incorporate new system calls.
- Download the linux kernel from http://www.kernel.org Your should only use/download the version 2.6.20.1.
- To learn how to compile the kernel use http://www.linuxmigration.com/quickref/kernel/build.html
Use the tutorial in this link to learn how to write a new system call. You can also download the supporting material/tutorial for this assignment.Problem Statement
Write a new system call in Linux. The system call you write should take one argument and return the process state information for that process. Note that you will be adding this system call to the kernel 2.6.20.1 kernel you previously built. The prototype for your system call will be:
int eel602pinfo(struct pinfo *info);
pid_t is defined in /usr/include/sys/types.h.
You should define struct pinfo as
struct pinfo {
int pid; /* process id */
long state; /* current state of process */
long nice; /* process nice value */
int parent_pid; /* process id of parent */
int children; /* total number of children */
int youngest_child_pid; /* pid of youngest child */
int younger_sibling_pid; /* pid of younger sibling */
int older_sibling_pid; /* pid of older sibling */
unsigned long start_time; /* process start time */
long user_time; /* CPU time spent in user mode */
long sys_time; /* CPU time spent in system mode */
long uid; /* user id of process owner */
char comm[16]; /* name of program executed */
};
n /usr/src/linux/include/linux/pinfo.h as part of your solution.
Your system call should return 0 unless an error occurs. Your code should handle errors that can occur but not handle any errors that cannot occur. At a minimum, your system call should detect the following conditions and respond as described:
If the address for the pinfo structure is null, return -22.
If a value to be set in pinfo is accessible through a pointer which is null, set the value in pinfo to -1. For example, the youngest_child_pid should be set to -1 if the process does not have a child. The referenced error code is defined as EINVAL in /usr/src/linux/asm/errno.h.
You should start by reviewing the "How System Calls Work in Linux/i86" section of the Linux Kernel Hacker's (LKH) Guide. Note that because the Linux kernel changed over time, the procedure is no longer exactly correct. However, there are messages appended at the end of the section that will help significantly. Two more recent articles that discuss how to add system calls can be found ?here? and ?here?.
Each system call must be assigned a number. Your should assign the number 222 to your system call/ whatever is the next available number.
You should create a patch to store changes you've made to the kernel and to submit your changes for this problem. If you've changed 50 lines of code, a patch will be about 50 lines long. This is much easier to store, email, or move around than the full 283,000-line source tree. To create a patch, first back up your .config file, then do a make distclean in your changed source tree (you don't want object files, config files, etc. in your patch). Then, from the directory above your source tree , run
diff -ruN linux-2.6.17-13 linux-2.6.17-changed > changed.patch
Notice that the original source tree is first, and the changed second. There is a particular "algebra" to patches. You may find it helpful to think of diff as subtracting two source trees. The result of this subtraction is a patch. To apply a patch, then, is to add this difference back. If you supply the -R option to the patch program, you subtract the difference, instead of adding. If you thus "subtract the difference" from your changed tree, you end up with the original tree. So, to contain all the information of n kernel trees, all you need is the n - 1 patches between them, and only one full kernel tree (and it can be any one of the n).
Linux maintains a list of all processes in a doubly linked list. Each entry in this list is a task_struct structure, which is defined in /usr/src/linux/include/linux/sched.h. In /usr/src/linux/include/asm/current.h, current is defined to inline a function which returns the address of the task_struct of the currently running process. All of the information to be returned in the pinfo structure can be determined by starting with current.
In order to learn about system calls, you may also find it helpful to search the Linux kernel for other system calls and see how they are defined. The file kernel/sched.c might give some useful examples of this. The getpid() and getuid system calls might be useful starting points. The system call sys_getpid defined in /usr/src/linux/kernel/sched.c uses current and provides a good reference point for defining your system call.
Your kernel has partially reserved a slot in the syscall table for ni_syscall (when you see it, you will know what we are talking about). Since ni_syscall means "not implemented", do not be afraid to commandeer that slot.
To test your system call, write a simple program that calls pinfo. Your program should print all the process state information for the calling process. Run the program several times. Which fields in the pinfo structure change? Which ones do not? For each field in the pinfo structure, discuss how frequently it changes and why as part of your writeup.
Although system calls are generally accessed through a library (libc), your test program should access your system call directly. This is accomplished by utilizing the appropriate _syscall macro in /usr/include/asm/unistd.h. As explained in LKH, which _syscall macro you use is determined by the number of arguments passed to your system call.
The output of the program should be easy to read, and the program should check for errors such as invalid input or too many or too few arguments. The ps command will provide valuable help in verifying the accuracy of information printed by your program. You can access detailed information on the ps command by entering man ps.
One of cool features of Linux is the ease with which you can implement kernel modules. Kernel modules are pieces of code that can be added to a running kernel; they are often used for the implementation of device-drivers. Make another system call that returns just the name of the program executed and not any other information specified in part one of this assignment. Write a loadable kernel module that executes this system call. Later when the kernel module is inserted the message Module inserted should come on the screen and then your system call should start executing. When your code has finished executing, you have to remove the module and a message Module removed should come on the screen. Remember that you should be compiling your module on the host machine, but you will need to specify the correct include path for the version of the kernel that you are using in your Makefile. Here you should not compile the kernel. In order to learn about kernel module programming, you should check out The Kernel Module Programmer's Guide as well as Loadable Kernel Modules.
Remember, as a safety measure, you are strongly encouraged to copy the source files you plan to modify to your home directory on the host system.
printk() statements in system calls will print their output to the console whenever they are invoked. To request printk() messages be sent to a log file, insert the following line into the /etc/syslog.conf file:
kern.* /var/kern.log
This will cause printk() messages to be written to /var/kern.log. You can send a signal to request the syslog daemon re-read the updated /etc/syslog.conf file, with the command kill -1 pid where pid is the process ID of the syslogd process.
A lot of your problems will come from system administration issues. If you are not familiar with unix, you might want to pick up a book on system administration or consult some of the guides at The Linux Documentation Project.
Submission
You must submit a single file named <YourEntryNumber>.tar.gz or <YourEntryNumber>.tgz. All other filenames will be penalized.
That tar file should contain one subdirectory named <YourEntryNumber> which should contain the kernel patch, the readme file, sample output, and testing programs. Your readme file should describe what changes you made to the kernel, how to run your test programs and what the expected output should be.For each programming part, if your program(s) do not work, you must submit file named nonworking.txt. Inside this file, you need to describe what problems you ran into, where your program fails and what you think are the reasons.
Do not remove your kernels from the machine you develop on, keep them around so that we can test it on your machine incase there are problems applying the kernel patch during the demo time. Incase the machine is shared, the bootloader should have:
* <YourEntryNumber> Kernel Test 1
* <YourEntryNumber> Kernel Test 2
* <YourEntryNumber> Kernel Test 3
* <SomeoneElseEntryNumber> Kernel Test 1
* <SomeoneElseEntryNumber> Kernel Test 2
in addition to the default kernel.
Your single tar file can be uploaded in assignment # 4 using this link. Please make sure that you upload the assignment only once. In case of multiple submissions, ONLY first submission will be used for grading.Deadline
The (new) submission deadline for the assignment # 4 is 06/04/2007 [23:59 Hrs].
The assignment # 4 submission after 06/04/2004 will also be acceptable but with a penalty of 15%. However no submission beyond 16/04/2007 [23:59 Hrs] will be accepted.
Questions
If you have any further questions relating to this assignment, you may contact the TA, Jayant Kumar Gandhi.