In the last post, I mentioned the /proc system but never got much further. We’ll delve a little deeper in this post.
So what is /proc anyway? According to wikipedia – “The proc file is a special file system … that acts as an interface to internal data structures in the kernel. It can be used to obtain information about the system and to change certain kernel parameters at runtime (sysctl).”
We’ll leverage this interface as a way to interact with our “Hello, World” kernel module.
We’ll jump right into our example again, except this time we’re going to work with a main.c file and our new proc.c file.
Firstly the main.c is mostly a copy of the hello.c from the previous post, but with a few additions.
\#include /\* We're doing Kernel work \*/
\#include /\* Specifically a Kernel Module \*/
\#include /\* Needed for the module_init/exit() macros \*/
\#include "hello.h"
static int __init hello_init(void)
{
int rc;
printk(KERN_INFO "Hello, world!\n");
/\* create the /proc file \*/
rc = my_proc_init();
if (rc == -1)
return -1;
return 0;
}
static void __exit hello_exit(void)
{
my_proc_cleanup();
printk(KERN_INFO "Goodbye, world!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rob Hartzenberg ");
MODULE_DESCRIPTION("Hello world example.");
MODULE_SUPPORTED_DEVICE("hello");
Here comes the fun part – the /proc handling.
\#include /\* We're doing kernel work \*/
\#include /\* Specifically, a module \*/
\#include /\* Necessary because we use the proc fs \*/
\#include /\* for copy_from_user \*/
\#include
\#include
\#include
\#include "hello.h"
\#define PROCFS_MAX_SIZE 1024
\#define PROCFS_NAME "hello"
static char procfs_buffer\[PROCFS_MAX_SIZE];
static unsigned long procfs_buffer_size = 0;
static struct proc_dir_entry *entry;
static struct proc_dir_entry *parent;
/\*\*
\* This function is called when the /proc file is read.
\*/
static ssize_t procfile_read(struct file \*file, char __user \*buffer, size_t count, loff_t *offset)
{
if (\*offset > 0 || count < PROCFS_MAX_SIZE) /\* we have finished to read, return 0 */
return 0;
/\* fill the buffer, return the buffer size \*/
if(copy_to_user(buffer, procfs_buffer, procfs_buffer_size))
return -EFAULT;
\*offset = procfs_buffer_size;
return procfs_buffer_size;
}
/\*\*
\* This function is called when the /proc file is written
\*/
static ssize_t procfile_write(struct file\* file,const char __user \*buffer,size_t count,loff_t *f_pos){
int tlen;
char *tmp = kzalloc((count+1),GFP_KERNEL);
if(!tmp)return -ENOMEM;
if(copy_from_user(tmp,buffer,count)){
kfree(tmp);
return EFAULT;
}
tlen = PROCFS_MAX_SIZE;
if (count < PROCFS_MAX_SIZE)
tlen = count;
memcpy(&procfs_buffer,tmp,tlen);
procfs_buffer_size = tlen;
kfree(tmp);
return tlen;
}
static int procfile_show(struct seq_file \*m,void \*v){
static char *str = NULL;
seq_printf(m,"%s\n",str);
return 0;
}
/\*\*
\* Open the procfile
\*/
static int procfile_open(struct inode \*inode,struct file \*file){
return single_open(file,procfile_show,NULL);
}
static struct proc_ops proc_fops = {
.proc_lseek = seq_lseek,
.proc_open = procfile_open,
.proc_read = procfile_read,
.proc_release = single_release,
.proc_write = procfile_write,
};
/\*\*
\*This function is called from main.c when the module is loaded
\*/
int __init my_proc_init(void)
{
parent = proc_mkdir(PROCFS_NAME,NULL);
entry = proc_create("system",0777,parent,&proc_fops);
if(!entry)
return -1;
return 0;
}
/\*\*
\*This function is called from main.c when the module is unloaded
\*/
void __exit my_proc_cleanup(void)
{
remove_proc_entry("system", parent);
remove_proc_entry(PROCFS_NAME, NULL);
}
And we need a .h file to include the function prototypes.
/\* Just the function prototypes for now \*/
int __init my_proc_init(void);
void __exit my_proc_cleanup(void);
The Makefile also needs to be adjusted to work with our two files instead of just the one. Notice the differences in the top part of the file.
ifneq ($(KERNELRELEASE),)
\# kbuild part of makefile
obj-m := hello.o
hello-y := main.o proc.o
else
\# Run this Makefile as follows:
\# make clean && make all && make test
\#
KDIR= /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) M=$(PWD) modules
test:
scp ./hello.ko dev@test-instance:~/
\-ssh test-instance sudo rmmod hello
ssh test-instance sudo insmod ./hello.ko
ssh test-instance cat /proc/hello/system
clean:
rm -f *~
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) M=$(PWD) clean
endif
And that should be it, compile and test and see how it works. Once you have the module loaded, you should be able to `cat /proc/hello/system`, `echo “this is a test” > /proc/hello/system` then `cat /proc/hello/system` again and observe the changes.
rob@rob-Ghost:~/Dev/hello$ sudo insmod ./hello.ko
rob@rob-Ghost:~/Dev/hello$ cat /proc/hello/system
rob@rob-Ghost:~/Dev/hello$ echo "Hello, world!" > /proc/hello/system
rob@rob-Ghost:~/Dev/hello$ cat /proc/hello/system
Hello, world!
rob@rob-Ghost:~/Dev/hello$ sudo rmmod hello
rob@rob-Ghost:~/Dev/hello$