To complete this assignment, it is vital that you have carefully completed and understood the content in the following guides:
You can work in a group with up to three-four students. You can also work alone, but you get no extra credits for this.
First, you’ll add a new system call that computes some basic statistics on an array of data. In practice, it makes little sense to have this as a sys-call; however, it allows us to become familiar with accessing memory between user and kernel space before accessing other kernel data structures.
Create a cs300/ in ther kernel folder, create a file named array_stats.h with the contents:
// Define the array_stats struct for the array_stats sys-call.
#ifndef _ARRAY_STATS_H_
#define _ARRAY_STATS_H_
struct array_stats{
long min;
long max;
long sum;
};
#endif
Create a new sys-call named array_stats (function sys_array_stats()):
cs300/ folder in a file name array_stats.c. Use
#include "array_stats.h"
The sys-call’s prototype is:
asmlinkage long sys_array_stats(
struct array_stats *stats,
long data[],
long size);
stats: A pointer to one array_stats structure allocated by the user-space application. Structure will be written to by the sys-call to store the minimum, maximum, and sum of all values in the array pointed to by data.data: An array of long int values passed in by the user-space application.size: The number of elements in data. Must be > 0.The array_stats sys-call must:
stats structure based on the data array. The values in data are signed (positive or negative). Nothing special need be done if the sum of all the data overflows/underflows a long.-EINVAL if size <= 0.-EFAULT if any problems access stats or data.data array into.printk() calls in the kernel to print out debug information. You may leave some of these printk() messages in your solution as these messages are not technically displayed by the user-space application. The messages you leave in should be helpful such as showing parameters values or errors it caught; they should not be of the sort “running line 17”, or “past loop 1”. It is usually useful to printk() the parameters you are given, and printk() any error conditions you handle.copy_from_user() macro. This macro safely copies data from a user-level pointer to a kernel-level variable: if there’s a problem reading or writing the memory, it stops and returns non-zero without crashing the kernel.
copy_from_user() to copy one value at a time from the user’s data array into this variable. If a copy fails (copy_from_user() returns non-zero) then have your sys-call end immediately and return -EFAULT.data array using copy_from_user()!size because it is passed by value so there is no possible problem access memory.copy_to_user() which checks the pointer is valid (non-null), inside the user-program’s memory space, and is writable (vs read-only).
struct array_stats inside your sys-call function. Compute the correct values in this struct first, then at the very end use copy_to_use() to copy the contents to user’s pointer.main().In this section, you’ll implement a sys-call which returns information about the current process, plus its ancestors (its parent process, it’s grandparent process, and so on).
cs300/ folder, create a file named process_ancestors.h with the contents:// Structure to hold values returned by process_ancestors sys-call
#ifndef _PROCESS_ANCESTORS_H
#define _PROCESS_ANCESTORS_H
#define ANCESTOR_NAME_LEN 16
struct process_info {
long pid; /* Process ID */
char name[ANCESTOR_NAME_LEN]; /* Program name of process */
long state; /* Current process state */
long uid; /* User ID of process owner */
long nvcsw; /* # voluntary context switches */
long nivcsw; /* # involuntary context switches */
long num_children; /* # children process has */
long num_siblings; /* # sibling process has */
};
#endif
Create new sys-call named process_ancestors (function sys_process_ancestors()):
Implement it in your kernel’s cs300/ folder in a file name process_ancestors.c. Use
#include "process_ancestors.h"
syscall_64.tbl).The sys-call’s prototype is:
asmlinkage long sys_process_ancestors(
struct process_info info_array[],
long size,
long *num_filled)
info_array[]: An array of process_info structs that will be written to by the kernel as it fills in information from the current process on up through its ancestors.size: The number of structs in info_array[]. This is the maximum number of structs that the kernel will write into the array (starting with the current process as the first entry and working up from there). The size may be larger or smaller than the actual number of ancestors of the current process: larger means some entries are left unused (see num_filled); smaller means information about some processes not written into the array.num_filled: A pointer to a long. To this location the kernel will store the number of structs (in info_array[]) which are written by the kernel. May be less than size if the number of ancestors for the current process is less than size.process_ancestors sys-call must:
info_array[] with the correct values.
info_array[0]; the parent of the current process into info_array[1]; grandparent into info_array[2]; and so on.info_array[] are left unmodified.-EINVAL if size <= 0.-EFAULT if any problems access info_array or num_filled.task_struct structures: each process has a task_struct. You can find the task_struct for the current process using the macro current.
long pid = current->pid;
info_array[0].current->parent), and copy its info into info_array[1].cur_task->parent == cur_task).
parent pointer points to itself. This process has PID 0 and is the idle task (named swapper).printk) to ensure you have the correct values. Then work on getting the data into the process_info structs and handling ancestors.process_info:
task_struct structure. Look for fields with a matching name.
task_struct is defined in include/linux/sched.h (in your kernel folder). To include this in your sys-call implementation use: #include <linux/sched.h>task_struct.comm field.cred field). Inside cred, you want to look at the uid field.task_struct.children, and task_struct.sibling.
list_head for a node because in a circular linked list, each node can be thought of as the head of the list.list_head) to get the node (list_head) of the next element in the list.next and prev field pointing to other list_head structures) and accessing the full structure that contains the node. For example, given a list_head struct that is in a task_struct, the kernel includes macros to give you the full task_struct! However, you have (mercifully) been spared having to do this. If you are interested, for fun try printing out the PID of each of the sibling processes.ps command in your QEMU virtual machine to display information on running processes and verify the sys-call output. See ps’s man page for how to select the information it displays.In CourSys, create a group for your submission. Even if you worked alone, you need to be in a group (of 1 in this case) to submit. Submit the following to CourSys:
Git or patch).An archive file (tar.gz) of your user mode folder containing your test code for the sys-calls. Must include a makefile with a target “transfer” to copy the statically linked executables to QEMU.
May include the provided array_statistics test code.
* Please remember that all submissions will automatically be compared for unexplainable similarities.
* Marking will be done on a 64-bit system. If you did your development on a 32-bit system, your code must still compile and run perfectly on a 64 bit system. Specifically, double check that you have:
1. Created the necessary rows in `arch/x86/syscall/syscall_64.tbl`
These will allow us to call your functions, but the numbers must match exactly those numbers listed in this assignment. (In 32-bit the numbers differ from 64bit). 2. Your user-level code uses the correct 64-bit sys-call numbers.
Hint: Make your user-level code check the GCC defined constant _LP64 and automatically pick the correct sys-call number:
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#if _LP64 == 1
#define _CS300_TEST_ 340
#else
#define _CS300_TEST_ 390
#endif
int main(int argc, char *argv[])
{
printf("\nDiving to kernel level\n\n");
int result = syscall(_CS300_TEST_ , 12345);
printf("\nRising to user level w/ result = %d\n\n", result);
return 0;
}
```
Created/updated by Brian Fraser and Mohamed Hefeeda. ↩