mirror of https://github.com/tLDP/LDP
256 lines
8.8 KiB
Plaintext
256 lines
8.8 KiB
Plaintext
<sect1><title>The /proc File System</title>
|
|
|
|
<indexterm><primary><filename role=directory>/proc</filename> filesystem</primary></indexterm>
|
|
<indexterm><primary>filesystem</primary><secondary><filename role=directory>/proc</filename></secondary></indexterm>
|
|
|
|
<para>In Linux there is an additional mechanism for the kernel and kernel modules to send information to processes --- the
|
|
<filename role="directory">/proc</filename> file system. Originally designed to allow easy access to information about
|
|
processes (hence the name), it is now used by every bit of the kernel which has something interesting to report, such as
|
|
<filename>/proc/modules</filename> which has the list of modules and <filename>/proc/meminfo</filename> which has memory usage
|
|
statistics.</para>
|
|
|
|
<indexterm><primary><filename>/proc/modules</filename></primary></indexterm>
|
|
<indexterm><primary><filename>/proc/meminfo</filename></primary></indexterm>
|
|
|
|
<para>The method to use the proc file system is very similar to the one used with device drivers --- you create a structure
|
|
with all the information needed for the <filename role="directory">/proc</filename> file, including pointers to any handler
|
|
functions (in our case there is only one, the one called when somebody attempts to read from the <filename
|
|
role="directory">/proc</filename> file). Then, <function>init_module</function> registers the structure with the kernel and
|
|
<function>cleanup_module</function> unregisters it.</para>
|
|
|
|
<para>The reason we use <function>proc_register_dynamic</function><footnote><para>In version 2.0, in version 2.2 this is done
|
|
for us automatically if we set the inode to zero.</para></footnote> is because we don't want to determine the inode number
|
|
used for our file in advance, but to allow the kernel to determine it to prevent clashes. Normal file systems are located on a
|
|
disk, rather than just in memory (which is where <filename role="directory">/proc</filename> is), and in that case the inode
|
|
number is a pointer to a disk location where the file's index-node (inode for short) is located. The inode contains
|
|
information about the file, for example the file's permissions, together with a pointer to the disk location or locations
|
|
where the file's data can be found.</para>
|
|
|
|
<indexterm><primary><function>proc_register_dynamic</function></primary></indexterm>
|
|
<indexterm><primary><function>proc_register</function></primary></indexterm>
|
|
<indexterm><primary>inode</primary></indexterm>
|
|
|
|
<para>Because we don't get called when the file is opened or closed, there's no where for us to put
|
|
<varname>MOD_INC_USE_COUNT</varname> and <varname>MOD_DEC_USE_COUNT</varname> in this module, and if the file is opened and
|
|
then the module is removed, there's no way to avoid the consequences. In the next chapter we'll see a harder to implement, but
|
|
more flexible, way of dealing with <filename role="directory">/proc</filename> files which will allow us to protect against
|
|
this problem as well.</para>
|
|
|
|
|
|
<example><title>procfs.c</title>
|
|
<programlisting><![CDATA[
|
|
/* procfs.c - create a "file" in /proc
|
|
*
|
|
* Copyright (C) 2001 by Peter Jay Salzman
|
|
*
|
|
* 08/02/2006 - Updated by Rodrigo Rubira Branco <rodrigo@kernelhacking.com>
|
|
*/
|
|
|
|
/* Kernel Programming */
|
|
#define MODULE
|
|
#define LINUX
|
|
#define __KERNEL__
|
|
|
|
#include <linux/kernel.h> /* We're doing kernel work */
|
|
#include <linux/module.h> /* Specifically, a module */
|
|
|
|
/* Deal with CONFIG_MODVERSIONS */
|
|
#if CONFIG_MODVERSIONS==1
|
|
#define MODVERSIONS
|
|
#include <linux/modversions.h>
|
|
#endif
|
|
|
|
|
|
/* Necessary because we use the proc fs */
|
|
#include <linux/proc_fs.h>
|
|
|
|
|
|
|
|
/* In 2.2.3 /usr/include/linux/version.h includes a
|
|
* macro for this, but 2.0.35 doesn't - so I add it
|
|
* here if necessary. */
|
|
#ifndef KERNEL_VERSION
|
|
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
|
|
#endif
|
|
|
|
|
|
|
|
/* Put data into the proc fs file.
|
|
|
|
Arguments
|
|
=========
|
|
1. The buffer where the data is to be inserted, if
|
|
you decide to use it.
|
|
2. A pointer to a pointer to characters. This is
|
|
useful if you don't want to use the buffer
|
|
allocated by the kernel.
|
|
3. The current position in the file.
|
|
4. The size of the buffer in the first argument.
|
|
5. Zero (for future use?).
|
|
|
|
|
|
Usage and Return Value
|
|
======================
|
|
If you use your own buffer, like I do, put its
|
|
location in the second argument and return the
|
|
number of bytes used in the buffer.
|
|
|
|
A return value of zero means you have no further
|
|
information at this time (end of file). A negative
|
|
return value is an error condition.
|
|
|
|
|
|
For More Information
|
|
====================
|
|
The way I discovered what to do with this function
|
|
wasn't by reading documentation, but by reading the
|
|
code which used it. I just looked to see what uses
|
|
the get_info field of proc_dir_entry struct (I used a
|
|
combination of find and grep, if you're interested),
|
|
and I saw that it is used in <kernel source
|
|
directory>/fs/proc/array.c.
|
|
|
|
If something is unknown about the kernel, this is
|
|
usually the way to go. In Linux we have the great
|
|
advantage of having the kernel source code for
|
|
free - use it.
|
|
*/
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
|
|
int procfile_read(char *buffer,
|
|
char **buffer_location, off_t offset,
|
|
int buffer_length, int *eof, void *data)
|
|
#else
|
|
int procfile_read(char *buffer,
|
|
char **buffer_location,
|
|
off_t offset,
|
|
int buffer_length,
|
|
int zero)
|
|
#endif
|
|
{
|
|
int len; /* The number of bytes actually used */
|
|
|
|
/* This is static so it will still be in memory
|
|
* when we leave this function */
|
|
static char my_buffer[80];
|
|
|
|
static int count = 1;
|
|
|
|
/* We give all of our information in one go, so if the
|
|
* user asks us if we have more information the
|
|
* answer should always be no.
|
|
*
|
|
* This is important because the standard read
|
|
* function from the library would continue to issue
|
|
* the read system call until the kernel replies
|
|
* that it has no more information, or until its
|
|
* buffer is filled.
|
|
*/
|
|
if (offset > 0)
|
|
return 0;
|
|
|
|
/* Fill the buffer and get its length */
|
|
len = sprintf(my_buffer,
|
|
"For the %d%s time, go away!\n", count,
|
|
(count % 100 > 10 && count % 100 < 14) ? "th" :
|
|
(count % 10 == 1) ? "st" :
|
|
(count % 10 == 2) ? "nd" :
|
|
(count % 10 == 3) ? "rd" : "th" );
|
|
count++;
|
|
|
|
/* Tell the function which called us where the
|
|
* buffer is */
|
|
*buffer_location = my_buffer;
|
|
|
|
/* Return the length */
|
|
return len;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
|
|
struct proc_dir_entry *Our_Proc_File;
|
|
#else
|
|
struct proc_dir_entry Our_Proc_File =
|
|
{
|
|
0, /* Inode number - ignore, it will be filled by
|
|
* proc_register[_dynamic] */
|
|
4, /* Length of the file name */
|
|
"test", /* The file name */
|
|
S_IFREG | S_IRUGO, /* File mode - this is a regular
|
|
* file which can be read by its
|
|
* owner, its group, and everybody
|
|
* else */
|
|
1, /* Number of links (directories where the
|
|
* file is referenced) */
|
|
0, 0, /* The uid and gid for the file - we give it
|
|
* to root */
|
|
80, /* The size of the file reported by ls. */
|
|
NULL, /* functions which can be done on the inode
|
|
* (linking, removing, etc.) - we don't
|
|
* support any. */
|
|
(struct file_operations *) procfile_read, /* The read function for this file,
|
|
* the function called when somebody
|
|
* tries to read something from it. */
|
|
NULL /* We could have here a function to fill the
|
|
* file's inode, to enable us to play with
|
|
* permissions, ownership, etc. */
|
|
};
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* Initialize the module - register the proc file */
|
|
int init_module()
|
|
{
|
|
/* Success if proc_register[_dynamic] is a success,
|
|
* failure otherwise. */
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
|
|
/* In version 2.2, proc_register assign a dynamic
|
|
* inode number automatically if it is zero in the
|
|
* structure , so there's no more need for
|
|
* proc_register_dynamic
|
|
*/
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
|
|
Our_Proc_File=create_proc_read_entry("test", 0444, NULL, procfile_read, NULL);
|
|
|
|
if ( Our_Proc_File == NULL )
|
|
return -ENOMEM;
|
|
else
|
|
return 0;
|
|
#else
|
|
return proc_register(&proc_root, &Our_Proc_File);
|
|
#endif
|
|
#else
|
|
return proc_register_dynamic(&proc_root, &Our_Proc_File);
|
|
#endif
|
|
|
|
/* proc_root is the root directory for the proc
|
|
* fs (/proc). This is where we want our file to be
|
|
* located.
|
|
*/
|
|
}
|
|
|
|
|
|
/* Cleanup - unregister our file from /proc */
|
|
void cleanup_module()
|
|
{
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
|
|
remove_proc_entry("test", NULL);
|
|
#else
|
|
proc_unregister(&proc_root, Our_Proc_File.low_ino);
|
|
#endif
|
|
}
|
|
|
|
MODULE_LICENSE("GPL");
|
|
]]></programlisting>
|
|
</example>
|
|
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
<!--
|
|
vim:textwidth=128
|
|
-->
|