We have seen a very simple example for a /proc file where we only read the file /proc/helloworld.
It's also possible to write in a /proc file. It works the same way as read, a function is called when the /proc
file is written. But there is a little difference with read, data comes from user, so you have to import data from
user space to kernel space (with copy_from_user or get_user)
The reason for copy_from_user or get_user is that Linux memory (on Intel
architecture, it may be different under some other processors) is segmented. This means that a pointer, by itself,
does not reference a unique location in memory, only a location in a memory segment, and you need to know which
memory segment it is to be able to use it. There is one memory segment for the kernel, and one for each of the processes.
The only memory segment accessible to a process is its own, so when writing regular programs to run as processes,
there's no need to worry about segments. When you write a kernel module, normally you want to access the kernel memory
segment, which is handled automatically by the system. However, when the content of a memory buffer needs to be passed between
the currently running process and the kernel, the kernel function receives a pointer to the memory buffer which is in the
process segment. The put_user and get_user macros allow you to access that
memory. These functions handle only one caracter, you can handle several caracters with copy_to_user and
copy_from_user. As the buffer (in read or write function) is in kernel space, for write function
you need to import data because it comes from user space, but not for the read function because data is already
in kernel space.
Example 5-2. procfs2.c
/**
* procfs2.c - create a "file" in /proc
*
*/
#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 <asm/uaccess.h> /* for copy_from_user */
#define PROCFS_MAX_SIZE 1024
#define PROCFS_NAME "buffer1k"
/**
* This structure hold information about the /proc file
*
*/
static struct proc_dir_entry *Our_Proc_File;
/**
* The buffer used to store character for this module
*
*/
static char procfs_buffer[PROCFS_MAX_SIZE];
/**
* The size of the buffer
*
*/
static unsigned long procfs_buffer_size = 0;
/**
* This function is called then the /proc file is read
*
*/
int
procfile_read(char *buffer,
char **buffer_location,
off_t offset, int buffer_length, int *eof, void *data)
{
int ret;
printk(KERN_INFO "procfile_read (/proc/%s) called\n", PROCFS_NAME);
if (offset > 0) {
/* we have finished to read, return 0 */
ret = 0;
} else {
/* fill the buffer, return the buffer size */
memcpy(buffer, procfs_buffer, procfs_buffer_size);
ret = procfs_buffer_size;
}
return ret;
}
/**
* This function is called with the /proc file is written
*
*/
int procfile_write(struct file *file, const char *buffer, unsigned long count,
void *data)
{
/* get buffer size */
procfs_buffer_size = count;
if (procfs_buffer_size > PROCFS_MAX_SIZE ) {
procfs_buffer_size = PROCFS_MAX_SIZE;
}
/* write data to the buffer */
if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size) ) {
return -EFAULT;
}
return procfs_buffer_size;
}
/**
*This function is called when the module is loaded
*
*/
int init_module()
{
/* create the /proc file */
Our_Proc_File = create_proc_entry(PROCFS_NAME, 0644, NULL);
if (Our_Proc_File == NULL) {
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
PROCFS_NAME);
return -ENOMEM;
}
Our_Proc_File->read_proc = procfile_read;
Our_Proc_File->write_proc = procfile_write;
Our_Proc_File->owner = THIS_MODULE;
Our_Proc_File->mode = S_IFREG | S_IRUGO;
Our_Proc_File->uid = 0;
Our_Proc_File->gid = 0;
Our_Proc_File->size = 37;
printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME);
return 0; /* everything is ok */
}
/**
*This function is called when the module is unloaded
*
*/
void cleanup_module()
{
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME);
}