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.
No comments:
Post a Comment