______ System Call Project: Examples (CS 273 (OS), Spring 2022)
Home
>>    




System Call Project: Examples

CS 273 (OS), Spring 2022

For the project, think up some new service or feature for a kernel to provide, and implement it in terms of system calls. The new calls should:

  • Provide a service or feature that is not possible with the standard kernel.

    • Yes: A mean system call that makes it possible for a non-root process to improve its scheduling priority. With standard kernels, only root processes can improve their priority (using the existing nice() system call), so this is a new feature that is impossible with standard kernels.
    • No: A dupclose() system call that performs a dup of a file descriptor followed by a close of that descriptor, and returns the result of the dup. This dupclose() behavior could be implemented at the user level with standard system calls.
  • Choose a service or feature whose effects can be verified.

    • Yes: A mean() system call can be demostrated by writing a user program that prints its priority (e.g., using getpriority()), then calls mean() and prints its priority again.
    • No: A system call getcounter() that prints the value of the field counter in the process table entry for the caller process cannot be verified for correctness.

  • Your system call should satisfy the usual calling conventions for system calls. Thus, your system call handler should return a negative value between -125 and -1 for errors (so that your library function will return -1 and set errno as usual), and should return a non-negative integer on success.

Examples of ideas for added system calls

The following examples suggest some ideas that satisfy the two criteria above. These are to get your imagination started only. Don't feel constrained by these examples: be creative! However, start conservatively, because modifying the kernel can (not surprisingly) get complex in a hurry.
Recommendation: Start with a modest idea, such as idea 1 below, then add more ambitious ideas after you have that experince.
Submit all your successful added system calls for your project.

  1. Write a pair of system calls, one of which writes a value into a new field of your own in the process table entry, and the other which reads that value for a process.

    • For example, one new system call rab_putval with one int argument might assign that integer value to a new field rab_val added to the end of the task_struct structure (except using your own initials or email). A second system call rab_getval with one integer argument might return the int value of the field rab_val for a (potentially different) process whose PID is passed as that argument. These two calls would represent a new capability for your kernel, enabling a process to share an int value of its choice with all other processes.
    • Be sure to add your field at the end of the task_struct structure, not near the beginning, for reasons discussed in class.
    • As an additional feature, you could figure out how to assign an initial value to the field rab_val, such as 0, or the process's PID, or the PID of the parent process.

  2. You could write a system call that copies the value of some kernel data structure onto a region of user space provided by the user.
    • Thus, for example, a user-level program with code excerpted below might store the data in the i-node for a file with inode 123456 in the variable ei using a new system call rab_getinode() (note that the user must allocate the space for ei, not the kernel).
      struct ext2_inode {
              __u16   i_mode;         /* File mode */
              __u16   i_uid;          /* Low 16 bits of Owner Uid */
              __u32   i_size;         /* Size in bytes */
      	...
      };
      
      struct ext2_inode ei;  /* allocate a variable of the i-node type */
      ...
      rab_getinode(123456, &ei);  
      
    • To test this idea with a user-level program, you could print fields such the three appearing above that could also be seen in ls -lsi output. Note that the i flag for ls prints i-node numbers, needed for your system call.
    • You could extend this idea by passing a pathname to the file instead of an i-node number (which would require learning how paths are converted to i-node numbers in the kernel).
    • Can you think of other system data structures that you could provide to the user in other added system calls, whose values could be verified?

  3. Write a mean system call as suggested above, enabling a user process to improve it's own scheduling priority (unlike nice, which can only lower priorities for non-root processes).

    • This will involve a little research, because nice() shares implementation with other related system calls.
    • You may think of other superuser privileges that you could extend to ordinary users through a system call that you add.
  4. Write one system call that enables a user process to disable interrupts, and another system call for re-enabling interrupts.

    • This would require learning about spin locks, which are used to disable interrupts in multicore Linux
    • You could demonstrate this by booting your user-mode kernel and logging in at both of the xterm windows. Run a program that disables interrupts in one login; this will freeze the other login (and many other things), since it the first program will keep its time slice until it enables interrupts again, because clock interrupts will also be disabled.
    • This is hardly a desirable addition to a kernel in a practical sense, but it does satisfy the criteria above for this project.
    • Alternatively, you could use spin locks to implement some new synchronization feature for the user, such as a "test and set lock" feature for the C language.

  5. Create a new system call that modifies setitimer() (for the ITIMER_REAL timer) to deliver a signal other than SIG_ALRM at a specified time in the future.

    • This would involve learning about how signals are implemented, and how to substitute a different signal handler that is a slight modification of the default one.
    • To test, write a user level program that sets up a signal handler for SIG_INT (or some other chosen signal) but not for SIG_ALRM or any other signals, then uses your new system call to arrange for SIG_INT to be sent a few seconds later, and pause()s. Have the signal handler print a message when the signal is received, and have the program print a message after the pause().
    • Alternatively, you could write a system call that allows a process to handle the uncatchable signal 9 (SIG_KILL).

Note: These are just suggestions to get you started: use your imagination! But develop your project incrementally. For example, if you are interested in the i-node idea above, it's wise to

  • First implement a system call that is a clone of another system call, as discussed in class.
  • Next, add another system call that reports some integer field value in the process table entry that you can verify (even if it might be available through another system call, at least your system call will report it in a way that you can check).
  • Now, instead of simply returning that field value from your system call, try requiring the user to allocate an integer variable and pass that (user-space) memory location to the kernel through an argument of your system call (like the status argument for wait()), and write that integer value on the user's variable; have the test program print the resulting value of that variable.
  • Finally, write another system call that copies i-node information onto a user-provided variable of the right type.
  • For extra features, you could create another system call that accepts a path instead of an i-node number as an argument, or additional system calls for providing other system data structures, etc.