1. Introduction to IO multiplexing in Linux
IO multiplexing is a synchronous IO model. IO multiplexing allows one process to monitor multiple file descriptors. Once a file descriptor is ready, the application is notified to perform corresponding read and write operations. When no file descriptors are ready, the application blocks, freeing up CPU resources.
At the application layer, Linux provides three models for implementing IO multiplexing, namely select, poll and epoll. Both the poll function and the select function can monitor multiple file descriptors and obtain prepared file descriptors through polling. But the epoll function turns active polling into passive notification, passively receiving notifications when events occur. In a single thread, the select function can monitor a maximum of 1024 file descriptors, and there is no difference between the poll function and the select function, except that the poll function does not have a maximum file descriptor limit.
1.1. Poll function of application layer
The poll function in Linux applications is as follows:
int poll(struct pollfd *fds, nfds_t nfds,int timeout);
The first parameter fds: The set of file descriptors to be monitored and the events to be monitored are an array. The array elements are all of the structure pollfd type. The pollfd structure is as follows:
struct pollfd {<!-- --> int fd; //monitored file descriptor short events; //Events to wait for short revents; //actual events };
In the pollfd structure, the first member fd is the monitored file descriptor. The second member events is the event to be monitored. The types of events that can be monitored are as follows:
POLLIN has data to read POLLPRI has urgent data to read POLLOUT can write data POLLERR An error occurred in the file descriptor specified POLLHUP The specified file descriptor is hung POLLNVAL Invalid request POLLRDNORM is equivalent to POLLIN
The third member is the return event, and the specific return event is set by the Linux kernel.
Second parameter nfds: The number of file descriptors to be monitored by the poll function
The third parameter timeout: Specify the waiting time, the unit is ms. Regardless of whether the I/O is ready or not, POLL will return when the time is up. If timepoll is greater than 0, wait for the specified time. If timeout is equal to 0, return immediately. If timeout is equal to -1, it will be returned after the event occurs.
1.2. Poll function of driver layer
When an application uses the select or poll function to perform non-blocking access to the driver, the poll function of the file_operations operation set in the driver will be executed. Therefore, the poll function in the driver needs to be improved. The poll function prototype in the driver is as follows:
unsigned int (*poll)(struct file *filp,struct poll_table_struct *wait);
Function parameters:
filp: the file descriptor to be opened
wait: structure poll_table_struct type pointer, this parameter is passed by the application. Generally this parameter is passed to the poll_wait function.
Return value:
Return resource status to the application. The resource status that can be returned is as follows:
POLLIN has data that can be read
POLLPRI has urgent data that needs to be read.
POLLOUT can write data
POLLERR An error occurred in the specified file descriptor
POLLHUP The specified file descriptor is hung
POLLNVAL invalid request
POLLRDNORM is equivalent to POLLIN, normal data can be read.
Function functions:
This function needs to perform the following two tasks. First, call poll_wait() on the waiting queue that may cause the device file status to change, and add the corresponding waiting queue head to the poll_table. Then return a mask indicating whether non-blocking read and write access to the device is possible.
Call the poll_wait function in the driver’s poll function, pay attention! The poll_wait function does not cause blocking. The poll_wait function prototype is as follows:
void poll_wait(struct file *filp,wait_queue_head_t *queue,poll_table *wait);
The parameter queue is the waiting queue head to be added to poll_table, and the parameter wait is poll_table, which is the wait parameter of the poll function in file_operations.
2. Code examples
2.1, Application layer program
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <poll.h> int main(int argc, char *argv[]) {<!-- --> int fd;//File descriptor to be monitored char buf1[32] = {<!-- -->0}; char buf2[32] = {<!-- -->0}; struct pollfd fds[1]; int ret; fd = open("/dev/test", O_RDWR); //Open the /dev/test device, blocking access if (fd < 0) {<!-- --> perror("open error \ "); return fd; } fds[0] .fd =fd; fds[0].events = POLLIN; printf("read before \ "); while (1) {<!-- --> ret = poll(fds,1,3000); if(!ret) {<!-- --> printf("time out !!\ "); }else if(fds[0].revents == POLLIN) {<!-- --> read(fd,buf1,sizeof(buf1)); //Read data from the /dev/test file printf("buf is %s \ ",buf1); sleep(1); } } printf("read after\ "); close(fd); //Close the file return 0; }
2.2. Driver layer program
#include <linux/init.h> #include <linux/module.h> #include <linux/kdev_t.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/wait.h> #include <linux/poll.h> struct device_test{<!-- --> dev_t dev_num; //Device number int major; //major device number int minor; //Minor device number struct cdev cdev_test; // cdev struct class *class; //class struct device *device; //device char kbuf[32]; int flag; //flag bit }; struct device_test dev1; DECLARE_WAIT_QUEUE_HEAD(read_wq); //Define and initialize the waiting queue head /*Open device function*/ static int cdev_test_open(struct inode *inode, struct file *file) {<!-- --> file->private_data= & amp;dev1;//Set private data return 0; } /*Write data function to device*/ static ssize_t cdev_test_write(struct file *file, const char __user *buf, size_t size, loff_t *off) {<!-- --> struct device_test *test_dev=(struct device_test *)file->private_data; if (copy_from_user(test_dev->kbuf, buf, size) != 0) // copy_from_user: User space transfers data to kernel space {<!-- --> printk("copy_from_user error\r\ "); return -1; } test_dev->flag=1; wake_up_interruptible( & amp;read_wq); return 0; } /**Read data from device*/ static ssize_t cdev_test_read(struct file *file, char __user *buf, size_t size, loff_t *off) {<!-- --> struct device_test *test_dev=(struct device_test *)file->private_data; if(file->f_flags & amp; O_NONBLOCK ){<!-- --> if (test_dev->flag !=1) return -EAGAIN; } wait_event_interruptible(read_wq,test_dev->flag); if (copy_to_user(buf, test_dev->kbuf, strlen( test_dev->kbuf)) != 0) // copy_to_user: transfer data from kernel space to user space {<!-- --> printk("copy_to_user error\r\ "); return -1; } return 0; } static int cdev_test_release(struct inode *inode, struct file *file) {<!-- --> return 0; } static __poll_t cdev_test_poll(struct file *file, struct poll_table_struct *p){<!-- --> struct device_test *test_dev=(struct device_test *)file->private_data; //Set private data __poll_t mask=0; poll_wait(file, & amp;read_wq,p); //Application blocking if (test_dev->flag == 1) {<!-- --> mask |= POLLIN; } return mask; } /*Device operation function*/ struct file_operations cdev_test_fops = {<!-- --> .owner = THIS_MODULE, //Point the owner field to this module to avoid uninstalling the module while the module's operations are being used. .open = cdev_test_open, //Point the open field to the chrdev_open(...) function .read = cdev_test_read, //point the open field to the chrdev_read(...) function .write = cdev_test_write, //Point the open field to the chrdev_write(...) function .release = cdev_test_release, //Point the open field to the chrdev_release(...) function .poll = cdev_test_poll, //Point the poll field to the chrdev_poll(...) function }; static int __init chr_fops_init(void) //Driver entry function {<!-- --> /*Register character device driver*/ int ret; /*1 Create device number*/ ret = alloc_chrdev_region( & amp;dev1.dev_num, 0, 1, "alloc_name"); //Dynamic allocation of device numbers if (ret < 0) {<!-- --> goto err_chrdev; } printk("alloc_chrdev_region is ok\ "); dev1.major = MAJOR(dev1.dev_num); //Get the major device number dev1.minor = MINOR(dev1.dev_num); //Get the minor device number printk("major is %d \r\ ", dev1.major); //Print the major device number printk("minor is %d \r\ ", dev1.minor); //Print the minor device number /*2 Initialize cdev*/ dev1.cdev_test.owner = THIS_MODULE; cdev_init( & amp;dev1.cdev_test, & amp;cdev_test_fops); /*3 Add a cdev to complete the character device registration to the kernel*/ ret = cdev_add( & amp;dev1.cdev_test, dev1.dev_num, 1); if(ret<0) {<!-- --> goto err_chr_add; } /*4 Create class*/ dev1. class = class_create(THIS_MODULE, "test"); if(IS_ERR(dev1.class)) {<!-- --> ret=PTR_ERR(dev1.class); goto err_class_create; } /*5 Create device*/ dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test"); if(IS_ERR(dev1.device)) {<!-- --> ret=PTR_ERR(dev1.device); goto err_device_create; } return 0; err_device_create: class_destroy(dev1.class); //Delete class err_class_create: cdev_del( & amp;dev1.cdev_test); //Delete cdev err_chr_add: unregister_chrdev_region(dev1.dev_num, 1); //Unregister the device number err_chrdev: return ret; } static void __exit chr_fops_exit(void) //Driver exit function {<!-- --> /*Log out character device*/ unregister_chrdev_region(dev1.dev_num, 1); //Unregister the device number cdev_del( & amp;dev1.cdev_test); //Delete cdev device_destroy(dev1.class, dev1.dev_num); //Delete device class_destroy(dev1.class); //Delete class } module_init(chr_fops_init); module_exit(chr_fops_exit);
2.3, Linux IO multiplexing API key points
Application layer
struct pollfd fds[1]; fds[0] .fd =fd; fds[0].events = POLLIN; ret = poll(fds,1,3000);
Driver layer
static __poll_t cdev_test_poll(struct file *file, struct poll_table_struct *p) {<!-- --> struct device_test *test_dev=(struct device_test *)file->private_data; //Set private data __poll_t mask=0; poll_wait(file, & amp;read_wq,p); //Application blocking if (test_dev->flag == 1) {<!-- --> mask |= POLLIN; } return mask; }