procfs
In this program, you are going to learn
How to Communicate between the Userspace and Kernel Space ?
How to use below APIs ?
procfs
can act as a bridge connecting the user space and the kernel space.Userspace programs can use proc files to read the information exported by the kernel. Every entry in the proc file system provides some information from the kernel.
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.
Add the list of headers to refer the APIs used in the source code.
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/procfs.h>
#include <linux/kobject.h>
#include <linux/err.h>
Add the module macros which contains the information about macros such as author, description and license.
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux_usr");
MODULE_DESCRIPTION("Simple Linux Device Driver - procfs");
Initialize the functions and the variables which are used in the source code.
int32_t value;
char etx_array[20] = "try_proc_array\n";
static int len = 1;
dev_t dev;
static struct class *dev_class;
static struct cdev etx_cdev;
static struct proc_dir_entry *parent;
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off);
static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t *off);
static int open_proc(struct inode *inode, struct file *file);
static int release_proc(struct inode *inode, struct file *file);
static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length, loff_t *offset);
static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t *off);
static const struct file_operations fops = {
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.release = etx_release,
};
static const struct file_operations proc_fops = {
.open = open_proc,
.read = read_proc,
.write = write_proc,
.release = release_proc
};
Add the module init function to execute once when the module is loaded.
static int __init etx_driver_init(void)
{
if ((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) < 0) {
pr_info("Cannot allocate major number\n");
return -1;
}
pr_info("Major = %d Minor = %d\n", MAJOR(dev), MINOR(dev));
/*Creating cdev structure*/
cdev_init(&etx_cdev, &fops);
/*Adding character device to the system*/
if ((cdev_add(&etx_cdev, dev, 1)) < 0) {
pr_info("Cannot add the device to the system\n");
goto r_class;
}
/*Creating struct class*/
dev_class = class_create(THIS_MODULE, "etx_class");
if (IS_ERR(dev_class)) {
pr_info("Cannot create the struct class\n");
goto r_class;
}
/*Creating device*/
if (IS_ERR(device_create(dev_class, NULL, dev, NULL, "etx_device"))) {
pr_info("Cannot create the Device 1\n");
goto r_device;
}
/*Create proc directory. It will create a directory under "/proc" */
parent = proc_mkdir("etx", NULL);
if (parent == NULL) {
pr_info("Error creating proc entry");
goto r_device;
}
/*Creating Proc entry under "/proc/etx/" */
proc_create("etx_proc", 0666, parent, &proc_fops);
pr_info("Device Driver Insert...Done!!!\n");
return 0;
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev, 1);
return -1;
}
proc_mkdir
using this we can create the directory under /proc/*
parent = proc_mkdir("etx", NULL);
proc_create
used to create a proc entry by the nameetx_proc
under /proc. This proc entry should be created in the Driver init function.
proc_create("etx_proc", 0666, parent, &proc_fops);
Add the module exit function which is executed when the module is unloaded from the kernel.
static void __exit etx_driver_exit(void)
{
/* remove complete /proc/etx */
proc_remove(parent);
device_destroy(dev_class, dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
pr_info("Device Driver Remove...Done!!!\n");
}
proc_remove
used to remove the complete parent directory.
proc_remove(parent);
Add procfs APIs
static int open_proc(struct inode *inode, struct file *file)
{
pr_info("proc file opened.....\t");
return 0;
}
static int release_proc(struct inode *inode, struct file *file)
{
pr_info("proc file released.....\n");
return 0;
}
static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
{
pr_info("proc file read.....\n");
if (len)
len = 0;
else {
len = 1;
return 0;
}
if (copy_to_user(buffer, etx_array, 20))
pr_err("Data Send : Err!\n");
return length;
}
// This function will be called when we write the procfs file
static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t *off)
{
pr_info("proc file wrote.....\n");
if (copy_from_user(etx_array, buff, len))
pr_err("Data Write : Err!\n");
return len;
}
// This function will be called when we open the Device file
static int etx_open(struct inode *inode, struct file *file)
{
pr_info("Device File Opened...!!!\n");
return 0;
}
// This function will be called when we close the Device file
static int etx_release(struct inode *inode, struct file *file)
{
pr_info("Device File Closed...!!!\n");
return 0;
}
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
pr_info("Read function\n");
return 0;
}
static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
pr_info("Write Function\n");
return len;
}
Add the mention init and exit function which is executed the module is loaded and unloaded.
module_init(etx_driver_init);
module_exit(etx_driver_exit);
1#include <linux/kernel.h>
2#include <linux/init.h>
3#include <linux/module.h>
4#include <linux/kdev_t.h>
5#include <linux/fs.h>
6#include <linux/cdev.h>
7#include <linux/device.h>
8#include <linux/slab.h> //kmalloc()
9#include <linux/uaccess.h> //copy_to/from_user()
10#include <linux/ioctl.h>
11#include <linux/proc_fs.h>
12#include <linux/err.h>
13
14MODULE_LICENSE("GPL");
15MODULE_AUTHOR("linux_usr");
16MODULE_DESCRIPTION("Simple Linux device driver - Process Filesystem");
17
18int32_t value;
19char etx_array[20] = "try_proc_array\n";
20static int len = 1;
21
22
23dev_t dev;
24static struct class *dev_class;
25static struct cdev etx_cdev;
26static struct proc_dir_entry *parent;
27
28
29static int __init etx_driver_init(void);
30static void __exit etx_driver_exit(void);
31
32
33static int etx_open(struct inode *inode, struct file *file);
34static int etx_release(struct inode *inode, struct file *file);
35static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off);
36static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t *off);
37
38
39static int open_proc(struct inode *inode, struct file *file);
40static int release_proc(struct inode *inode, struct file *file);
41static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length, loff_t *offset);
42static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t *off);
43
44static const struct file_operations fops = {
45 .owner = THIS_MODULE,
46 .read = etx_read,
47 .write = etx_write,
48 .open = etx_open,
49 .release = etx_release,
50};
51
52static const struct file_operations proc_fops = {
53 .open = open_proc,
54 .read = read_proc,
55 .write = write_proc,
56 .release = release_proc
57};
58
59
60static int open_proc(struct inode *inode, struct file *file)
61{
62 pr_info("proc file opened.....\t");
63 return 0;
64}
65
66
67static int release_proc(struct inode *inode, struct file *file)
68{
69 pr_info("proc file released.....\n");
70 return 0;
71}
72
73
74static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
75{
76 pr_info("proc file read.....\n");
77 if (len)
78 len = 0;
79 else {
80 len = 1;
81 return 0;
82 }
83
84 if (copy_to_user(buffer, etx_array, 20))
85 pr_err("Data Send : Err!\n");
86
87 return length;
88}
89
90// This function will be called when we write the procfs file
91
92static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t *off)
93{
94 pr_info("proc file wrote.....\n");
95
96 if (copy_from_user(etx_array, buff, len))
97 pr_err("Data Write : Err!\n");
98
99 return len;
100}
101
102// This function will be called when we open the Device file
103
104static int etx_open(struct inode *inode, struct file *file)
105{
106 pr_info("Device File Opened...!!!\n");
107 return 0;
108}
109
110// This function will be called when we close the Device file
111
112static int etx_release(struct inode *inode, struct file *file)
113{
114 pr_info("Device File Closed...!!!\n");
115 return 0;
116}
117
118
119static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
120{
121 pr_info("Read function\n");
122 return 0;
123}
124
125
126static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
127{
128 pr_info("Write Function\n");
129 return len;
130}
131
132
133static int __init etx_driver_init(void)
134{
135 if ((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) < 0) {
136 pr_info("Cannot allocate major number\n");
137 return -1;
138 }
139 pr_info("Major = %d Minor = %d\n", MAJOR(dev), MINOR(dev));
140
141
142 /*Creating cdev structure*/
143 cdev_init(&etx_cdev, &fops);
144
145 /*Adding character device to the system*/
146 if ((cdev_add(&etx_cdev, dev, 1)) < 0) {
147 pr_info("Cannot add the device to the system\n");
148 goto r_class;
149 }
150
151 /*Creating struct class*/
152 dev_class = class_create(THIS_MODULE, "etx_class");
153 if (IS_ERR(dev_class)) {
154 pr_info("Cannot create the struct class\n");
155 goto r_class;
156 }
157
158 /*Creating device*/
159 if (IS_ERR(device_create(dev_class, NULL, dev, NULL, "etx_device"))) {
160 pr_info("Cannot create the Device 1\n");
161 goto r_device;
162 }
163
164 /*Create proc directory. It will create a directory under "/proc" */
165 parent = proc_mkdir("etx", NULL);
166
167 if (parent == NULL) {
168 pr_info("Error creating proc entry");
169 goto r_device;
170 }
171
172 /*Creating Proc entry under "/proc/etx/" */
173 proc_create("etx_proc", 0666, parent, &proc_fops);
174
175 pr_info("Device Driver Insert...Done!!!\n");
176 return 0;
177
178r_device:
179 class_destroy(dev_class);
180r_class:
181 unregister_chrdev_region(dev, 1);
182 return -1;
183}
184
185
186static void __exit etx_driver_exit(void)
187{
188 /* remove complete /proc/etx */
189 proc_remove(parent);
190
191 device_destroy(dev_class, dev);
192 class_destroy(dev_class);
193 cdev_del(&etx_cdev);
194 unregister_chrdev_region(dev, 1);
195 pr_info("Device Driver Remove...Done!!!\n");
196}
197
198module_init(etx_driver_init);
199module_exit(etx_driver_exit);
1obj-m += driver_proc.o
2all:
3 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
4clean:
5 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Run Make to compile the module.
$make
make -C /lib/modules/5.4.0-150-generic/build M=$HOME/kernel_driver_proc modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-150-generic'
CC [M] $HOME/kernel_driver_proc/driver_proc.o
Building modules, stage 2.
MODPOST 1 modules
CC [M] $HOME/kernel_driver_proc/driver_proc.mod.o
LD [M] $HOME/kernel_driver_proc/driver_proc.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-150-generic'
Run ls to check if driver_proc.ko is generated or not.
$ ls -l
total 36
-rw-rw-r-- 1 test test 154 Feb 26 13:18 Makefile
-rw-rw-r-- 1 test test 47 Feb 26 13:18 modules.order
-rw-rw-r-- 1 test test 0 Feb 26 13:18 Module.symvers
-rw-rw-r-- 1 test test 820 Feb 26 13:18 driver_proc.c
-rw-rw-r-- 1 test test 5880 Feb 26 13:18 driver_proc.ko
-rw-rw-r-- 1 test test 47 Feb 26 13:18 driver_proc.mod
-rw-rw-r-- 1 test test 919 Feb 26 13:18 driver_proc.mod.c
-rw-rw-r-- 1 test test 3448 Feb 26 13:18 driver_proc.mod.o
-rw-rw-r-- 1 test test 3320 Feb 26 13:18 driver_proc.o
Run insmod to load the module.
$ sudo insmod ./driver_proc.ko
Once the module is loaded into kernel do read and write operations to procfs.
$ ls /proc/etx/
etx_proc
$ cat /proc/etx/etx_proc
try_proc_array
$ echo "device driver" > /proc/etx/etx_proc
$ cat /proc/etx/etx_proc
device driver
Run rmmod to unload the module.
$ sudo rmmod driver_proc
Check dmesg and we can clearly see the read and write operations are executed.
[ 9680.443352] Major = 239 Minor = 0
[ 9680.443468] Device Driver Insert...Done!!!
[ 9773.486681] proc file opened.....
[ 9773.486688] proc file read.....
[ 9773.536962] proc file read.....
[ 9773.537018] proc file released.....
[ 9791.229041] proc file opened.....
[ 9791.229055] proc file wrote.....
[ 9791.229059] proc file released.....
[ 9797.128045] proc file opened.....
[ 9797.128055] proc file read.....
[ 9797.142568] proc file read.....
[ 9797.142597] proc file released.....
[ 9823.179016] Device Driver Remove...Done!!!
API |
Learning |
---|---|
proc_mkdir |
To create the directory under /proc/* |
proc_create |
To create a proc entry |
proc_remove |
To remove the complete parent directory |
Previous Chapters
Other File System topics
Next Chapter