Programming with Linux proc files

poornaMoksha's Avatar author of Programming with Linux proc files
This is an article on Programming with Linux proc files in Linux.
As we have already discussed the procfile system in my previous article. Here we will learn about how to create a procfile which can be read and written to.

Since, proc files act as an interface between user space and kernel space, so creating, reading and writing a proc file will not be simple programming. Programming with proc files involves kernel level programming in form of loadable kernel modules.

Example



As I have already discussed how to program LKMs, so I will not repeat it here. Lets start with creating Proc files :
  1. The function used for creating proc files is 'create_proc_entry'. It takes 3 arguments- name of proc file, permissions to the file, a location in the /proc filesystem in which the file is to reside and returns a pointer to structure 'struct proc_dir_entry'.
  2. The return pointer ctan be used to configure other aspects of the proc file, such as the function to call when a read is performed on the file.
  3. The structure 'proc_dir_entry' is as follows :

    Code:
    struct proc_dir_entry {
    
        const char *name;            // virtual file name
    
        mode_t mode;                // mode permissions
    
        uid_t uid;                // File's user id
    
        gid_t gid;                // File's group id
    
        struct inode_operations *proc_iops;    // Inode operations functions
    
        struct file_operations *proc_fops;    // File operations functions
    
        struct proc_dir_entry *parent;        // Parent directory
    
        ...
    
        read_proc_t *read_proc;            // /proc read function
    
        write_proc_t *write_proc;        // /proc write function
    
        void *data;                // Pointer to private data
    
        atomic_t count;                // use count
    
        ...
    
    };
  4. To remove a file from /proc, use the 'remove_proc_entry' function.
  5. We have declared/defined two functions 'my_read' and 'my_write' that get called when proc file is read and written to respectively.
  6. Since the data is coming from user space in case of 'my_write' function, so we cannot directly use the pointer to the data passed by the user space.
  7. To solve the above problem we use the function 'copy_from_user'
  8. Similarly 'copy_to_user' is used in case of a read for proc file is issued.
Enough of theory, lets look at a real example :

Code:
#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/proc_fs.h>

#include <linux/string.h>

#include <linux/vmalloc.h>

#include <asm/uaccess.h>



#define MAX_LENGTH       PAGE_SIZE

static struct proc_dir_entry *proc_entry;
static char *info;  // Space for my_proc_entry strings
static int write_index;  // Index to write
static int read_index;  // Index to read

int my_read( char *page, char **start, off_t off,
                   int count, int *eof, void *data );

ssize_t my_write( struct file *filp, const char __user *buff,
                        unsigned long len, void *data );

int init_my_module( void )

{
  int ret = 0;
  info = (char *)vmalloc( MAX_LENGTH );

  if (!info) {
    ret = -ENOMEM;
  } else {
    memset( info, 0, MAX_LENGTH );
    proc_entry = create_proc_entry( "my_proc_entry", 0644, NULL );

    if (proc_entry == NULL) {
      ret = -ENOMEM;
      vfree(info);
      printk(KERN_INFO "my_proc_entry: Couldn't create proc entry\n");

    } else {
      write_index = 0;
      read_index = 0;
      proc_entry->read_proc = my_read;
      proc_entry->write_proc = my_write;
      printk(KERN_INFO "my_proc_entry: Module loaded.\n");
    }
  }
  return ret;

}


void cleanup_my_module( void )

{
  remove_proc_entry("my_proc_entry", proc_entry);
  vfree(info);
  printk(KERN_INFO "my_proc_entry: Module unloaded.\n");

}



ssize_t my_write( struct file *filp, const char __user *buff,
                        unsigned long len, void *data )

{

  int space_available = (MAX_LENGTH-write_index)+1;

  if (len > space_available) {
    printk(KERN_INFO "my_proc_entry: space full!\n");
    return -ENOSPC;
  }



  if (copy_from_user( &info[write_index], buff, len )) {
    return -EFAULT;
  }

  write_index += len;
  info[write_index-1] = 0;
  return len;

}



int my_read( char *page, char **start, off_t off,
                   int count, int *eof, void *data )

{
  int len;

  if (off > 0) {
    *eof = 1;
    return 0;
  }

  /* Wrap-around */

  if (read_index >= write_index) read_index = 0;
  len = sprintf(page, "%s\n", &info[read_index]);
  read_index += len;
  return len;

}


module_init( init_my_module );

module_exit( cleanup_my_module );
Makefile for the above LKM :

Code:
obj-m += proc.o

all:
        sudo make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        sudo make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Compilation and insertion of module:
Code:
practice # make

sudo make -C /lib/modules/2.6.32-21-generic/build M=/home/himanshu/practice modules
make: Entering directory `/usr/src/linux-headers-2.6.32-21-generic'
  CC [M]  /home/himanshu/practice/proc.o
/home/himanshu/practice/proc.c: In function ‘init_my_module’:
/home/himanshu/practice/proc.c:49: warning: assignment from incompatible pointer type
  Building modules, stage 2.
  MODPOST 1 modules
  LD [M]  /home/himanshu/practice/proc.ko
make: Leaving directory `/usr/src/linux-headers-2.6.32-21-generic'

practice # insmod ./proc.ko
Now, when we see our proc file system, we will find an entry for my_proc_entry.

To play around, run the following command :

Code:
$ echo "hello world" > /proc/my_proc_entry
now, try to view the contents of the proc file 'my_proc_entry'. You will see that the string 'hello world' will be displayed.

Conclusion



To conclude, programming with proc files is extremely useful where-in there is a requirement to expose dynamic kerenl information to the user space and take parameters as input from user space and change the kernel configuration dynamically.

Stay tuned for more!!!!!