DEV Community

Ömer Gülen
Ömer Gülen

Posted on

How to add system call (syscall) that prints elapsed time of a process with given PID to the kernel and test it?

In my previous tutorial 👇

I described how to add syscall to the kernel, compile it and test it in detail. As promised, I now would like to describe how to create a syscall that prints out the elapsed time of a process with a given process id(PID).

This task was my Operating Systems Programming Assignment.

In this tutorial, unlike the previous tutorial I will not go over into every detail, but point out the different parts of the process.

After changing my current directory into kernel source files, I create another folder for my new function. You don't need to do it, but I prefer it this way.

mkdir petime && cd petime
Enter fullscreen mode Exit fullscreen mode

I will call my function petime short for process elapsed time.

After I've created a C file which my code goes into.

vim petime.c
Enter fullscreen mode Exit fullscreen mode

In my C code, I used SYSCALL_DEFINE1 Macro from <linux/syscalls.h>. Because I needed to pass one integer argument to the function.

Also, you can use it for different argument counts. While argument count is X, the syntax will be like SYSCALL_DEFINEX.

Example:

// For 2 parameters
SYSCALL_DEFINE2(function_name, int, first_int, int, second_int){
  // code goes here
}

// For 1 parameter
SYSCALL_DEFINE1(function_name, int, first_int){
  // code goes here
}

// For no-parameters
SYSCALL_DEFINE0(function_name){
  // code goes here
}
Enter fullscreen mode Exit fullscreen mode

In addition to this Macro, I used the task_struct struct to get the process' start time and time elapsed since the boot with ktime_get_ns() function.

My C file which contains my syscall:

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/syscalls.h>

// Used this macro to be able to pass variables to the syscall
// It takes one integer, PID.
SYSCALL_DEFINE1(sys_petime, int, pid)
{
  // Using task_struct struct from sched.h, which has task's (process)
  // various data mostly about scheduling.
  struct task_struct *task;
  u64 start_time, elapsed;

  // Get process with PID.
  task = pid_task(find_vpid(pid), PIDTYPE_PID);
  // If process is not found.
  if (task == NULL)
  {
    // Print the error to the kernel buffer
    printk(KERN_INFO "Cannot find a process with that PID: %d\n", pid);
  }
  else
  {
    // Taken from "taskstats.c" source code (line 240-241).
    // Used the logic behind calculating the delta time in that source.
    // Get current nanoseconds since boot
    start_time = ktime_get_ns();
    // Difference
    elapsed = start_time - task->real_start_time;
    // Print result to the kernel buffer
    printk(KERN_INFO "PID: %d has ELAPSED %llu nanoseconds.", pid, elapsed);
  }

  // If nothing goes wrong, return 0 as successful
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

ktime_get_ns() is like timestamp but it doesn't count from 1 Jan 1970 instead it counts nanoseconds since the Operating System boots.

task->real_start_time is the boot based start time of the process in nanoseconds.

So as a result, the difference of total elapsed time since boot and the boot based start time of a process gives the elapsed time of a process.

Then, I've created Makefile file of my syscall:

vim Makefile
Enter fullscreen mode Exit fullscreen mode

and inserted the default-ish configuration:

obj-y := petime.o
Enter fullscreen mode Exit fullscreen mode

Now, I need to let my general Makefile know about my new folder and function.

Go back to the kernel source folder and edit Makefile:

cd .. && vim Makefile
Enter fullscreen mode Exit fullscreen mode

Find the line with the core-y, it should be similar to this:

core-y      += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ hello/
Enter fullscreen mode Exit fullscreen mode

hello/ is the folder from our previous tutorial.

I've added my new folder petime to the end of this line. Result should be looking like this:

core-y      += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ hello/ petime/
Enter fullscreen mode Exit fullscreen mode

After saving the Makefile, we need to add our new syscall to the syscalls tables.

I will describe the tutorial according to 64-bit Operating System, if you want to do it on 32-bit OS, you can go back to my previous tutorial find the details of the following steps for 32-bit OS.

For 64-bit OS, I add my syscall to the end of my file(table):

549 64  petime  __64_sys_sys_petime
Enter fullscreen mode Exit fullscreen mode

I've put sys 2 times, like _sys_sys_ because my function name was already sys_petime but you need to prepend __64_sys_ of the function name in this table, so result becomes __64_sys_sys_petime.

Then, we need to add our function signature to the syscalls header file syscalls.h.

vim include/linux/syscalls.h
Enter fullscreen mode Exit fullscreen mode

Then, add the following line to the end of the document before the #endif statement:

asmlinkage long sys_petime(int);
Enter fullscreen mode Exit fullscreen mode

Now, we can compile it the same as our Hello World example.

After the, compilation we can test it with the following C code:

#include <stdio.h>
#include <linux/kernel.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>

int main(int argc, char **argv)
{
  // Get args from terminal and call petime (get elapsed time) with that PID.
  long int helloCheck = syscall(549, atoi(argv[1]));
  printf("System call sys_petime returned %ld\n", helloCheck);
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

And the result is such that:
Terminal output of syscall

Please comment below your questions and feedback.

Discussion (2)

Collapse
stalim profile image
Stalim Rivero

I got this error:

arch/x86/entry/syscall_64.o:(.rodata+0x1120): undefined reference to `__64_sys_sys_petime'
Makefile:1080: recipe for target 'vmlinux' failed

Collapse
nguyenquangduc2000 profile image
Nguyen Quang Duc

change this in include/linux/syscalls.h
asmlinkage long sys_petime(int);
to:
asmlinkage long __64_sys_sys_petime(int);