Introduction to the /proc file system (Linux kernel development – part 4)

  1. Preface
    Hi all,
    As I continue with my Linux kernel development I now reached the situation where I came across the /proc pseudo file system.
    This virtual file system plays an important role and “strongly” associated with the kernel, so it is worth talking and exploring it. In this post I will go over, not too deeply and hopefully not in a shallow manner about this file system, its usages in the context (also) of device drivers.
  2. Introduction
    “The Linux kernel has two primary functions: to control access to physical devices on the computer and to schedule when and how processes interact with these devices. The /proc/ directory — also called the proc file system — contains a hierarchy of special files which represent the current state of the kernel — allowing applications and users to peer into the kernel’s view of the system.
    Within the /proc/ directory, one can find a wealth of information detailing the system hardware and any processes currently running. In addition, some of the files within the /proc/ directory tree can be manipulated by users and applications to communicate configuration changes to the kernel.
    Meaning, the /proc file system is a sort of “read (and also a little write) interface” towards the kernel and the device it manages.
    One can cat many files under the /proc file system such as, for example, /proc/interrupts that displays real-time & up-to-date information about interrupts in the system.

    1. It is worth mention that some of the information in files in this file system is not so “user friendly”  so to the “rescue” comes utilities that “converts” that information for a more human readable form (utilities such as top, free, lspci).
    2. Also it is worth to note that some of the files in this folder are only readable by the root user.
  3. /proc file system files usages

    1. As a general rule, most of the files under the /proc file system are read-only, i.e. they are meant to be used in order to get (retrieve) information. However, some of the files can be adjusted (written to) in order to alter (modify) kernel settings/configurations at run time, where most of them are under the /proc/sys sub folder. For example, in order to modify the host name of the machine one can execute the command:
    $ echo www.example.com > /proc/sys/kernel/hostname

    which actually modifies the value of the hostname file from the /proc file system.
    IMPORTANT NOTE:A useful (and very widely used) utility that is used to alter files (settings) in the kernel is the sysctl (located under /sbin/sysctl) .

  4. /proc file system of our interest
    Coming back to the context where this post is – which is how the /proc file system is “linked” to kernel modules, it is worth to mention some files of the /proc file system that are widely used in the context of kernel module development:

    1. /proc/module
      This file displays a list of all modules loaded currently in the system. The lsmod utility (located under /sbin/lsmod) under the hood, reads information from this file).
    2. /proc/devices
      This file displays the various character and block devices currently configured (not including devices whose modules are not loaded).
  5. Device drivers and the /proc file system
    In a way, one can say that each file under the /proc file system is “tied” to a “kernel function”. Device drivers utilizes the /proc file system by “exposing” information about them via “entries” in the /proc file system which are created (and removed) by them.
    The /proc file system is dynamic – so modules (drivers) can add and remove entries whenever required (typically upon loading and unloading respectively). The struct that is used to hold this information is:

    1. struct proc_dir_entry
    2. Typically when the module is loaded (i.e. – in the init_module function) the entry is created and when the module is unloaded (in the exit_module) the entry is removed.
  6. The file operation structure
    Each module in the kernel that wishes to “expose file wise” operations (such as read/write,etc…) can expose it via the structure struct file_operations. This struct can be thought off as an interface (from OOP) that has ~25 functions, each of which the module can implement or not to (thus leaving this capability not supported).
  7. Kernel module implementation
    The very “minimal” kernel module that creates and is “accessible” via an entry in the /proc file system is as follows:

    #include <linux/module.h>	/* Specifically, a module */
    #include <linux/kernel.h>	/* We're doing kernel work */
    #include <linux/proc_fs.h>	/* Necessary because we use the proc fs */
    #include <linux/uaccess.h>	/* For copy_to_user */
    #include <linux/timekeeping.h>
    
    #define procfs_entry_name "sampleModuleForPorcFs"
    
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("A simple kernel module with an /proc fs entry");
    MODULE_AUTHOR("Guy Avraham");
    
    struct timespec curr_tm;
    
    /*
    * This structure hold information about the /proc file
    */
    struct proc_dir_entry *proc_file_entry;
    
    /* Return the time stamp of the "read operation" */
    
    ssize_t procfile_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
    {
    	int ret = 0, cnt;
    	char tmp_buff [100] = {0};
    	getnstimeofday(&curr_tm);
    	if (snprintf(tmp_buff, 100, "%s read at:%.2lu:%.2lu:%.2lu:%.6lu \n", procfs_entry_name,
                       (curr_tm.tv_sec / 3600) % (24), (curr_tm.tv_sec / 60) % (60), 
                       (curr_tm.tv_sec % 60), (curr_tm.tv_nsec / 1000)));
    
    	if (*ppos > 0) {
    		/* we have finished to read, return 0 */
    		ret  = 0;
    	} else if (tmp_buff[0]) {
    		/* fill the buffer, return the buffer size */
    		cnt = sizeof(tmp_buff);
    		if (count < cnt)
    			cnt = count;
    		copy_to_user(buffer, tmp_buff, cnt);
    		*ppos += cnt;
    		ret= cnt;
    	}
    
    	return ret;
    }
    
    /* File operations structure */
    
    static const struct file_operations proc_file_fops = {
     .owner = THIS_MODULE,
     .read  = procfile_read,
    };
    
    /* Init function */
    
    int init_module()
    {
    	proc_file_entry = proc_create(procfs_entry_name, 0644, NULL, &proc_file_fops);  // New in Kernel 3.10
    	
    	if (proc_file_entry == NULL) {
    		remove_proc_entry(procfs_entry_name, NULL);
    		printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
    		       procfs_entry_name);
    		return -ENOMEM;
    	}
    
    	getnstimeofday(&curr_tm);
    	printk(KERN_INFO "/proc/%s created at: %.2lu:%.2lu:%.2lu:%.6lu \n", procfs_entry_name,
                       (curr_tm.tv_sec / 3600) % (24), (curr_tm.tv_sec / 60) % (60), 
                       (curr_tm.tv_sec % 60), (curr_tm.tv_nsec / 1000) );
    	
    	return 0;
    }
    
    void cleanup_module()
    {
    	remove_proc_entry(procfs_entry_name, NULL);
    	printk(KERN_INFO "/proc/%s removed\n", procfs_entry_name);
    }
    
  8. NOTES

    1. The only thing that this module does upon “reading” its content is to display a string with its name and the current time stamp. This can be seen when “cat-ing” the module entry in the /proc as follows:
      [gavraham@localhost procFsExample]$ cat /proc/sampleModuleForPorcFs 
      sampleModuleForPorcFs read at:13:26:32:587886
    2. In this module the “load” and “unload” functions are init_module and cleanup_module respectively.
    3. The THIS_MODULE macro indicates that the current module (within this file) is the “owner” of the file_operations struct. This is important in order to uniquely identify it and “link” it to the correct module.
    4. The entry in the /proc folder is created in the init_module function and removed in the cleanup_module function. When the module is loaded, if one runs the command ls /proc, one of the entries will be indeed:
      proc_entry_1
  9. Conclusions
    In this post I have introduced the following:

    1. The /proc file system and its usage.
    2. The struct file_operations, its main usage and purpose and a very minimal implementation and creation of it for read capabilities.
    3. Very minimal kernel module that creates (and removes) an entry in the /proc file system and implements the read operation by “leveraging” the entry in the /proc file system of it.

      Resources:
      a) RHEL documentation on the proc
      b) Good Q&A on StackOverflow regarding the THIS_MODULE macro

      The picture: Typical Meadow landscape view, in the state of Mato Grosso do Sul, Brazil.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s