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.
As I have already discussed how to program LKMs, so I will not repeat it here. Lets start with creating Proc files :
Makefile for the above LKM :
Compilation and insertion of module:
Now, when we see our proc file system, we will find an entry for my_proc_entry.
To play around, run the following command :
now, try to view the contents of the proc file 'my_proc_entry'. You will see that the string 'hello world' will be displayed.
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!!!!!
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 :
- 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'.
- 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.
- 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 ... }; - To remove a file from /proc, use the 'remove_proc_entry' function.
- We have declared/defined two functions 'my_read' and 'my_write' that get called when proc file is read and written to respectively.
- 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.
- To solve the above problem we use the function 'copy_from_user'
- Similarly 'copy_to_user' is used in case of a read for proc file is issued.
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 );
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
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
To play around, run the following command :
Code:
$ echo "hello world" > /proc/my_proc_entry
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!!!!!
