Assignment 1
Assignment 1
Goal: This assignment helps students to understand steps of modifying, compiling and installing Linux
kernel.
Content: In detail, student will add a new system call which helps applications to know the parent and a
child of a given process. This task requires student to understand system call invocation mechanism as well
as steps of compiling and installing Linux kernel.
Result: After this assignment, student should know how to modify Linux kernel and deploy their own
kernel on a given machine.
1
CONTENTS CONTENTS
Contents
1 Introduction 3
1.1 System calls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Requirement and Marking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4 System call 8
4.1 The role of system call . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2 Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.4 Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.5 Wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.6 Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
5 Submission 14
5.1 Source code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
5.2 Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Appendices 14
B Kernel module 16
Page 2 of 18
1 INTRODUCTION
1 Introduction
1.1 System calls
System calls provide an interface to the services made available by an operating system. These calls are
generally available as routines written in C and C++, although certain low-level tasks (for example, tasks
where hardware must be accessed directly) may have to be written using assembly-language instructions.
As you can see, even simple programs may make heavy use of the operating system. Frequently, systems
execute thousands of system calls per second. Most programmers never see this level of detail, however.
Typically, application developers design programs according to an application programming interface (API).
The API specifies a set of functions that are available to an application programmer, including the parameters
that are passed to each function and the return values the programmer can expect. The functions that make
up an API typically invoke the actual system calls on behalf of the application programmer.
EXAMPLE OF STANDARD API As an example of a standard API, consider the open() function
that is available in UNIX and Linux systems. The API for this function is obtained from the man page. A
description of this API appears below:
$ man open
#i n c l u d e <unistd.h>
A program that uses the open() function must include the unistd.h header file, as this file defines int data
types (among other things). The parameters passed to open() are as follows.
• const *path - The relative or absolute path to the file that is to be opened.
• int oflags - A bitwise ‘or’ separated list of values that determine the method in which the file is to
be opened.
• mode t mode - A bitwise ‘or’ separated list of values that determine the permissions of the file if it is
created.
The file descriptor returned is always the smallest integer greater than zero that is still available. If a negative
value is returned, then there was an error opening the file.
Figure 1: The handling of a user application invoking the open() system call.
Page 3 of 18
1.2 Requirement and Marking 1 INTRODUCTION
The relationship between an API, the system-call interface, and the operating system is shown in Figure 1,
which illustrates how the operating system handles a user application invoking the open() system call.
Original OS
Ubuntu Server
Kernel 4.4.0-21-generic
Compiling and
trim Kernel
Updated OS
Ubuntu Server 3 points
New Kernel 5.0.5-StudentID
Add SysCall
Report 3 points
Page 4 of 18
2 COMPILING LINUX KERNEL
2.1 Preparation
Set up Virtual machine Compiling and installing a new kernel is a risky task so you should work with
the kernel inside a virtual machine. You must download and install Ubuntu 18.04 on a virtual machine to
do this assignment. You could use VMWare or Virtual Box to install the virtual machine. When create
the virtual machine remember to allocate suitable resources to get the best performance of your system when
doing this assignment (originally, number of (virtual) core equal to physical core, about 4GB ram depend
on your system, and 40 GB hard disk space).
Important: Because making a mistake when compiling or installing a new kernel could cause the entire
machine to crash, you must strictly follow instructions in this section. We also encourage you to frequently
take snapshots to avoid repeating time consuming tasks and quickly restore the virtual machine.
Install the core packages Get Ubuntu’s toolchain (gcc, make, and so forth) by installing the build-
essential metapackage:
$ sudo apt -get update
$ sudo apt -get install build -essential
Install kernel-package:
$ sudo apt -get install kernel -package
Create a kernel compilation directory: It is recommended to create a separate build directory for
your kernel(s). In this example, the directory kernelbuild will be created in the home directory:
$ mkdir ˜/ kernelbuild
Download the kernel source: Warning: systemd requires kernel version 3.11 and above (4.2 and above
for unified cgroups hierarchy support). See /usr/share/systemd/README for more information. In this
assignment, you should choose the version 5.0.5 for consistency.
Download the kernel source from http://www.kernel.org. This should be the tarball (tar.xz) file for your
chosen kernel. It can be downloaded by simply right-clicking the tar.xz link in your browser and selecting
Save Link As..., or any other number of ways via alternative graphical or command-line tools that utilize
HTTP, FTP, RSYNC, or Git.
In the following command-line example, wget has been installed and is used inside the ∼/kernelbuild
directory to obtain kernel 5.0.5.
$ cd ˜/ kernelbuild
$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux -5.0.5. tar.xz
QUESTION: Why we have to use another kernel source from the server such as http://www.kernel.org,
can we compile the original kernel (the local kernel on the running OS) directly?
Page 5 of 18
2.2 Configuration 2 COMPILING LINUX KERNEL
2.2 Configuration
This is the most crucial step in customizing the default kernel to reflect your computer’s precise specifications.
Kernel configuration is set in its .config file, which includes the use of Kernel modules. By setting the
options in .config properly, your kernel and computer will function most efficiently. Since making our own
configuration file is a complicated process, we could borrow the content of configuration file of an existing
kernel currently used by the virtual machine. This file is typically located in /boot/ so our job is simply
copy it to the source code directory:
$ cp /boot/config -$(uname -r) ˜/ kernelbuild /. config
Important: Do not forget to rename your kernel version in the General Setup. Because we reuse the
configure file of current kernel, if you skip this, there is the risk of overwriting one of your existing kernels
by mistake. To edit configure file through terminal interface, we must install some essential packages first:
$ sudo apt -get install fakeroot ncurses -dev xz-utils bc flex libelf -dev bison
To change kernel version, go to General setup option, Access to the line “(-ARCH) Local version - append
to kernel release”. Then enter a dot “.” followed by your MSSV. For example:
.1701234
Page 6 of 18
2.3 Build the configured kernel 3 TRIM THE KERNEL
vmlinuz is “the kernel”. Specifically, it is the kernel image that will be uncompressed and loaded into
memory by GRUB or whatever other boot loader you use.
Then build the loadable kernel modules. Similarly, you can run this command in parallel.
$ make modules
or
$ make -j 4 modules
QUESTION: What is the meaning of these two stages, namely “make” and “make modules”? What are
created and what for?
Check out your work: After installing the new kernel by steps described above. Reboot the virtual
machine by typing
sudo reboot
After logging into the computer again, run the following command.
uname -r
If the output string contains your MSSV then it means your custom kernel has been compiled and installed
successfully. You could progress to the next part.
Page 7 of 18
4 SYSTEM CALL
the configuration, you can rebuild the kernel (follow steps in section 2.3) and reboot the system to check the
result.
REQUIREMENT:
• The new kernel must smaller than the default one (You should determine that which is the new
kernel). This includes the boot image is smaller in size, less modules are compiled, etc. Consequently,
the compile of the new kernel will be reduced.
• dmesg to see the messages printed by device drivers or look at /var/log/syslog to see the system log
file
• lspci or lsusb to see the devices that connect to the PCI bus or USB port respectively
• Look at /proc/cpuinfo or /proc/meminfo to see details about the processor and memory you have
When doing the assignment, booting the new kernel may not work. You can choose the previous kernel by
enterring the GRUB menu by pressing SHIFT at the boot time. You also can display GRUB by default
when booting the kernel.
sudo vim /etc/default/grub
And comment out GRUB HIDDEN TIMEOUT. To finish update GRUB by enter:
sudo update -grub
Configuring the kernel is a trial and error process so remember to back up the last configuration file by
a sensible name. You also can use a version control system (github for example) for the management of
changes.
In addition, since the version 2.6.32, make localcongfig is introduced. You can learn about it (link)
4 System call
Now you have known how to a kernel and install it to a system. In this section, you will learn how to add a
new system call to the kernel.
Page 8 of 18
4.2 Prototype 4 SYSTEM CALL
procinfos contain information of three processes: proc current process or process with pid; parent proc parent
of the first process; oldest child proc the oldest child process of the first process. The information of processes
is store in struct proc info and contains: pid process is of the process; name name of the program which is
executed.
4.2 Prototype
The prototype of our system call is described as below:
lo ng get_proc_info(pid_t pid , s t r u c t proinfos * info);
To invoke get proc info system call, user must provide the PID of the process or −1 in case of current process.
If the system call find out the process having given PID it will get information of the process and put it in
output parameter *info and return 0. However, if the system call cannot find such process, it will return
EINVAL
In addition, every time get proc info is invoked a message including your student ID must print to the kernel.
4.3 Implementation
Now you add the get proc info into the kernel and implement it. You look at appendix A to see the process
of add a new system call into a kernel. The process of add get proc info can be described in brief:
In the kernel source directory enter the following command:
$ pwd
˜/ kernelbuild
$ mkdir get_proc_info
$ cd get_proc_info
5 $ touch sys_get_proc_info.c
Page 9 of 18
4.3 Implementation 4 SYSTEM CALL
HINT:
• To find the current process: look at arch/x86/include/asm/current.h or for simple use macro
current (current -> pid)
• To after the trimming process the time to build the kernel is reduced to about 10 minutes but it is till
a long time to compile. To make to the development of system call as fast as possible, you can use
kernel module to test the system call represented as a module in advance (Appd B).
QUESTION: What is the meaning of fields of the line that just add to the system call table (548, 64,
get proc info, i.e.)
Add new system call to the system call header file:
$ ˜/ kernelbuild/include/linux/
Open syscalls.h and add the following line before #endif statement.
s t r u c t proc_info;
s t r u c t procinfos;
asmlinkage long sys_get_proc_info(pid_t pid , s t r u c t procinfos * info);
Page 10 of 18
4.4 Testing 4 SYSTEM CALL
$ make -j 8
$ make modules -j 8
$ sudo make modules_install
$ sudo make install
5 $ sudo reboot
4.4 Testing
After booting to the new kernel, create a small C program to check if the system call has been integrated
into the kernel.
#i n c l u d e <sys/syscall.h>
#i n c l u d e <stdio.h>
#i n c l u d e <unistd.h>
5 #d e f i n e SIZE 200
i n t main (){
lo ng sys_return_value;
unsigned long info[SIZE];
10 sys_return_value = syscall (548, -1, &info);
printf("My student ID: %lu\n", info [0]);
r e t u r n 0;
}
QUESTION: Why this program could indicate whether our system call works or not?
4.5 Wrapper
Although get proc info system call works properly, we still have to invoke it through its number which
is quite inconvenient for other programmers so we need to implement a C wrapper for it to make it easy to
use. This can be done outside the kernel. Thus, to avoid recompile the kernel again, we leave out kernel
source code directory and create another directory to store source code for our wrapper. We first create a
header file which contains the prototype of the wrapper and declare procinfos and proc info struct.
Naming the header file with get proc info.h and put the following lines into its content:
#i f n d e f _GET_PROC_INFO_H_
#d e f i n e _GET_PROC_INFO_H_
#i n c l u d e <unistd.h>
5 #i n c l u d e <unistd.h>
s t r u c t procinfos {
lo ng studentID;
s t r u c t proc_info proc;
10 s t r u c t proc_info parent_proc;
s t r u c t proc_info oldest_child_proc;
};
s t r u c t proc_info {
15 pid_t pid;
char name [16];
};
Page 11 of 18
4.6 Validation 4 SYSTEM CALL
Note: You must define fields in procinfos and proc info struct in the same order as you did in the
kernel.
QUESTION: Why we have to redefine procinfos and proc info struct while we have already defined
it inside the kernel?
We then create a file named get proc info.c to hold the source code file for wrapper. The content of
this file should be as follows:
#i n c l u d e "get_proc_info.h"
#i n c l u d e <linux/kernel.h>
#i n c l u d e <sys/syscall.h>
#i n c l u d e <unistd.h>
5
Hint: You could implement your wrapper based on the code of our test program above.
4.6 Validation
You could check your work by write an additional test module to call this functions but do not include the
test part to your source file (get proc info.c). After making sure that the wrapper work properly, we
then install it to our virtual machine. First, we must ensure everyone could access this function by making
the header file visible to GCC. Run following command to copy our header file to header directory of our
system:
$ sudo cp <path to get_proc_info.h> /usr/include
QUESTION: Why root privilege (e.g. adding sudo before the cp command) is required to copy the header
file to /usr/include?
We then compile our source code as a shared object to allow user to integrate our system call to their
applications. To do so, run the following command:
$ gcc -share -fpic get_proc_info.c -o libget_proc_info.so
If the compilation ends successfully, copy the output file to /usr/lib. (Remember to add sudo before cp
command).
QUESTION: Why we must put -share and -fpic option into gcc command?.
We only have the last step: check all of your work. To do so, write following program, and compile it with
-lget proc info option. The result should be consistent with the process information.
#i n c l u d e <get_proc_info.h>
#i n c l u d e <sys/types.h>
#i n c l u d e <unistd.h>
#i n c l u d e <stdio.h>
5 #i n c l u d e <stdint.h>
i n t main() {
Page 12 of 18
4.6 Validation 4 SYSTEM CALL
i f (get_proc_info(mypid , &info) == 0) {
// TODO: print all information in struct procinfos info
}else{
15 printf("Cannot get information from the process %d\n", mypid);
}
// If necessary , uncomment the following line to make this program run
// long enough so that we could check out its dependence
// sleep (100);
20 }
You should write several tests using get process time to validate the correctness.
Page 13 of 18
A ADD A NEW SYSTEM CALL TO KERNEL
5 Submission
5.1 Source code
After you finish the assignment, you need to compress the following code files into a zip file:
Requirement: you have to code the system call followed by the coding style. Reference:
https://www.gnu.org/prep/standards/html node/Writing-C.html.
5.2 Report
As Figure 2 shown, the score for compiling kernel is 2 points, System call is 3 points, Wrapper of System
call is 2 points and the report is 3 points. For the content of assignment report, describe steps of adding
procmem system call to Linux kernel. The report layout is follows:
In the report, just describe steps in short, succinct paragraphs. You have to add your answer to questions
with the highlight word “QUESTION” throughout this instruction to related sections. Please do not
answer questions as a list of items. One addition requirement for your report: describe in detail the process
you study and implement the core (source code) of the system call. Your score are given based on the
correctness of your answers and the clarify of the report content.
Appendices
A Add a new system call to kernel
A.1 Define a new system call sys hello( )
To add a new system call to kernel, you first have to define the new system call. In this appendix, sys hello()
is used which print a message into the kernel log. In the kernel source directory, create a new directory
named hello and change the directory to hello:
mkdir hello
cd ./ hello
Page 14 of 18
A.2 Add hello/ to the kernel’s Makefile A ADD A NEW SYSTEM CALL TO KERNEL
#i n c l u d e <linux/kernel.h>
A.3 Add the new system call to the system call table
Modern processors support invoking system calls in many different ways depend on their architecture. Since
our virtual machine runs on x86 processors, we only consider about Linux’s system call implementation for
this architecture.
In those file, each system call is declared in one row with following information: number, ABI, name, entry
point and compat entry point separated by a TAB. System calls are call from user space through their
numbers so our system call’s number must be unique.
In the kernel source folder, run the following command to change the directory:
cd arch/x86/entry/syscalls/
Go to the last of ‘syscall 64.tbl’ and add the following line. (if you are on a 32-bit system you’ll need to
change ‘syscall 32.tbl’. For 64-bit, change ‘syscall 64.tbl’.)
548 64 hello sys_hello
A.4 Add new system call to the system call header file
At this time, we have told the kernel that we have a new system call to be deployed but we still do not let
it know the definition (e.g. its input parameters, return values, etc.) of this new system call. The next job
is to explicitly define this system call. To do so, we must add necessary information to kernel’s header files.
Go to the kernel source file and change the directory to include/linux/
cd include/linux/
Page 15 of 18
A.5 Install/update Kernel B KERNEL MODULE
Open syscalls.h and add the following line to near the end of file (before #endif statement)
asmlinkage long sys_hello(void);
In case of all the steps are done correctly, you will get the out put.
System call sys_hello returned 0
This will display Hello World at the end of the kernel’s message.
This is end process of adding sys hello into you linux kernel.
B Kernel module
Treated the system call as a Kernel Module is a simple and fast way to save time compiling the kernel.
To see how Kernel Module actually work, look at the Hello World example:
/*
* hello -1.c - The simplest kernel module.
*/
#i n c l u d e <linux/module.h> / * Needed by all modules * /
5 #i n c l u d e <linux/kernel.h> / * Needed for KERN_INFO * /
Page 16 of 18
B KERNEL MODULE
i n t init_module( void )
{
printk(KERN_INFO "Hello world 1.\n");
10
/*
* A non 0 return means init_module failed; module can’t be loaded.
*/
r e t u r n 0;
15 }
Kernel module must have at least two functions: a “start” (initialization) function called init module()
which is called when the module is insmoded into the kernel, and an “end” (clean up) function called
cleanup module() which is called just before it is rmmoded.
Compiling kernel modules and run it: please refer to the following link. For a deeper view of Kernel
Module, look at link.
In this assignment, you can use the Kernel Module to test before writing the system call and compiling it.
#i n c l u d e <linux/module.h>
#i n c l u d e <linux/moduleparam.h>
#i n c l u d e <linux/kernel.h>
#i n c l u d e <linux/init.h>
5 #i n c l u d e <linux/stat.h>
MODULE_LICENSE("GPL");
i n t init_module( void )
{
printk(KERN_INFO "Kernel module starts\n");
15 printk(KERN_INFO "Input argument from insmod: %ld\n", PID);
// TODO: Implement the kernel module
r e t u r n 0;
}
Page 17 of 18
B KERNEL MODULE
Reversion History
Page 18 of 18