Pages

Creating a proc read entry

After understanding the basics of proc entries and their creation in the post "Creating a proc Entry" In this post we will see how to write a module to create a read only entry under the /proc file system.

To create a proc entry we need to include the file proc_fs.h



d Let us create a function create_new_proc_entry  in which we will call the function create_read_proc_entry as shown below


Note: The following module will not work for kernel versions 3.10 and above. Please refer to the post Creating read only proc entry in kernel versions above 3.10 for the new method of proc entry creation

This should create a read only entry under /proc with the name "hello" having default permissions.
We want to create the entry under /proc directly and not in any sub-directory in /proc hence the third argument is NULL .
The function that would get executed when the proc entry is read is "read_proc" .
We are not passing any data to the function, hence the last argument is NULL


The next step is to implement the read_proc function. The read function's prototype is




Arguments:
*buf : The kernel allocates a page of memory to any process that attempts to read a proc entry. The page pointer points to that buffer of memory into which the data is written.
**start: This pointer is used when the reading of the proc file should not start from the beginning of the file but from a certain offset. For small reads this is generally set to NULL.
off : The offset from the beginning of the file where the file pointer currently points to
count : The number of bytes of data to be read
data : The data passed from the create_read_proc_entry function call.
eof: is set to 1 to indicate end of file



The read_proc function returns an integer value which basically gives the number of bytes of data placed in the page buffer.

In our code we will just put a print statement in the read_proc function.



Note the printing in proc entries is using the command sprintf.

The proc entry should get created as soon as we do insertion of the module. The init function is the one that gets called when the module gets inserted so we add a call to the create_new_proc_entry in the init function of the module.


The proc entry need to be removed once we remove the module, hence add a call to remove_proc_entry in the cleanup function.

In our case it would be


Combining the above mentioned snippets into a module, the code will look as follows.

code: proc_read.c



Makefile :



Using the same makefile as before we can compile this into a .ko file.
Once compiled insert the module using the insmod command




If the insmod is successful then run the command



You should see the output as what ever you have given as print in the read_proc function.

Creating a proc entry - 1

In the past few posts we learnt how to write a small character driver that was capable of reading and writing from an array.
As a user if we want to look into what the kernel is up to, we would need our application to read the data in kernel space. But applications running in the user space can not access the data in the kernel space, in such situations the proc file system comes to rescue.
The proc file system is an interface between the kernel and the userspace. Every entry in the proc file system provides some information from the kernel.
For eg:
 The entry "meminfo"  gives the details of the memory being used in the system.
To read the data in this entry just run
cat /proc/meminfo


Similarly the "modules" entry gives details of all the modules that are currently a part of the kernel.
cat /proc/modules

The proc file system is also very useful when we want to debug a kernel module. While debugging we might want to know the values of various variables in the module or may be the data that module is handling. In such situations we can create a proc entry for our selves and dump what ever data we want to look into in the entry.
We will be using the same example character driver that we created in the previous post to create the proc entry.

The proc entry can also be used to pass data to the kernel by writing into the kernel, so there can be two kinds of proc entries
An entry that only reads data from the kernel space
An entry that reads as well as writes data into and from kernel space.

We will start by creating a proc entry for only reading first and then move to a a proc entry for write.
Creating proc entry:

Kernel provides the following functions to create a proc entry

create_proc_read_entry()
create_proc_entry()

Both of these functions are defined in the file linux/proc_fs.h. 


The create_proc_entry is a generic function that allows to create both read as well as write entries.
create_proc_read_entry is a function specific to create only read entries.

Its possible that most of the proc entries are created to read data from the kernel space that is why the kernel developers have provided a direct function to create a read proc entry.

We will first use the create_proc_read_entry to create our entry and then look into create_proc_entry. 


The proc entries are basically based on the structure "proc_dir_entry". Meaning every proc entry that gets created is represented by a structure of the kind
"proc_dir_entry"  by the kernel.
The structure "proc_dir_entry"   looks as follows in 2.6.33-21


struct proc_dir_entry {
unsigned int low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
loff_t size;
const struct inode_operations *proc_iops;
/*
* NULL ->proc_fops means "PDE is going away RSN" or
* "PDE is just created". In either case, e.g. ->read_proc won't be
* called because it's too late or too early, respectively.
*
* If you're allocating ->proc_fops dynamically, save a pointer
* somewhere.
*/
const struct file_operations *proc_fops;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
int pde_users; /* number of callers into module in progress */
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
struct completion *pde_unload_completion;
struct list_head pde_openers; /* who did ->open, but not ->release */
};

All the fields might not be clear right now, we will get into them as and when we need.

The prototype of the function create_proc_read_entry is


struct proc_dir_entry *create_proc_read_entry(const char *name,
mode_t mode, struct proc_dir_entry *base, 
read_proc_t *read_proc, void * data)

name :  This the name by which entry will be created under the proc file system.
            for eg: if we pass "first_proc_entry"  , then it will create a file by the name
            first_proc_entry  under the /proc directory.
mode  :  mode sets the permissions for the entry created, if 0 is passed it takes    
                system default settings
base : Base is the base directory in which the entry should be created, this is
          useful when you want to create a proc entry under a sub folder in /proc. If
          you pass NULL it will create it under /proc by default
read_proc:  read_proc is a pointer to the function that should be called every time
                the proc entry is read. The function should be implemented in the
                driver. It is this functions which we will use to display what ever data        
                we  want to display to the user.
data :  This is not used by the kernel, but is passed as it is to the read_proc
            function. The data is used by the driver writer to pass any private data
            that has to be passed to the read_proc function.  It can be passed as NULL
             if no data has to be passed.


Once a module gets registered with the kernel, the corresponding proc entry should also be created. So the call to the function create_read_proc_entry is included in the "init" function of the driver.

Removing a proc entry: 

When a module is removed from the kernel, it should also remove any proc entries it created. The function that enables the removal of proc entry is "remove_proc_entry" which has the following prototype
void remove_proc_entry(const char *name, struct proc_dir_entry *parent);


name: Name of the proc entry that has to be removed.
parent: In case the proc entry lies in a subdirectory under the proc filesystem, we
              can pass the subdirectories here.

With this introduction to proc filesystem and the corresponding function calls that will allow us to create the entries we can start writing the code to create our first proc entry.