procfs ====== .. tab-set:: .. tab-item:: procfs * In this program, you are going to learn .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to Communicate between the Userspace and Kernel Space ? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to use below APIs ? * `proc_mkdir `_ * `proc_create `_ * `proc_remove `_ .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Topics in this section, * :ref:`Explanation of program part by part ` * :ref:`List of headers ` * :ref:`Module macros ` * :ref:`Initialize variables and functions ` * :ref:`Module init function ` * :ref:`Module exit function ` * :ref:`Add procfs APIs ` * :ref:`Mention init and exit function ` * :ref:`See the full program below ` * :ref:`Makefile ` * :ref:`Compile and Load ` * :ref:`Summary of procfs APIs ` .. _p2_procfs_4: .. tab-set:: .. tab-item:: Explanation of program part by part * ``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. .. _p2_procfs_5: .. tab-set:: .. tab-item:: List of headers * Add the list of headers to refer the APIs used in the source code. .. code-block:: c #include #include #include #include #include #include #include #include #include #include #include #include .. _p2_procfs_6: .. tab-set:: .. tab-item:: Modules macros * Add the module macros which contains the information about macros such as author, description and license. .. code-block:: c MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux_usr"); MODULE_DESCRIPTION("Simple Linux Device Driver - procfs"); .. _p2_procfs_7: .. tab-set:: .. tab-item:: Initialize function and variables * Initialize the functions and the variables which are used in the source code. .. code-block:: c 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 }; .. _p2_procfs_8: .. tab-set:: .. tab-item:: Module init functions * Add the module init function to execute once when the module is loaded. .. code-block:: c 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/* .. code-block:: c parent = proc_mkdir("etx", NULL); * ``proc_create`` used to create a proc entry by the name ``etx_proc`` under /proc. This proc entry should be created in the Driver init function. .. code-block:: c proc_create("etx_proc", 0666, parent, &proc_fops); .. _p2_procfs_9: .. tab-set:: .. tab-item:: Module exit function * Add the module exit function which is executed when the module is unloaded from the kernel. .. code-block:: c 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. .. code-block:: c proc_remove(parent); .. _p2_procfs_11: .. tab-set:: .. tab-item:: Add the procfs APIs * Add procfs APIs .. code-block:: c 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; } .. _p2_procfs_10: .. tab-set:: .. tab-item:: Mention the module init and exit function * Add the mention init and exit function which is executed the module is loaded and unloaded. .. code-block:: c module_init(etx_driver_init); module_exit(etx_driver_exit); .. _p2_procfs_0: .. tab-set:: .. tab-item:: See the full program below .. tab-set:: .. tab-item:: driver_proc.c .. literalinclude:: p2_procfs/driver_proc.c :language: c :linenos: .. _p2_procfs_1: .. tab-set:: .. tab-item:: Makefile .. literalinclude:: p2_procfs/Makefile :language: c :linenos: .. _p2_procfs_2: .. tab-set:: .. tab-item:: Compile and Load * Run Make to compile the module. .. code-block:: shell $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. .. code-block:: shell $ 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. .. code-block:: shell $ sudo insmod ./driver_proc.ko * Once the module is loaded into kernel do read and write operations to procfs. .. code-block:: shell $ 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. .. code-block:: shell $ sudo rmmod driver_proc * Check dmesg and we can clearly see the read and write operations are executed. .. code-block:: shell [ 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!!! .. _p2_procfs_3: .. tab-set:: .. tab-item:: Summary =============================== ======================================= 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 =============================== ======================================= .. card:: See Also * Previous Chapters * :doc:`../chapter1_basics/chapter1_basics` * :doc:`../chapter2_kthreads/chapter2_kthreads` * :doc:`../chapter3_tasklets/chapter3_tasklets` * :doc:`../chapter4_workqueue/chapter4_workqueue` * :doc:`../chapter5_timer/chapter5_timer` * :doc:`../chapter8_linkedList/chapter8_linkedList` * Other File System topics * :doc:`p1_sysfs` * :doc:`p3_debugfs` * Next Chapter * :doc:`../chapter10_netlink/chapter10_netlink`