Friday, 20 September 2013

Linux: Creating an entry in /proc file system (Part 1: The hello_proc pseudo file)

I am using this column to explain the process of adding an entry to the /proc file system, by writing a linux module. After beginning with some theory, I'll describe a hello_proc kernel module for adding an entry to the /proc file system and end the column with a little introduction of 'sequence' files. I plan to go into sequence files in a later column.
Oh yeah, a warning, some of this column has been deduced by going through the kernel code and may be incorrect. Please feel free to comment and point at any inaccuracies. I make use of the linux kernel 3.11.1, please note that the old way of creating a /proc entry by making use of create_proc_entry() has been done away with in favor of proc_create(). I will be using the new approach.

The /proc file system

Look at the man page, by doing man proc. The man page will tell you that it is pseudo-file system which is used as an interface to kernel data structures. It is commonly mounted at /proc.
We say that the /proc file system contains pseudo-files, because these files, as opposed to normal files, take up no space on the hard drive. The kernel creates an illusion of a file by implementing all (well, almost all) file operations, whose content is generated dynamically by a kernel module. Since the kernel module runs in a privileged mode and in the kernel address space, a module has access to all kernel data structures and effectively the whole system. The /proc file system thus provides an interface between the user space and a kernel module! Isn't it cool!
Let's have a look at a /proc file existing in a linux kernel, by default. Look at /proc/version.

$ cat /proc/version
Linux version 3.11.1 (root@ubuntu) 
(gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) )
#1 SMP Fri Jan 27 23:40:56 PST 2012
To see that this content is not stored in a normal file, have a look at the file size.

$ ls -l /proc/version
-r--r--r-- 1 root root 0 2013-09-18 21:27 /proc/version
The file size is zero, because the contents of the file is being generated by an underlying kernel code. The contents of the file are generated dynamically and afresh and this is the reason that the contents of the file might be different, every time it is read. A few other proc files that you can explore are:

/proc/cpuinfo
/proc/meminfo
/proc/interrupts
....actually you can look at any file inside /proc


A hello_proc module to add a dummy entry into the proc file system

Enough talk, let's get down to business and get our hands dirty. Here is code for a kernel module which creates a file in the /proc file system. This pseudo-file does, well, nothing except output 'Hello proc!\n' when read.

#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

static int hello_proc_show(struct seq_file *m, void *v) {
  seq_printf(m, "Hello proc!\n");
  return 0;
}

static int hello_proc_open(struct inode *inode, struct  file *file) {
  return single_open(file, hello_proc_show, NULL);
}

static const struct file_operations hello_proc_fops = {
  .owner = THIS_MODULE,
  .open = hello_proc_open,
  .read = seq_read,
  .llseek = seq_lseek,
  .release = single_release,
};

static int __init hello_proc_init(void) {
  proc_create("hello_proc", 0, NULL, &hello_proc_fops);
  return 0;
}

static void __exit hello_proc_exit(void) {
  remove_proc_entry("hello_proc", NULL);
}

MODULE_LICENSE("GPL");
module_init(hello_proc_init);
module_exit(hello_proc_exit);
Most of the above code is self explanatory. For a more general introduction about modules, have a look at my previous post. Here are a few points: (NOTE: For now, please accept sequence files as data buffers, at face value.)
  • Nothing special about the header files. linux/module.h is required for necessary module macros and functions. linux/proc_fs.h is required for the functions proc_create() and remove_proc_entry() for creating a pseudo-file in the proc file system. linux/seq_file.h is explained later.
  • The hello_proc_show() is what decides the output.
  • The hello_proc_open() is the open callback, called when the proc file is opened. Here, single_open() means that all the data is output at once. More on this when I discuss sequence files.
  • hello_proc_fops is the file operations structure used to define file manipulation callbacks for our pseudo-file
  • The file is actually created using proc_create(file_name, permission_bits, parent_dir, file_operations) where "hello_proc" is name, 0 in the permission_bits parameter means default 0444 permission for file, NULL in parent_dir means that our file is located at /proc.
  • The function remove_proc_entry(name, parent_dir) is used to remove our pseudo-file

Create linux kernel object code as described in my previous post. (I know, even I hate the way modules are compiled!)
Now, install the module, have a look at /proc/hello_proc, remove the module.

$ sudo insmod hello_proc.ko
$ cat /proc/hello_proc
Hello proc!
$ ls -l /proc/hello_proc
-r--r--r-- 1 root root 0 2013-09-18 21:32 /proc/hello_proc
$ sudo rmmod hello_proc
And, we are done!

Wait, what the hell were those sequence files?

Umm, the linux implementation of the proc file system has a limitation. Our module cannot output more than one page of data to the pseudo-file at once. A page is a pre-defined amount of memory, typically 4096 bytes, and is available in the PAGE_SIZE macro. This limitation is bypassed by using sequence files, which provide an interface to print multi-paged outputs easily, as a (you guessed it right!) sequence of pages. The interface is so convenient, that it is also used for single paged output like ours.
I plan to cover multi-paged outputs using sequence files in part 2 of this column, so stay tuned!

4 comments:

  1. Thanks a lot
    Nice explanation without unnecessary details and bulk of theory (which I hate).
    Thanks...

    ReplyDelete
  2. what is the single_open() function ????????

    ReplyDelete
  3. Excellent and this helped me move on from the older proc interface API (manual buffer handling).

    ReplyDelete
  4. Thank you for this post;
    What if i wish to read an input from the proc file? how would i do that ?

    ReplyDelete