"Mr. Wang, today we will talk about AIO and device drivers. This is the last section of device-driven notifications and asynchronous IO. Next time we will begin to talk about more advanced things, such as interruptions, clock, etc"
In the Linux kernel, each IO request corresponds to a kiocb struct, and its ki_filp Member points to the corresponding file pointer. Through is_sync_kiocb, you can determine that a Kiocb is a synchronous IO request. If it is not true, it indicates an asynchronous IO request.
Block devices and network devices are asynchronous. Only the character device driver must specify that AIO is supported. It must be noted that AIO is not required for most character devices. Only a few are needed.
In the character device driver, file_operations contains three AIO-related functions. As follows:
ssize_t (*aio_read) (struct kiocb *iocb, char *buffer, size_t count ,loff_t offset); ssize_t (*aio_write) (struct kiocb *iocb, const char *buffer, size_t count ,loff_t offset); int (*aio_fsync) (struct kiocb *iocb, int datasync);
Aio_read () and aio_write () are different from the offset parameters in read () and write () in file_operation. It directly transmits values, while the latter transmits pointers. These two functions do not necessarily complete read/write operations. They only initiate and initialize read/write operations.
Let's take a look at the actual code:
// Asynchronous read static ssize_t xxx_aio_read (struct kiocb * iocb, char * buffer, size_t count, loff_t offset) {return xxx_defer_op (0, iocb, buf, count, pos );} // asynchronously write static ssize_t xxx_aio_write (struct kiocb * iocb, const char * buffer, size_t count, loff_t offset) {return xxx_defer_op (1, iocb, (char *) buf, count, pos);} // initialize asynchronous IOstatic int xxx_defer_op (int write, struct kiocb * iocb, char * buf, size_t count, loff_t pos) {struct async_work * async_wk; int result; // if (write) {result = xxx_write (iocb-> ki_filp, buf, count, & pos );} else {result = xxx_read (iocb-> ki_filp, buf, count, & pos) ;}// if synchronizing IOCB, return the status if (is_sync_kiocb (iocb) return resutl; // otherwise, execute async_wk = kmalloc (sizeof (* async_wk), GFP_KERNEL); if (async_wk = NULL) return result; async_wk-> aiocb = iocb; async _ wk-> result = result; INIT_WORK (& async_wk-> work, tasks, async_wk); schedule_delayed_work (& async_wk-> work, HZ/100); return-EIOCBOUEUED; // control the permission returned to the user space} // After the delay, execute static void forward (void * p) {struct async_work * async_wk = (struct async_work *) p; aio_complete (async_wk_iocb, async_wk-> result, 0); kfree (async_wk );}
In the above Code, there is an async_work struct defined as follows:
Struct async_work {struct kiocb * iocb; // kiocb struct pointer intresult; // execution result struct work_struct work; // work struct };
In the above Code, the most important thing is to use the aync_work struct to delay the operation. schedule_delayed_work can be used to schedule the operation. The call of aio_complete is used to notify the kernel driver that the operation has been completed.
Finally, the contents of this chapter are all finished. There are five sections in a row, Mr. Wang. You should organize them and start new content next time.