The proc files implement the simplest method of communication and data exchange between the Linux kernel or its drivers, and the user space applications or human users. The kernel provides many proc files for setting various settings, and getting plenty of information (see the article about process procs). Each driver or loadable kernel module can add more proc files in the /proc directory, in order to set its specific parameters or to get its specific information. The proc files are not really files, but referred as “pseudo-files”, where all the /proc directory is a “pseudo-filesystem”. The reason for this name convention is the fact that unlike other filesystems, these files do not really exist. Instead, the kernel (or each driver which needs a proc file) registers a file, defines its permissions and implements a read and/or write functions. The read function will be invoked whenever a user space application wishes to read information, so the information that is read is actually generated by the kernel upon request. The same applies for the write function.
Now that we know what a proc file is, let’s see how we can create and use it. First, in order to gain access to the proc functions and types, you will need to include the linux/proc_fs.h header file.
Creating a new sub directory in /proc
If you wish to place your proc files in a designated sub directory, it is possible to create one. This is usually done when your driver has more than one file, and therefore, it is easier to access them and a cleaner implementation. The following example function creates a newdirerctory under /proc directory in a given name, and returns a pointer. Note the S_IFDIR flags which marks this entry as a directory (defined at linux/stat.h file).
Registering a new proc file
The next stage, is to actually create a file entry. As I mentioned, the proc files are not real files, but yet we need to declare them by registering them with the proc filesystem. In this example, the file “rt-embedded” will be created under /proc. It is possible to create a file deeper in the tree simply by specifing the desired location (for example; net/rt-embedded will create the file under /proc/net). The file is created with 0644 permissions, means that the owner user can read and write, and the rest can read only (don’t forget the linux/stat.h file). The last parameter is optional and denotes the entry parent. Once the entry is created, we need to specify the read and write functions, according to the permissions we declared. We’ll see then next.
Implementing the read function
Once a file is registered, its contents is generated upon request by a “read” function, which fetches all the required information from the driver’s internal data structures and formats the data in a human readable form. The read functions must be implemented only if there is a read access to the file, or in other words, when this file provides information. The read function’s prototype is a bit complicated. Let’s see the example first:
There are many bad implementations of the read function which ignore all the parameters. Here’s the description of each parameter:
- buf: A pointer to a buffer that the kernel allocated for the proc file data.
- start: A double pointer to indicate the start data.
- offset: The offset of the data, from the start, that is currently requested.
- count: The amount of bytes the reader wants.
- eof: End-Of-File flag.
When calling the read function, the kernel usually allocates a page of memory (usually 4KB) for the data. In case the reading application allocates a small buffer, it might require that this function will be called multiple times in order to complete reading the entire data. Therefore, the kernel also provides the count, start and offset variables. The read function must honor the offset variable and set the start pointer to the appropriate offset, as requested. Only when the complete data has been transfered, then the read function turns the EOF flag on. The kernel will not call the read function again for this purpose, since the function marked that it has no more data to send.
Implementing the write function
The write function is used only in case the kernel or driver allows data to be set by the caller. For example, if you have 8 LEDs on your target, your LED driver can accept writing a number in a designated proc file which will light up a specific LED. Implementing this function is not mandatory in case the proc file is in read-only mode. When working with the write function, the data needs to be copied from the user’s address space to the kernel’s user space.
The file and data parameters are not required for general use. The kernel sends the user space buffer in the buffer variable and its size in the count variable. We need to verify that the data that was sent is in the right size, and in case we transfer structures of data, we can add a magic number member which will hold a constant value, just to make sure that the expected data type was actually sent. Then, we must copy the data to the kernel space because it is impossible to access it as is with the user space pointer. After we’ve copied the data, we can start parsing and working on the data. The return value tells the kernel if we failed (by returning a negative value) or processed all the data (by returning the count number).
Removal of a proc file
It is possible to remove a proc file from the proc filesystem. This function is mostly used by Loadable Kernel modules which are in the process for shutting down during removal (using rmmod). In order to remove a proc file, just use the following function with the file name:
Usage example from a user space application
Now let’s see how we can access these proc files. The simplest way to access them is manually, in the Linux shell. In order to read a contents of a proc file, we simply use the “cat” command and the file name, for example: cat /proc/my_proc_file. In order to write data to a proc file, we simply use the “echo” command with redirection of the output to the destination file, for example: echo “write data” > /proc/my_proc_file.
We can also access these files from our user space applications. The process is actually quite similar and very simple. Each program who wants to read (or write) data, is required to open the file in the standard way (either with stream access; fopen, or descriptor access; open). Then, it reads the contents of the file to a buffer and then it closes it. The data, is always in human readable text mode. Therefore, the program needs to convert it back to binary representation in case it needs to work or analyze the data.
Other proc helper functions
The kernel provides some more proc APIs that may short-cut or encapsulate some of the actions or parameters when using the standard create_proc_entry function. Some examples of such APIs:
Refer to the linux/proc_fs.h file for the complete list of available API.
|Check out the ads, there could be something that may interest you there. The ads revenue helps me to pay for the domain and storage.|