I'm trying to understand some example code my lecturer gave me.
It is a method of transferring data from user space into kernel space via a /proc file. This is the only code he gave me and I feel like I'm missing the user space program and I don't think he's explained what's going on very well.
He's trying to demonstrate locking files via semaphores, and also transferring the data I believe. The things I'm struggling to understand are:
- what is the "reference count" (He mentions it in the comments for procfs_open and procfs_close)
- Why would he use a proc file? It appears to do nothing.
- What do module_put and try_module_get do? I can't find any remotely good explanations online.
- How would I trigger the kernelWrite function from userspace? So that I know how I can actually transfer the data, not just receive it.
Here is the code:
#define BUFFERLENGTH 256
#define INCREASE_COUNTER 'I'
#define SHOW_COUNTER 'S'
#define PROC_ENTRY_FILENAME "kernelWrite"
DECLARE_RWSEM(counter_sem); /* semaphore to protect counter access */
static struct proc_dir_entry *Our_Proc_File;
int counter1 = 0;
int counter2 = 0;
/* displays the kernel table - for simplicity via printk */
void show_table (void) {
int tmp1;
int tmp2;
down_read (&counter_sem); /* lock for reading */
tmp1 = counter1;
tmp2 = counter2;
up_read (&counter_sem); /* unlock reading */
printk (KERN_INFO "kernelWrite:The counters are %d, %d\n", tmp1, tmp2);
}
void increase_counter (void) {
down_write (&counter_sem); /* lock for writing */
counter1++;
counter2++;
up_write (&counter_sem);
}
/* This function reads in data from the user into the kernel */
ssize_t kernelWrite (struct file *file, const char __user *buffer, size_t count, loff_t *offset) {
char command;
printk (KERN_INFO "kernelWrite entered\n");
if (get_user (command, buffer)) {
return -EFAULT;
}
switch (command) {
case INCREASE_COUNTER:
increase_counter ();
break;
case SHOW_COUNTER:
show_table ();
break;
default:
printk (KERN_INFO "kernelWrite: Illegal command \n");
}
return count;
}
/*
* The file is opened - we don't really care about
* that, but it does mean we need to increment the
* module's reference count.
*/
int procfs_open(struct inode *inode, struct file *file)
{
printk (KERN_INFO "kernelWrite opened\n");
try_module_get(THIS_MODULE);
return 0;
}
/*
* The file is closed - again, interesting only because
* of the reference count.
*/
int procfs_close(struct inode *inode, struct file *file)
{
printk (KERN_INFO "kernelWrite closed\n");
module_put(THIS_MODULE);
return 0; /* success */
}
const struct file_operations File_Ops_4_Our_Proc_File = {
.owner = THIS_MODULE,
.write = kernelWrite,
.open = procfs_open,
.release = procfs_close,
};
int init_module(void)
{
/* create the /proc file */
Our_Proc_File = proc_create_data (PROC_ENTRY_FILENAME, 0644, NULL, &File_Ops_4_Our_Proc_File, NULL);
/* check if the /proc file was created successfuly */
if (Our_Proc_File == NULL){
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
PROC_ENTRY_FILENAME);
return -ENOMEM;
}
printk(KERN_INFO "/proc/%s created\n", PROC_ENTRY_FILENAME);
return 0; /* success */
}
void cleanup_module(void)
{
remove_proc_entry(PROC_ENTRY_FILENAME, NULL);
printk(KERN_INFO "/proc/%s removed\n", PROC_ENTRY_FILENAME);
printk(KERN_INFO "kernelWrite:Proc module unloaded.\n");
}