Homework 2
W4118 Fall 2000
DUE: Monday, 10/9/2000 at the beginning of class

W4118 Fall 2000 - Homework 2

Submission Instructions:

All non-programming problems in this assignment are to be done by yourself. All group programming problems in this assignment are to be done in your assigned groups. Each of you should turn in one hardcopy for the non-programming problems. Each group should turn in one hardcopy for the programming problems. Both hardcopies should have your name and email address clearly written on the first page.

Programming problems are to be done in your assigned groups using the VM that has been assigned to your group. For all programming problems you will be required to submit source code, a README file documenting your files and code, and a test run of your programs. For source code submissions, you only need to submit new source code files that you created and kernel source code files that you changed. You should clearly indicate your names, email addresses, and assigned group number on your submission. Each group is required to submit one writeup for the programming assignment using the SUBMIT program. Refer to the homework submission page on the class web site for additional submission instructions.

Non-Programming Problems:

Exercise numbers refer to the course textbook. Each problem is worth 4 points unless otherwise indicated.

  1. Exercise 3.7

  2. Exercise 3.10

  3. Exercise 3.15

  4. Exercise 4.1

  5. Exercise 4.4

  6. Exercise 4.5

  7. Exercise 5.1

  8. Exercise 5.2

  9. Exercise 5.3

  10. Exercise 5.6

Programming Problems:

System calls allow unprivileged user-space programs to issue requests to the kernel for low-level procedures (device access, process signaling, etc.). Generally speaking, system calls form an operating system's interface. Most operating systems contain several hundred system calls and adding new ones should be part of any kernel hacker's toolkit. For this assignment we will learn how to both hard-code system calls into a kernel and use kernel modules to add new system calls at runtime.

This portion of the assignment consists of five parts.

  1. (10 pts) Compile and install your own Linux kernel on your VM. To ensure that you will always have a bootable kernel that you can use, do not delete the existing Linux kernel image. Instead, create a new kernel image, named kern1, that you can boot from using LILO. On the "resources" section of the class website you will find a document explaining how to build your kernel: the Linux Kernel HOW-TO. Be sure to include module support in the initial "make config" steps, or else parts 3 and 4 of this assignment will not work correctly. We have a sample config file that may help with the initial configuration stages. Finally, modify /etc/lilo.conf to allow you to boot your kernel.

  2. (5 pts) Change your Linux kernel so the very first bootup message (after the "Loading XXX......" message, where XXX is your kernel name) will be your group's name.

  3. (25 pts) The Linux kernel maintains state for each process as it executes. A process is identified by a process id number called a PID. Write a new system call in Linux that takes a PID and a pointer to struct proc_struct as its arguments, and populates the structure with the process state information for that process. The prototype of the system call will be:

    int proc_info(pid_t pid, struct proc_struct *proc);
    
    pid_t is defined in /usr/include/sys/types.h. You should define struct proc_struct as

    struct proc_struct {
           pid_t pid;                 /* id of process */
           pid_t parent_pid;          /* process id of parent */
           pid_t youngest_child_pid;  /* pid of youngest child */
           pid_t younger_sibling_pid; /* pid of younger sibling */
           pid_t older_sibling_pid;   /* pid of older sibling */
           pid_t prev_pid;            /* pid of prev process on run queue */
           pid_t next_pid;            /* pid of next process on run queue */
           long priority;             /* priority of process */
           unsigned long rt_priority; /* real-time priority of process */
           unsigned long policy;      /* policy of process */
           long state;                /* state of process */
           long user_time;            /* CPU time spent in user mode */
           long sys_time;             /* CPU time spent in system mode */
           long cutime;               /* total user time of children */
           long cstime;               /* total system time of children */
           int uid;                   /* user id of process */
           int gid;                   /* group id of process */
           char comm[16];             /* name of program executed */
    };
    
    You should create a new file include/linux/proc_info.h in which this structure is declared. The definition of this system call should be added to the file kernel/sched.c.

    Your system call should return 0 if it is able to locate the process information for the target pid. At a minimum, your system call should detect the following conditions and respond as described:

    The referenced error codes are defined as ESRCH and EINVAL, respectively, in /usr/src/linux/asm/errno.h.

    Note that the majority of a Linux process's information is contained within the task_struct structure defined in include/linux/sched.h. This structure is analogous to the Process Control Block mentioned in your textbook. Two instances of task_struct have special meaning: current refers to the process currently running and init_task refers to the idle process that the system uses to waste processor time. Both current and init_task may serve as your primary anchorpoints when navigating the kernel.

    Some references you might find useful are the Kernel Hacker's Guide, particularly the section on "How System Calls Work in Linux/i86", The Linux Kernel, and the optional class textbook, Linux Kernel Internals. In addition, Linux Journal recently had a very good article about Implementing System Calls.

  4. (10 pts) One of Linux's cool features 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). Write a loadable kernel module that creates the same system call as question 3 of this assignment, this time called proc_info2. This system call should occupy index number 200 of the syscall table.

    In order to learn about kernel module programming, you should check out The Kernel Module Programmer's Guide as well as Loadable Kernel Modules.

  5. (10 pts) Create a command-line utility that formats and prints out a process's information, as yielded by the proc_info system call. Your syscall will be declared through the use of a syscall macro as follows:
    _syscall2(int, proc_info,pid_t, pid, struct proc_struct *, proc);
    
    Write a second version of this utility, for the proc_info2 system call, and benchmark your two utilities against one another. Which method of using a system call is faster? Why?

HINT:

In order to learn about system calls, you may 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.